For the complete documentation index, see llms.txt. This page is also available as Markdown.

1.12.0

April 8, 2026

BoxLang 1.12.0 marks a meaningful turning point: after establishing a rock-solid foundation across our runtime, compiler, CFML compatibility layer, and module ecosystem, BoxLang has entered its innovation cycle. The language is mature, battle-tested, and production-deployed by many custmoers now.

Now we're accelerating forward with the kinds of expressive, modern features that make writing BoxLang genuinely enjoyable.

This release packs a lot. On the language side, you get full struct and array destructuring, spread syntax in both literals and function calls, and a new and clean .. range operator — features you'd expect from any contemporary dynamic language, now native to BoxLang.

The new directory and file watcher framework brings real-time filesystem monitoring with a flexible listener API that integrates directly with any Application.bx, globally or a-la-carte. Custom assert messages, a new getLocalHostIp() BIF, and pluggable getSystemSetting() namespace providers round out the feature set. There are also meaningful performance wins: a new method-handle caching strategy, a Java CDS shared archive for faster cold starts, and improved struct iteration.

Under the hood, 1.12.0 continues to sharpen the parser, broaden JDBC coverage, improve compat fidelity, and squash a long list of bugs across locales, date formatting, Oracle stored procedures, symlinks, and more.

If you haven't yet adopted BoxLang for production work, 1.12.0 is an excellent release to do so. The platform is stable, the community is growing, and the language is only getting better from here.

🚀 Major Highlights

🧩 Struct and Array Destructuring

BoxLang now supports destructuring for both structs and arrays.

If you're new to the term, destructuring is a concise way to pull values out of a struct or array and assign them to variables in a single step, instead of reading each value manually one at a time.

user = { name: "Luis", role: "admin", meta: { team: "core" } }
({ name, meta: { team } } = user)

numbers = [ 1, 2, 3, 4, 5 ]
[ first, ...middle, last ] = numbers

In this example, the struct assignment pulls name from user.name and team from the nested user.meta.team. The array assignment pulls out the first and last values, while ...middle captures everything in between as an array. In both cases, destructuring lets you unpack data in one statement instead of writing several separate assignments.

Highlights:

  • Struct destructuring with shorthand and explicit renaming

  • Array destructuring with positional bindings

  • Nested destructuring patterns

  • Default values

  • Rest capture

  • Scoped assignment targets when explicitly named

  • Middle-rest support for arrays

🌟 Spread Syntax

BoxLang now supports spread syntax in literals and function calls.

If you're new to the term, spread syntax lets you take the contents of an existing array or struct and expand them into a function call or into a new array or struct literal.

In this example, ...args expands the array so its values are passed to add() as normal arguments. In the struct literal, ...base copies the existing keys from base into config alongside the other values you declare. In the array literal, ...left and ...right insert the contents of those arrays into merged instead of nesting them. Spread syntax is essentially a way to expand an existing collection into a new call or literal.

Highlights:

  • Function-call spread for arrays and structs

  • Array literal spread

  • Unordered and ordered struct literal spread

  • Multi-source merging with declaration-order precedence

  • Struct shorthand keys like { host, port }

🔢 Inclusive Range Operator

BoxLang now supports the inclusive .. range operator.

If you're new to the term, a range operator is a compact way to describe a sequence of values between two endpoints. In BoxLang, .. creates an inclusive integer range, which means it includes both the starting and ending numbers.

In this example, 1..5 produces all integers from 1 through 5, while 5..1 produces the same kind of range in descending order. a..b means the start and end values can come from variables or expressions, so the final range is determined at runtime based on whatever a and b contain.

This gives BoxLang a compact way to generate ascending or descending integer ranges.

👀 Directory and File Watchers

BoxLang now ships with a built-in WatcherService for monitoring directories and reacting to filesystem events in real time. Watchers support multiple listener patterns and integrate cleanly with the application framework.

Four listener patterns are supported:

  • Closure — single function receives all events

  • Struct of closures — per-kind handlers (onCreate, onModify, onDelete, onOverflow) plus optional onEvent catch-all

  • Class name string — runtime instantiates and calls the class

  • Class instance — a pre-created BoxLang class instance is reused across all events

Application-scoped watchers can also be declared directly in Application.bx via this.watchers and are auto-started-stopped with the application life-cycle.

Watchers can also be configured globally in boxlang.json under the watchers key, allowing you to establish file system monitoring at runtime startup without requiring an Application.bx.

New Watcher BIFs:

