Releases: atomic-ehr/codegen
Release list
v0.0.16
The big one: Python now supports profiles. The Python generator has mostly caught up with TypeScript this release — profile classes, generic Bundle[T], and nicer defaults (fhirpy + camelCase out of the box). Plus some lint tightening, a tidier examples layout, and a couple of package-resolution fixes.
Python profiles 🎉
generateProfile: true now spits out a typed wrapper class for each FHIR profile, just like the TypeScript side. You get create() / apply() / from_resource() constructors, to_resource() to unwrap, and a validate() that checks required fields, fixed values, slice cardinality, must-support, prohibited choices, enums, and reference targets. Fields, extensions, and slices all get typed get_X / set_X accessors (extensions can return flat values, a raw Extension, or a wrapped profile). A profile_helpers.py runtime ships with each profiles/ package. — @MikhailArtemyev in #167
# before — raw Pydantic, every fixed value by hand
obs = Observation(resource_type="Observation", status="final",
code=CodeableConcept(coding=[Coding(code="85354-9", system="http://loinc.org")]),
component=[ObservationComponent(...)], ...)
# after — the profile class fills fixed values, slices, and meta.profile for you
bp = UscoreBloodPressureProfile.create(status="final", subject=Reference(reference="Patient/123"))👉 See it end-to-end in the US Core profiles example.
Other Python goodies
-
Generic
Bundle[T].Bundle,BundleEntry,BundleEntryResponse, andDomainResourceare now generic —Bundle[Patient]narrows statically, and bareBundle.from_json()still dispatches byresourceTypeat runtime. No moreResourceFamilyunion. — @MikhailArtemyev in #164bundle: Bundle[Patient] = Bundle[Patient].model_validate(response.json()) # typed bundle = Bundle.from_json('{"type":"collection","entry":[...]}') # still polymorphic
-
Nicer defaults. Two config changes worth knowing about:
fhirpyis now the default client. The booleanfhirpyClientis deprecated (still works) — useclient: "fhirpy" | "none"instead, or just omit it. — @ryukzak in #187fieldFormatnow defaults tocamelCase, so models keep their FHIR spelling instead of converting to snake_case. Opt back in with.python({ fieldFormat: "snake_case" }). — @ryukzak in #189
.python({ client: "none" }) // plain Pydantic, no fhirpy .python({ fieldFormat: "snake_case" }) // old snake_case behavior
-
Fixes. Extensions now serialize with proper FHIR aliases (
valueCoding, notvalue_coding) and the profile generator honorsfieldFormatend-to-end (#178),profile_helpers.pymoved into the generatedprofiles/package (#179), and thepython-us-coreexample ismypy --strictclean (#180, #183). — @MikhailArtemyev, @ryukzak
Elsewhere
- TypeSchema: stop recursing forever on packages with cyclic dependencies. — @ryukzak in #174
- API: fix SQL-on-FHIR generation by adding the missing
hl7.fhir.r5.coredependency. — @ryukzak in #175 - Lint: turn on a batch of stricter Biome rules and fix everything they flagged. — @ryukzak in #182
- Examples: consolidate the example dirs —
typescript-r4-us-core,python-r4-us-core, CCDA moved toon-the-fly/ccda, and deduped TS profile tests. — @ryukzak in #184
Contributors
Full Changelog: v0.0.15...v0.0.16
v0.0.15
This release focuses on TypeScript profile correctness — generated validate() methods and choice setters now match what a FHIR server accepts, closing several gaps where invalid resources passed client-side validation only to be rejected at write time.
Highlights (TypeScript profiles)
-
Inherited required fields are now validated. Profile snapshots silently dropped any base-resource cardinality the profile differential didn't re-state, so
validate()skipped inherited required fields (e.g.Provenance.target,Provenance.recorded). These now emitvalidateRequired()calls. — by @sussdorff in #166 -
choice[x]mutual exclusion is enforced in setters. A polymorphicvalue[x]permits at most one variant, but setters assigned independently — populating two left both set and serialized invalid FHIR. Setters now clear sibling variants, and a publicclearValue()/clearEffective()helper lets you drop a choice entirely. — by @ryukzak in #172// before — both survive (invalid) obs.setValueQuantity({ value: 120, unit: "mmHg" }); obs.setValueCodeableConcept({ text: "unable to obtain" }); // => { valueQuantity: {...}, valueCodeableConcept: {...} } // after — setting one clears the others // => { valueCodeableConcept: {...} } obs.clearValue(); // or drop it explicitly
-
Fixed
CodeableConceptcodings are preserved.apply()now merges fixed values instead of replacing them, so caller-provided secondary codings survive while missing fixed codings are added. — by @sussdorff in #165
Fixes
- Skip base
choice[x]declarations when collecting inherited required fields — previously emitted avalidateRequired(res, profile, "value[x]")check against a key that never exists in FHIR JSON. — by @ryukzak in #170 - Placeholder-only ValueSet expansions (e.g. a sole
UNKcode) no longer generate enum validation constraints. — by @sussdorff in #165 - Profiles that relax a base field to
min: 0no longer emit a spurious required check. — by @sussdorff in #166
Internal
- Introduce
SnapshotProfileTypeSchema— a writer-ready profile shape that moves profile method derivation out of the TS writer into TypeSchema (output byte-identical seam for upcoming work). — by @ryukzak in #158 - Fix test-fixture pollution causing platform-dependent snapshot failures. — by @sussdorff in #155
Examples & CI
- Align the
typescript-us-coreexample with the tutorial and demoBundle<T>. — by @ryukzak in #157 - Add an example demo for inherited base-required field validation. — by @ryukzak in #171
- Bump Node to 22 for the package-managers CI job. — by @ryukzak in #156
Contributors
Full Changelog: v0.0.14...v0.0.15
v0.0.14
Highlights
- TS:
Bundle<T>and any specialization carrying typeFamily-rooted fields (top-level or nested) now exposes generic parameters, so callers can narrowBundle<Patient | Observation>andentry[].resourceis typed accordingly. Defaults preserve every existing call site. (#148) - TS: profile classes now emit a
static is(resource): resource is Basetype guard, so(bundle.entry ?? []).map(e => e.resource).filter(USCoreBloodPressureProfile.is)narrows without a hand-rolled predicate. (#147)
Improvements
- TS:
Reference<T>accepts every FHIR literal reference form —Type/id,http(s)://…,urn:uuid:…,urn:oid:…,#fragment— by @ryukzak in #144 - API:
prettyReportgroups generated files by language/generator and adds afileLimitoption to truncate long lists — by @ryukzak in #143 - TypeSchema: npm-style scoped package names (
@scope/name) now emit valid kebab-case directories — by @jkiddo in #152
Fixes
- TypeSchema: skip R5-only types when generating R4-target extensions and emit a clearer error pointing at the offending reference — by @ryukzak in #153
New Contributors
Full Changelog: v0.0.13...v0.0.14
v0.0.13
What's Changed
Fixes
- PY: Fix
to_json()omittingresourceTypefrom serialized output — @ryukzak in #136 - TS: Fix duplicate
metakey increateResourcewhenmetais a profile factory param (#137) — @sussdorff in #138
Improvements
- API: Add
ignorePackageIndexoption to force.index.jsonregeneration — @ryukzak in #139 - CI: Add on-the-fly generation examples for Norge R4 and KBV R4 — @ryukzak in #139
New Contributors
- @sussdorff made their first contribution in #138
Full Changelog: v0.0.12...v0.0.13
v0.0.12
What's Changed
- Fix work in CJS mode. CI: Rework package-managers job and update deps by @ryukzak in #135
- TypeSchema: Replace inline FHIRValue casts with proper FHIRCoding import by @ryukzak in #133
- TypeSchema: Precompute base names for profile methods by @ryukzak in #132
Full Changelog: v0.0.11...v0.0.12
v0.0.11
Highlights
This release focuses on the TypeScript Profile API — making generated profile classes more type-safe, more complete in validation, and easier to use with slices.
Python: Extension support (#107)
Extension support for the Python/Pydantic code generator.
TypeScript Profile API
Profile apply() / create() fixes (#121)
apply()now sets fixed-value fields and auto-populates discriminator-only slice stubs, matchingcreate()behavior- Fixed-value fields get a getter but no setter
- Extension
apply()sets URL, uses upsert for single extensions
Remove buildResource, fix type safety (#126)
- Remove
buildResource<T>()helper —createResourcenow uses plain object literals for better type checking - Generate
Reference<string /* Resource */>for family type references so narrower references likeReference<"Patient">are assignable to base type fields - Fix
tryPromoteChoiceto correctly resolve reference targets with generic params
SliceFlat / SliceFlatAll type split (#127)
Slice flat types are now split into two:
SliceFlat— setter input, discriminator fields omitted (auto-applied)SliceFlatAll— getter return, includes readonly discriminator literal types
setVSCat(input?: VSCatSliceFlat): this // no discriminators needed
getVSCat(): VSCatSliceFlatAll | undefined // includes { coding: [...] }Array API for unbounded slices (#129)
Slices with max: * now use array-based setters/getters instead of single-element API:
// Before — second call replaces first
bundle.setOrganizationEntry({ resource: org1 });
bundle.setOrganizationEntry({ resource: org2 });
// After — set all at once
bundle.setOrganizationEntry([{ resource: org1 }, { resource: org2 }]);Slice field validation (#130)
validate() now checks required fields inside matched slice elements. For example, BP profile reports missing valueQuantity in component slices:
profile.validate().errors
// [
// "component[SystolicBP].valueQuantity is required",
// "component[DiastolicBP].valueQuantity is required",
// ]Cleanup (#131)
- Remove dead
stripMatchKeysexport - Document Profile API in CLAUDE.md
Contributors
Full Changelog: v0.0.10...v0.0.11
v0.0.10
Features
TypeSchema: Auto-collect extension dependencies during tree shake (#103)
- Extension definitions used by profiles are now included automatically — no need to list them manually in tree shake config
- Add
ignoreExtensionstree shake rule for excluding specific extensions by canonical URL
TypeSchema/TS: Choice type narrowing and validation for profiles (#105)
- Restrict
ChoiceFieldDeclaration.choicesto only the variants declared by the most derived constraint - Emit
validateExcludedfor disallowed choice variants in profile validation (e.g. body weight only permitsvalueQuantity, 10 others are excluded)
TypeSchema/API: Collision resolution config (#106)
- When multiple FHIR schemas produce a binding enum with the same name (e.g.
ObservationCategoryfromObservationandDiagnosticReport), the generator must pick one — previously it auto-selected the most common source and emitted a warning - Add
resolveCollisionsoption to.typeSchema()for explicit control over which source wins - IR report now shows
(selected)/(auto)marks and suggests config snippets for unresolved collisions
TS: Type discriminator support for slicing (#108)
- Support
typediscriminator in slicing — match slices byresourceTypefield - Generate typed getters/setters for type-discriminated slices (e.g.
getPatientEntry()/setPatientEntry()) - Example: Bundle Profile SD, profile-typed-bundle.test.ts.
TS: Generic type-family fields (#109)
- Emit generic type parameters for fields typed as a resource base type (
Resource,DomainResource) BundleEntry<T extends Resource = Resource>,DomainResource<T>— unparameterized usage is unchanged- Profile slices use narrowed generics:
BundleEntry<Patient>,BundleEntry<Organization>
TypeSchema refactoring
- Renames (#114, #117):
RegularTypeSchema→SpecializationTypeSchemaIdentifier→TypeIdentifierNestedTypeSchema→NestedType
- Remove generated profile override interface (#104)
- Narrow
FieldSlicingdiscriminator type fromstringto literal union (#110) - Add
typeFamily.complexTypes, removeresourceChildren(#111) - Make
SpecializationTypeSchemaa discriminated union:ResourceTypeSchema | ComplexTypeTypeSchema | LogicalTypeSchema(#113) - Split
resolveintoresolve(Identifier)andresolveType(TypeIdentifier)(#113) - Split dependency types:
Identifier[]for specializations,TypeIdentifier[]for profiles (#118)
Maintenance
Full Changelog: v0.0.9...v0.0.10
v0.0.9
Highlights
US Core profile support — generated profile classes now cover US Core IG with typed extension setters, component slice accessors, and validate() returning { errors, warnings } with must-support field tracking. See the blog post and working examples.
What's Changed
TypeScript:
- Fix: skip choice flattening for primitive variant types by @ryukzak in #99
- Refactor TypeScript profile codegen: typed helpers, reduced casts, resolved references by @ryukzak in #101
- US Core profile support, unified getter/setter API, validate() with errors/warnings and must-support by @ryukzak in #102
Python:
- Added fhirpy support search/fetch API by @MikhailArtemyev in #89
- Binding support by @MikhailArtemyev in #97
Internal:
- Treeshake US Core example to Patient, BP, and BodyWeight by @ryukzak in #100
- New logger with suppression functionality by @MikhailArtemyev in #79
Full Changelog: v0.0.8...v0.0.9
v0.0.8
What's Changed
- TS: Fix primitive array extension types and add extension tests by @ryukzak in #76
- TS: Enhance profile code generation with factory methods, extensions, and pattern values by @ryukzak in #81
- Python: feat: improve support for fhirpy client (rebased) by @ruscoder, @MikhailArtemyev in #88
- Fix logic-promotion + Improve test coverage by @MikhailArtemyev in #73
- Detect non-BackboneElement nested types via elements-based analysis by @ryukzak in #83
- Unify FHIR package patching via preprocessPackage hook by @ryukzak in #84
- Add profile class enhancements: getters, validation, and BP support by @ryukzak in #90
- Auto-populate required slices in profile createResource() by @ryukzak in #91
- Preserve original SD slice name casing in generated methods by @ryukzak in #94
- Flatten single-variant choice types and optional slice auto-fields by @ryukzak in #95
Internal:
- Refactor TypeScript writer into modular file structure by @ryukzak in #82
- Update docs, README, and blog posts by @ryukzak in #93
- Fix US Core example imports and add Makefile target by @ryukzak in #92
Full Changelog: v0.0.7...v0.0.8
v0.0.7
What's Changed
- Export profile base interface alongside the profile class by @aartaka in #68
- TypeSchema: collision detection, open enum bindings, and file overwrite warnings by @ryukzak in #69
- Add CCDA (not CDA) tests by @aartaka in #70
- refactor: cleanup and reduce code complexity by @ryukzak in #71
- Export registerFromPackageMetas for use in CCDA converter by @aartaka in #72
- Improve cross-package type resolution and add Norwegian FHIR package support by @ryukzak in #75
Full Changelog: v0.0.6...v0.0.7