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 ] = numbersIn 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 optionalonEventcatch-allClass 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:
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
nullvalues
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
Spread-only bracket literal resolution is an experimental edge case. Use explicit keyed members when you want to force ordered-struct syntax.
🛠️ 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)
JDBCStoredatasource lookup andLong→Integercast 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 thelocaleargument (BL-2259)DateFormatmethods no longer apply the wrong locale when only a language string is supplied (BL-2260)European
dd.MM.yyyydates parse correctly (BL-2264)datetimeFormat()in compat now accepts bothtandTISO separators and passes through non-mask characters as literals (BL-2322, BL-2323)Compat
TimeFormatmmmask 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?