BIF
Purpose

watcherNew()

Create and register a new filesystem watcher without starting it

watcherStart()

Start a registered filesystem watcher by name

watcherStop()

Stop a running filesystem watcher by name

watcherRestart()

Restart a filesystem watcher by name (stop then start)

watcherShutdown()

Stop and unregister a filesystem watcher by name

watcherStopAll()

Stop all running filesystem watchers without removing them from the registry

watcherShutdownAll()

Stop all filesystem watchers and remove them from the registry

watcherList()

Return an array of all registered filesystem watcher names

watcherExists()

Check whether a filesystem watcher with the given name is registered

watcherGet()

Get a registered filesystem watcher by name

watcherGetAll()

Return all registered filesystem watchers as a struct keyed by name

See Directory + File Watchers for the full API, BIF reference, listener contracts, and global boxlang.json configuration examples.

✅ Custom assert Messages

The assert statement now accepts a custom message after a colon, so you can provide more context when an assertion fails. This message is included in the thrown AssertException if the assertion condition evaluates to falsy.

🌍 getLocalHostIp() BIF

A new getLocalHostIp() BIF returns the resolved IP address(es) of the local machine:

⚙️ getSystemSetting() Pluggable Namespace Providers

getSystemSetting() now supports pluggable namespace providers, enabling modules to register custom namespaces for system-setting resolution. Provider lookup is chained so the first match wins.

🛑 Custom Error Pages (Web Runtime)

Web applications can now configure custom error pages through Application.bx or the runtime configuration, replacing the default BoxLang error output with branded or user-friendly HTML.

🔄 CFC Script Wrappers

CFML compatibility now includes script wrapper support for CFC tags, ensuring that ColdFusion CFC patterns compile and execute correctly inside BoxLang.

📝 Arbitrary Java Logging Category Redirection

Any Java logging category (e.g., third-party library loggers) can now be redirected to a specific BoxLang logger via boxlang.json configuration, giving you full control over noisy library output. This is invaluable when integrating third-party dependencies that produce verbose or unwanted log noise.

You can also silence a category completely by routing it to a logger configured with "level": "OFF".

✨ New Features

Destructuring declarations and assignments

Struct destructuring supports:

  • shorthand bindings

  • explicit renaming

  • nested patterns

  • missing-key defaults

  • rest capture for remaining keys

  • scoped targets in direct assignments

Array destructuring supports:

  • positional bindings

  • nested patterns

  • rest bindings

  • middle-rest extraction

  • predictable short-array behavior

  • defaults for missing or null values

Spread in function arguments

Function-call spread is supported in both BoxLang and CFML grammars.

Arrays expand as positional arguments. Structs expand as named arguments.

This works with:

  • built-in functions

  • member methods

  • closures

  • lambdas

  • returned function expressions

Literal spread and shorthand keys

Spread syntax is now supported in these literal forms:

  • Array literals like [ 1, ...values, 4 ]

  • Unordered struct literals like { ...defaults, debug: true }

  • Ordered struct literals like [ ...defaults, retries: 5 ]

Struct literals also support shorthand keys.

Ambiguous spread-only bracket literals

Spread-only bracket literals are resolved at runtime:

  • array sources resolve to arrays

  • struct sources resolve to ordered structs

  • mixed source types are rejected

🛠️ Improvements

Performance Improvements

1.12.0 ships a new strategy for method handle caching (BL-2304, BL-2306) that ties caches to their owning class loader, dramatically reducing cache pollution across application reloads. PagePoolClear now also flushes these method handle caches (BL-2303), and entrySet() on structs has been made more efficient (BL-2282).

A Java Shared Class Data Archive (CDS/SAR) is now generated during install, allowing the native binary to skip class verification on startup and shave significant time off cold starts (BL-2299).

JDBC Improvements

  • Nested transactions are now Lucee-compatible — inner transactions use savepoints instead of silently rolling back (BL-2271)

  • Isolation levels are validated on inner transactions to prevent unsupported combinations (BL-2289)

  • JDBCStore datasource lookup and LongInteger cast issues on MySQL are fixed (BL-2265, BL-2268)

  • Inline datasource structs are now accepted in all locations that previously required a named string (BL-2311)

bx:execute / systemExecute() Improvements

Exit codes are now exposed as a return value and via exitCodeVariable on both the component and BIF (BL-2262). A blocking deadlock that occurred when the err stream buffer filled up has also been resolved (BL-2263).

Locale and Date Format Fixes

Several long-standing locale and date-format issues have been resolved:

  • monthAsString() now correctly honours the locale argument (BL-2259)

  • DateFormat methods no longer apply the wrong locale when only a language string is supplied (BL-2260)

  • European dd.MM.yyyy dates parse correctly (BL-2264)

  • datetimeFormat() in compat now accepts both t and T ISO separators and passes through non-mask characters as literals (BL-2322, BL-2323)

  • Compat TimeFormat mm mask behaviour corrected (BL-2295)

📚 Documentation

See the new and updated language pages for full syntax and examples:


🎶 Release Notes

Bugs

BL-2219 Application updates made with `bx:application` do not persist to subsequent requests

BL-2239 template case statement is checking delimiter instead of delimiters

BL-2259 MonthAsString Not honoring locale argument correctly

BL-2260 Incorrect Locale Being Applied on DateFormat Methods When only only lang string is supplied.

BL-2261 bx:execute not setting the `errorVariable` correctly

BL-2263 systemExecute() can hang when err out buffer is full

BL-2264 European dd.MM.yyyy format date parses incorrectly

BL-2265 Datasource not found error creating JDBCStore cache

BL-2266 Compat: Improve Parsing Performance with Explicit Mask

BL-2268 JDBCStore throws Long cannot be cast to class java.lang.Integer on mysql datasources

BL-2272 improve fall back serializer for JSON

BL-2274 miniserver doesn't follow symlinks

BL-2278 parse error on #foo[1]# neq bar

BL-2279 Error calling Oracle proc with proc result after positional args

BL-2285 lexer doesn't match non-breaking spaces as whitespace

BL-2287 CF truncates startRow and endRow attributes to cfloop

BL-2288 content component doesn't handle spreadsheet object passed as binary variable

BL-2293 Declare UDF inside of catch block

BL-2295 Compat: TimeFormat mm mask behaviour

BL-2298 Calling static method from another static method within a closure throws a not found exception

BL-2301 CF compat allow invalid cflock types

BL-2302 Put actual path in server.coldfusion.rootdir

BL-2308 QoQ allow table to have more than one dot

BL-2312 XOR operator precedence

BL-2314 mis-identifying tag component with comment at end of line

BL-2315 queryNew - Unknown Column Type "long"

BL-2317 parse error with text operator before decimal

BL-2318 inout refcursor params don't work on Oracle stored procs

BL-2321 fileWrite() should accept file object, not just a string

BL-2322 datetimeFormat() in compat allows t but not T

BL-2323 datetime format should passthru non-mask chars

BL-2325 directoryList() errors on invalid symlinks in listing

BL-2329 Adobe compat

BL-2330 CF compat preserveSingleQuotes() not required for UDF calls

Improvements

BL-417 Directory / File Watchers

BL-2220 Add a custom message to an assert expression statement: assert expression : message

BL-2252 Add cfhtmlbody tag for cfml compatibility

BL-2262 Allow retrieving exit code in execute component and systemExecute() BIF

BL-2271 [JDBC] Nested transactions are not Lucee-compatible

BL-2289 JDBC - Validate isolation levels on inner transactions

BL-2296 Return early from isDate for blank strings

BL-2299 Introduce the usage and creation of a Java shared archive to improve native binary startups

BL-2303 PagePoolClear now clears the method handle caches as well.

BL-2304 New approach to caching method handles by leveraging class loader location and unique cachekey resolution class

BL-2309 Add missing arrayNew() features needed for compat

BL-2310 Allow arbitrary Java logging categories to be redirected to the pre-defined BoxLang loggers

BL-2311 Allow datasource as struct in all places

BL-2313 repeatString() truncate decimal count

BL-2319 Allow compat's onServerStart to run inside of request context

BL-2320 getter on runtime to see config path used to load runtime

BL-2326 Refactor Config Load to only Merge Environment Overrides Once

BL-2332 create dump template for BoxFile

New Features

BL-310 Spread operator

BL-311 Implement Array Destructuring assignment

BL-312 Implement Struct Destructuring assignment

BL-2275 getSystemSetting() pluggable namespace provider support

BL-2276 range operator

BL-2280 Allow custom error pages used in web runtime

BL-2282 Improve performance of entrySet() on structs

BL-2300 getLocalHostIp() bif

BL-2306 Move method handle caching into related class loader

BL-2307 Add new types to systemCacheClear()

Story

BL-1827 CFC script wrappers

Last updated

Was this helpful?