npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atomic-ehr/codegen

v0.0.17

Published

Code generation tools for FHIR resources and TypeSchema definitions

Readme

Atomic EHR Codegen

npm canary npm version CI SDK Tests

Table of Contents

A powerful, extensible code generation toolkit for FHIR (Fast Healthcare Interoperability Resources) that transforms FHIR specifications into strongly-typed code for multiple programming languages.

Features

  • [x] Multi-Package Support — Load packages from the FHIR registry, remote TGZ files, or a local folder with custom StructureDefinitions
    • Tested with hl7.fhir.r4.core, US Core, C-CDA, SQL on FHIR, etc.
  • [x] Resources & Complex Types — Generates typed definitions with proper inheritance
  • [x] Value Set Bindings — Strongly-typed enums from FHIR terminology bindings
  • [x] Profiles — Factory methods with auto-populated fixed values and required slices (R4 profiles, US Core)
    • Extensions — flat typed accessors (e.g. setRace() on US Core Patient), standalone extension profiles
    • Slicing — typed get/set accessors with discriminator matching
    • Validation — runtime validate() for required fields, fixed values, slice cardinality, enums, references
  • [x] Extensible Architecture — Three-stage pipeline: FHIR packages → TypeSchema IR → code generation
    • TypeSchema is a universal intermediate representation — add a new language by writing only the final generation stage
    • Built-in generators: TypeScript, Python/Pydantic, C#, and Mustache templates
  • [x] TypeSchema Transformations:
    • [x] Tree Shaking — include only the resources and fields you need; automatically resolves dependencies
    • [x] Logical Model Promotion — promote FHIR logical models to first-class resources
    • [ ] Renaming — custom naming conventions for generated types and fields
  • [ ] Search Builders — type-safe FHIR search query construction
  • [ ] Operation Generation — type-safe FHIR operation calls

| Feature | TypeScript | Python | C# | Mustache | |-----------------------------------|------------|---------|------|----------| | Resources & Complex Types | yes | yes | yes | template | | Polymorphic container Bundle<T> | yes | yes | no | no | | Value Set Bindings | inline | limited | enum | template | | Primitive Extensions | yes | opt-in | no | no | | Profiles | yes | yes | no | no | | Profile Validation | yes | yes | no | no |

Guides

Versions & Release Cycle

Installation

# Using npm
npm install @atomic-ehr/codegen

# Using bun
bun add @atomic-ehr/codegen

# Using yarn
yarn add @atomic-ehr/codegen

Quick Start

  1. Write SDK generation script (generate-types.ts):

    import { APIBuilder, prettyReport } from '@atomic-ehr/codegen';
    
    const builder = new APIBuilder()
        .fromPackage("hl7.fhir.r4.core", "4.0.1")
        .typescript({})
        .outputTo("./examples/typescript-r4-us-core/fhir-types")
        .introspection({ typeTree: "./type-tree.yaml" });
    
    const report = await builder.generate();
    console.log(prettyReport(report));
  2. Run the script with:

    • npm exec tsx generate-types.ts
    • bun run generate-types.ts
    • pnpm exec tsx generate-types.ts

Usage Examples

See the examples/ directory for working demonstrations:

  • typescript-r4-us-core/ - FHIR R4 core + US Core type generation with resource creation, profiles, and extensions
  • on-the-fly/ccda/ - C-CDA on FHIR type generation (logical models, generated on the fly)
  • python-r4-us-core/ - Python/Pydantic models for FHIR R4 core + US Core profiles, with the default fhirpy async client
  • python-r4/ - Python/Pydantic model generation with the simple requests-based client
  • csharp/ - C# class generation with namespace configuration
  • mustache/ - Java generation with Mustache templates and post-generation hooks
  • typescript-custom-packages/ - Loading packages from local folders or remote TGZ URLs (SQL-on-FHIR)

For detailed documentation, see examples/README.md.

Architecture

The toolkit uses a three-stage architecture (details: link):

  1. Input - FHIR packages & resolves canonicals
  2. Intermediate representation - TypeSchema provides a universal representation for FHIR data entities and processing utilities
  3. Generation - Generate code for TypeScript, Python, etc.

The APIBuilder provides a fluent interface for configuring and generating code:

