diff --git a/.lune/hello_lune.luau b/.lune/hello_lune.luau index c50fd7b..ed152d9 100644 --- a/.lune/hello_lune.luau +++ b/.lune/hello_lune.luau @@ -1,6 +1,7 @@ local fs = require("@lune/fs") local net = require("@lune/net") local process = require("@lune/process") +local serde = require("@lune/serde") local stdio = require("@lune/stdio") local task = require("@lune/task") @@ -145,10 +146,8 @@ local result = process.exec("ping", { if result.ok then assert(#result.stdout > 0, "Result output was empty") - local min, avg, max, stddev = string.match( - result.stdout, - "min/avg/max/stddev = ([%d%.]+)/([%d%.]+)/([%d%.]+)/([%d%.]+) ms" - ) + local min, avg, max, stddev = + string.match(result.stdout, "min/avg/max/stddev = ([%d%.]+)/([%d%.]+)/([%d%.]+)/([%d%.]+) ms") print(string.format("Minimum ping time: %.3fms", assert(tonumber(min)))) print(string.format("Maximum ping time: %.3fms", assert(tonumber(max)))) print(string.format("Average ping time: %.3fms", assert(tonumber(avg)))) @@ -172,7 +171,7 @@ local apiResult = net.request({ headers = { ["Content-Type"] = "application/json", } :: { [string]: string }, - body = net.jsonEncode({ + body = serde.encode("json", { title = "foo", body = "bar", }), @@ -192,7 +191,7 @@ type ApiResponse = { userId: number, } -local apiResponse: ApiResponse = net.jsonDecode(apiResult.body) +local apiResponse: ApiResponse = serde.decode("json", apiResult.body) assert(apiResponse.title == "foo", "Invalid json response") assert(apiResponse.body == "bar", "Invalid json response") print("Got valid JSON response with changes applied") diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c3591..b82d685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,43 +10,96 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## `0.9.0` -### Breaking changes +This release has been a long time coming, and many breaking changes have been deferred until this version. +If you are an existing Lune user upgrading to this version, you will **most likely** be affected - please read the full list of breaking changes below. -- Added two new process spawning functions - `process.create` and `process.exec`, removing the previous `process.spawn` API completely. ([#211]) +### Breaking changes & additions - To migrate from `process.spawn`, use the new `process.exec` API which retains the same behavior as the old function. +- The behavior of `require` has changed, according to the latest Luau RFCs and specifications. + + For the full details, feel free to read documentation [here](https://github.com/luau-lang/rfcs), 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 stdio of the process. + read and write to standard input and output streams of the child process. ```lua local child = process.create("program", { - "cli-argument", - "other-cli-argument" + "first-argument", + "second-argument" }) -- Writing to stdin child.stdin:write("Hello from Lune!") - -- Reading from stdout + -- Reading partial data from stdout local data = child.stdout:read() - print(buffer.tostring(data)) + 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. -- `Runtime::run` now returns a more useful value instead of an `ExitCode` ([#178]) +- 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 [#178]: https://github.com/lune-org/lune/pull/178 [#211]: https://github.com/lune-org/lune/pull/211 [#248]: https://github.com/lune-org/lune/pull/248 [#250]: https://github.com/lune-org/lune/pull/250 +[#265]: https://github.com/lune-org/lune/pull/265 +[#267]: https://github.com/lune-org/lune/pull/267 +[#284]: https://github.com/lune-org/lune/pull/284 +[#285]: https://github.com/lune-org/lune/pull/285 +[#288]: https://github.com/lune-org/lune/pull/288 +[#305]: https://github.com/lune-org/lune/pull/305 +[#306]: https://github.com/lune-org/lune/pull/306 ## `0.8.9` - October 7th, 2024 diff --git a/Cargo.lock b/Cargo.lock index 623019e..145450d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,9 +473,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", diff --git a/crates/lune-std-net/src/lib.rs b/crates/lune-std-net/src/lib.rs index 0d43b40..334a302 100644 --- a/crates/lune-std-net/src/lib.rs +++ b/crates/lune-std-net/src/lib.rs @@ -1,6 +1,5 @@ #![allow(clippy::cargo_common_metadata)] -use bstr::BString; use mlua::prelude::*; use mlua_luau_scheduler::LuaSpawnExt; @@ -20,8 +19,6 @@ use self::{ websocket::NetWebSocket, }; -use lune_std_serde::{decode, encode, EncodeDecodeConfig, EncodeDecodeFormat}; - const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau")); /** @@ -45,8 +42,6 @@ pub fn module(lua: Lua) -> LuaResult { .build()? .into_registry(&lua); TableBuilder::new(lua)? - .with_function("jsonEncode", net_json_encode)? - .with_function("jsonDecode", net_json_decode)? .with_async_function("request", net_request)? .with_async_function("socket", net_socket)? .with_async_function("serve", net_serve)? @@ -55,16 +50,6 @@ pub fn module(lua: Lua) -> LuaResult { .build_readonly() } -fn net_json_encode(lua: &Lua, (val, pretty): (LuaValue, Option)) -> LuaResult { - let config = EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default())); - encode(val, lua, config) -} - -fn net_json_decode(lua: &Lua, json: BString) -> LuaResult { - let config = EncodeDecodeConfig::from(EncodeDecodeFormat::Json); - decode(json, lua, config) -} - async fn net_request(lua: Lua, config: RequestConfig) -> LuaResult { let client = NetClient::from_registry(&lua); // NOTE: We spawn the request as a background task to free up resources in lua diff --git a/crates/lune-std-net/types.d.luau b/crates/lune-std-net/types.d.luau index 7a7204b..b404a13 100644 --- a/crates/lune-std-net/types.d.luau +++ b/crates/lune-std-net/types.d.luau @@ -188,6 +188,7 @@ export type WebSocket = { ```lua local net = require("@lune/net") + local serde = require("@lune/serde") -- Sending a web request local response = net.request("https://www.google.com") @@ -200,14 +201,14 @@ export type WebSocket = { url = "https://dummyjson.com/products/add", method = "POST", headers = { ["Content-Type"] = "application/json" }, - body = net.jsonEncode({ + body = serde.encode("json", { title = "Cool Pencil", }) }) - local product = net.jsonDecode(response.body) + local product = serde.decode("json", response.body) print(product.id, "-", product.title) - -- Starting up a webserver + -- Starting up an http server net.serve(8080, function(request) return { status = 200, @@ -263,33 +264,6 @@ function net.serve(port: number, handlerOrConfig: ServeHttpHandler | ServeConfig return nil :: any end ---[=[ - @within Net - @tag must_use - - Encodes the given value as JSON. - - @param value The value to encode as JSON - @param pretty If the encoded JSON string should include newlines and spaces. Defaults to false - @return The encoded JSON string -]=] -function net.jsonEncode(value: any, pretty: boolean?): string - return nil :: any -end - ---[=[ - @within Net - @tag must_use - - Decodes the given JSON string into a lua value. - - @param encoded The JSON string to decode - @return The decoded lua value -]=] -function net.jsonDecode(encoded: string): any - return nil :: any -end - --[=[ @within Net @tag must_use diff --git a/crates/lune-std-roblox/types.d.luau b/crates/lune-std-roblox/types.d.luau index 0011be1..ac1563e 100644 --- a/crates/lune-std-roblox/types.d.luau +++ b/crates/lune-std-roblox/types.d.luau @@ -330,6 +330,7 @@ end ```lua local roblox = require("@lune/roblox") + local serde = require("@lune/serde") local net = require("@lune/net") local cookie = roblox.getAuthCookie() @@ -344,7 +345,7 @@ end }, }) - local responseTable = net.jsonDecode(response.body) + local responseTable = serde.decode("json", response.body) local responseLocation = responseTable.locations[1].location print("Download link to place: " .. responseLocation) ``` diff --git a/scripts/brick_color.luau b/scripts/brick_color.luau index 1bf66ed..87a53c7 100644 --- a/scripts/brick_color.luau +++ b/scripts/brick_color.luau @@ -1,10 +1,11 @@ local fs = require("@lune/fs") local net = require("@lune/net") +local serde = require("@lune/serde") local URL = "https://gist.githubusercontent.com/Anaminus/49ac255a68e7a7bc3cdd72b602d5071f/raw/f1534dcae312dbfda716b7677f8ac338b565afc3/BrickColor.json" -local json = net.jsonDecode(net.request(URL).body) +local json = serde.decode("json", net.request(URL).body) local contents = "" diff --git a/tests/net/request/compression.luau b/tests/net/request/compression.luau index 8399fff..b6fc52f 100644 --- a/tests/net/request/compression.luau +++ b/tests/net/request/compression.luau @@ -1,4 +1,5 @@ local net = require("@lune/net") +local serde = require("@lune/serde") -- Should decompress automatically by default @@ -17,7 +18,7 @@ assert( .. tostring(response.statusMessage) ) -local success, json = pcall(net.jsonDecode, response.body) +local success, json = pcall(serde.decode, "json" :: "json", response.body) assert(success, "Failed to decode json response\n" .. tostring(json)) -- Content encoding header should no longer exist when automatically decompressed @@ -45,7 +46,7 @@ assert( .. tostring(response2.statusMessage) ) -local success2 = pcall(net.jsonDecode, response2.body) +local success2 = pcall(serde.decode, "json" :: "json", response2.body) assert(not success2, "Decompression disabled still returned json response") -- Content encoding header should still exist when not automatically decompressed diff --git a/tests/net/request/query.luau b/tests/net/request/query.luau index d0d55ba..cba0fcc 100644 --- a/tests/net/request/query.luau +++ b/tests/net/request/query.luau @@ -1,4 +1,5 @@ local net = require("@lune/net") +local serde = require("@lune/serde") local QUERY: { [string]: string } = { Key = "Value", @@ -24,7 +25,7 @@ assert( -- We should get a json response here with an "args" table which is our query -local success, json = pcall(net.jsonDecode, response.body) +local success, json = pcall(serde.decode, "json" :: "json", response.body) assert(success, "Failed to decode json response\n" .. tostring(json)) local args = if type(json.args) == "table" then json.args else nil diff --git a/tests/net/request/user_agent.luau b/tests/net/request/user_agent.luau index 04491b7..0b1bb7c 100644 --- a/tests/net/request/user_agent.luau +++ b/tests/net/request/user_agent.luau @@ -1,9 +1,10 @@ local net = require("@lune/net") +local serde = require("@lune/serde") local runtime, version = table.unpack(_VERSION:split(" ")) local expectedUserAgent = runtime:lower() .. "/" .. version local userAgent: string = - net.jsonDecode(net.request("https://www.whatsmyua.info/api/v1/ua").body)[1].ua.rawUa + serde.decode("json", net.request("https://www.whatsmyua.info/api/v1/ua").body)[1].ua.rawUa assert(userAgent == expectedUserAgent, "Expected user agent to be " .. expectedUserAgent) diff --git a/tests/roblox/instance/custom/async.luau b/tests/roblox/instance/custom/async.luau index 8df4d52..471f143 100644 --- a/tests/roblox/instance/custom/async.luau +++ b/tests/roblox/instance/custom/async.luau @@ -1,5 +1,6 @@ local net = require("@lune/net") local roblox = require("@lune/roblox") +local serde = require("@lune/serde") roblox.implementMethod("HttpService", "GetAsync", function(_, url: string) local response = net.request({ @@ -10,7 +11,7 @@ roblox.implementMethod("HttpService", "GetAsync", function(_, url: string) end) roblox.implementMethod("HttpService", "JSONDecode", function(_, value) - return net.jsonDecode(value) + return serde.decode("json", value) end) -- Reference: https://create.roblox.com/docs/reference/engine/classes/HttpService#GetAsync