Skip to content

feat: resolve() of external references via a pluggable resolver - #4

Open
fjsj wants to merge 5 commits into
feat/terminologyfrom
feat/async-resolve
Open

fjsj wants to merge 5 commits into
feat/terminologyfrom
feat/async-resolve

Conversation

@fjsj

@fjsj fjsj commented Jul 7, 2026 •

Copy link
Copy Markdown
Member

Adds a host resolver so expressions such as subject.resolve().name.given can read an external Patient through options.resolver, while preserving contained and Bundle resolution.

Targets feat/terminology (#3), updated to 7b65822 with current main (f66e445). Merge #3 first.

resolve() tries the originating resource and Bundle first. Unresolved external references reach (reference: string) => Promise<unknown> unchanged; fragment misses stay local. The host owns server-base handling, authentication, and transport. Local results retain their indexed ancestry for pathname() and later resolution; returned external resources own subsequent contained navigation.

The callback runs at most once per distinct reference per async evaluation through #3's cache/replay mechanism. Non-resource answers produce empty results, and callback rejections propagate. Without a resolver, external misses remain empty; synchronous calls that need a configured resolver direct callers to evaluateAsync().

Validation on 226a3c0: 8,840 tests passed, 1,430 existing skips, across 69 files. Typecheck, lint, static FHIRPath checks, generated inference, type budgets, coverage, build, installed-package checks, and demo typecheck/tests/build all pass. Coverage: 99.41% statements, 96.85% branches, 99.81% functions, 99.40% lines. No thresholds or budgets changed.

The local Smart Forms comparison passes 76/76 extraction, 28/28 renderer, and 9/9 service/scoring/resolution probes. Population passes 208/209 unchanged assertions plus the separate full-response date assertion; the mismatch preserves four valid dates that fhirpath.js omits. All 29 local SDC tests and 33 focused conformance probes pass. The adapter and broad assessment tools remain local; #119 and #120 remain separate follow-ups.

Independent thermo-nuclear review approved 226a3c0 with no blockers, including checks for indexed ancestry, multiple remote/contained hops, negative caching, and exclusion of external resources from local pathname() results.

@fjsj
fjsj force-pushed the feat/async-resolve branch from 4990512 to 59aa80e Compare July 7, 2026 12:27
Closes the external-resolve gap from the README deferred-features
register, building on the evaluateAsync() suspend-and-replay path:

- New ReferenceResolver option: resolve() still handles contained (#id)
  and Bundle-internal references synchronously itself, and hands anything
  else — absolute urls, relative Type/id references outside the Bundle —
  to the caller's resolver, one awaited call per distinct reference.
- Fragment (#id) misses never consult the resolver: fragments are
  internal by definition.
- A resolver answer of undefined (or any non-object) yields empty,
  like every other unresolvable reference; resolver rejections propagate.
- Without a resolver the behavior is unchanged: external references
  quietly resolve to nothing. With one, a sync evaluate() that actually
  hits an external reference fails with a pointer to evaluateAsync().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fjsj

fjsj commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

The SDC assessment reproduces the two remote-resolution gaps this PR addresses: relative Patient/relative and absolute https://example.test/fhir/Patient/absolute references resolve with fhirpath.js 4.11.0 but return empty on current main. These are deterministic provider-response tests, not live network tests.

#121 fixes the separate local scope gap mentioned in this PR description. It resolves #id within the owning Bundle entry, sibling/container references from contained resources, and chained local resolution while retaining element ancestry. Its focused tests also cover identical reference objects reused in different entries and typed results reused as variables.

I am updating this PR after #3 and will run the thermo-nuclear review loop before publishing the replacement commits. The remote fallback should compose with the new local resolver, preserve the resolved resource's scope for subsequent navigation, and keep fragment misses local. Please retain the current analyzer's target-profile typing during that update.

Public focus/context ergonomics remain tracked separately in #119. The broader assessment scripts and copied upstream fixtures stay local.

This branch has not been deployed

No deployments
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.

1 participant