Repository navigation
v2.4.0
fumifier 2.4.0
This release adds new higher-order functions, date/time formatting options, and epoch-millis support for date-like FHIR primitives. It also includes FLASH parser and evaluator fixes, parser and regex hardening, more accurate diagnostic metadata, and redaction of HTTP/client error details from diagnostics.
No public API signatures changed. See Upgrade Notes for behavior changes that integrators should review.
Highlights
- New higher-order functions:
$safe,$all,$any, and$memoize. $search(..., { fetchAll: true, transform })now accepts inlinefumifierfunctions astransform.$rightNow()can now format its output, and$now(),$fromMillis(), and$rightNow()accept picture aliases (date,dateTime,instant,time).- Numeric (epoch-millis) values assigned to FHIR
date,dateTime, andinstantprimitives are now converted to canonical UTC output. - Nested HTTP/client errors are no longer serialized into verbose diagnostics or wrapped runtime errors.
- Several FLASH parser/evaluator fixes, plus parser recovery and regex hardening.
- More accurate source locations and execution IDs in diagnostics.
New Features
Higher-Order Functions: $safe, $all, $any, $memoize (Issues #86, #87, #88, #96, #97)
fumifier now adds four public higher-order functions: $safe, $all, $any, and $memoize. With them, expressions can wrap helpers that might fail without throwing, check collection predicates with sequential sync/async short-circuiting, and reuse repeated results within a single evaluation.
$safe()returns structured success/error result objects with sanitized public error fields.$all()and$any()support sync and async predicates and short-circuit in a deterministic order.$memoize()caches fulfilled results for one evaluation and shares in-flight async work between duplicate calls. Failed entries are dropped so that later retries run again.
$search(..., { fetchAll: true, transform }) now also works from the public expression surface when transform is an inline fumifier function. Previously only native JavaScript callbacks worked.
Wrapped mapping parse failures now keep their informative parser messages instead of degrading to [object Object].
Compatibility notes:
$memoize()rejects argument trees that contain function or symbol values.$searchtransform follows the@outburn/fhir-clientcontract and is only valid withfetchAll: true.maxResultsstill applies to the raw fetched resources before transform filtering.
Picture Aliases and Formatted $rightNow()
$rightNow() can now format the current wall-clock time using the same picture pipeline as $now() and $fromMillis(). This release also adds a small set of exact picture aliases for the date/time formatting helpers.
$rightNow()stays backward-compatible: called with no arguments, it still returns epoch milliseconds.$rightNow(picture)and$rightNow(picture, timezone)now return formatted strings, so callers no longer need to format the result separately.$now()and$fromMillis()now accept the same exact picture aliases:date->YYYY-MM-DDdateTime-> second-precision timestamp with timezoneinstant-> millisecond-precision timestamp with timezonetime->HH:MM:SS.sss
Examples:
$rightNow('date')returns a calendar date such as2026-07-14.$rightNow('instant')returns a full timestamp such as2026-07-14T12:34:56.789Z.$fromMillis(0, 'dateTime')returns1970-01-01T00:00:00Z.$fromMillis(0, 'dateTime', '-0500')returns1969-12-31T19:00:00-05:00.
Notes for integrators:
- Alias matching is exact and case-sensitive. Only the known keywords are rewritten.
- Raw picture strings are unchanged and go through the same formatter as before.
- Aliases apply to formatting only.
$toMillis()parsing behavior is unchanged. - Timezone handling is unchanged. Aliases honor the optional timezone argument the same way raw pictures do.
Epoch-Millis Support for Date-Like FHIR Primitives
fumifier now converts runtime numeric values assigned to FHIR date, dateTime, and instant primitives to canonical form as UTC epoch-millis values. Previously these values fell back to generic string conversion.
- Numeric inputs for
date,dateTime, andinstantnow go through the date-like conversion path instead of being output as stringified numbers. - Valid finite epoch-millis values now produce canonical UTC FHIR output:
date->YYYY-MM-DDdateTime->YYYY-MM-DDTHH:mm:ss.SSSZinstant->YYYY-MM-DDTHH:mm:ss.SSSZ
- Invalid numeric values now raise the existing
F5111validation error instead of appearing in the output as plain strings. This includes non-finite numbers and epoch-millis values outside the range supported by JavaScriptDate.
Examples:
- Assigning
0toPatient.birthDatenow yields1970-01-01. - Assigning
0toPatient.deceasedDateTimenow yields1970-01-01T00:00:00.000Z. - Assigning
0toObservation.issuednow yields1970-01-01T00:00:00.000Z. - Oversized numeric literals that were previously stringified into the output are now rejected with
F5111.
Notes for integrators:
- This change only affects runtime JavaScript
numbervalues assigned to FHIRdate,dateTime, andinstantprimitives. - String-based date-like behavior is unchanged, including precision preservation for year-only and year-month inputs.
Security and Privacy
HTTP Error Redaction (Issue #89)
fumifier now prevents raw nested HTTP/client errors from being serialized into verbose diagnostics and wrapped runtime errors. This applies to the FHIR helper, $useFhirServer, $eval, and mapping-wrapper paths.
Previously, auth config, authorization headers, and other nested request details could be exposed by accident. These are now removed. Safe diagnostic fields are kept when available: error code, message, source message, HTTP status, and safe request metadata.
This is a bug fix for the package. The public verbose contract was always meant to return sanitized diagnostics rather than raw client internals. In addition, normalized 404 handling no longer produces malformed "resource not found" diagnostics when a search-style failure has no concrete resource id.
Consumers that read serialized nested error payloads such as sourceError, error, or cause should switch to the stable safe fields on diagnostics and wrapped errors: code, message, sourceMessage, sourceErrorCode, status, request, and operationOutcome where present.
Bug Fixes
FLASH Wildcard Variable Parsing (Issue #84)
This fixes a FLASH parser issue with wildcard variable assignments inside FLASH blocks.
- FLASH now accepts bare wildcard variable assignments such as
$value := *;wherever FLASH already expects a value expression. - The unwrapped form now behaves the same as the parenthesized workaround
$value := (*);. - Existing protections against malformed rules remain in place for empty
*rules and malformed double-star rules. - Fixed cases where
$value := *;could be rejected withF1024(Malformed FLASH rule: Rule is empty) even though the parenthesized form parsed successfully.
Compatibility notes: Parenthesized expressions still work. Users who adopted the parenthesized workaround do not need to change their expressions.
Nested Array Pattern Injection (Issue #85)
This fixes a FLASH evaluation issue with profile-driven pattern injection for nested array-typed elements.
- FLASH no longer double-wraps array-valued pattern content when a slice injects a complex value such as
Identifier.type.coding. - Profile-driven identifier slices such as
identifier[ppn]now produce the same flatcodingstructure as the equivalent explicit FLASH assignments. - Explicit child assignments on the same
CodeableConceptstill merge normally without reintroducing nested arrays.
Fixed cases where evaluating identifier[ppn].type could return:
{
"coding": [
[
{
"code": "PPN",
"system": "http://terminology.hl7.org/CodeSystem/v2-0203"
}
]
]
}The same expression now returns the correct flat array:
{
"coding": [
{
"code": "PPN",
"display": "Passport number",
"system": "http://terminology.hl7.org/CodeSystem/v2-0203"
}
]
}Compatibility notes: Explicit FLASH assignments for Identifier.type and similar structures work as before. The fix is internal and only removes an incorrect extra array layer from auto-injected pattern values.
Nested Block Comments (Issue #63)
fumifier now skips nested /* ... */ block comments correctly. Inner comment markers no longer cause the rest of the outer comment to be parsed as live code.
Previously, expressions with nested block comments could produce misleading parser or evaluator errors because trailing comment text was tokenized as real code. Expressions that are valid after the comment now evaluate normally. Expressions that are still invalid after the comment is removed now report the real parser error instead.
Expression semantics are unchanged apart from the corrected comment handling. Unterminated block comments still report the existing S0106 error.
Parser and Regex Hardening
This release improves parser recovery, regex handling, and the token metadata returned by the browser build for FLASH/FUME expressions.
- After an earlier syntax error, parser recovery now keeps later
/characters as division operators instead of misreporting them as unterminated regex literals. - Missing-right-hand-side errors now point directly at the offending operator in cases like
1/,(1/), and$var := ;. - Browser tokenization now returns stable regex token coordinates and explicit regex flags.
- Exported ASTs now use serializable regex metadata instead of live compiled
RegExpobjects. This improves JSON round-tripping and AST mobility workflows. - Regex literals are now compiled once per evaluation context and reused within that evaluation, which reduces repeated regex setup in per-item flows such as filters.
Fixes:
- Fixed misleading
S0302regex errors that could appear after unrelated earlier syntax errors. - Added a more actionable missing-right-hand-side diagnostic:
S0218. - Stabilized regex node and token coordinates for downstream editor and tooling consumers.
Notes for integrators: Regex entries in exported ASTs and browser token arrays no longer expose live RegExp instances. Consumers should read regex metadata from value and flags instead.
Function Error Spans
fumifier now keeps the full function call-head span for runtime function-validation diagnostics. This covers both contextual signature failures and direct argument-type validation failures.
Previously, function-call errors such as T0411 and T0410 could report start at the opening parenthesis instead of the function name. They could also drop start: 0 entirely when the call was at the start of an expression.
Verbose diagnostics for function calls now consistently point to the callee span. This makes it easier to highlight and trace invalid calls such as $search(...) and other registered helper or user-defined function calls.
Error codes, signature validation behavior, and function execution semantics are unchanged. Only the reported source metadata for these diagnostics has been corrected.
Diagnostic Helper Metadata
fumifier now keeps stable execution IDs and helper source-location metadata in verbose diagnostics emitted by $warn, $info, and $trace.
This fixes cases where helper-generated diagnostic entries could show executionId: 'unknown', especially when the helper ran from a derived frame such as a nested function call or block scope.
Verbose helper diagnostics now include the helper call-site position, start, and line fields again. This makes it easier to trace warnings and debug entries back to the expression source that emitted them.
Helper diagnostic codes, buckets, messages, log behavior, and $trace return semantics are unchanged.
Upgrade Notes
No public API signatures changed in this release. Integrators should review these behavior changes:
- Regex metadata in ASTs and browser tokens: exported ASTs and browser token arrays no longer contain live
RegExpinstances. Readvalueandflagsinstead. - HTTP/client error payloads: nested
sourceError,error, andcausepayloads are no longer serialized. Usecode,message,sourceMessage,sourceErrorCode,status,request, andoperationOutcome. - Numeric date-like values: numbers assigned to
date,dateTime, andinstantare now converted to canonical UTC output. Out-of-range or non-finite numbers now raiseF5111instead of being stringified. $memoize()arguments: argument trees that contain function or symbol values are rejected.$searchtransform: only valid withfetchAll: true.maxResultsapplies before transform filtering.- New diagnostic code:
S0218(missing right-hand side).
Validation Summary
- Picture aliases /
$rightNow(): added implementation coverage for alias behavior in$rightNow(),$now(), and$fromMillis(). Rebuilt the package outputs, andnpx mocha test/implementation-tests.test.js --timeout=20000passed (79 passing). - Epoch-millis date-like primitives: added FLASH fixture coverage for valid numeric conversion and out-of-range
F5111handling. The FLASH-only regression suite passed after rebuild. The verbose-policy regression suite passed with the newF5111case included. - FLASH wildcard variable (#84): wildcard-hardening and parser recovery regression coverage passed.
- Nested array pattern injection (#85):
flash-pattern-injectionregression coverage passed, including the issue reproducer, an explicit non-slice control, mixed identifier cases, and explicit child assignments. The full Mocha regression suite passed after the fix (2549 passing, 1 pending). - Parser and regex hardening: parser, browser, and AST validation completed, along with broader Mocha regression coverage.
What's Changed
- Parser Slash, Regex, and Recovery Hardening by @mechanik-daniel in #92
- Fix FLASH wildcard variable assignment parsing by @mechanik-daniel in #93
- Fix/HTTP Error Redaction by @mechanik-daniel in #94
- Fix/nested array wrapping for pattern-injected CodeableConcept.coding by @mechanik-daniel in #98
- Feat/HOF Issues 86, 87, 88, 96, 97 by @mechanik-daniel in #99
- Fix/diagnostic helpers position metadata by @mechanik-daniel in #102
- Fix/function error position by @mechanik-daniel in #105
- Fix/date accepts numbers by @mechanik-daniel in #106
- Feat/enhance right now by @mechanik-daniel in #107
- Fix/nested block comments by @mechanik-daniel in #108
- Release/v2.4.0 by @mechanik-daniel in #115
Full Changelog: v2.3.1...v2.4.0