December 2025 (version 2.25.12.18)

reviewed: 18 December 2025

🚀 New Features

Response Start Detection

A new response function has been added:

response.hasStarted()

If true, the response has begun and headers have already been sent to the client.

<@url> Variants for Document Viewing and Downloading

We’ve added two new <@url> qualifier variants that simplify generating URLs for viewing and downloading documents.

<@url/document/view 
    document:[document]
>
<@url/document/download 
    document:[document]
>

These qualifiers work anywhere a standard <@url> is accepted, and they can be dropped straight into qualifiers such as <@page/button>.

With these additions, developers no longer need to hand craft the URL generation logic, just use the appropriate qualifier and the correct link is produced automatically.


🔧 Updates

Property Bag Enhancements

Property bag resolution is now composable

Resolving a property bag now returns itself:

<
    translations: pb.resolve([translations])
    // ...
>

pb.set() now returns the value that was set

This makes it easier to chain with other functions:

if pb.set([pb], 'isValid', script.call('./validate'))
    // ...
end-if

Nested property deletion support

You can now delete nested items using paths:

pb.delete([pb], 'assets/1/value')

Previously, deleting nested paths would not error but would also not delete.


Improved raise Statement Support

The raise statement now accepts a qualifier directly as the message, which is automatically invoked:

raise exceptionType.fatal, <@text EN: 'Something has gone wrong' NL: 'Er is iets misgegaan'>

Manual qualifier.invoke() usage will still work, but is no longer necessary.


Icon Qualifier Improvements

  • Added excluded support
  • Added tooltip support

Simplified Inline Tooltip Support

Previously, tooltips always required a full qualifier:

<@page/icon
    name: 'box'
    tooltip: <@page/tooltip
        label: 'My tooltip'
    >
>

Now, simple tooltips can also be expressed directly:

<@page/icon
    tooltip: 'My tooltip'
>
  • Any qualifier can be used as a simplified tooltip (<@page/text> for example) and will be automatically invoked and escaped.
  • If the qualifier provided is as a <@page/...> qualifier, the tooltip is automatically marked as HTML-safe.
  • Full <@page/tooltip> remains supported for more complex requirements.

🐞 Fixes

Report Builder

  • For SQL reports the dynamic Business Object is now regenerated when changing the reports datasource.

Isolated Context Groups

Whitespace in the context group is now ignored correctly:

set('name', 'Seth')
set('age',  10)

isolate 'name, age'
    page.render([name])     // "Seth"
    page.render([age])      // Previously "", now "10"
end-isolate 'name'

Inline View Descriptor Handling

Fixed an issue where setting inLineView to null() was incorrectly treated as if an inline view existed.

Active Expression Stability

Errors from active expressions in the Report Builder, Application Maintenance, and Ad-hoc Queries are now captured and logged, but not thrown.

The pages will now display, minus any erroneous entries.

Form Transaction Handling

Exceptions captured by _onError() no longer cause a transaction rollback when submitting an <@page/data/form>.

Property Bag Merge Fixes

Anonymous (unnamed) items are now always merged correctly and assigned a new internal GUID each time. This ensures correct behavior when merging qualifiers from within the script cache.

Where Clause Special Case Sensitivity

The special tokens used in WHERE clauses (e.g., #null, #true, etc.) are now treated case‑insensitively, so #NULL, #True, or any other capitalization will be recognized correctly.


🔒 Security

Safer Redirect Behavior

Buffered response bodies are no longer returned to the client during response.redirect().

If needed, a response body can still be forced via response.flush(), but this is now an intentional operation.


Thread-Safe Procedure Default Values

Default parameter values in script procedures are now thread-safe.

Example of the previous issue (mutating shared defaults):

function example(pb: <>)
    pb.set(guid([pb]), guid())
    return [pb]
end-if

// After three calls without passing a pb parameter,
// the returned property bag would accumulate values like this:
// <
//     1f8b2a3c-7d4e-41f9-a6b5-9c3e2d7f1a84: "e5c9d2b1-3a6f-44b2-9d78-2f1e6c5b7d90"
//     a3d4e6f9-8b2c-4a71-93e5-5d7c1b2a8f33: "7e9f2c4b-1a5d-48e3-b6a9-0c3d5f7e2b11"
//     c2b1d8e4-6f3a-49c2-8d7e-1a9b3c5d6f22: "9a7c5e3b-2d4f-47a1-9c6b-3e8f1d2a5c77"
// >

Default parameter values no longer persist mutated state across calls.


XSS Vulnerability Fix

Fixed an XSS vulnerability in:

  • Select input placeholders
  • Select input option values

This impacted both option list selects and foreign key selects.


Select2 Service Replay Fix

  • Previously generated descriptors now return 403.
  • Descriptors are now tied to the specific user that generated them.
  • Sessionless systems are unaffected (descriptors are inherently public).

Dedicated Email Out Profile Page

A new optional page has been added to prevent exposing passwords on systems with strict credential-handling requirements.

To enable:

  1. Update the application maintenance entry for email out profiles.
  2. Change entity from email/outProfilenull
  3. Set the URL to: application/maintenance/email/outProfile:main