const builder = new APIBuilder()

    // Input sources (choose one or combine)
    .fromPackage("hl7.fhir.r4.core", "4.0.1") // NPM registry package
    .fromPackageRef("https://...package.tgz") // Remote TGZ file
    .localStructureDefinitions({ ... })       // Loose JSON files

    // Type Schema processing
    .typeSchema({
        treeShake: { ... },        // Include only specified types
        promoteLogical: { ... },   // Process logical models as resources
        resolveCollisions: { ... },// Resolve duplicate schema collisions
    })

    // Code generator (choose one)
    .typescript({                              // TypeScript generator
        generateProfile?: boolean,
        withDebugComment?: boolean,
        openResourceTypeSet?: boolean,
    })
    .python({                                   // Python generator
        client?: "fhirpy" | "none",            // client integration (default: fhirpy)
        generateProfile?: boolean,             // generate profile wrapper classes
        primitiveTypeExtension?: boolean,
        allowExtraFields?: boolean,
        fieldFormat?: "snake_case" | "camelCase",
    })
    .csharp({                                  // C# generator
        rootNamespace: "Fhir.Types",
        staticSourceDir?: "./static",
    })

    // Output configuration
    .outputTo("./generated/types")             // Output directory
    .cleanOutput(true)                         // Clean before generation

    // Optional: Introspection & debugging
    .throwException()                          // Throw on errors (optional)
    .introspection({
        typeSchemas: "./schemas",              // Export TypeSchemas
        typeTree: "./tree.yaml",               // Export type tree
        fhirSchemas: "./fhir-schemas",         // Export FHIR schemas
        structureDefinitions: "./sd"           // Export StructureDefinitions
    })

    // Execute generation
    .generate();                                // Returns GenerationReport

Each method returns the builder instance, allowing method chaining. The generate() method executes the pipeline and returns a report with success status and generated file details.

Generator Options

Each language generator accepts its own option object. All options are optional; the tables below list the defaults.

TypeScript.typescript({ ... })

| Option | Type | Default | Description | |--------|------|---------|-------------| | generateProfile | boolean | true | Generate profile wrapper classes (factories, typed slice/extension accessors, validate()). | | primitiveTypeExtension | boolean | true | Emit sibling _field properties for primitive element extensions. | | openResourceTypeSet | boolean | false | For resource families (Resource, DomainResource), keep the resourceType union open by adding a string fallback instead of a closed literal union. | | extensionGetterDefault | "flat" \| "profile" \| "raw" | "flat" | Default return shape for generated extension getters. | | sliceGetterDefault | "flat" \| "raw" | "flat" | Default return shape for generated slice getters (flat strips discriminators, raw returns the full FHIR element). | | lineWidth | number | 120 | Maximum line width before wrapping. | | withDebugComment | boolean | false | Emit comments tracing each generated type back to its source schema. |

Python.python({ ... })

| Option | Type | Default | Description | |--------|------|---------|-------------| | client | "fhirpy" \| "none" | "fhirpy" | Client integration baked into the models: "fhirpy" makes models extend FhirpyBaseModel for the fhirpy async client; "none" emits plain Pydantic models. | | fieldFormat | "camelCase" \| "snake_case" \| "PascalCase" | "camelCase" | Naming convention for generated model fields. | | generateProfile | boolean | false | Generate profile wrapper classes (create(), typed accessors, validate()) around the Pydantic models. | | primitiveTypeExtension | boolean | false | Emit primitive element extension fields. | | allowExtraFields | boolean | false | Allow fields not present in the schema on generated models (Pydantic extra). | | rootPackageName | string | "fhir_types" | Root Python package name for the generated module tree. | | withDebugComment | boolean | false | Emit comments tracing each generated type back to its source schema. |

fhirpyClient?: boolean is deprecated — use client instead (true"fhirpy", false"none").

C#.csharp({ ... })

| Option | Type | Default | Description | |--------|------|---------|-------------| | rootNamespace | string | "Fhir.Types" | Root namespace for generated classes. | | staticSourceDir | string | — | Directory of static .cs source files copied verbatim into the output. | | withDebugComment | boolean | false | Emit comments tracing each generated type back to its source schema. |

Input - FHIR packages & resolves canonicals

The input stage leverages Canonical Manager to handle FHIR package management and dependency resolution. It processes FHIR packages from multiple sources (registry, local files, TGZ archives) and resolves all canonical URLs to their concrete definitions, ensuring all references between resources are properly linked before transformation.

The Register component wraps Canonical Manager specifically for codegen purposes, providing:

  • Multi-package indexing for fast canonical URL lookups across package boundaries
  • Package-aware resolution with automatic dependency tree traversal
  • FHIR-to-TypeSchema conversion using the @atomic-ehr/fhirschema translator
  • Element snapshot generation that merges inherited properties from base resources

Load Local StructureDefinitions & TGZ Archives

