Sabre v0.4.0

Written by Reuben Roessler@ Sat Oct 10 2026

Installing Sabre

curl -fsSL https://sabre.rroessler.io/install.sh | bash

Upgrading Sabre

Terminal
sabre upgrade

Environment Files

With this release of Sabre, your program will have the capability to load environment files and read/write environment variables of the running process. To use this feature, the --env file flag has been implemented:

# Automatically find a potential ".env" file in you current working directory.
sabre run --env ./script.sabre

# Manually specify a desired environment file (any number of files can be loaded).
sabre run --env .env ./script.sabre

To then read these environment variables at runtime, the sabre:env crate can be used:

import "sabre:env" as Env;

let my_var = Env.get("MY_VAR");
Debug.println("MY_VAR: {0}".fmt(my_var));

Note:

Sabre currently supports the standard Dotenv syntax.

Format Flags

Previously the sabre format command used a default configuration when called. Specialized formatting could only be requested through the use of the VSC extension for Sabre. This has now be fixed so that unless specified, files to be formatted will use their parent _crate.jsonc formatting options.

# This will look for a "_crate.jsonc" file in each directory upwards from "script.sabre"
sabre format ./script.sabre

# Otherwise you can manually specify an alternative configuration to the used
sabre format --config ./configs/format.jsonc ./script.sabre

Testing Features

Sabre's test runner now has more feature flags and options implemented for improved support.

Bailout After failures

The --bailout flag can be used to abort running tests after a given number of tests fail. By default, Sabre runs all tests and reports all failures, however running testing in a CI it can be preferrable to exit early to reduce usage.

# Exit after exactly 1 failure
sabre test --bailout

# Exit after a given number of failures
sabre test --bailout nn

Randomize Execution Order

To run tests in a randomized order, the --randomize or --seed flags can be use. This helps detect tests that rely upon shared state or a particular execution order.

# This will full randomize the execution order and print the "seed" used
sabre test --randomize

To then reproduce randomization results, the --seed flag can be used. This can be useful to always produce the same randomized test execution order, or run tests in the same order as a previous --randomize run.

# Reproduce a specific randomized run
sabre test --seed 123456

Note:

The --seed flag automatically sets the --randomize flag, so only one ever needs to be specified.

Reporting Improvements

Sabre aims to support a variety of reporting formats, both built-in and custom. These can be specified with the --reporter option:

# Explicitly setting a desired reporter
sabre test --reporter text

Note:

Currently on the text reporter has been implemented. More to come in the future for other reporter formats.

Additionally, test results can be piped to a desired output using the --outfile option:

# Pipe output to a desired location
sabre test --outfile report.txt

Buffer Builtin

To improve performance and simplify the representation of filesystem outputs, the Buffer built has been created. Synonymous with the Buffer object in Node JS, it is a fixed-length sequence of bytes. The reasoning for adding this builtin is to directly map internal buffers from the filesystem or other sources to Sabre. This will help developers by creating a lossless representation of various internal and external buffers. For now it has been created alongside the the sabre:fs crate to directly map to file contents.

// We can simply encode some arbitrary text.
let buffer = Buffer.encode("Hello, World!");

// Then decode it at a later point with an optional encoding.
let text = Buffer.decode(buffer);

// This will then print "Hello, World!" to the console.
Debug.println(text);

Filesystem Crate

The sabre:fs crate has had some simple methods implemented to start exposing low-level filesystem operations. This includes the following:

// Get the filesystem crate.
import "sabre:fs" as FS;

// Create a temporary file that can be automictically disposed.
use temporary = FS.temp_file();

// This generates a randomly named temporary file.
Debug.println("Path:", temporary.path());

// We can then verify that the entry exists on the system.
Debug.println("Entry exists:", FS.exists_entry(temporary.path()));

// Or more explicitly that is a file that exists on the system.
Debug.println("File exists:", FS.exists_file(temporary.path()));

// Prepare a desired set of content for the temporary file.
let input = "Content for my temporary file";

// Let's write some arbitrary content to the file.
FS.write_text(temporary.path(), input);

// Or we can write raw buffers directly.
FS.write_file(temporary.path(), Buffer.encode(input));

