Skip to content

Changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

0.11.3 - October 27th, 2026

Roblox instance properties retired by the reflection database now read and write through their replacements, matching current Roblox behavior. A new optional OCALE workflow compares deterministic checks between LuneBlox and a real Roblox DataModel. This release also updates compression dependencies past four security advisories.

Added

  • Reverse property migrations from the Roblox reflection database. Reading or writing a legacy property such as BasePart.BrickColor, Decal.Texture or UICorner.CornerRadius now converts to or from its canonical replacement properties. Legacy values already stored in a document still take precedence, and migrated values survive binary model round-trips
  • A manually dispatched and weekly OCALE conformance workflow. One Luau script runs unchanged in LuneBlox and Roblox, and a comparator reports structural differences. The workflow requires a dedicated test universe and repository secrets, never runs for untrusted pull requests, and reuses the last saved place version instead of uploading a new one
  • Lute-based text diff and glob test helpers, with provenance and license retained. Model deserialization tests now use glob discovery, and conformance mismatches print readable diffs

Security

  • zstd 0.14.3 and zstd-safe 8.2.0 replace all affected 0.13.x, 0.14.1 and 7.x copies (RUSTSEC-2026-0391, RUSTSEC-2026-0398 and RUSTSEC-2026-0403)
  • zstd-sys 2.1.2 fixes a separate compression advisory (RUSTSEC-2026-0410)

0.11.2 - October 25th, 2026

The Roblox client moved to 0.744 and turned DebugCodegenOptSize off, so native code gets back the optimization the flag skipped; LuneBlox follows. The macOS binaries are built with a newer compiler, which made 0.11.1’s interpreter about a third slower than the source allows. Luau itself, mlua and the libraries are those of 0.11.1.