Use the new localPackage helper to point the builder at an on-disk FHIR package folder (for example, an unpublished implementation guide). If you only have loose StructureDefinition JSON files, group them under a folder and pass it to localStructureDefinitions. Canonical Manager handles copying, indexing, and dependency installation in both scenarios, so the API builder only needs to describe where the files live and what upstream packages they depend on.

.localStructureDefinitions({
    package: { name: "example.local.structures", version: "0.0.1" },
    path: "./custom-profiles",
    dependencies: [{ name: "hl7.fhir.r4.core", version: "4.0.1" }],
})
.localTgzPackage("./packages/my-custom-ig.tgz")

The example above points Canonical Manager at ./custom-profiles and installs the HL7 R4 core dependency automatically. The localTgzPackage helper registers .tgz artifacts that Canonical Manager already knows how to unpack.

Intermediate - Type Schema

Type Schema serves as a universal intermediate representation that bridges FHIR's complex hierarchical structure with programming language constructs. It transforms FHIR StructureDefinitions into a flattened, code-generation-friendly format that:

  • Unifies all FHIR elements (Resources, Types, ValueSets) into a consistent structure
  • Flattens nested paths for direct field access without complex traversal
  • Enriches definitions with resolved references, value set expansions, and type dependencies
  • Simplifies FHIR concepts like choice types and extensions for easier code generation

This approach enables generating idiomatic code for any programming language while preserving FHIR semantics and constraints. Learn more: Type Schema specification.

Tree Shaking

Tree shaking optimizes the generated output by including only the resources you explicitly need and their dependencies. Instead of generating types for an entire FHIR package (which can contain hundreds of resources), you can specify exactly which resources to include:

.typeSchema({
    treeShake: {
        "hl7.fhir.r4.core#4.0.1": {
            "http://hl7.org/fhir/StructureDefinition/Patient": {},
            "http://hl7.org/fhir/StructureDefinition/Observation": {}
        }
    }
})

This feature automatically resolves and includes all dependencies (referenced types, base resources, nested types, and extension definitions used by profiles) while excluding unused resources, significantly reducing the size of generated code and improving compilation times.

Field-Level Tree Shaking

Beyond resource-level filtering, tree shaking supports fine-grained field selection using selectFields (whitelist) or ignoreFields (blacklist):

.typeSchema({
    treeShake: {
        "hl7.fhir.r4.core#4.0.1": {
            "http://hl7.org/fhir/StructureDefinition/Patient": {
                selectFields: ["id", "name", "birthDate", "gender"]
            },
            "http://hl7.org/fhir/StructureDefinition/Observation": {
                ignoreFields: ["performer", "note"]
            }
        }
    }
})

Configuration Rules:

  • selectFields: Only includes the specified fields (whitelist approach)
  • ignoreFields: Removes specified fields, keeps everything else (blacklist approach)
  • These options are mutually exclusive - you cannot use both in the same rule
  • ignoreExtensions: Removes specific extensions from a profile by canonical URL

Polymorphic Field Handling:

FHIR choice types (like multipleBirth[x] which can be boolean or integer) are handled intelligently. Selecting/ignoring the base field affects all variants, while targeting specific variants only affects those types.

Logical Model Promotion

Some implementation guides expose logical models (logical-kind StructureDefinitions) that are intended to be used like resources in generated SDKs. The code generator supports promoting selected logical models to behave as resources during generation.

Use the programmatic API via APIBuilder:

const builder = new APIBuilder({})
  .fromPackage("my.custom.pkg", "4.0.1")
  .typeSchema({
    promoteLogical: {
      "my.custom.pkg": [
        "http://example.org/StructureDefinition/MyLogicalModel"
      ]
    }
  })

Resolving Schema Collisions

When multiple StructureDefinitions produce the same binding (e.g. ObservationCategory from both Observation and ObservationDefinition), the schemas may differ in strength or value set. By default the generator picks the most common variant and emits a warning:

! ts: 'urn:fhir:binding:ObservationCategory' from 'shared' has 2 versions (#duplicateSchema)

To fix this, add resolveCollisions to .typeSchema() specifying which source should win for each binding URL:

.typeSchema({
    resolveCollisions: {
        "urn:fhir:binding:ObservationCategory": {
            package: "hl7.fhir.r4.core#4.0.1",
            canonical: "http://hl7.org/fhir/StructureDefinition/Observation",
        },
    },
})
  • package — the FHIR package ID (name#version) that contains the preferred source
  • canonical — the StructureDefinition URL that should provide the authoritative binding

The generated README.md report (from .introspection()) lists all collisions with version details and includes a ready-to-paste resolveCollisions config for any unresolved ones. Example output:

## Schema Collisions

- `urn:fhir:binding:CommunicationReason` (2 versions)
  - Version 1 (selected): Communication (hl7.fhir.r4.core#4.0.1)
  - Version 2: CommunicationRequest (hl7.fhir.r4.core#4.0.1)
- `urn:fhir:binding:ProcessPriority` (2 versions)
  - Version 1 (auto): Claim (hl7.fhir.r4.core#4.0.1), CoverageEligibilityRequest (hl7.fhir.r4.core#4.0.1)
  - Version 2: ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1)

### Suggested `resolveCollisions` config

.typeSchema({
    resolveCollisions: {
        "urn:fhir:binding:ProcessPriority": {
            package: "hl7.fhir.r4.core#4.0.1",
            canonical: "http://hl7.org/fhir/StructureDefinition/Claim",
        },
    },
})
  • (selected) — resolved by your resolveCollisions config
  • (auto) — picked automatically (most common variant); add to config to make explicit

Generation

The generation stage transforms Type Schema into target language code using two complementary approaches:

1. Writer-Based Generation (Programmatic)

For languages with built-in support (TypeScript, Python, C#), extend the Writer class to implement language-specific generators:

  • FileSystemWriter: Base class providing file I/O, directory management, and buffer handling (both disk and in-memory modes)
  • Writer: Extends FileSystemWriter with code formatting utilities (indentation, blocks, comments, line management)
  • Language Writers (TypeScript, Python, CSharp): Implement language-specific generation logic by traversing TypeSchema index and generating corresponding types, interfaces, or classes (see also: Type Schema: Python SDK for FHIR)

Each language writer maintains full control over output formatting while leveraging high-level abstractions for common code patterns. Writers follow language idioms and best practices, with optimized output for production use.

When to use: Full control needed, complex generation logic, performance-critical, language has a dedicated writer, production-grade output

2. Mustache Template-Based Generation (Declarative)

For custom languages or formats, use Mustache templates to define code generation rules without programming:

  • Template Files: Declarative Mustache templates that describe output structure
  • Configuration: JSON config file controlling type filtering, naming, and post-generation hooks
  • ViewModels: Type Schema automatically transformed into template-friendly data structures

Templates enable flexible code generation for any language or format (Go, Rust, GraphQL, documentation, configs) by describing the output format rather than implementing generation logic.

When to use: Custom language support, quick prototyping, template-driven customization, non-code output


Profile Classes

When generating TypeScript with generateProfile: true, the generator creates profile wrapper classes that provide a fluent API for working with FHIR profiles. These classes handle complex profile constraints like slicing and extensions automatically.

import { observation_bpProfile as bpProfile } from "./profiles/Observation_observation_bp";

// create() auto-sets fixed values (code, meta.profile) and required slice stubs
const bp = bpProfile.create({
    status: "final",
    subject: { reference: "Patient/pt-1" },
});

// Slice setters — discriminator values (LOINC codes) applied automatically
// Single-variant choice types (value[x] → valueQuantity) are flattened:
bp.setVSCat({ text: "Vital Signs" })
    .setSystolicBP({ value: 120, unit: "mmHg" })
    .setDiastolicBP({ value: 80, unit: "mmHg" })
    .setEffectiveDateTime("2024-06-15");

bp.validate(); // [] — valid

// Get plain FHIR JSON — ready for API calls, storage, etc.
const obs = bp.toResource();
// obs.component[0].valueQuantity.value === 120
// obs.component[0].code.coding[0].code === "8480-6"

Slicing & Choice Type Flattening:

// Flat getter (default) — discriminator stripped, choice type flattened
bp.getSystolicBP();        // { value: 120, unit: "mmHg" }

// Raw getter — full FHIR element including discriminator values
bp.getSystolicBP('raw');   // { code: { coding: [...] }, valueQuantity: { value: 120, ... } }

Wrapping Existing Resources:

// Wrap any resource to read slices
const bp2 = bpProfile.from(existingObservation);
bp2.getSystolicBP();       // { value: 120, unit: "mmHg" }
bp2.getVSCat();            // { text: "Vital Signs" }
bp2.getEffectiveDateTime(); // "2024-06-15"

Validation:

const errors = bp.validate();
// [] — empty means valid
// ["effective: at least one of effectiveDateTime, effectivePeriod is required"]

See examples/typescript-r4-us-core/ for R4 and US Core profile tests.

Python (generateProfile: true) produces equivalent profile classes — create(), typed accessors, and validate() — wrapping a Pydantic model via _resource. See examples/python-r4-us-core/.

Support


Built by the Atomic Healthcare team