Add fhirpath.zig WASM engine support (R4 + R5)#66
Conversation
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>
There was a problem hiding this comment.
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)andfhirpath.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.
| description: "A Zig/WASM FHIRPath engine running locally in the browser.", | ||
| external: false, | ||
| supportsAST: false, | ||
| supportsXML: true | ||
| }, |
| description: "A Zig/WASM FHIRPath engine running locally in the browser for FHIR R5.", | ||
| external: false, | ||
| supportsAST: false, | ||
| supportsXML: true | ||
| }, |
| 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(); |
There was a problem hiding this comment.
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.
| 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); | ||
| } |
| 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; | ||
|
|
Summary
joshuamandel.com/fhirpath.zigWhat's changed
types/fhirpath_test_engine.ts— Registers two new engines:fhirpath.zig (R4)andfhirpath.zig (R5)withexternal: false(browser-local)helpers/fhirpath_api_engine.ts— Adds:joshuamandel.com/fhirpath.zig/fhirpath.wasm)evaluateExpressionUsingFhirpathZig()function that evaluates expressions and converts results toFhirPathEvaluationResultgetEngineInfo()andevaluateFhirPathExpression()to recognize and dispatch to the zig engineHow it works
The engine loads via dynamic ESM import from the deployed GitHub Pages site:
fhirpath.js(JS wrapper) +fhirpath.wasm(WASM binary)model-r4.bin/model-r5.bin(FHIR schema blobs)These are built and deployed automatically by CI on the fhirpath.zig repo.
Limitations
supportsAST: false)supportsXML: false)Test plan
name.given)🤖 Generated with Claude Code