Changed

  • The fast flag table holds 38 flags, from 35, and follows the Roblox client 0.744. The flags of 0.11.1 keep their values but one:
    • DebugCodegenOptSize (native code generation) goes from true to false. With it on, the code generator skipped block linearization and the constant propagation along the chains it builds. On an Apple M1, a Luau networking library’s generated modules (BlinkBlox 1.3.4) fired a thousand events 13 to 36% faster natively and decoded them 19 to 38% faster: Entities fired in 1.58 ms from 2.47 and Booleans decoded in 1.75 from 2.81, two release builds that differ only in the table, in ABBA order. Interpreted code did not move. A native timing taken on 0.11.1 or earlier reads slower than the same code runs now
  • Three flags are new, all on, none of them changing what a script computes:
    • LuauCodegenA64ForgLoopArray (native code, ARM64). A generic for over a table walks its array part in native code instead of calling out for each step. It is declared in the ARM64 backend, which only an aarch64 build links, so it is the first flag in a second table, ROBLOX_AARCH64_FFLAGS, that only aarch64 builds apply: on x86-64 Luau does not register it, nothing runs that code, and setting it there printed did not apply. The generator puts every flag of a CodeGen/src/*A64* source there; the x86-64 backend is linked on ARM64 too, so its flags stay in the shared table
    • LuauCodegenX64IntSpillRestore (native code, x86-64). The register allocator no longer restores an integer from a stack slot that holds it as a double, a fix for code it generated wrong
    • LuauBufferCage (VM, buffers). Buffers are allocated through a cage allocator when the host sets one with lua_setbuffercage. LuneBlox sets none, so buffers are allocated as before; the flag is on so that the table is the client’s
  • The macOS binaries are built on GitHub’s macos-26 image, with Apple Clang 21, from macos-15’s Xcode 16.4 and Clang 17, which compiles Luau’s C++. 0.11.1’s macOS aarch64 binary ran interpreted code about 30% slower than the same source built with Clang 21: those modules fired a thousand booleans in 15.8 ms interpreted where a local build took 11.0, with the same flags and the same cargo arguments. CI tests on the same image. The Linux binaries stay on Ubuntu 22.04 for the glibc they link against, and were not measured

0.11.1 - October 25th, 2026

A fix to luneblox setup, and a dependency’s security fix. Luau, its fast flag table, mlua and the libraries are those of 0.11.0.

Fixed

  • luneblox setup no longer puts Enum and warn in .luaurc’s globals in a project that loads the Roblox types, and takes them out where 0.11.0 put them. luau-lsp types a name listed there as any over what any definitions declare, so on the Roblox platform, luau-lsp’s default, the entry hid the Roblox types: local n: number = Enum.KeyCode.A was an error without it and nothing with it. Setup now adds the two names only where .vscode/settings.json sets "luau-lsp.platform.type": "standard" or "luau-lsp.types.roblox": false, and elsewhere removes them, with the key if nothing else is in it; other names in globals are kept. The settings are read line by line, so comments and trailing commas are fine. It prints which it did. A project that runs setup on every check no longer finds .luaurc changed afterwards
  • ctutils 0.4.5, from 0.4.3, which digest uses under serde’s hashing and the roblox library’s types. Its masks and u128 comparisons were not constant-time (RUSTSEC-2026-0385)

0.11.0 - October 22nd, 2026

Roblox’s Enum is a global, as a Roblox client has it, so a spec of game code can name Enum.KeyCode.ButtonA and compare items the way the code under test does, without the roblox library. A new global is a minor release: a script that read an Enum global it never set sees one now. Luau, its fast flag table, mlua and the libraries are those of 0.10.19.

Added

  • Enum as a global in every script, whenever the roblox library is built in (the roblox feature of lune-std, std-roblox of lune; both are on by default). It is the userdata require("@lune/roblox").Enum gives, so the two are rawequal, and every enum and item read through one is the same userdata as through the other: Enum.KeyCode.ButtonA == roblox.Enum.KeyCode.ButtonA, a property set from one reads back as the other’s item. It has every enum of the reflection database, with Name, Value, EnumType, GetEnumItems, FromName and FromValue, and an unknown enum or item raises the library’s error (The enum item 'X' does not exist for enum 'KeyCode'). A custom environment given to luau.load gets it with the other globals unless injectGlobals is false
  • luneblox setup adds Enum and warn to globals in .luaurc, the globals LuneBlox has that luau-lsp’s standard platform does not declare, so a strict script that uses either no longer reports Unknown global. luau-lsp types a name listed there as any, which is how far the typedefs went for roblox.Enum already. Names already in globals are kept

0.10.19 - October 21st, 2026

A security release. The certificate checks behind net reject chains through intermediates whose private keys are public. Nothing else changes: Luau, its fast flag table, mlua and the libraries are those of 0.10.18.

Fixed

  • rustls-webpki 0.103.16, from 0.103.15, which checks the certificates of every TLS connection net.request, net.socket and net.tcp make. It no longer accepts a certificate path through the distrusted emSign intermediates, whose private keys are now known (RUSTSEC-2026-0382). Lune 0.11.1 ships the same fix; its other one, fractional numbers passed to roblox constructors, has been in LuneBlox since 0.10.13
  • webpki-roots 1.0.10, from 1.0.9, the root certificates net trusts. It drops emSign ECC Root CA - G3, the root those distrusted intermediates chain to (RUSTSEC-2026-0381)
  • The documentation site is built with Vite 8.3.5, from 8.3.1. Vite runs when the site is built and was never part of the runtime

0.10.18 - October 20th, 2026

What the open issues of Lune asked for that a Roblox-faithful runtime should have: Roblox’s Random, utf8.graphemes, and buffers in JSON the way a game writes them. Errors that named nothing now say what happened - a require in a standalone binary, a prompt with no terminal - and fs.metadata on a socket or FIFO no longer ends the script with status 0. rustls moves past four advisories. Luau, its fast flag table and mlua are those of 0.10.17.

Added

  • Random in roblox: Random.new(seed?), NextInteger, NextNumber, NextUnitVector, Shuffle and Clone (#46). It is the PCG32 generator of Luau’s math.random, seeded the same way, so Random.new(seed) gives what math.random gives after math.randomseed(seed); whether it gives what a live Roblox client gives for a seed is not verified. Without a seed it starts from a random state. NextInteger takes ranges wider than math.random does, and raises interval is empty when max is below min. Shuffle raises an error on a frozen table or one with holes
  • serde.encode("json", ...) writes a buffer as Roblox’s HttpService:JSONEncode does: {"m":null,"t":"buffer","base64":"..."}, or zbase64 with the bytes compressed with zstd when that is shorter. serde.decode reads both forms back as a buffer from JSON and JSONC, so a buffer a game stored in a DataStore decodes here (#254). A buffer was an error before. An object with any other key stays a table
  • utf8.graphemes(s, i?, j?), which Roblox adds to Luau’s utf8 library: an iterator over the grapheme clusters of s, giving the first and last byte of each, so a letter with its combining marks, a flag or a joined emoji is one step (#237). i and j narrow it as in string.sub; invalid UTF-8 is an error

Changed

  • tostring on a DateTime gives its RFC 3339 string, on a response from net.request its status (200 OK), and on a request in a net.serve handler its method and path (GET /some/path). print shows them as <DateTime(...)>, <Response(200 OK)> and <Request(GET /some/path)>, where it showed only the type name (#334)
  • stdio.prompt with no terminal to ask in raises stdio.prompt needs a terminal, saying whether stderr or the keyboard is missing. It raised a bare IO error: not a terminal, which did not name the prompt. A redirected stdin still prompts on the keyboard, through /dev/tty
  • require of a file from a binary made with luneblox build, or from any chunk that is not a file, says that only a script run from a file can require files and that a standalone binary’s modules must be bundled into its script. It said require is not supported in this context (#338). The standard libraries are required by name and keep working

Fixed

  • fs.metadata on a socket, a FIFO or a device file raises an error naming the path. It panicked, which ended the script where it stood and exited with status 0, as if it had finished
  • rustls 0.23.46, from 0.23.45, which net uses for TLS. It fixes four advisories (RUSTSEC-2026-0346 to 0349), among them a downgrade of later connections to classical key exchange by an unauthenticated server hello

0.10.17 - October 12th, 2026

What Lune 0.11.0 brought that LuneBlox did not have yet, without its one break: enum items with a stable identity, Enum:FromName and Enum:FromValue, a web server that answers a table without a status, and line coverage from an upstream pull request still open. A place or model a script parses and drops is freed, which upstream does not do yet. Luau, its fast flag table and mlua are those of 0.10.16.

Added

  • luneblox run --coverage[=FILE] writes the line coverage of the script, the modules it requires and the chunks it loads with luau.load to an LCOV file, lcov.info by default. Flags for run go before the script path. The report is written also when the script errors (#421)
  • Enum:FromName and Enum:FromValue in roblox. FromValue truncates a fractional value and finds nothing for a negative one; FromName accepts an item’s former names (#423)

Changed

  • An EnumItem and an Enum keep one identity: two reads of Enum.KeyCode.X are the same userdata, so it works as a table key and with rawequal, as on Roblox. A former name gives the same userdata as the current one: rawequal(Enum.KeyCode.Unknown, Enum.KeyCode.None) (#420)
  • A property or method added with roblox.implementProperty or roblox.implementMethod is found before a child of the same name, which shadowed it before (#412)
  • The net.serve handle’s stop is declared as a method and documented as handle:stop(), the way Lune 0.11.0 calls it. handle.stop() keeps working: Lune 0.11.0 made it an error, and LuneBlox runs code written for either release
  • glam 0.34 and zip 9, the two breaking releases 0.10.16 held back. blocking, async-io, async-lock, serde_json and dialoguer leave the crates whose code never used them
  • The typedefs declare net.http, net.ws and the ip and port of a server handle, say what Regex:captures returns, and where roblox.getAuthCookie reads the cookie from (#199, #264, #321). The Studio path functions’ typedefs move to roblox/studio.luau, which luneblox setup writes with the others
  • lune_utils::jit, an alias of lune_utils::process::ProcessJitEnablement, is removed, as upstream removed it

Fixed

  • A place or model returned by roblox.deserializePlace or roblox.deserializeModel is freed once the script holds none of its instances and the garbage collector has run. It stayed in memory until its root was destroyed, so a script parsing files in a loop grew without bound: 0.10.15 held 200 MB after parsing a 130 KB place 400 times, where it now holds what one parse does (#415)
  • net.serve answers a response table without status with 200. It answered with a silent 500 (#416)

0.10.16 - October 10th, 2026

A maintenance release. Luau, its fast flag table, mlua and what the libraries do are those of 0.10.15: a script runs as it did.

Changed

  • Twenty crates move to their newest compatible releases in Cargo.lock, among them hyper 1.12.0, zstd 0.14.1, toml 1.1.8, smallvec 1.16.3 and zerocopy 0.8.62. No manifest changes, so no crate crosses a version its dependents did not already allow. glam 0.34 and zip 9 are breaking releases and stay where they are
  • The tools and actions the repository builds and checks itself with are the newest releases: lefthook 2.2.1, the released LuneBlox that runs scripts/, actions/setup-node 7.1.0, actions/download-artifact 8.0.2, actions/upload-artifact 7.0.2 and taiki-e/install-action 2.87.27
  • The documentation site is built with Astro 7.3.8, Starlight 0.42.6 and starlight-links-validator 0.27.0

Fixed

  • The documentation site was built with a postcss-selector-parser that could be made to spend quadratic time on a selector. It is overridden to 7.1.6, which fixes that. The parser runs when the site is built and was never part of the runtime

0.10.15 - October 9th, 2026

The Roblox client moved to 0.742 and turned on three more of the fast flags Luau 0.740 declares. LuneBlox takes them. One changes what a script compiled at optimization level 2 can print; the other two change what happens when memory runs out and how foreign bytecode is loaded. Luau itself, mlua and the libraries are those of 0.10.14.

Changed

  • The fast flag table holds 35 flags, from 32, and follows the Roblox client 0.742. The 32 flags of 0.10.14 keep their values. The three new ones, all on:
    • LuauCompileNoFoldVectorEqW (compiler, constant folding). Two vector constants that differ only in a fourth component are no longer compared at compile time. The VM keeps three components, so such vectors are equal when the script runs, and the compiler said otherwise: vector.create(1, 2, 3, 4) == vector.create(1, 2, 3, 5) was folded to false and is now true, as it always was without the fold. Only code compiled at optimization level 2 folds vector.create at all - a script with --!optimize 2, or luau.compile with optimizationLevel = 2 - so a script run at the default level 1 compiles to the same bytecode as before. crates/lune/tests/fflag_effect.rs pins this through the built binary, with the flags on and off
    • LuauTableRobustOom (VM, tables). A table that changes size allocates its new array and hash parts first and switches to them only when both exist, so an allocation that fails halfway leaves the table as it was; a hash part too large for its size field raises table overflow before anything is allocated; and freezing a table that holds metamethods skips its lookup shortcut when there is no memory for it, where it raised an error before. What a table holds, the order it is traversed in and the errors a script sees are unchanged while memory lasts
    • LuauLoadRemapOptionalUserdata (VM, bytecode loader). The type annotations bytecode carries for native code generation keep their “optional” bit when a tagged userdata type is mapped to the host’s. The compiler in LuneBlox is never given userdata type names, so it writes no such annotation: this is reached only by bytecode compiled elsewhere and passed to luau.load

Fixed

  • The test that checks TLS against seven public servers (tests/net/request/https) no longer fails a build when one of them is briefly unavailable: an answer of 408, 429 or 5xx, or a request that could not be sent, is asked for again, four requests in all. Every server still has to answer ok, and any other status fails on the first answer; tests/net/request/retry pins both against a local server. The Microsoft server asked is now the sign-in metadata endpoint, which answers in about a second, where the Azure updates feed took seven and answered 503 to one build

0.10.14 - October 9th, 2026

Scripts run exactly as in 0.10.13: the same Luau, the same fast flags in effect, the same libraries. What moves is around them - a fast flag table that can no longer disagree with the binary unnoticed, and release archives that carry their license and can be checked against a checksum.

Added

  • A fast flag in the table that Luau does not take is reported on stderr when the flags are applied: warning: Luau fast flag `<name>` did not apply. It was skipped silently before. No flag in this release’s table fails, so nothing is printed; a test sets every flag in the table, and another runs a script through the built binary and fails if anything reaches stderr
  • Each release carries SHA256SUMS.txt, with the SHA-256 checksum of every archive
  • scripts/benchmark.luau, seven small VM kernels that print a median time each, for comparing two builds or two sets of fast flags on the same machine

Changed

  • The fast flag table holds 32 flags, from 33. LuauPrettyPrintVisualizeIndexerAccess is gone from it: Luau declares the flag in its AST pretty printer, which the runtime does not link, so the flag was never registered and setting it never took effect - in 0.10.13 as well. The 32 flags that did take effect are unchanged, and are still those of the Roblox client 0.741. The generator (scripts/generate_roblox_fflags.luau) now leaves out flags declared in sources the runtime does not link
  • Each release archive holds LICENSE.txt next to the binary. It held the binary alone before
  • The release scripts stop at the first failed step, so a release cannot go out with an archive, a license or a checksum missing

0.10.13 - October 4th, 2026

Every dependency moves to its latest release - mlua 0.12.2 from crates.io, the Roblox 741 reflection database, the fast flags of the Roblox client 0.741 - with what scripts relied on kept as it was: fractional numbers are still truncated where an integer is expected, and an enum item Roblox renamed still answers to its former name.

Added

  • An enum item answers to the names it had before Roblox renamed it, as on Roblox: Enum.KeyCode.Unknown is Enum.KeyCode.None, and reports None as its name. The 72 former names come from Roblox’s API dump (scripts/generate_enum_legacy_names.luau)

Changed

  • The reflection database describes Roblox 741 (rbx_reflection_database 3.0.1, from 728), with rbx_binary and rbx_xml 3.0.1. Every other dependency is at its latest compatible version
  • Luau’s fast flags follow the Roblox client 0.741, which turns on three more of the flags Luau 0.740 declares: LuauCompileRecursiveAliases, LuauFrozenMetaButterfly and LuauPromoteProto
  • mlua 0.12.1 -> 0.12.2, with mlua-sys 0.13 taken from crates.io: the release carries Luau 0.740, so the patch that pointed mlua-sys at an mlua commit is gone. The vendored luau0-src stays
  • A number with a fractional part is still truncated where an integer is expected, as on Roblox - UDim.new(0, 1.5), Color3.fromRGB(127.5, 0, 0), Vector3int16.new(1.9, 0, 0), a port, a compression level. mlua 0.12.2 turned these into errors; lune_utils::Truncated keeps them
  • serde.encode and serde.decode accept values nested up to 128 levels deep and raise an error past that. Encoding had no limit before and overflowed the stack on a deep enough table

0.10.12 - September 29th, 2026

Native code no longer takes the process down on ARM64 when a function has more than 256 constants: a large generated module loads with codegenEnabled on Apple Silicon as it does on x64.

Fixed

  • Loading a module with codegenEnabled no longer kills the process on ARM64 (Apple Silicon, Linux aarch64). Luau 0.740’s ARM64 register allocator could evict a value loaded from a constant past K255 and plan to reload it from there, which no single ARM64 load reaches; Luau’s assertion stopped the process with no message (exit code 133 run directly). Such a value now goes to a spill slot, as Luau did before its release 708, and the function still compiles to native code. The fix is carried in a vendored copy of luau0-src (vendor/luau0-src) until a Luau release has it

0.10.11 - September 29th, 2026

A coroutine’s error reaches the code that resumed it unchanged, as on Roblox, so error values that are tables, and messages compared exactly, work the same here as in a live game.

Fixed

  • coroutine.resume returns the value a coroutine threw unchanged, as on Roblox: a table error comes back as the same table, and a string as thrown, with no runtime error: prefix or traceback. coroutine.wrap propagates the same value, and coroutine.close on a coroutine that errored returns false and that value instead of the thread itself

0.10.10 - September 29th, 2026

CI no longer runs on the Node.js 20 actions GitHub has deprecated. The runtime is unchanged.

Changed

  • CI and the release workflow use the Node.js 24 majors of their actions: actions/checkout v7, upload-artifact v7, download-artifact v8, softprops/action-gh-release v3, and CompeyDev/setup-rokit v0.2.1, whose v0.1 pulled in actions/cache@v4

0.10.9 - September 28th, 2026

A full garbage collection a script can ask for, so a benchmark can start each measurement from the same heap.

Added

  • luau.collect() in @lune/luau runs a full garbage collection cycle. Scripts run sandboxed, where collectgarbage accepts only "count", as on Roblox, and that stays so: a benchmark that timed several libraries in one process had each inherit the heap the one before it left, which moved an allocating decode by up to 40% with its place in the order

Fixed

  • The net.serve handles test passes on a Windows set to a language other than English: it matched the refused connection’s message, which Windows words in the system’s language, and now also takes its code, os error 10061

0.10.8 - September 28th, 2026

CI now runs the same gates as the pre-commit hooks.

Changed

  • CI runs selene and the type-checking mode, file-size and English checks next to luau-lsp, cargo-deny in a job of its own, and clippy over all targets with -D warnings
  • Every CI job has read-only permissions and a timeout, so a hung gate fails with its logs
  • CI installs the prebuilt cargo-nextest instead of letting cargo-binstall fall back to a source build, which failed on the macOS aarch64 runner
  • The process exec test waits up to 120 seconds for a hang on Windows instead of 30, which PowerShell’s cold start alone reached on the Windows ARM runners

0.10.7 - September 28th, 2026

Open Lune pull requests brought over, every dependency on its latest release, and the tree held at zero by git hooks: clippy with -D warnings, luau-lsp, selene, stylua, cargo-deny, and checks that every Luau file declares its type-checking mode and no source file passes 500 lines.

Added

  • Added base64 and hex encoding formats to serde.encode and serde.decode (#404)
  • Added XDG Base Directory support: the build cache and REPL history now live in $XDG_CACHE_HOME/luneblox and $XDG_STATE_HOME/luneblox on XDG-compliant systems, while existing ~/.luneblox locations keep being used when present. Both can be overridden with the LUNEBLOX_CACHE and LUNEBLOX_STATE environment variables (#342)

Changed

  • Updated every dependency to its latest release, including the RustCrypto hashing crates and an unpinned blake3; hashes and HMACs are unchanged. The unused, unmaintained webpki is removed
  • Attributes prefixed with RBX may now be read and written in the roblox standard library (#413)
  • The serde standard library now uses saphyr for YAML instead of the unmaintained serde_yaml2 (#406)
  • luneblox setup writes the roblox and datetime typedefs as directory modules (roblox/init.luau with its submodules beside it) and removes the single-file typedefs they replace; @lune/roblox and @lune/datetime resolve as before

Fixed

  • Fixed CFrame.lookAt, CFrame.lookAlong and CFrame.new(pos, lookAt) producing incorrect rotations when not rotating purely around the Y axis (#419)
  • Fixed Color3:ToHex() not matching Roblox rounding and casing (#410)
  • Fixed YAML scalars being decoded with the wrong types in the serde standard library (#406)

0.10.6 - LuneBlox

The first release of LuneBlox, a Lune that runs Luau the way Roblox runs it. The command is luneblox and _VERSION reads LuneBlox 0.10.6+740.

Changed

  • Luau 0.709 -> 0.740, the version Roblox runs, through mlua 0.11.6 -> 0.12.1 and mlua-sys patched to mlua’s own “Bump Luau to 0.740” commit until a crates.io release carries it
  • Luau’s fast flags are set as the live Roblox client sets them (crates/lune/src/rt/roblox_fflags.rs, generated by scripts/generate_roblox_fflags.luau); LUNE_ROBLOX_FFLAGS=0 keeps Luau’s defaults. luneblox build sets them before compiling a standalone executable’s bytecode
  • Luau’s C++ is built at -O3; the workspace’s opt-level = "z" had reached it through cc and built the VM with -Os. pcall is 34% faster, a Luau-to-Luau metamethod call 29%, a called closure 24%
  • The binary is luneblox, and typedefs, the cross-compilation cache and the REPL history live in ~/.luneblox; script folders (lune, .lune) and the @lune/* libraries keep their names
  • luneblox build --target downloads base executables from LuneBlox’s releases
  • Releases are published to GitHub only; nothing goes to crates.io

Fixed

  • task.spawn and task.defer dropped a thread that was resuming another coroutine: mlua 0.12 reports it as Normal where 0.11 said Resumable

Fixed (from Lune’s unreleased changes)

  • Fixed the close method on web sockets always erroring with “Socket has been closed” instead of closing the socket
  • Fixed the documentation for serde.hmac saying it returns a base64 string, when it returns a hex string

0.10.5 - July 2nd, 2026

Added

  • Added QueryDescendants to instances in the roblox standard library.

  • Added registerClass and registerService functions to the roblox standard library, letting classes and services that the reflection database does not know about - such as those only present in specific Roblox builds - be implemented and used:

    local roblox = require("@lune/roblox")
    roblox.registerService("FileSystemService")
    roblox.implementMethod("FileSystemService", "ReadFile", function(_, path: string)
    return -- ... read and return the file contents ...
    end)
    local game = roblox.Instance.new("DataModel")
    local fss = game:GetService("FileSystemService")

Changed

  • Refactored the roblox standard library to support multiple independent doms. Instances now have stable references, and place or model files containing colliding referents can be loaded at the same time without conflicts.
  • Updated to Luau version 0.709
  • Updated to rbx-dom database version 0.728

Fixed

  • Fixed task.wait not gracefully handling negative, NaN, and other non-finite durations
  • Fixed datatype arithmetic in @lune/roblox not working with a scalar on the left-hand side, such as 2 * Vector3.new(1, 2, 3)
  • Fixed various completeness and correctness issues across datatypes in the roblox standard library
  • Fixed web socket close codes not being set to 1005 (no status) or 1006 (abnormal closure) when a connection was closed without one
  • Fixed error stack traces truncating real file paths on Windows
  • Fixed NumberSequence.new(start, end) silently ignoring its second constructor argument
  • Fixed ColorSequence.new(start, end) silently ignoring its second constructor argument (#391)
  • Fixed the close method on a child process writer not closing it (#359)
  • Fixed truncation of lune build output paths containing dots on non-Windows targets (#396)
  • Fixed deserialization of Roblox place/model files failing due to Tags now serializing using SharedString (#397)

0.10.4 - October 14th, 2025

Added

  • Added support for the jsonc serialization format in the serde standard library
  • Added an mlua feature flag to lune-roblox, so that it may be used from Rust without bundling the entirety of Luau

Changed

  • Updated to rbx-dom database version 0.694
  • Updated to Luau version 0.694

Fixed

  • Fixed attribute name validation being too strict in the roblox standard library, not allowing the characters /.-
  • Fixed various issues in the roblox standard library caused by an outdated reflection database and version of rbx-dom (#358)

0.10.3 - October 6th, 2025

Changed

  • Updated to Luau version 0.693

Fixed

  • Fixed readToEnd being slow for child processes with large output (#354)
  • Fixed process.exec not reading stdio until child exits (#353)

0.10.2 - August 31st, 2025

Added

  • Added support for the ZSTD compression format in the serde standard library (#339)
  • Added support for UniqueId properties to the roblox standard library (#343)
  • Added prebuilt Lune binaries for Windows on ARM to GitHub releases

Changed

  • Updated to Luau version 0.688
  • Lune no longer depends on liblzma, making it easier to install on macOS
  • Prebuilt binaries for Ubuntu now use an older version (22.04) for better GLIBC compatibility

Fixed

  • Fixed Lune crashing while emitting an error and parsing its source

0.10.1 - July 16th, 2025

Fixed

  • Fixed a regression where it was not possible to run directories with init.luau files in them directly using lune run directory-name.
  • Fixed a panic when calling process.exit inside a file that require was called on. (#333)
  • Fixed a panic when calling process.exit inside a request handler for net.serve. (#333)

0.10.0 - July 15th, 2025

This version of Lune contains a major internal refactoring of the require function, now using the proper require-by-string APIs exposed by Luau. If you relied on any (incorrect) behavior of relative, non-@self requires, from within init.luau files in Lune 0.9.0, you may need to update your code. No other usages of require will be affected - but if you previously encountered any internal bugs or panics during require, these will have been fixed!

Added

  • Added support for TCP client in the net standard library. It may be used either with TLS or not, and basic usage looks as such:

    -- Plain TCP connections
    local stream = net.tcp.connect("example.com", 80)
    -- TLS connections (shorthand)
    local tlsStream = net.tcp.connect("example.com", 443, true)
    -- Connections with custom TLS setting & TTL
    local customStream = net.tcp.connect("192.168.1.100", 8080, {
    tls = false,
    ttl = 128
    })
    -- Interacting with the stream
    tlsStream:write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
    while true do
    local data = tlsStream:read()
    if data ~= nil then
    print(data)
    else
    break -- Connection was closed
    end
    end
  • Added submodules to the net standard library: http, tcp, and ws.

    These will be the preferred way of interacting with the net standard library going forward, but none of the old functions have been removed or deprecated yet to allow users time to migrate.

    In a future major version of Lune, direct functions such as net.socket will be removed in favor of their equivalent functions in submodules, such as net.ws.connect. Here is the full new list of functions:

    • net.http.request
    • net.http.serve
    • net.tcp.connect
    • net.ws.connect
  • Added a method with_lib to the Runtime struct in the lune crate, to allow registering custom libraries.

Changed

  • Updated to Luau version 0.682
  • Upgraded to mlua version 0.11 - if you use any of the Lune crates as a dependency, you may also need to upgrade

Fixed

  • Fixed errors being emitted twice when the error is thrown from the main (entrypoint) script
  • Fixed a panic when calling net.request and related functions in the main body of a module during require
  • Fixed various issues with not conforming to the new Luau require-by-string semantics

0.9.4 - June 13th, 2025

Changed

  • lune setup now properly sets up a .luaurc file instead of using legacy VSCode-specific settings
  • process.args and process.env are now plain tables again - not userdata - thank you to everyone who provided feedback on this and the usability issues!

Fixed

  • Fixed invalid handling of http redirects in net.request
  • Fixed not being able to download binaries for cross-compiling with lune build
  • Fixed binary output when running lune build not being deterministic and sometimes truncating
  • Fixed cargo install lune failing due to a yanked dependency (#323)

0.9.3 - May 6th, 2025

Added

  • Added support for non-UTF8 strings in arguments to process.exec and process.spawn

Changed

  • Improved cross-platform compatibility and correctness for values in process.args and process.env, especially on Windows

Fixed

  • Fixed stdin not being properly closed when not providing the stdin option to process.exec
  • Fixed various crashes during require that had the error cannot mutably borrow app data container

0.9.2 - April 30th, 2025

Changed

  • Improved performance of net.request and net.serve when handling large request bodies
  • Improved performance and memory usage of task.spawn, task.defer, and task.delay

Fixed

  • Fixed accidental breakage of net.request in version 0.9.1

0.9.1 - April 29th, 2025

Added

  • Added support for automatic decompression of HTTP requests in net.serve (#310)

Fixed

  • Fixed net.serve no longer serving requests if the returned ServeHandle is discarded (#310)
  • Fixed net.serve having various performance issues (#310)
  • Fixed Lune still running after cancelling a task such as task.delay(5, ...) and all tasks having completed

0.9.0 - April 25th, 2025

The next major version of Lune has finally been released!

This release has been a long time coming, and many breaking changes have been made. If you are an existing Lune user upgrading to this version, you will most likely be affected. The full list of breaking changes can be found on below.

Breaking changes & additions

  • The behavior of require has changed, according to the latest Luau RFCs and specifications.

    For the full details, feel free to read documentation here, otherwise, the most notable changes here are:

    • Paths passed to require must start with either ./, ../ or @ - require statements such as require("foo") will now error and must be changed to require("./foo").
    • The behavior of require from within init.luau and init.lua files has changed - previously require("./foo") would resolve to the file or directory foo as a sibling of the init file, but will now resolve to the file or directory foo which is a sibling of the parent directory of the init file. To require files inside of the same directory as the init file, the new @self alias must be used - like require("@self/foo").
  • The main lune run subcommand will no longer sink flags passed to it - lune run -- will now literally pass the string -- as the first value in process.args, and -- is no longer necessary to be able to pass flag arguments such as --foo and -b properly to your Lune programs.

  • Two new process spawning functions - process.create and process.exec - replace the previous process.spawn API. (#211)

    To migrate from process.spawn, use the new process.exec API which retains the same behavior as the old function, with slight changes in how the stdin option is passed.

    The new process.create function is a non-blocking process creation API and can be used to interactively read and write to standard input and output streams of the child process.

    local child = process.create("program", {
    "first-argument",
    "second-argument"
    })
    -- Writing to stdin
    child.stdin:write("Hello from Lune!")
    -- Reading partial data from stdout
    local data = child.stdout:read()
    print(data)
    -- Reading the full stdout
    local full = child.stdout:readToEnd()
    print(full)
  • Removed net.jsonEncode and net.jsonDecode - please use the equivalent serde.encode("json", ...) and serde.decode("json", ...) instead

  • WebSocket methods in net.socket and net.serve now use standard Lua method calling convention and colon syntax. This means socket.send(...) is now socket:send(...), socket.close(...) is now socket:close(...), and so on.

  • Various changes have been made to the Lune Rust crates:

    • Runtime::run now returns a more useful value instead of an ExitCode (#178)
    • All Lune standard library crates now export a typedefs function that returns the source code for the respective standard library module type definitions
    • All Lune crates now depend on mlua version 0.10 or above
    • Most Lune crates have been migrated to the smol and async-* ecosystem instead of tokio, with a full migration expected soon (this will not break public types)
    • The roblox crate re-export has been removed from the main lune crate - please depend on lune-roblox crate directly instead

Added

  • Added functions for getting Roblox Studio locations to the roblox standard library (#284)
  • Added support for the Content datatype in the roblox standard library (#305)
  • Added support for EnumItem instance attributes in the roblox standard library (#306)
  • Added support for RFC 2822 dates in the datetime standard library using fromRfc2822 (#285) - the fromIsoDate function has also been deprecated (not removed yet) and fromRfc3339 should instead be preferred for any new work.
  • Added a readLine function to the stdio standard library for reading line-by-line from stdin.
  • Added a way to disable JIT by setting the LUNE_LUAU_JIT environment variable to false before running Lune.
  • Added process.endianness constant (#267)

Changed

  • Documentation comments for several standard library properties have been improved (#248, #250)
  • Error messages no longer contain redundant or duplicate stack trace information
  • Updated to Luau version 0.663
  • Updated to rbx-dom database version 0.670

Fixed

  • Fixed deadlock in stdio.format calls in __tostring metamethods (#288)
  • Fixed task.wait and task.delay not being guaranteed to yield when duration is set to zero or very small values
  • Fixed __tostring metamethods sometimes not being respected in print and stdio.format calls

0.8.9 - October 7th, 2024

Changed

  • Updated to Luau version 0.640

0.8.8 - August 22nd, 2024

Fixed

  • Fixed errors when deserializing Lighting.AttributesSerialize by updating rbx-dom dependencies (#245)

0.8.7 - August 10th, 2024

Added

  • Added a compression level option to serde.compress (#224)
  • Added missing vector methods to the roblox library (#228)

Changed

  • Updated to Luau version 0.635
  • Updated to rbx-dom database version 0.634

Fixed

  • Fixed fs.readDir with trailing forward-slash on Windows (#220)
  • Fixed __type and __tostring metamethods not always being respected when formatting tables

0.8.6 - June 23rd, 2024

Added

  • Added a builtin API for hashing and calculating HMACs as part of the serde library (#193)

    Basic usage:

    local serde = require("@lune/serde")
    local hash = serde.hash("sha256", "a message to hash")
    local hmac = serde.hmac("sha256", "a message to hash", "a secret string")
    print(hash)
    print(hmac)

    The returned hashes are sequences of lowercase hexadecimal digits. The following algorithms are supported: md5, sha1, sha224, sha256, sha384, sha512, sha3-224, sha3-256, sha3-384, sha3-512, blake3

  • Added two new options to luau.load:

    • codegenEnabled - whether or not codegen should be enabled for the loaded chunk.
    • injectGlobals - whether or not to inject globals into a passed environment.

    By default, globals are injected and codegen is disabled. Check the documentation for the luau standard library for more information.

  • Implemented support for floor division operator / __idiv for the Vector2 and Vector3 types in the roblox standard library (#196)

  • Fixed the _VERSION global containing an incorrect Lune version string.

Changed

  • Sandboxing and codegen in the Luau VM is now fully enabled, resulting in up to 2x or faster code execution. This should not result in any behavior differences in Lune, but if it does, please open an issue.
  • Improved formatting of custom error objects (such as when fs.readFile returns an error) when printed or formatted using stdio.format.

Fixed

  • Fixed __type and __tostring metamethods on userdatas and tables not being respected when printed or formatted using stdio.format.

0.8.5 - June 1st, 2024

Changed

  • Improved table pretty formatting when using print, warn, and stdio.format:
    • Keys are sorted numerically / alphabetically when possible.
    • Keys of different types are put in distinct sections for mixed tables.
    • Tables that are arrays no longer display their keys.
    • Empty tables are no longer spread across lines.

Fixed

  • Fixed formatted values in tables not being separated by newlines.
  • Fixed panicking (crashing) when using process.spawn with a program that does not exist.
  • Fixed instance:SetAttribute("name", nil) throwing an error and not removing the attribute.

0.8.4 - May 12th, 2024

Added

  • Added a builtin API for regular expressions.

    Example basic usage:

    local Regex = require("@lune/regex")
    local re = Regex.new("hello")
    if re:isMatch("hello, world!") then
    print("Matched!")
    end
    local caps = re:captures("hello, world! hello, again!")
    print(#caps) -- 2
    print(caps:get(1)) -- "hello"
    print(caps:get(2)) -- "hello"
    print(caps:get(3)) -- nil

    Check out the documentation for more details.

  • Added support for buffers as arguments in builtin APIs (#148)

    This includes APIs such as fs.writeFile, serde.encode, and more.

  • Added support for cross-compilation of standalone binaries (#162)

    You can now compile standalone binaries for other platforms by passing an additional target argument to the build subcommand:

    Terminal window
    lune build my-file.luau --output my-bin --target windows-x86_64

    Currently supported targets are the same as the ones included with each release of Lune on GitHub. Check releases for a full list of targets.

  • Added stdio.readToEnd() for reading the entire stdin passed to Lune

Changed

  • Split the repository into modular crates instead of a monolith. (#188)

    If you previously depended on Lune as a crate, nothing about it has changed for version 0.8.4, but now each individual sub-crate has also been published and is available for use:

    • lune (old)
    • lune-utils
    • lune-roblox
    • lune-std-* for every builtin library

    When depending on the main lune crate, each builtin library also has a feature flag that can be toggled in the format std-*.

    In general, this should mean that it is now much easier to make your own Lune builtin, publish your own flavor of a Lune CLI, or take advantage of all the work that has been done for Lune as a runtime when making your own Rust programs.

  • Changed the User-Agent header in net.request to be more descriptive (#186)

  • Updated to Luau version 0.622.

Fixed

  • Fixed not being able to decompress lz4 format in high compression mode
  • Fixed stack overflow for tables with circular keys (#183)
  • Fixed net.serve no longer accepting ipv6 addresses
  • Fixed headers in net.serve being raw bytes instead of strings

0.8.3 - April 15th, 2024

Fixed

  • Fixed require not throwing syntax errors (#168)
  • Fixed require caching not working correctly (#171)
  • Fixed case-sensitivity issue in require with aliases (#173)
  • Fixed itertools dependency being marked optional even though it is mandatory (#176)
  • Fixed test cases for the net built-in library on Windows (#177)

0.8.2 - March 12th, 2024

Fixed

  • Fixed REPL panicking after the first evaluation / run.
  • Fixed globals reloading on each run in the REPL, causing unnecessary slowdowns.
  • Fixed net.serve requests no longer being plain tables in Lune 0.8.1, breaking usage of things such as table.clone.

0.8.1 - March 11th, 2024

Added

  • Added the ability to specify an address in net.serve. (#142)

Changed

  • Update to Luau version 0.616.
  • Major performance improvements when using a large amount of threads / asynchronous Lune APIs. (#165)
  • Minor performance improvements and less overhead for net.serve and net.socket. (#165)

Fixed

  • Fixed fs.copy not working with empty dirs. (#155)
  • Fixed stack overflow when printing tables with cyclic references. (#158)
  • Fixed not being able to yield in net.serve handlers without blocking other requests. (#165)
  • Fixed various scheduler issues / panics. (#165)

0.8.0 - January 14th, 2024

Breaking Changes

  • The Lune CLI now uses subcommands instead of flag options:

    • lune script_name arg1 arg2 arg3 -> lune run script_name arg1 arg2 arg3
    • lune --list -> lune list
    • lune --setup -> lune setup

    This unfortunately hurts ergonomics for quickly running scripts but is a necessary change to allow us to add more commands, such as the new build subcommand.

  • The createdAt, modifiedAt, and accessedAt properties returned from fs.metadata are now DateTime values instead of numbers.

  • The Lune struct has been renamed to Runtime in the Lune rust crate.

Added

  • Added support for compiling single Lune scripts into standalone executables! (#140)

    Example usage:

    -- my_cool_script.luau
    print("Hello, standalone!")
    Terminal window
    > lune build my_cool_script.luau
    # Creates `my_cool_script.exe` (Windows) or `my_cool_script` (macOS / Linux)
    Terminal window
    > ./my_cool_script.exe # Windows
    > ./my_cool_script # macOS / Linux
    > "Hello, standalone!"

    To compile scripts that use require and reference multiple files, a bundler such as darklua should preferrably be used. You may also distribute files alongside the standalone binary, they will still be able to be require-d. This limitation will be lifted in the future and Lune will automatically bundle any referenced scripts.

  • Added support for path aliases using .luaurc config files!

    For full documentation and reference, check out the official Luau RFC, but here’s a quick example:

    .luaurc
    {
    "aliases": {
    "modules": "./some/long/path/to/modules",
    },
    }
    -- ./some/long/path/to/modules/foo.luau
    return { World = "World!" }
    -- ./anywhere/you/want/my_script.luau
    local mod = require("@modules/foo")
    print("Hello, " .. mod.World)
  • Added support for multiple values for a single query, and multiple values for a single header, in net.request. This is a part of the HTTP specification that is not widely used but that may be useful in certain cases. To clarify:

    • Single values remain unchanged and will work exactly the same as before.

      -- https://example.com/?foo=bar&baz=qux
      local net = require("@lune/net")
      net.request({
      url = "example.com",
      query = {
      foo = "bar",
      baz = "qux",
      }
      })
    • Multiple values on a single query / header are represented as an ordered array of strings.

      -- https://example.com/?foo=first&foo=second&foo=third&bar=baz
      local net = require("@lune/net")
      net.request({
      url = "example.com",
      query = {
      foo = { "first", "second", "third" },
      bar = "baz",
      }
      })

Changed

  • Update to Luau version 0.606.

Fixed

  • Fixed the print and warn global functions yielding the thread, preventing them from being used in places such as the callback to table.sort.
  • Fixed the overwrite option for fs.move not correctly removing existing files / directories. (#133)

0.7.11 - October 29th, 2023

Changed

  • Update to Luau version 0.601.

Fixed

  • Fixed roblox.getAuthCookie not being compatible with the latest cookie format by upgrading rbx_cookie.

0.7.10 - October 25th, 2023

Added

  • Added the GetDebugId instance method to the roblox built-in. This will return the internal id used by the instance, and as the name implies, it should be primarily used for debugging purposes and cases where you need a globally unique identifier for an instance. It is guaranteed to be a 32-digit hexadecimal string.

Fixed

  • Fixed issues with SecurityCapabilities on instances in the roblox built-in by upgrading rbx-dom.

0.7.9 - October 21st, 2023

Added

  • Added implementProperty and implementMethod to the roblox built-in library to fill in missing functionality that Lune does not aim to implement itself.

    Example usage:

    local roblox = require("@lune/roblox")
    local part = roblox.Instance.new("Part")
    roblox.implementMethod("BasePart", "TestMethod", function(_, ...)
    print("Tried to call TestMethod with", ...)
    end)
    part:TestMethod("Hello", "world!")

Changed

  • Update to Luau version 0.599.
  • Stdio options when using process.spawn can now be set with more granularity, allowing stderr & stdout to be disabled individually and completely to improve memory usage when they are not being used.

0.7.8 - October 5th, 2023

Added

  • Added a new datetime built-in library for handling date & time values, parsing, formatting, and more. (#94)

    Example usage:

    local DateTime = require("@lune/datetime")
    -- Creates a DateTime for the current exact moment in time
    local now = DateTime.now()
    -- Formats the current moment in time as an ISO 8601 string
    print(now:toIsoDate())
    -- Formats the current moment in time, using the local
    -- time, the French locale, and the specified time string
    print(now:formatLocalTime("%A, %d %B %Y", "fr"))
    -- Returns a specific moment in time as a DateTime instance
    local someDayInTheFuture = DateTime.fromLocalTime({
    year = 3033,
    month = 8,
    day = 26,
    hour = 16,
    minute = 56,
    second = 28,
    millisecond = 892,
    })
    -- Extracts the current local date & time as separate values (same values as above table)
    print(now:toLocalTime())
    -- Returns a DateTime instance from a given float, where the whole
    -- denotes the seconds and the fraction denotes the milliseconds
    -- Note that the fraction for millis here is completely optional
    DateTime.fromUnixTimestamp(871978212313.321)
    -- Extracts the current universal (UTC) date & time as separate values
    print(now:toUniversalTime())
  • Added support for passing stdin in process.spawn (#106)

  • Added support for setting a custom environment in load options for luau.load, not subject to getfenv / setfenv deoptimizations

  • Added Terrain:GetMaterialColor and Terrain:SetMaterialColor (#93)

  • Added support for a variable number of arguments for CFrame methods (#85)

Changed

  • Update to Luau version 0.596.
  • Update to rbx-dom database version 0.596.
  • process.spawn now uses powershell instead of /bin/bash as the shell on Windows, with shell = true.
  • CFrame and Vector3 values are now rounded to the nearest 2 ^ 16 decimal place to reduce floating point errors and diff noise. Note that this does not affect intermediate calculations done in lua, and only happens when a property value is set on an Instance.

Fixed

  • Fixed the process built-in library not loading correctly when using Lune in REPL mode.
  • Fixed list subcommand not listing global scripts without a local .lune / lune directory present.
  • Fixed net.serve stopping when the returned ServeHandle is garbage collected.
  • Fixed missing trailing newline when using the warn global.
  • Fixed constructor for CFrame in the roblox built-in library not parsing the 12-arg overload correctly. (#102)
  • Fixed various functions for CFrame in the roblox built-in library being incorrect, specifically row-column ordering and some flipped signs. (#103)
  • Fixed cross-service Instance references disappearing when using the roblox built-in library (#117)

0.7.7 - August 23rd, 2023

Added

  • Added a REPL to Lune. (#83)

    This allows you to run scripts within Lune without writing files!

    Example usage, inside your favorite terminal:

    Terminal window
    # 1. Run the Lune executable, without any arguments
    lune
    # 2. You will be shown the current Lune version and a blank prompt arrow:
    Lune v0.7.7
    >
    # 3. Start typing, and hit enter when you want to run your script!
    # Your script will run until completion and output things along the way.
    > print(2 + 3)
    5
    > print("Hello, lune changelog!")
    Hello, lune changelog!
    # 4. You can also set variables that will get preserved between runs.
    # Note that local variables do not get preserved here.
    > myVariable = 123
    > print(myVariable)
    123
    # 5. Press either of these key combinations to exit the REPL:
    # - Ctrl + D
    # - Ctrl + C
  • Added a new luau built-in library for manually compiling and loading Luau source code. (#82)

    Example usage:

    local luau = require("@lune/luau")
    local bytecode = luau.compile("print('Hello, World!')")
    local callableFn = luau.load(bytecode)
    callableFn()
    -- Additionally, we can skip the bytecode generation and
    -- load a callable function directly from the code itself.
    local callableFn2 = luau.load("print('Hello, World!')")
    callableFn2()

Changed

  • Update to Luau version 0.591.

  • Lune’s internal task scheduler and require functionality has been completely rewritten.
    The new scheduler is much more stable, conforms to a larger test suite, and has a few additional benefits:

    • Built-in libraries are now lazily loaded, meaning nothing gets allocated until the built-in library gets loaded using require("@lune/builtin-name"). This also improves startup times slightly.
    • Spawned processes using process.spawn now run on different thread(s), freeing up resources for the main thread where luau runs.
    • Serving requests using net.serve now processes requests on background threads, also freeing up resources. In the future, this will also allow us to offload heavy tasks such as compression/decompression to background threads.
    • Groundwork for custom / user-defined require aliases has been implemented, as well as absolute / cwd-relative requires. These will both be exposed as options and be made available to use some time in the future.
  • When using the serde built-in library, keys are now sorted during serialization. This means that the output of encode is now completely deterministic, and wont cause issues when committing generated files to git etc.

Fixed

  • Fixed not being able to pass arguments to the thread using coroutine.resume. (#86)
  • Fixed a large number of long-standing issues, from the task scheduler rewrite:
    • Fixed require hanging indefinitely when the module being require-d uses an async function in its main body.
    • Fixed background tasks (such as net.serve) not keeping Lune alive even if there are no lua threads to run.
    • Fixed spurious panics and error messages such as Tried to resume next queued future but none are queued.
    • Fixed not being able to catch non-string errors properly, errors were accidentally being wrapped in an opaque userdata type.

0.7.6 - August 9th, 2023

Changed

  • Update to Luau version 0.588
  • Enabled Luau JIT backend for potential performance improvements 🚀
    If you run into any strange behavior please open an issue!

Fixed

  • Fixed publishing of the Lune library to crates.io
  • Fixed serde.decode deserializing null values as userdata instead of nil.
  • Fixed not being able to require files with multiple extensions, eg. module.spec.luau was not require-able using require("module.spec").
  • Fixed instances and roblox built-in library APIs erroring when used asynchronously/concurrently.

0.7.5 - July 22nd, 2023

Added

  • Lune now has a new documentation site!
    This addresses new APIs from version 0.7.0 not being available on the docs site, brings much improved searching functionality, and will help us keep documentation more up-to-date going forward with a more automated process. You can check out the new site at lune-org.github.io.

  • Added fs.copy to recursively copy files and directories.

    Example usage:

    local fs = require("@lune/fs")
    fs.writeDir("myCoolDir")
    fs.writeFile("myCoolDir/myAwesomeFile.json", "{}")
    fs.copy("myCoolDir", "myCoolDir2")
    assert(fs.isDir("myCoolDir2"))
    assert(fs.isFile("myCoolDir2/myAwesomeFile.json"))
    assert(fs.readFile("myCoolDir2/myAwesomeFile.json") == "{}")
  • Added fs.metadata to get metadata about files and directories.

    Example usage:

    local fs = require("@lune/fs")
    fs.writeFile("myAwesomeFile.json", "{}")
    local meta = fs.metadata("myAwesomeFile.json")
    print(meta.exists) --> true
    print(meta.kind) --> "file"
    print(meta.createdAt) --> 1689848548.0577152 (unix timestamp)
    print(meta.permissions) --> { readOnly: false }
  • Added roblox.getReflectionDatabase to access the builtin database containing information about classes and enums.

    Example usage:

    local roblox = require("@lune/roblox")
    local db = roblox.getReflectionDatabase()
    print("There are", #db:GetClassNames(), "classes in the reflection database")
    print("All base instance properties:")
    local class = db:GetClass("Instance")
    for name, prop in class.Properties do
    print(string.format(
    "- %s with datatype %s and default value %s",
    prop.Name,
    prop.Datatype,
    tostring(class.DefaultProperties[prop.Name])
    ))
    end
  • Added support for running directories with an init.luau or init.lua file in them in the CLI.

Changed

  • Update to Luau version 0.583

Fixed

  • Fixed publishing of Lune to crates.io by migrating away from a monorepo.
  • Fixed crashes when writing a very deeply nested Instance to a file. (#62)
  • Fixed not being able to read & write to WebSocket objects at the same time. (#68)
  • Fixed tab character at the start of a script causing it not to parse correctly. (#72)

0.7.4 - July 7th, 2023

Added

  • Added support for CFrame and Font types in attributes when using the roblox builtin.

Fixed

  • Fixed roblox.serializeModel still keeping some unique ids.

0.7.3 - July 5th, 2023

Changed

  • When using roblox.serializeModel, Lune will no longer keep internal unique ids.
    This is consistent with what Roblox does and prevents Lune from always generating a new and unique file.
    This previously caused unnecessary diffs when using git or other kinds of source control. (Relevant issue)

0.7.2 - June 28th, 2023

Added

  • Added support for init files in directories, similar to Rojo, or index.js / mod.rs in JavaScript / Rust.
    This means that placing a file named init.luau or init.lua in a directory will now let you require that directory.

Changed

  • The lune --setup command is now much more user-friendly.
  • Update to Luau version 0.581

0.7.1 - June 17th, 2023

Added

  • Added support for TLS in websockets, enabling usage of wss://-prefixed URLs. (#57)

Fixed

  • Fixed closeCode erroring when being accessed on websockets. (#57)
  • Fixed issues with UniqueId when using the roblox builtin by downgrading rbx-dom.

0.7.0 - June 12th, 2023

Breaking Changes

  • Globals for the fs, net, process, stdio, and task builtins have been removed, and the require("@lune/...") syntax is now the only way to access builtin libraries. If you have previously been using a global such as fs directly, you will now need to put local fs = require("@lune/fs") at the top of the file instead.

  • Migrated several functions in the roblox builtin to new, more flexible APIs:

    • readPlaceFile -> deserializePlace
    • readModelFile -> deserializeModel
    • writePlaceFile -> serializePlace
    • writeModelFile -> serializeModel

    These new APIs no longer use file paths, meaning to use them with files you must first read them using the fs builtin.

  • Removed CollectionService and its methods from the roblox builtin library - new instance methods have been added as replacements.

  • Removed Instance:FindFirstDescendant which was a method that was never enabled in the official Roblox API and will soon be removed.
    Use the second argument of the already existing find methods instead to find descendants.

  • Removed the global printinfo function - it was generally not used, and did not work as intended. Use the stdio builtin for formatting and logging instead.

  • Removed support for Windows on ARM - it’s more trouble than its worth right now, we may revisit it later.

Added

  • Added serde.compress and serde.decompress for compressing and decompressing strings using one of several compression formats: brotli, gzip, lz4, or zlib.

    Example usage:

    local INPUT = string.rep("Input string to compress", 16) -- Repeated string 16 times for the purposes of this example
    local serde = require("@lune/serde")
    local compressed = serde.compress("gzip", INPUT)
    local decompressed = serde.decompress("gzip", compressed)
    assert(decompressed == INPUT)
  • Added automatic decompression for compressed responses when using net.request. This behavior can be disabled by passing options = { decompress = false } in request params.

  • Added support for finding scripts in the current home directory. This means that if you have a script called script-name.luau, you can place it in the following location:

    • C:\Users\YourName\.lune\script-name.luau (Windows)
    • /Users/YourName/.lune/script-name.luau (macOS)
    • /home/YourName/.lune/script-name.luau (Linux)

    And then run it using lune script-name from any directory you are currently in.

  • Added several new instance methods in the roblox builtin library:

  • Implemented the second argument of the FindFirstChild / FindFirstChildOfClass / FindFirstChildWhichIsA instance methods.

Changed

  • Update to Luau version 0.579
  • Both stdio.write and stdio.ewrite now support writing arbitrary bytes, instead of only valid UTF-8.

Fixed

  • Fixed stdio.write and stdio.ewrite not being flushed and causing output to be interleaved. (#47)
  • Fixed typeof returning userdata for roblox types such as Instance, Vector3, …

0.6.7 - May 14th, 2023

Added

  • Replaced all of the separate typedef & documentation generation commands with a unified lune --setup command.

    This command will generate type definition files for all of the builtins and will work with the new require("@lune/...") syntax. Note that this also means that there is no longer any way to generate type definitions for globals - this is because they will be removed in the next major release in favor of the beforementioned syntax.

  • New releases now include prebuilt binaries for arm64 / aarch64!
    These new binaries will have names with the following format:

    • lune-windows-0.6.7-aarch64.exe
    • lune-linux-0.6.7-aarch64
    • lune-macos-0.6.7-aarch64
  • Added global types to documentation site

0.6.6 - April 30th, 2023

Added

  • Added tracing / logging for rare and hard to diagnose error cases, which can be configured using the env var RUST_LOG.

Changed

  • The _VERSION global now follows a consistent format Lune x.y.z+luau to allow libraries to check against it for version requirements.

    Examples:

    • Lune 0.0.0+0
    • Lune 1.0.0+500
    • Lune 0.11.22+9999
  • Updated to Luau version 0.573

  • Updated rbx-dom to support reading and writing Font datatypes

Fixed

  • Fixed _G not being a readable & writable table
  • Fixed _G containing normal globals such as print, math, …
  • Fixed using instances as keys in tables

0.6.5 - March 27th, 2023

Changed

  • Functions such as print, warn, … now respect __tostring metamethods.

Fixed

  • Fixed access of roblox instance properties such as Workspace.Terrain, game.Workspace that are actually links to child instances.
    These properties are always guaranteed to exist, and they are not always properly set, meaning they must be found through an internal lookup.
  • Fixed issues with the CFrame.lookAt and CFrame.new(Vector3, Vector3) constructors.
  • Fixed issues with CFrame math operations returning rotation angles in the wrong order.

0.6.4 - March 26th, 2023

Fixed

  • Fixed instances with attributes not saving if they contain integer attributes.
  • Fixed attributes not being set properly if the instance has an empty attributes property.
  • Fixed error messages for reading & writing roblox files not containing the full error message.
  • Fixed crash when trying to access an instance reference property that points to a destroyed instance.
  • Fixed crash when trying to save instances that contain unsupported attribute types.

0.6.3 - March 26th, 2023

Added

  • Added support for instance tags & CollectionService in the roblox built-in.
    Currently implemented methods are listed on the docs site.

Fixed

  • Fixed accessing a destroyed instance printing an error message even if placed inside a pcall.
  • Fixed cloned instances not having correct instance reference properties set (ObjectValue.Value, Motor6D.Part0, …)
  • Fixed Instance::GetDescendants returning the same thing as Instance::GetChildren.

0.6.2 - March 25th, 2023

This release adds some new features and fixes for the roblox built-in.

Added

  • Added GetAttribute, GetAttributes and SetAttribute methods for instances.
  • Added support for getting & setting properties that are instance references.

Changed

  • Improved handling of optional property types such as optional cframes & default physical properties.

Fixed

  • Fixed handling of instance properties that are serialized as binary strings.

0.6.1 - March 22nd, 2023

Fixed

  • Fixed writePlaceFile and writeModelFile in the new roblox built-in making mysterious “ROOT” instances.

0.6.0 - March 22nd, 2023

Added

  • Added a roblox built-in

    If you’re familiar with Remodel, this new built-in contains more or less the same APIs, integrated into Lune.
    There are just too many new APIs to list in this changelog, so head over to the docs sit to learn more!

  • Added a serde built-in

    This built-in contains previously available functions encode and decode from the net global.
    The plan is for this built-in to contain more serialization and encoding functionality in the future.

  • require has been reimplemented and overhauled in several ways:

    • New built-ins such as roblox and serde can only be imported using require("@lune/roblox"), require("@lune/serde"), …
    • Previous globals such as fs, net and others can now also be imported using require("@lune/fs"), require("@lune/net"), …
    • Requiring a script is now completely asynchronous and will not block lua threads other than the caller.
    • Requiring a script will no longer error when using async APIs in the main body of the required script.

    All new built-ins will be added using this syntax and new built-ins will no longer be available in the global scope, and current globals will stay available as globals until proper editor and LSP support is available to ensure Lune users have a good development experience. This is the first step towards moving away from adding each library as a global, and allowing Lune to have more built-in libraries in general.

    Behavior otherwise stays the same, and requires are still relative to file unless the special @ prefix is used.

  • Added net.urlEncode and net.urlDecode for URL-encoding and decoding strings

Changed

  • Renamed the global info function to printinfo to make it less ambiguous

Removed

  • Removed experimental net.encode and net.decode functions, since they are now available using require("@lune/serde")
  • Removed option to preserve default Luau require behavior

0.5.6 - March 11th, 2023

Added

  • Added support for shebangs at the top of a script, meaning scripts such as this one will now run without throwing a syntax error:

    #!/usr/bin/env lune
    print("Hello, world!")

Fixed

  • Fixed fs.writeFile and fs.readFile not working with strings / files that are invalid utf-8

0.5.5 - March 8th, 2023

Added

  • Added support for running scripts by passing absolute file paths in the CLI
    • This does not have the restriction of scripts having to use the .luau or .lua extension, since it is presumed that if you pass an absolute path you know exactly what you are doing

Changed

  • Improved error messages for passing invalid file names / file paths substantially - they now include helpful formatting to make file names distinct from file extensions, and give suggestions on how to solve the problem
  • Improved general formatting of error messages, both in the CLI and for Luau scripts being run

Fixed

  • Fixed the CLI being a bit too picky about file names when trying to run files in lune or .lune directories
  • Fixed documentation misses from large changes made in version 0.5.0

0.5.4 - March 7th, 2023

Added

  • Added support for reading scripts from stdin by passing "-" as the script name
  • Added support for close codes in the net WebSocket APIs:
    • A close code can be sent by passing it to socket.close
    • A received close code can be checked with the socket.closeCode value, which is populated after a socket has been closed - note that using socket.close will not set the close code value, it is only set when received and is guaranteed to exist after closure

Changed

  • Update to Luau version 0.566

Fixed

  • Fixed scripts having to be valid utf8, they may now use any kind of encoding that base Luau supports
  • The net WebSocket APIs will no longer return nil for partial messages being received in socket.next, and will instead wait for the full message to arrive

0.5.3 - February 26th, 2023

Fixed

  • Fixed lune --generate-selene-types generating an invalid Selene definitions file
  • Fixed type definition parsing issues on Windows

0.5.2 - February 26th, 2023

Fixed

  • Fixed crash when using stdio.color() or stdio.style() in a CI environment or non-interactive terminal

0.5.1 - February 25th, 2023

Added

  • Added net.encode and net.decode which are equivalent to net.jsonEncode and net.jsonDecode, but with support for more formats.

    WARNING: Unstable API

    This API is unstable and may change or be removed in the next major version of Lune. The purpose of making a new release with these functions is to gather feedback from the community, and potentially replace the JSON-specific encoding and decoding utilities.

    Example usage:

    local toml = net.decode("toml", [[
    [package]
    name = "my-cool-toml-package"
    version = "0.1.0"
    [values]
    epic = true
    ]])
    assert(toml.package.name == "my-cool-toml-package")
    assert(toml.package.version == "0.1.0")
    assert(toml.values.epic == true)

Fixed

  • Fixed indentation of closing curly bracket when printing tables

0.5.0 - February 23rd, 2023

Added

  • Added auto-generated API reference pages and documentation using GitHub wiki pages
  • Added support for query in net.request parameters, which enables usage of query parameters in URLs without having to manually URL encode values.
  • Added a new function fs.move to move / rename a file or directory from one path to another.
  • Implemented a new task scheduler which resolves several long-standing issues:
    • Issues with yielding across the C-call/metamethod boundary no longer occur when calling certain async APIs that Lune provides.
    • Ordering of interleaved calls to task.spawn/task.defer is now completely deterministic, deferring is now guaranteed to run last even in these cases.
    • The minimum wait time possible when using task.wait and minimum delay time using task.delay are now much smaller, and only limited by the underlying OS implementation. For most systems this means task.wait and task.delay are now accurate down to about 5 milliseconds or less.

Changed

  • Type definitions are now bundled as part of the Lune executable, meaning they no longer need to be downloaded.
    • lune --generate-selene-types will generate the Selene type definitions file, replacing lune --download-selene-types
    • lune --generate-luau-types will generate the Luau type definitions file, replacing lune --download-luau-types
  • Improved accuracy of Selene type definitions, strongly typed arrays are now used where possible
  • Improved error handling and messages for net.serve
  • Improved error handling and messages for stdio.prompt
  • File path representations on Windows now use legacy paths instead of UNC paths wherever possible, preventing some confusing cases where file paths don’t work as expected

Fixed

  • Fixed process.cwd not having the correct ending path separator on Windows
  • Fixed remaining edge cases where the task and coroutine libraries weren’t interoperable
  • Fixed task.delay keeping the script running even if it was cancelled using task.cancel
  • Fixed stdio.prompt blocking all other lua threads while prompting for input

0.4.0 - February 11th, 2023

Added

  • Web Sockets

    net now supports web sockets for both clients and servers!
    Note that the web socket object is identical on both client and server, but how you retrieve a web socket object is different.

    Server API

    The server web socket API is an extension of the existing net.serve function.
    This allows for serving both normal HTTP requests and web socket requests on the same port.

    Example usage:

    net.serve(8080, {
    handleRequest = function(request)
    return "Hello, world!"
    end,
    handleWebSocket = function(socket)
    task.delay(10, function()
    socket.send("Timed out!")
    socket.close()
    end)
    -- The message will be nil when the socket has closed
    repeat
    local messageFromClient = socket.next()
    if messageFromClient == "Ping" then
    socket.send("Pong")
    end
    until messageFromClient == nil
    end,
    })

    Client API

    Example usage:

    local socket = net.socket("ws://localhost:8080")
    socket.send("Ping")
    task.delay(5, function()
    socket.close()
    end)
    -- The message will be nil when the socket has closed
    repeat
    local messageFromServer = socket.next()
    if messageFromServer == "Ping" then
    socket.send("Pong")
    end
    until messageFromServer == nil

Changed

  • net.serve now returns a NetServeHandle which can be used to stop serving requests safely.

    Example usage:

    local handle = net.serve(8080, function()
    return "Hello, world!"
    end)
    print("Shutting down after 1 second...")
    task.wait(1)
    handle.stop()
    print("Shut down succesfully")
  • The third and optional argument of process.spawn is now a global type ProcessSpawnOptions.

  • Setting cwd in the options for process.spawn to a path starting with a tilde (~) will now use a path relative to the platform-specific home / user directory.

  • NetRequest query parameters value has been changed to be a table of key-value pairs similar to process.env. If any query parameter is specified more than once in the request url, the value chosen will be the last one that was specified.

  • The internal http client for net.request now reuses headers and connections for more efficient requests.

  • Refactored the Lune rust crate to be much more user-friendly and documented all of the public functions.

Fixed

  • Fixed process.spawn blocking all lua threads if the spawned child process yields.

0.3.0 - February 6th, 2023

Added

  • Added a new global stdio which replaces console

  • Added stdio.write which writes a string directly to stdout, without any newlines

  • Added stdio.ewrite which writes a string directly to stderr, without any newlines

  • Added stdio.prompt which will prompt the user for different kinds of input

    Example usage:

    local text = stdio.prompt()
    local text2 = stdio.prompt("text", "Please write some text")
    local didConfirm = stdio.prompt("confirm", "Please confirm this action")
    local optionIndex = stdio.prompt("select", "Please select an option", { "one", "two", "three" })
    local optionIndices = stdio.prompt(
    "multiselect",
    "Please select one or more options",
    { "one", "two", "three", "four", "five" }
    )

Changed

  • Migrated console.setColor/resetColor and console.setStyle/resetStyle to stdio.color and stdio.style to allow for more flexibility in custom printing using ANSI color codes. Check the documentation for new usage and behavior.
  • Migrated the pretty-printing and formatting behavior of console.log/info/warn/error to the standard Luau printing functions.

Removed

  • Removed printing functions console.log/info/warn/error in favor of regular global functions for printing.

Fixed

  • Fixed scripts hanging indefinitely on error

0.2.2 - February 5th, 2023

Added

  • Added global types for networking & child process APIs
    • net.request gets NetFetchParams and NetFetchResponse for its argument and return value
    • net.serve gets NetRequest and NetResponse for the handler function argument and return value
    • process.spawn gets ProcessSpawnOptions for its third and optional parameter

Changed

  • Reorganize repository structure to take advantage of cargo workspaces, improves compile times

0.2.1 - February 3rd, 2023

Added

  • Added support for string interpolation syntax (update to Luau 0.561)

  • Added network server functionality using net.serve

    Example usage:

    net.serve(8080, function(request)
    print(`Got a {request.method} request at {request.path}!`)
    local data = net.jsonDecode(request.body)
    -- For simple text responses with a 200 status
    return "OK"
    -- For anything else
    return {
    status = 203,
    headers = { ["Content-Type"] = "application/json" },
    body = net.jsonEncode({
    message = "echo",
    data = data,
    })
    }
    end)

Changed

  • Improved type definitions file for Selene, now including constants like process.env + tags such as readonly and mustuse wherever applicable

Fixed

  • Fixed type definitions file for Selene not including all API members and parameters
  • Fixed process.exit exiting at the first yield instead of exiting instantly as it should

0.2.0 - January 28th, 2023

Added

  • Added full documentation for all global APIs provided by Lune! This includes over 200 lines of pure documentation about behavior & error cases for all of the current 35 constants & functions. Check the README to find out how to enable documentation in your editor.

  • Added a third argument options for process.spawn:

    • cwd - The current working directory for the process
    • env - Extra environment variables to give to the process
    • shell - Whether to run in a shell or not - set to true to run using the default shell, or a string to run using a specific shell
    • stdio - How to treat output and error streams from the child process - set to "inherit" to pass output and error streams to the current process
  • Added process.cwd, the path to the current working directory in which the Lune script is running

0.1.3 - January 25th, 2023

Added

  • Added a --list subcommand to list scripts found in the lune or .lune directory.

0.1.2 - January 24th, 2023

Added

  • Added automatic publishing of the Lune library to crates.io

Fixed

  • Fixed scripts that terminate instantly sometimes hanging

0.1.1 - January 24th, 2023

Fixed

  • Fixed errors containing ./ and / or ../ in the middle of file paths
  • Potential fix for spawned processes that yield erroring with “attempt to yield across metamethod/c-call boundary”

0.1.0 - January 24th, 2023

Added

  • task now supports passing arguments in task.spawn / task.delay / task.defer
  • require now uses paths relative to the file instead of being relative to the current directory, which is consistent with almost all other languages but not original Lua / Luau - this is a breaking change but will allow for proper packaging of third-party modules and more in the future.
    • NOTE: If you still want to use the default Lua behavior instead of relative paths, set the environment variable LUAU_PWD_REQUIRE to true

Changed

  • Improved error message when an invalid file path is passed to require
  • Much improved error formatting and stack traces

Fixed

  • Fixed downloading of type definitions making json files instead of the proper format
  • Process termination will now always make sure all lua state is cleaned up before exiting, in all cases

0.0.6 - January 23rd, 2023

Added

  • Initial implementation of Roblox’s task library, with some caveats:

    • Minimum wait / delay time is currently set to 10ms, subject to change
    • It is not yet possible to pass arguments to tasks created using task.spawn / task.delay / task.defer
    • Timings for task.defer are flaky and deferred tasks are not (yet) guaranteed to run after spawned tasks

    With all that said, everything else should be stable!

    • Mixing and matching the coroutine library with task works in all cases
    • process.exit() will stop all spawned / delayed / deferred threads and exit the process
    • Lune is guaranteed to keep running until there are no longer any waiting threads

    If any of the abovementioned things do not work as expected, it is a bug, please file an issue!

Fixed

  • Potential fix for spawned processes that yield erroring with “attempt to yield across metamethod/c-call boundary”

0.0.5 - January 22nd, 2023

Added

  • Added full test suites for all Lune globals to ensure correct behavior
  • Added library version of Lune that can be used from other Rust projects

Changed

  • Large internal changes to allow for implementing the task library.
  • Improved general formatting of errors to make them more readable & glanceable
  • Improved output formatting of non-primitive types
  • Improved output formatting of empty tables

Fixed

  • Fixed double stack trace for certain kinds of errors

0.0.4 - January 21st, 2023

Added

  • Added process.args for inspecting values given to Lune when running (read only)
  • Added process.env which is a plain table where you can get & set environment variables

Changed

  • Improved error formatting & added proper file name to stack traces

Removed

  • Removed ... for process arguments, use process.args instead
  • Removed individual functions for getting & setting environment variables, use process.env instead

0.0.3 - January 20th, 2023

Added

  • Added networking functions under net

    Example usage:

    local apiResult = net.request({
    url = "https://jsonplaceholder.typicode.com/posts/1",
    method = "PATCH",
    headers = {
    ["Content-Type"] = "application/json",
    },
    body = net.jsonEncode({
    title = "foo",
    body = "bar",
    }),
    })
    local apiResponse = net.jsonDecode(apiResult.body)
    assert(apiResponse.title == "foo", "Invalid json response")
    assert(apiResponse.body == "bar", "Invalid json response")
  • Added console logging & coloring functions under console

    This piece of code:

    local tab = { Integer = 1234, Hello = { "World" } }
    console.log(tab)

    Will print the following formatted text to the console, with syntax highlighting:

    {
    Integer = 1234,
    Hello = {
    "World",
    }
    }

    Additional utility functions exist with the same behavior but that also print out a colored tag together with any data given to them: console.info, console.warn, console.error - These print out prefix tags [INFO], [WARN], [ERROR] in blue, orange, and red, respectively.

Changed

  • The json api is now part of net
    • json.encode becomes net.jsonEncode
    • json.decode become net.jsonDecode

Fixed

  • Fixed JSON decode not working properly

0.0.2 - January 19th, 2023

Added

  • Added support for command-line parameters to scripts

    These can be accessed as a vararg in the root of a script:

    local firstArg: string, secondArg: string = ...
    print(firstArg, secondArg)
  • Added CLI parameters for downloading type definitions:

    • lune --download-selene-types to download Selene types to the current directory
    • lune --download-luau-types to download Luau types to the current directory

    These files will be downloaded as lune.yml and luneTypes.d.luau respectively and are also available in each release on GitHub.

0.0.1 - January 18th, 2023

Initial Release