// Read the buffer of a file is then just as simple.
Debug.println("Buffer:", FS.read_file(temporary.path()));

// Or read the decoded buffer of a file as well.
Debug.println("Value: '{0}'".fmt(FS.read_text(temporary.path())));

// If the file was manually created without resource disposal, it can also be removed manually.
FS.remove_entry(temporary.path());

Deference Policy

To further improve the use of explicit resource manager, the ::defer execution policy has been created. This allows creating a Disposable value from a given callback that can be executed at the end of a scope. This is useful for running one-off cleanup handlers or other end-of-scope routines.

// This callback cleans up constructed resources.
let cleanup = fn (value: Any) {
    Debug.println("Cleanup Called!");
    Debug.println("Input Argument:", value);
};

// Call our callback with the execution policy attached.
use scoped: Disposable = cleanup::defer(42);

// After the scope ends, our bound callback will be called.
Debug.println("Program Running!");

Upcoming Work

This release of Sabre started laying further foundations for ongoing builtin crate support, whilst also continuing to maintain correctness and improve developer experience. Following this, the core goals outlined from the previous progress reports still applies. This being to continue to improve developer features to then expand the builtin crates. Keep tuned to the next available progress report for more information!


Bug Fixes

Package / crates

  • Moved: Removed the sabre:codec crate as it is currently unnecessary.

Package / vscode

  • Fixed: The settings open command now correctly opens the user-settings for Sabre.

Package / website

  • Added: There is now a https://sabre.rroessler.io/rss.xml RSS feed endpoint.

Toolkit / sabre bundle

  • Fixed: Removed unnecessary copies of the base binary to be bundled improving bundling speed.

Toolkit / sabre format

  • Added: Implemented the --config option for defining a common manifest for formatting configuration.
  • Moved: Removed the --json flag for formatting since it requires diffing of two strings to implement.

Toolkit / sabre run

  • Added: Implemented the --env option for defining embeddable environment variables.
  • Fixed: Improved reading speed of files by eliminating an erroneous copy into internal storage.
  • Added: Prepared foundations for the Buffer builtin type to be used for fixed-length contiguous sequences of bytes.
  • Fixed: Commit 2ad55c4 introduced a nullptr stack reference for jitted functions. This has been reverted.
  • Fixed: Class members immutability was previously inverted and has now been corrected to match the type-system.
  • Added: Implemented the List.swap method for swapping two values by indices in a List value.
  • Added: The internal Object::Instance value now expects keys to be assigned to each field reference value. This will allow for serialization and reflection of Object values.

Toolkit / sabre serve

  • Fixed: Changed LSP analysis to refresh the entire project on changes. Currently a minor bug where partial refreshing does not work.

Toolkit / sabre test

  • Added: Implemented the --seed option can be used to define a randomization seed for testing.
  • Added: Implemented the --randomize flag can be used to shuffle test ordering for all tests.
  • Added: Implemented the --outfile option for defining a file to pipe testing reports to.
  • Added: Implemented the --reporter option, however only the text reporter is currently implemented.
  • Added: Implemented the --bailout option for setting a threshold of test failures to bail at.

Crate / sabre:env

  • Added: Prepared the env crate for managing the process environment.
  • Added: Implemented the get function for getting environment variables.
  • Added: Implemented the set function for setting environment variables.
  • Added: Implemented the drop function for deleting environment variables.

Crate / sabre:fs

  • Moved: Renamed the fsys crate to fs for upcoming work with the builtin crates.
  • Added: Implemented the read_file method for reading files to a Buffer value.
  • Added: Implemented the read_text method for reading files to a String value.
  • Added: Implemented the write_file method for writing files with a Buffer value.
  • Added: Implemented the write_text method for writing files with a String value.
  • Added: Implemented the remove_entry method for removing entries from the filesystem.
  • Added: Implemented the temp_file method for generating temporary files.
  • Added: Implemented the exists_file method for checking a file entry exists.
  • Added: Implemented the exists_entry method for checking a any filesystem entry exists.

Crate / sabre:os

  • Added: Prepared a stub crate for the os crate for operating system interface methods.