{"id":39591562,"url":"https://github.com/fatal10110/lua-redis-wasm","last_synced_at":"2026-01-21T11:00:49.899Z","repository":{"id":333195948,"uuid":"1134749100","full_name":"fatal10110/lua-redis-wasm","owner":"fatal10110","description":"WebAssembly-based Redis Lua 5.1 script engine for Node.js. Execute Redis-compatible Lua scripts without a live Redis server. Perfect for testing, sandboxing, and local development.","archived":false,"fork":false,"pushed_at":"2026-01-17T22:05:20.000Z","size":139,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-18T16:16:57.845Z","etag":null,"topics":["embedded-lua","lua","nodejs","redis","redis-scripts","typescript","wasm"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fatal10110.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-15T06:33:27.000Z","updated_at":"2026-01-17T22:04:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fatal10110/lua-redis-wasm","commit_stats":null,"previous_names":["fatal10110/lua-redis-wasm"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/fatal10110/lua-redis-wasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatal10110%2Flua-redis-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatal10110%2Flua-redis-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatal10110%2Flua-redis-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatal10110%2Flua-redis-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fatal10110","download_url":"https://codeload.github.com/fatal10110/lua-redis-wasm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatal10110%2Flua-redis-wasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28565001,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T08:53:44.001Z","status":"ssl_error","status_checked_at":"2026-01-19T08:52:40.245Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded-lua","lua","nodejs","redis","redis-scripts","typescript","wasm"],"created_at":"2026-01-18T07:44:53.098Z","updated_at":"2026-01-19T09:00:43.395Z","avatar_url":"https://github.com/fatal10110.png","language":"TypeScript","readme":"# lua-redis-wasm\n\n[![npm version](https://img.shields.io/npm/v/lua-redis-wasm.svg)](https://www.npmjs.com/package/lua-redis-wasm)\n[![CI](https://github.com/fatal10110/lua-redis-wasm/workflows/ci/badge.svg)](https://github.com/fatal10110/lua-redis-wasm/actions)\n[![Node.js Version](https://img.shields.io/node/v/lua-redis-wasm.svg)](https://nodejs.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nA 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.\n\n## Features\n\n- **Redis-compatible Lua 5.1** - Uses the exact Lua version embedded in Redis\n- **Binary-safe** - Full support for null bytes in scripts, arguments, and return values\n- **Host integration** - Implement `redis.call`, `redis.pcall`, and `redis.log` in JavaScript\n- **Resource limits** - Fuel-based instruction limiting, reply size caps, and memory coordination\n- **Redis standard libraries** - Includes `cjson`, `cmsgpack`, `struct`, and `bit` modules\n- **TypeScript support** - Full type definitions included\n\n## Installation\n\n```bash\nnpm install lua-redis-wasm\n```\n\n**Requirements:** Node.js \u003e= 22\n\n## Quick Start\n\n```typescript\nimport { LuaWasmEngine } from \"redis-lua-wasm\";\n\nconst engine = await LuaWasmEngine.create({\n  host: {\n    redisCall(args) {\n      const cmd = args[0].toString();\n      if (cmd === \"PING\") return { ok: Buffer.from(\"PONG\") };\n      if (cmd === \"GET\") return Buffer.from(\"value\");\n      return { err: Buffer.from(\"ERR unknown command\") };\n    },\n    redisPcall(args) {\n      return this.redisCall(args);\n    },\n    log(level, message) {\n      console.log(`[${level}] ${message.toString()}`);\n    },\n  },\n});\n\n// Simple evaluation\nconst result = engine.eval(\"return 1 + 1\"); // Returns: 2\n\n// With KEYS and ARGV\nconst data = engine.evalWithArgs(\n  \"return {KEYS[1], ARGV[1]}\",\n  [Buffer.from(\"user:1\")],\n  [Buffer.from(\"hello\")],\n);\n```\n\n## API\n\n### LuaWasmEngine.create(options)\n\nCreates a new engine instance with host integration.\n\n```typescript\nconst engine = await LuaWasmEngine.create({\n  host: RedisHost,       // Required: host callbacks\n  limits?: EngineLimits, // Optional: resource limits\n  wasmPath?: string,     // Optional: custom WASM file path\n  wasmBytes?: Uint8Array // Optional: pre-loaded WASM binary\n});\n```\n\n### LuaWasmEngine.createStandalone(options)\n\nCreates an engine without host integration. `redis.call` and `redis.pcall` return errors.\n\n```typescript\nconst engine = await LuaWasmEngine.createStandalone({});\nengine.eval(\"return math.sqrt(16)\"); // Works\nengine.eval(\"return redis.call('PING')\"); // Returns error\n```\n\n### engine.eval(script)\n\nEvaluates a Lua script and returns the result.\n\n```typescript\nengine.eval(\"return 'hello'\"); // Returns: Buffer.from(\"hello\")\nengine.eval(\"return {1, 2, 3}\"); // Returns: [1, 2, 3]\n```\n\n### engine.evalWithArgs(script, keys, args)\n\nEvaluates a script with binary-safe `KEYS` and `ARGV` arrays.\n\n```typescript\nengine.evalWithArgs(\n  \"return {KEYS[1], ARGV[1], ARGV[2]}\",\n  [Buffer.from(\"key:1\")],\n  [Buffer.from(\"arg1\"), Buffer.from(\"arg2\\x00with-null\")],\n);\n```\n\n### LuaWasmEngine (Convenience)\n\nAlternative API that combines loading and creation.\n\n#### LuaWasmEngine.create(options)\n\n```typescript\nconst engine = await LuaWasmEngine.create({ host: myHost });\n```\n\n#### LuaWasmEngine.createStandalone(options)\n\n```typescript\nconst engine = await LuaWasmEngine.createStandalone();\n```\n\n## Host Interface\n\nThe host must implement three callbacks:\n\n```typescript\ntype RedisHost = {\n  redisCall: (args: Buffer[]) =\u003e ReplyValue; // For redis.call()\n  redisPcall: (args: Buffer[]) =\u003e ReplyValue; // For redis.pcall()\n  log: (level: number, message: Buffer) =\u003e void; // For redis.log()\n};\n```\n\n### redisCall\n\nCalled when Lua executes `redis.call(...)`. Arguments arrive as `Buffer[]`. Throw an error to return it to Lua.\n\n### redisPcall\n\nCalled when Lua executes `redis.pcall(...)`. Return `{ err: Buffer }` instead of throwing to match Redis behavior.\n\n### log\n\nCalled when Lua executes `redis.log(level, message)`. Level is a numeric Redis log level.\n\n## Reply Types\n\nReturn values are Redis-compatible:\n\n```typescript\ntype ReplyValue =\n  | null // Lua nil\n  | number // Integer (safe range)\n  | bigint // Integer (64-bit)\n  | Buffer // Bulk string\n  | { ok: Buffer } // Status reply (+OK)\n  | { err: Buffer } // Error reply (-ERR)\n  | ReplyValue[]; // Array\n```\n\n### Determining the Response Type\n\nUse type guards to inspect what Lua returned:\n\n```typescript\nconst result = engine.eval(script);\n\n// Check for null (Lua nil)\nif (result === null) {\n  console.log(\"Got nil\");\n}\n\n// Check for integer\nelse if (typeof result === \"number\" || typeof result === \"bigint\") {\n  console.log(\"Got integer:\", result);\n}\n\n// Check for array (Lua table with sequential keys)\nelse if (Array.isArray(result)) {\n  console.log(\"Got array with\", result.length, \"elements\");\n  for (const item of result) {\n    // Each element is also a ReplyValue - handle recursively\n  }\n}\n\n// Check for status reply ({ok: Buffer}) - e.g. from SET, PING\nelse if (typeof result === \"object\" \u0026\u0026 \"ok\" in result) {\n  console.log(\"Got status:\", result.ok.toString());\n}\n\n// Check for error reply ({err: Buffer})\nelse if (typeof result === \"object\" \u0026\u0026 \"err\" in result) {\n  console.log(\"Got error:\", result.err.toString());\n}\n\n// Otherwise it's a bulk string (Buffer)\nelse if (Buffer.isBuffer(result)) {\n  console.log(\"Got bulk string:\", result.toString());\n}\n```\n\n### Lua Type Conversions\n\nThis matches Redis Lua behavior:\n\n```typescript\n// Lua nil → null\nengine.eval(\"return nil\"); // null\n\n// Lua number → number (or bigint for large values)\nengine.eval(\"return 42\"); // 42\nengine.eval(\"return 2^62\"); // 4611686018427387904n (bigint)\n\n// Lua string → Buffer\nengine.eval(\"return 'hello'\"); // Buffer.from(\"hello\")\n\n// Lua table (array) → ReplyValue[]\nengine.eval(\"return {1, 2, 3}\"); // [1, 2, 3]\nengine.eval(\"return {'a', 'b'}\"); // [Buffer, Buffer]\n\n// Status reply: commands like SET, PING return {ok: \"...\"}\n// In Lua: local resp = redis.call('SET', 'k', 'v') → resp.ok == \"OK\"\nengine.eval(\"return redis.call('SET', 'k', 'v')\"); // { ok: Buffer.from(\"OK\") }\nengine.eval(\"return redis.call('SET', 'k', 'v').ok\"); // Buffer.from(\"OK\")\n\n// Error reply: redis.pcall catches errors as {err: \"...\"}\n// In Lua: local resp = redis.pcall('INVALID') → resp.err == \"ERR ...\"\nengine.eval(\"return redis.pcall('INVALID')\"); // { err: Buffer.from(\"ERR ...\") }\n```\n\n\u003e **Note**: Status replies (`+OK`) become `{ok: \"...\"}` tables in Lua, matching real Redis behavior.\n\u003e Use `resp.ok` to access the status string.\n\n## Resource Limits\n\nProtect against runaway scripts with configurable limits:\n\n```typescript\nconst module = await load({\n  limits: {\n    maxFuel: 10_000_000, // Instruction budget\n    maxMemoryBytes: 64 * 1024 * 1024, // Memory cap (host-coordinated)\n    maxReplyBytes: 2 * 1024 * 1024, // Max reply size\n    maxArgBytes: 1 * 1024 * 1024, // Max single argument size\n  },\n});\nconst engine = module.create(host);\n```\n\n| Limit            | Description                  | Enforcement      |\n| ---------------- | ---------------------------- | ---------------- |\n| `maxFuel`        | Instruction count budget     | WASM runtime     |\n| `maxMemoryBytes` | Memory growth cap            | Host-coordinated |\n| `maxReplyBytes`  | Maximum reply payload size   | WASM runtime     |\n| `maxArgBytes`    | Maximum single argument size | WASM runtime     |\n\n## Included Lua Libraries\n\nThe engine includes Redis-standard Lua modules:\n\n- **cjson** - JSON encoding/decoding\n- **cmsgpack** - MessagePack serialization\n- **struct** - Binary data packing/unpacking\n- **bit** - Bitwise operations\n\nPlus standard Lua 5.1 libraries: `base`, `table`, `string`, `math`.\n\n## Use Cases\n\n- **Testing** - Unit test Redis Lua scripts without a Redis server\n- **Sandboxing** - Execute untrusted Lua with resource limits\n- **Development** - Rapid iteration on Lua scripts locally\n- **Embedding** - Add Redis-compatible scripting to Node.js applications\n\n## Compatibility\n\n| Feature                         | Status  |\n| ------------------------------- | ------- |\n| Redis version target            | 7.x     |\n| Lua version                     | 5.1     |\n| Binary-safe strings             | Yes     |\n| `redis.call` / `redis.pcall`    | Yes     |\n| `redis.log`                     | Yes     |\n| `redis.sha1hex`                 | Yes     |\n| Standard Lua libraries          | Yes     |\n| Redis Lua modules (cjson, etc.) | Yes     |\n| Debug / REPL helpers            | No      |\n| Redis Modules API               | Not yet |\n\n## Building from Source\n\n```bash\n# Build everything (requires Emscripten via Docker)\nnpm run build\n\n# Build steps individually\nnpm run build:wasm  # Compile C to WASM\nnpm run build:ts    # Compile TypeScript\n\n# Run tests\nnpm test\nnpm run test:skip-wasm  # Skip WASM rebuild\n```\n\n## Documentation\n\n- [Host Interface Contract](docs/host-interface.md)\n- [Binary ABI Specification](docs/abi.md)\n- [Limits and Compatibility](docs/limits-compat.md)\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on:\n\n- How to report bugs\n- How to suggest enhancements\n- Development setup\n- Pull request process\n- Coding standards\n\nPlease read our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing.\n\n## Security\n\nSecurity is important to us. If you discover a security vulnerability, please follow our [Security Policy](SECURITY.md) for responsible disclosure.\n\nFor general security considerations when using lua-redis-wasm, see the [Security Guide](SECURITY.md#security-considerations).\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/fatal10110/lua-redis-wasm/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/fatal10110/lua-redis-wasm/discussions)\n- **Documentation**: [docs/](docs/)\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for a list of changes in each release.\n\n## License\n\nThis package is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.\n\n### Third-Party Licenses\n\nThis project includes third-party code, all under the MIT License:\n\n- **Lua 5.1** - Copyright (C) 1994-2012 Lua.org, PUC-Rio\n- **lua_cjson** - Copyright (C) 2010-2012 Mark Pulford\n- **lua_cmsgpack** - Copyright (C) 2012 Salvatore Sanfilippo\n- **lua_struct** - Copyright (C) 2010-2018 Lua.org, PUC-Rio\n- **lua_bit** - Copyright (C) 2008-2012 Mike Pall\n\nSee [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for full license texts.\n\n## Acknowledgments\n\n- Redis team for the Lua integration design\n- Emscripten project for WebAssembly tooling\n- Contributors and maintainers of the included Lua libraries\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffatal10110%2Flua-redis-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffatal10110%2Flua-redis-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffatal10110%2Flua-redis-wasm/lists"}