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

luafend

v2.5.0

Published

Command line client for Luafend - Lua and Luau source code protection.

Readme

Luafend

Command line tool and Node.js library for Luafend — source protection for Lua and Luau. One package: protect a script from your terminal, from a build script, from CI, or from your own code.

Targets Luau, Lua 5.1 and Lua 5.4, covering Roblox, Garry's Mod, LuaJIT, embedded Lua and standalone hosts.

Install

npm install -g luafend

Or run it without installing:

npx luafend

Requires Node.js 18 or newer. No dependencies.

Two ways to run the command line

Type one word — in Command Prompt or PowerShell on Windows, in Terminal on macOS and Linux — and Luafend opens its own shell. Inside it, every command starts with a slash:

luafend

luafend ~ > /login
luafend ~ > /obfuscate main.lua --mode maximum
luafend ~ > /exit

Tab completes commands, files, flags and values. Typing -- suggests the flags that command accepts. /help lists everything.

Or drop the slash and run the same command straight from your own shell. Nothing opens, the job runs, you get your prompt back — this is the form a build script or CI job uses:

luafend login
luafend obfuscate main.lua --mode maximum --lua luau

Getting started

luafend login                    # opens your browser, no password typed
luafend obfuscate main.lua       # writes main.obf.lua

The first run asks for the mode and the Lua version and remembers both. Every run after that is silent, and prints what it used and where the setting came from.

Use it from code

The short way: set it on the package, then run.

const luafend = require("luafend");

luafend.key = "luf_YOUR_KEY";
luafend.mode = "maximum";
luafend.lua = "luau";

const result = await luafend.run('print("hello")');

if (result.ok) {
  console.log(result.output);
} else {
  console.log(result.error);
}

Set luafend.out = "main.obf.lua" and the file is written for you as UTF-8. Set luafend.source and run() needs no argument at all.

These settings live on the package, so there is one set of them per program. That is right for a script and wrong for concurrent work; for a server use a client, which carries its own:

const client = new luafend.Luafend({ key: "luf_YOUR_KEY" });
client.mode = "maximum";
const result = await client.run(source);

Or describe a single build one line at a time:

const luafend = require("luafend");

const job = new luafend.Job();
job.key = "luf_YOUR_KEY";
job.source = 'print("hello")';
job.mode = "maximum";
job.lua = "luau";

const result = await job.run();

if (result.ok) {
  console.log(result.output);
} else {
  console.log(result.error);
}

run() never rejects. Every Result also carries the measurements, with no flag to switch on:

console.log(result.size);        // bytes of the protected script
console.log(result.sourceSize);  // bytes of what you sent
console.log(result.seconds);     // how long it took
console.log(result.timestamp);   // when it finished, UTC
console.log(result.quotaLeft);   // obfuscations left this month

Set job.out = "main.obf.lua" and the file is written for you as UTF-8, line endings intact.

If you just want the string and are happy for a failure to throw, there is a one line form that rejects with a LuafendError instead:

console.log(await luafend.obfuscate('print("hello")', {
  key: "luf_YOUR_KEY", mode: "maximum", lua: "luau",
}));

Leave key out and LUAFEND_TOKEN is used, then the account from luafend login. mode is lite, balanced or maximum; lua is luau, lua-5.1 or lua-5.4.

Commands

| Command | What it does | |---|---| | login | Sign in through your browser | | logout | Sign out and revoke this machine's token | | whoami | Name, email, sign-in provider, plan | | billing | Plan, credits, monthly usage and limits | | obfuscate <file> | Protect one script | | batch <pattern> | Protect many files at once | | settings | Show, reset or write project settings | | cd [folder] | Show or change the current folder | | update | Check for a newer version and install it | | docs | Open the documentation | | version | Show the CLI version |

Options

| Flag | Meaning | |---|---| | --mode lite\|balanced\|maximum | How much protection. Lite is fastest, Maximum compiles the script into its own virtual machine | | --lua luau\|lua-5.1\|lua-5.4 | Which Lua the result has to run on | | -o, --out <path> | Where to write. A folder for batch, a file for obfuscate | | --pick | Choose mode and version again instead of using the saved ones | | --force | Overwrite an existing output file | | --stdout | Print the result instead of writing a file | | --all | batch only: rebuild every file, including unchanged ones |

Without --lua, the version is guessed from the file: a .luau extension or Roblox globals such as game:GetService mean Luau. The guess is always printed.

Protecting many files

luafend batch "src/**/*.lua" -o dist

* and ? never cross a path separator, ** matches any number of directories. A plain folder means every .lua and .luau inside it. Files that have not changed since their last build are skipped, so a rebuild does not spend your monthly quota twice. Requests are paced to your account's rate limit.

Project settings

luafend.json pins how a project builds, so everyone working on it gets the same result:

{
  "mode": "maximum",
  "lua": "luau",
  "out": "dist",
  "include": ["src/**/*.lua"]
}

It is searched for upwards from the current folder, so any subfolder of the project behaves the same. With an include list, plain luafend batch is enough.

luafend settings shows what applies and where each value came from. luafend settings init writes a starter file next to your token, which then applies everywhere you have no project file.

Signing in

login never asks for a password. It opens a consent page in the browser where you are already signed in and waits for approval. The token it receives is stored in your OS config directory, never in the working directory:

| | | |---|---| | Windows | %APPDATA%\luafend\config.json | | macOS | ~/Library/Application Support/luafend/config.json | | Linux | ~/.config/luafend/config.json |

On macOS and Linux the file is created with mode 0600. logout revokes the token on the server as well as deleting it here.

The token is only ever sent to the official Luafend API or to a server on your own machine.

In CI

Set LUAFEND_TOKEN instead of signing in. It takes priority over the config file, so no login step is needed:

- run: npx luafend batch "src/**/*.lua" -o dist --mode balanced --lua luau
  env:
    LUAFEND_TOKEN: ${{ secrets.LUAFEND_TOKEN }}

Without a terminal, nothing is ever asked interactively: a missing setting fails with the flag it needs named, rather than blocking on a prompt nobody can answer.

Exit codes:

| Code | Meaning | |---|---| | 0 | Success | | 1 | Error | | 2 | Not signed in, or the session expired | | 3 | Out of credits, or the plan does not allow that mode |

Environment

| Variable | Effect | |---|---| | LUAFEND_TOKEN | Use this token instead of the stored one | | LUAFEND_API | Point at a different API host, for local development | | NO_COLOR | Turn colour off | | LUAFEND_ASCII=1 | Plain ASCII instead of box drawing characters |

Links

  • Website — https://bypass.onl
  • Documentation — https://bypass.onl/docs
  • The same client for Python — https://pypi.org/project/luafend/