Skip to content

Add fhirpath.zig WASM engine support (R4 + R5)#66

Open
jmandel wants to merge 8 commits into
brianpos:masterfrom
jmandel:add-fhirpath-zig-engine
Open

Add fhirpath.zig WASM engine support (R4 + R5)#66
jmandel wants to merge 8 commits into
brianpos:masterfrom
jmandel:add-fhirpath-zig-engine

Conversation

@jmandel
Copy link
Copy Markdown
Collaborator

@jmandel jmandel commented Jan 28, 2026

Summary

  • Adds fhirpath.zig as a local browser-based FHIRPath engine for R4 and R5
  • The engine runs as WebAssembly in the browser (no server needed), loading artifacts from joshuamandel.com/fhirpath.zig
  • fhirpath.zig is a Zig-based FHIRPath implementation compiled to WASM with a JS wrapper

What's changed

types/fhirpath_test_engine.ts — Registers two new engines: fhirpath.zig (R4) and fhirpath.zig (R5) with external: false (browser-local)

helpers/fhirpath_api_engine.ts — Adds:

  • Lazy-loaded singleton WASM engine (loaded once from joshuamandel.com/fhirpath.zig/fhirpath.wasm)
  • Schema registration for R4/R5 model binaries on first use
  • evaluateExpressionUsingFhirpathZig() function that evaluates expressions and converts results to FhirPathEvaluationResult
  • Routing in getEngineInfo() and evaluateFhirPathExpression() to recognize and dispatch to the zig engine

How it works

The engine loads via dynamic ESM import from the deployed GitHub Pages site:

  1. fhirpath.js (JS wrapper) + fhirpath.wasm (WASM binary)
  2. model-r4.bin / model-r5.bin (FHIR schema blobs)

These are built and deployed automatically by CI on the fhirpath.zig repo.

Limitations

  • No AST support yet (supportsAST: false)
  • No XML support (supportsXML: false)
  • No debug trace support
  • Context expressions use a simplified approach (re-evaluating against stringified context nodes)

Test plan

  • Select "fhirpath.zig (R4)" engine and evaluate basic expressions (e.g., name.given)
  • Select "fhirpath.zig (R5)" engine and verify R5 model works
  • Verify error handling for invalid expressions
  • Confirm WASM loads only once across multiple evaluations

🤖 Generated with Claude Code

jmandel and others added 8 commits January 28, 2026 15:24
Integrates the fhirpath.zig engine as a local browser-based engine,
loading WASM and model binaries from joshuamandel.com/fhirpath.zig.
This is a Zig-based FHIRPath implementation compiled to WebAssembly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Set supportsXML: true for both R4 and R5 entries
- Detect XML input (starts with '<') and route to evalXml()
- Context expression on XML uses evalXml for initial eval,
  then JSON for per-context re-evaluation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Each context entry now carries its own isXml flag. When there's no
context expression, the original resource is passed as-is (XML or
JSON). Context sub-results from expression evaluation are always
JSON since node.data is a JS object.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
instantiate() now takes an options object instead of positional args,
and registerSchemaFromUrl is merged into registerSchema with a url option.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for running fhirpath.zig (Zig-compiled WebAssembly) as an in-browser FHIRPath evaluation engine for FHIR R4 and R5, integrating it into the engine registry and dispatch logic alongside existing local/RESTful engines.

Changes:

  • Register new engines fhirpath.zig (R4) and fhirpath.zig (R5) as browser-local engines.
  • Add lazy-loaded singleton initialization for the zig WASM engine and schema registration.
  • Route local evaluations to the zig engine when selected.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
types/fhirpath_test_engine.ts Adds two new fhirpath.zig engine entries (R4/R5) to the registry with capability flags.
helpers/fhirpath_api_engine.ts Implements WASM engine loading + schema setup and hooks it into getEngineInfo() / evaluateFhirPathExpression().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +401 to +405
description: "A Zig/WASM FHIRPath engine running locally in the browser.",
external: false,
supportsAST: false,
supportsXML: true
},
Comment on lines +413 to +417
description: "A Zig/WASM FHIRPath engine running locally in the browser for FHIR R5.",
external: false,
supportsAST: false,
supportsXML: true
},
Comment on lines +969 to +980
const FHIRPATH_ZIG_BASE = "https://joshuamandel.com/fhirpath.zig";

let _zigEnginePromise: Promise<any> | null = null;
let _zigSchemasRegistered: Set<string> = new Set();

async function getZigEngine(): Promise<any> {
if (!_zigEnginePromise) {
_zigEnginePromise = (async () => {
// Dynamic import of the JS wrapper from the deployed site
const mod = await import(/* webpackIgnore: true */ `${FHIRPATH_ZIG_BASE}/fhirpath.js`);
const FhirPathEngine = mod.FhirPathEngine;
const engine = await FhirPathEngine.instantiate();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue here is the reason why the PR hasn't been merged in.
I'd prefer this engine to be hooked in via a node hosted service similar to https://github.com/brianpos/fhirpath-lab-fhirpath-js-api.
This then
Currently the fhirpath.js engine is the only one that is "local" to the browser, even though others could be done that way.
This removes the burden of updates, or random external js loading and execution in the platform.
Though it does cost in terms of requiring a hosted service to call.
I have also introduced a mechanism to be able to tag them as only visible with "show advanced properties" is on (for less common/more advanced things) and also a new feature for enabling custom engine URLs via URL parameter.

Comment on lines +987 to +996
async function ensureZigSchema(engine: any, fhirVersion: string): Promise<string> {
const version = fhirVersion.toLowerCase();
const schemaName = version; // "r4" or "r5"
if (!_zigSchemasRegistered.has(schemaName)) {
await engine.registerSchema({
name: schemaName,
isDefault: _zigSchemasRegistered.size === 0,
});
_zigSchemasRegistered.add(schemaName);
}
Comment on lines +1003 to +1022
export async function evaluateExpressionUsingFhirpathZig(
options: FhirPathEvaluationOptions,
fhirVersion: 'R4' | 'R5' = 'R4'
): Promise<FhirPathEvaluationResult> {
const result: FhirPathEvaluationResult = {
results: [],
debugTraceData: [],
processedByEngine: `fhirpath.zig (${fhirVersion} WASM)`
};

try {
const engine = await getZigEngine();
const schemaName = await ensureZigSchema(engine, fhirVersion);

// Set current time
engine.setNowDate(new Date());

const resourceText = options.resourceJson || '{}';
const isXml = !!options.isXmlResource;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants