Skip to content

API: migrate package-defect handling to CM patches (Phase 1, #128) - #176

Open
ryukzak wants to merge 18 commits into
mainfrom
feat/phase1-patch-helpers
Open

API: migrate package-defect handling to CM patches (Phase 1, #128)#176
ryukzak wants to merge 18 commits into
mainfrom
feat/phase1-patch-helpers

Conversation

@ryukzak

@ryukzak ryukzak commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Phase 1 of the #128 migration (see docs/design/defect-handling-migration.md, PR #173): move the example preprocessPackage defect workarounds onto CanonicalManager's composable patches runtime (shipped in CM 0.0.25). Codegen-only; no CM changes.

Helpers + combinators — src/api/patches.ts

Unscoped transform helpers, scoped by nesting combinators:

  • Combinators: inPackage(match, handlers) and inResource(url, handlers) — scope a list of handlers (nest freely).
  • Transforms: injectDependency(deps), renameCanonical(renames), renameReferenceTarget(renames), swapBinding(swaps) (scoped via combinators); renamePackage(renames), patchCodeSystem(url, codes), and ensureDependency(deps) (self-matching — adds missing deps or adjusts versions, never injects a package into itself).

APIBuilder

  • New patches?: PatchesInput constructor option — CM's Partial<Patches> (per-phase handler arrays: packageJson / indexEntry / fhirResource). Warns if combined with an injected manager/register (which owns its own patch wiring).
  • New packageIndex?: PackageIndexMode constructor option ("use" / "recover" / "regenerate"), forwarded to CM; ignorePackageIndex is marked @deprecated (CM maps true"regenerate" and throws if both are set).
  • P1.7: generate() captures manager.report() into GenerationReport.inputReport (absent on the prebuilt-register path; empty for cache-served packages), and prettyReport renders it as an "Input fixes" section:
Input fixes (1):
  - recovered index for de.basisprofil.r4@1.6.0-ballot2: unparseable, 173 resources

Examples migrated off preprocessPackage

// kbv
patches: { packageJson: [inPackage("de.basisprofil.r4", [injectDependency({ "hl7.fhir.r4.core": "4.0.1" })])] },
packageIndex: "recover", // was ignorePackageIndex: true — heals the broken simplifier .index.json, warns on heal

// norge
patches: {
  packageJson: [
    ensureDependency({ "hl7.fhir.r4.core": "4.0.1" }), // every package except core itself
    renamePackage({ "simplifier.core.r4.rResources": "simplifier.core.r4.resources" }),
  ],
  fhirResource: [inResource(".../gd-RelatedPerson", [renameReferenceTarget({ /* Person→Patient ×3 */ })])],
}

// ccda (on its manual CM — nesting inPackage > inResource)
patches: { fhirResource: [
  inPackage("hl7.cda.uv.core", [renameCanonical({ ".../IVL_TS": ".../IVL-TS" })]),
  inPackage("hl7.cda.us.ccda", [inResource(".../CarePlanAct", [swapBinding({ "<nlm>": "<v3-xDocumentActMood>" })])]),
  patchCodeSystem("http://hl7.org/fhir/bundle-type", ["bundle", "subscription-notification"]),
] }

kbv, norge, and ccda have no preprocessPackage left.

Notes

  • Pins CM to the released 0.0.25 (ships the patches runtime, ./patch subpath, packageIndex mode, and report()).
  • The report covers CM's shipped ReportEntry kinds (index-recovery | exclusion | deprecation); transform helpers can't report what they patched until CM adds a patch kind — CM follow-up.

Verification

  • Rebased onto current main: typecheck, lint, and the standard suite (337/0) green.
  • Examples regenerate byte-identical through the patches runtime and typecheck: kbv (5/0 tests), norge, ccda (24/0), kbv-condition-diagnosis.
  • kbv's packageIndex: "recover" verified from a cold cache — logs Recovered de.basisprofil.r4@1.6.0-ballot2: .index.json is unparseable; scanning directory instead and produces identical output.

ryukzak added 12 commits July 29, 2026 16:18
Adds an optional `patches?: Partial<Patches>` constructor option, passed to the
CanonicalManager config; warns if set alongside an injected manager/register
(which owns its own patch wiring). No example changes yet.
…on (P1.1)

New src/api/patches/ helper module with injectDependency(match, deps) — a
PackagePatch that adds missing FHIR package dependencies to a matching manifest.
Migrates the dependency-injection blocks of the kbv-r4 and norge-r4 examples off
preprocessPackage onto CM's patches config (kbv's preprocessPackage is fully
removed; norge keeps its rename + resource fixes for P1.2/P1.3). Generated code
typechecks and example tests pass.
Switches @atomic-ehr/fhir-canonical-manager from 0.0.24 to the canary build that
ships the composable patches runtime, so the Phase-1 patch helpers resolve against
a published version (not a local link). To be repinned to the proper release once
CM #14 ships.
renamePackage(renames) — a PackagePatch that fixes manifest-name typos via an
old→new map. Migrates norge-r4's packageNameFixes off preprocessPackage onto
patches; norge's preprocessPackage now only handles the gd-RelatedPerson resource
fix (P1.3 next). Generated code typechecks.
Adds scoped resource-phase URL-rewrite helpers (shared blanket replace + a
ResourceScope of package/url). Migrates norge's gd-RelatedPerson Person->Patient
fix (renameReferenceTarget) — norge's preprocessPackage is now fully gone — and
ccda's IVL_TS->IVL-TS canonical typo (renameCanonical) onto its manual CM's
patches config. Both examples regenerate successfully; norge generated code
typechecks. ccda keeps preprocessPackage for CarePlanAct + bundle-type (P1.4/P1.5).
…1.5)

swapBinding(swaps, scope?) — swap a binding's ValueSet URL for an available one
(scoped URL replace). patchCodeSystem(url, codes) — add missing codes to a
CodeSystem. Migrates ccda's CarePlanAct ValueSet swap and bundle-type missing
codes; ccda's preprocessPackage is now fully removed. All three examples
(kbv/norge/ccda) regenerate successfully and their generated code typechecks.
…P1.6)

Adds nesting scope combinators forPackage(match, handlers) and forResource(url,
handlers); the transform helpers (injectDependency/renameCanonical/
renameReferenceTarget/swapBinding) drop their scope args and are scoped by the
combinators instead. APIBuilder.patches now accepts a single handler or a list
per phase (PatchesInput), normalized to CM's array form. Restyles all three
examples to the grouped/nested form, e.g.

  patches: { package: forPackage("de.basisprofil.r4", [injectDependency({ "hl7.fhir.r4.core": "4.0.1" })]) }

All three examples regenerate (Status: Success) and tests pass (kbv 5/0,
norge 3/0, ccda 24/0).
@ryukzak
ryukzak force-pushed the feat/phase1-patch-helpers branch from f5aa6f5 to 1bb935e Compare July 29, 2026 12:29
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