Summary
When a profile exposes meta as a required factory param (e.g. profiles that pin meta.profile with min: 1, such as the German KBV ITA FOR/ERP profiles), the generated createResource emits two meta: keys in the same object literal. This produces a TypeScript TS1117: An object literal cannot have multiple properties with the same name error and silently discards any caller-supplied meta.tag / meta.source / extra profile URLs, because the second key overwrites the first.
Repro
Any profile where the generated …ProfileRaw includes meta: Meta (i.e. meta ends up in ProfileFactoryInfo.params). In the wild: any KBV FOR/ERP profile in kbv.ita.for@1.3.1 or kbv.ita.erp@1.4.1, e.g. KBV_PR_ERP_Medication_FreeText, KBV_PR_FOR_Patient, KBV_PR_FOR_Organization.
Generated output (versions 0.0.10, 0.0.11, 0.0.12):
static createResource (args: KBV_PR_ERP_Medication_FreeTextProfileRaw) : Medication {
const resource: Medication = {
resourceType: "Medication",
code: args.code,
id: args.id,
meta: args.meta, // <-- #1
meta: { profile: [KBV_PR_ERP_Medication_FreeTextProfile.canonicalUrl] }, // <-- #2 (overwrites #1)
}
return resource;
}
tsc --noEmit reports:
error TS1117: An object literal cannot have multiple properties with the same name.
Impact
tsc breaks on every affected profile. In our project (mira-adapters) this hit 9 generated profile files across kbv-ita-erp / kbv-ita-for.
- Even if the duplicate were ignored, the semantics are wrong: the second
meta literal drops args.meta.tag, args.meta.source, args.meta.security, and any extra profile URLs the caller included.
Root cause
src/api/writer-generator/typescript/profile.ts:
collectProfileFactoryInfo puts every required field into params (via the direct scan and collectBaseRequiredParams). Profiles that pin meta.profile cause meta to be required on the base Resource element, so meta lands in params.
allFields (line 371-375) unpacks params into { name, value: "args." + name } entries.
- The
createResource writer loops over allFields and emits each as name: value, — producing meta: args.meta,.
- Immediately afterwards, when
hasMeta is true, the writer unconditionally emits meta: { profile: [canonicalUrl] }, — the second key.
This affects both emission branches (the Input-helper branch around line 491-500, and the standard branch around line 533-540).
Suggested fix
When meta is present in allFields, skip the generic meta: args.meta emission, and merge in the profile URL so the caller-supplied fields are preserved:
const hasMetaParam = allFields.some((f) => f.name === "meta");
// ...in the resource literal loop...
for (const f of allFields) {
if (f.name === "meta" && hasMeta) continue; // <-- skip, handled below
w.line(`${f.name}: ${f.value},`);
}
if (hasMeta) {
if (hasMetaParam) {
w.line(`meta: { ...args.meta, profile: [...(args.meta?.profile ?? []), ${profileClassName}.canonicalUrl] },`);
} else {
w.line(`meta: { profile: [${profileClassName}.canonicalUrl] },`);
}
}
After applying this, the same profile regenerates as:
static createResource (args: KBV_PR_ERP_Medication_FreeTextProfileRaw) : Medication {
const resource: Medication = {
resourceType: "Medication",
code: args.code,
id: args.id,
meta: { ...args.meta, profile: [...(args.meta?.profile ?? []), KBV_PR_ERP_Medication_FreeTextProfile.canonicalUrl] },
}
return resource;
}
This preserves caller meta.tag / meta.source / extra profile URLs and keeps from() / apply() happy because canonicalUrl is still guaranteed to be in meta.profile.
PR
Fix branch pushed to our fork: https://github.com/cognovis/codegen/tree/fix/profile-duplicate-meta-key (1 file changed, 18 insertions, 2 deletions). Happy to open a PR against main — will cross-link once done.
Versions affected
0.0.10, 0.0.11, 0.0.12 (tip of main). We stayed on 0.0.9 by manually hand-patching the 9 generated files after each regeneration — the real fix belongs here.
Summary
When a profile exposes
metaas a required factory param (e.g. profiles that pinmeta.profilewithmin: 1, such as the German KBV ITA FOR/ERP profiles), the generatedcreateResourceemits twometa:keys in the same object literal. This produces a TypeScriptTS1117: An object literal cannot have multiple properties with the same nameerror and silently discards any caller-suppliedmeta.tag/meta.source/ extra profile URLs, because the second key overwrites the first.Repro
Any profile where the generated
…ProfileRawincludesmeta: Meta(i.e.metaends up inProfileFactoryInfo.params). In the wild: any KBV FOR/ERP profile inkbv.ita.for@1.3.1orkbv.ita.erp@1.4.1, e.g.KBV_PR_ERP_Medication_FreeText,KBV_PR_FOR_Patient,KBV_PR_FOR_Organization.Generated output (versions 0.0.10, 0.0.11, 0.0.12):
tsc --noEmitreports:Impact
tscbreaks on every affected profile. In our project (mira-adapters) this hit 9 generated profile files across kbv-ita-erp / kbv-ita-for.metaliteral dropsargs.meta.tag,args.meta.source,args.meta.security, and any extra profile URLs the caller included.Root cause
src/api/writer-generator/typescript/profile.ts:collectProfileFactoryInfoputs every required field intoparams(via the direct scan andcollectBaseRequiredParams). Profiles that pinmeta.profilecausemetato be required on the baseResourceelement, sometalands inparams.allFields(line 371-375) unpacksparamsinto{ name, value: "args." + name }entries.createResourcewriter loops overallFieldsand emits each asname: value,— producingmeta: args.meta,.hasMetais true, the writer unconditionally emitsmeta: { profile: [canonicalUrl] },— the second key.This affects both emission branches (the Input-helper branch around line 491-500, and the standard branch around line 533-540).
Suggested fix
When
metais present inallFields, skip the genericmeta: args.metaemission, and merge in the profile URL so the caller-supplied fields are preserved:After applying this, the same profile regenerates as:
This preserves caller
meta.tag/meta.source/ extra profile URLs and keepsfrom()/apply()happy becausecanonicalUrlis still guaranteed to be inmeta.profile.PR
Fix branch pushed to our fork: https://github.com/cognovis/codegen/tree/fix/profile-duplicate-meta-key (1 file changed, 18 insertions, 2 deletions). Happy to open a PR against
main— will cross-link once done.Versions affected
0.0.10, 0.0.11, 0.0.12 (tip of main). We stayed on 0.0.9 by manually hand-patching the 9 generated files after each regeneration — the real fix belongs here.