Skip to content

fix(profile): avoid duplicate meta key when meta is a factory param - #138

Merged
ryukzak merged 1 commit into
atomic-ehr:mainfrom
cognovis:fix/profile-duplicate-meta-key
Apr 20, 2026
Merged

fix(profile): avoid duplicate meta key when meta is a factory param#138
ryukzak merged 1 commit into
atomic-ehr:mainfrom
cognovis:fix/profile-duplicate-meta-key

Conversation

@sussdorff

Copy link
Copy Markdown
Contributor

Fixes #137.

Summary

When a profile exposes meta as a required factory param (e.g. KBV ITA FOR/ERP profiles where meta.profile is pinned via min: 1), the generated createResource emitted two meta: keys in the same object literal — first meta: args.meta, from the generic param loop, then meta: { profile: [canonicalUrl] }, from the unconditional profile emission. This caused TS1117 and silently dropped caller-supplied meta.tag / meta.source / extra profile URLs.

Before / After

Before (0.0.10, 0.0.11, 0.0.12 — all broken for profiles with meta in Raw type):

const resource: Medication = {
    resourceType: "Medication",
    code: args.code,
    id: args.id,
    meta: args.meta,                                          // <-- duplicate
    meta: { profile: [...canonicalUrl] },                     // <-- duplicate
}

After:

const resource: Medication = {
    resourceType: "Medication",
    code: args.code,
    id: args.id,
    meta: { ...args.meta, profile: [...(args.meta?.profile ?? []), ...canonicalUrl] },
}

Profiles without meta in their Raw type continue to emit the existing single-line form — no behaviour change there.

Implementation

src/api/writer-generator/typescript/profile.ts, both emission branches (Input-helper and standard):

  1. Compute hasMetaParam = allFields.some((f) => f.name === "meta").
  2. During the field loop, skip meta when hasMeta is true.
  3. When emitting the meta: line, if hasMetaParam, spread args.meta and merge the profile URL into meta.profile; otherwise emit the current static form.

Validation

  • bun test test/unit/ — 174 pass / 4 pre-existing snapshot failures unrelated to this change (same on main).
  • bun test test/api/ — 75 pass / 0 fail (no snapshot changes).
  • bun run examples/typescript-r4/generate.ts — regenerates cleanly, zero diff in example output (none of the example profiles have meta in their Raw type, so no emission change).
  • Downstream: regenerating a consumer project (mira-adapters, German FHIR profiles) with this fix drops the tsc --noEmit error count from 9 → 0 for the duplicate-meta class.

Notes

  • from() and apply() validation is unaffected because the merged emission still guarantees the profile's canonicalUrl is in meta.profile.
  • Happy to add a regression test fixture (a minimal StructureDefinition that pins meta.profile min=1) if you'd like — wanted to ship the minimal fix first.

When a profile exposes `meta` as a required factory param (e.g. KBV
FOR/ERP profiles where `meta.profile` is pinned via min=1), the
TypeScript profile writer emitted two `meta:` keys in the same
`createResource` object literal — the first from the generic param
loop and the second from the unconditional `meta: { profile: [...] }`
emission. This produced TypeScript TS1117 errors and silently
dropped any caller-supplied `meta.tag` / `meta.source` / extra
profile URLs, because the second key overwrote the first.

Fix: when `meta` is already in `allFields`, suppress the generic
emission and merge the caller-supplied meta with the profile's
canonical URL:

    meta: { ...args.meta, profile: [...(args.meta?.profile ?? []), canonicalUrl] }

Profiles without `meta` in params are unchanged:

    meta: { profile: [canonicalUrl] }

Applies to both the Input-helper branch (with extension slices) and
the standard branch. Typecheck on consuming project (mira-adapters,
10+ KBV profiles) goes from 9 TS1117 errors to 0 after regeneration.
@ryukzak

ryukzak commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

@ryukzak
ryukzak merged commit b0d016e into atomic-ehr:main Apr 20, 2026
14 checks passed
sussdorff added a commit to cognovis/codegen that referenced this pull request Apr 21, 2026
…de (codegen-zpq)

All production-relevant fork-only code is merged upstream and ships in
@atomic-ehr/codegen@0.0.13:

- duplicate-meta-key fix (atomic-ehr#138) — landed upstream
- KBV on-the-fly workarounds (ignorePackageIndex, preprocessPackage) — landed upstream
- real-world regression test via examples/on-the-fly/kbv-r4/profile-patient.test.ts

Polaris / mira-adapters consumes @atomic-ehr/codegen directly from npm (always did —
the standing consumer-snapshot branch was speculative infrastructure). The fork is
retained only as a doc anchor, PR staging area, and future baking ground for the
scalar-slice-setters epic (codegen-4cw, blocked).

Changes:
- COGNOVIS.md: rewritten to document hibernate mode and reactivation criteria
- test/api/write-generator/meta-regression.test.ts + fixture + snapshot: removed
  (redundant with upstream examples/on-the-fly/kbv-r4/profile-patient.test.ts;
  synthetic code-shape assertions were brittle against legitimate generator refactors)
- cognovis/mira-adapters branch deleted, state preserved as tag
  archive/2026-04-mira-adapters for reactivation if needed
@sussdorff
sussdorff deleted the fix/profile-duplicate-meta-key branch July 24, 2026 15:19
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.

TS Profile: duplicate meta object literal key when meta is a factory param (TS1117)

2 participants