Les paquets FHIR sont le moyen qu'utilise l'écosystème FHIR pour distribuer du contenu : des archives au format npm contenant des StructureDefinitions, des ValueSets et des CodeSystems. Presque tous les outils les lisent — les serveurs FHIR et de terminologie, les validateurs, l'IG Publisher, les générateurs de SDK, etc.
Le format est simple, les registres sont ouverts, et le contenu n'est souvent pas ce dont un outil a besoin. Le registre contient plus d'un millier de paquets, provenant de programmes nationaux, de fournisseurs, d'équipes de projet et de HL7 lui-même, publiés selon des calendriers distincts et sur plusieurs versions du standard. Il était inévitable que certains présentent des défauts.
@atomic-ehr/codegen compile ces définitions en SDK typés, et les langages cibles sont inflexibles. TypeScript, C# et Python/Pydantic n'admettent pas les références pendantes : chaque type doit exister, chaque valeur d'énumération doit être un code réel, et — accessoirement — chaque dérivation doit restreindre la définition. Pour que codegen fonctionne, l'ensemble des paquets doit être cohérent de manière statique.
Ce à quoi s'attendre des paquets FHIR réels
Chaque exemple se trouve dans un paquet que vous pouvez installer aujourd'hui, à la version indiquée. Les défauts se répartissent en trois catégories, et chacune brise une étape différente de la compilation : le chargement des paquets, la résolution d'une référence, l'application d'une contrainte.
1. Métadonnées de paquet brisées
Le manifeste du paquet et l'index sont incorrects, de sorte que le chargeur ne peut même pas assembler l'ensemble des paquets.
Un index brisé ou manquant. La spécification exige un fichier .index.json, qui associe les canoniques aux noms de fichiers afin que les outils n'aient pas à ouvrir chaque fichier. Certains paquets en sont dépourvus (ehelse.fhir.no.grunndata@2.3.5, hl7.fhir.no.basis@2.2.2), et d'autres pointent vers des fichiers inexistants (de.basisprofil.r4@1.6.0-ballot2).
Une dépendance sur le noyau manquante. La spécification est explicite — « une dépendance sur un paquet noyau est obligatoire » — mais de nombreux paquets l'omettent (simplifier.core.r4* à 4.0.x, ehelse.fhir.no.grunndata@2.3.5, nhn.fhir.no.kjernejournal@1.0.0, sfm.030322@2.0.1) tout en utilisant des types du noyau R4.
Un manifeste en désaccord avec le registre. simplifier.core.r4.base@4.0.0 dépend de simplifier.core.r4.resources. Le registre sert ce paquet, mais le package.json à l'intérieur de l'archive indique :
{ "name": "simplifier.core.r4.rResources", "version": "4.0.0" }
2. Une référence qui ne se résout pas
La définition pointe vers quelque chose que l'ensemble des paquets ne contient pas.
Une canonique que personne ne définit. Dans hl7.cda.uv.core@2.0.2-sd, Material.sdtcExpirationTime est typé comme .../StructureDefinition/IVL_TS — avec un trait de soulignement. Le type de données est publié à .../IVL-TS, avec un trait d'union, et rien dans le paquet ne déclare l'orthographe avec le trait de soulignement.
Un type de données provenant d'une autre version de FHIR. hl7.fhir.uv.extensions.r4 est le pack d'extensions HL7 pour R4 — son manifeste indique "fhirVersions": ["4.0.1"]. Pendant trois ans, il a livré des extensions dont le value[x] utilisait CodeableReference ou Availability, des types de données ajoutés en R5 qui n'existent pas en 4.0.1 :
| Paquet | Publié | Extensions typées avec des types de données R5 seulement |
|---|---|---|
hl7.fhir.uv.extensions.r4@1.0.0 | mars 2023 | 10 |
hl7.fhir.uv.extensions.r4@5.1.0 | avr. 2024 | 9 |
hl7.fhir.uv.extensions.r4@5.2.0 | févr. 2025 | 8 |
hl7.fhir.uv.extensions.r4@5.3.0 | mai 2026 | 0 |
Le problème a été résolu dans la version 5.3.0, publiée en mai 2026. Les versions antérieures restent utilisées par le biais des dépendances de paquets. Quatre paquets dans la fermeture C-CDA déclarent extensions.r4, et chacun d'eux référence une ligne défectueuse : hl7.fhir.us.core, hl7.fhir.uv.xver-r5.r4 et hl7.terminology.r4 demandent 5.2.0, tandis que hl7.terminology demande encore 1.0.0. Demander la version corrigée vous-même ne change rien — chaque déclaration doit être redirigée avec ensureDependency, comme l'illustre l'exemple C-CDA.
3. Un profil qui contredit sa base
L'empaquetage est correct et toutes les canoniques se résolvent. La contrainte elle-même n'est pas légale — un profil ne peut que restreindre ce que sa base autorise déjà. Voici quelques exemples :
Il élargit au lieu de restreindre. Le R4 de base permet à RelatedPerson.patient de pointer vers exactement un élément :
// hl7.fhir.r4.core@4.0.1 — RelatedPerson.patient
"type": [{
"code": "Reference",
"targetProfile": ["http://hl7.org/fhir/StructureDefinition/Patient"]
}]
Le profil norvégien gd-RelatedPerson en est dérivé et en liste cinq :
// ehelse.fhir.no.grunndata@2.3.5 — gd-RelatedPerson, même élément
"type": [{
"code": "Reference",
"targetProfile": [
"http://hl7.org/fhir/StructureDefinition/Patient",
"http://hl7.org/fhir/StructureDefinition/Person",
// ...
]
}]
Il épingle une valeur inexistante. Le CodeSystem bundle-type définit exactement dix codes :
// hl7.fhir.r5.core@5.0.0 — CodeSystem/bundle-type
"concept": [
{ "code": "document" }, { "code": "message" },
{ "code": "transaction" }, { "code": "transaction-response" },
{ "code": "batch" }, { "code": "batch-response" },
{ "code": "history" }, { "code": "searchset" },
{ "code": "collection" }, { "code": "subscription-notification" }
]
Le profil batch-bundle du noyau R5 épingle Bundle.type à un onzième code :
// hl7.fhir.r5.core@5.0.0 — StructureDefinition/batch-bundle
{ "id": "Bundle.type", "path": "Bundle.type", "patternCode": "bundle" }
Les deux issues sont bloquées. Corriger le patternCode se heurte aux règles de HL7 : « les valeurs fixes et les patrons ne seront pas ajoutés, supprimés ou modifiés d'une façon qui pourrait invalider les instances existantes. » Ajouter bundle au CodeSystem n'est pas mieux — bundle-type est normatif et marqué content: "complete". Dans les deux cas, il faut un scrutin, pas un correctif.
Pourquoi attendre l'amont ne fonctionne pas
Soumettez quand même un rapport — les défauts qui ont été corrigés l'ont été parce que quelqu'un a ouvert un billet. Mais l'amont ne peut pas être votre seul plan.
La vitesse. FHIR vise une publication tous les « 18 à 24 mois environ ». L'écart réel entre R4 et R5 a été de 51 mois. Un billet accepté aujourd'hui atterrit dans une spécification qui n'existe pas encore, alors que votre compilation doit réussir maintenant.
L'immuabilité. HL7 l'a dit dans un avis de sécurité, expliquant pourquoi un correctif ne pouvait pas atteindre du contenu déjà publié : « les paquets publiés sont immuables. » Un correctif est donc toujours livré sous la forme d'une nouvelle version, et la migration vers celle-ci n'est souvent pas de votre ressort — un programme national épingle une version en scrutin, la réglementation américaine cite des versions d'IG dans 45 CFR § 170.215. Parfois, il n'existe rien de plus récent : hl7.fhir.r4.core n'a eu qu'une seule publication, 4.0.1, en octobre 2019.
L'impossibilité. Et certains défauts n'ont tout simplement pas de correctif amont — corriger batch-bundle signifie rouvrir du contenu normatif, ce que précisément le statut normatif empêche.
Corriger les paquets dans votre propre compilation
Attendre signifie donc attendre l'éditeur du paquet dont vous avez besoin et les éditeurs de tout ce dont il dépend. La correction doit plutôt se faire de votre côté. Mais exactement où ?
Modifier l'archive décompressée. C'est une correction courante et directe : modifiez le fichier et relancez la compilation. Mais la modification n'est pas consignée, la prochaine installation l'écrase, et d'autres outils peuvent lire le paquet modifié sans savoir qu'il a été altéré.
Bifurquer, intégrer ou réhéberger le paquet. Vous êtes maintenant ancré à la version que vous avez bifurquée, et chaque nouvelle version en amont doit être re-bifurquée manuellement ou silencieusement ignorée. Votre copie ne remplace pas non plus l'original : tout ce qui, dans la fermeture, dépend encore du vrai paquet le récupère en même temps que le vôtre, et les mêmes canoniques se retrouvent définies deux fois.
Traiter le défaut comme un cas particulier dans votre générateur de code. Un fait concernant le paquet de quelqu'un d'autre se retrouve alors dans votre code source, là où personne ne pensera à le chercher.
Codegen emprunte la voie restante : la correction est déclarée dans votre configuration codegen, et le paquet est lu à travers elle.
Ce que vous pouvez corriger
Les correctifs s'appliquent dans le chargeur, avant que le générateur ne voie quoi que ce soit.
Cinq d'entre eux, pour les défauts mentionnés précédemment :
patches: {
packageJson: [
// Most Norge packages use R4 core types without declaring the dependency.
ensureDependency({ "hl7.fhir.r4.core": "4.0.1" }),
// Published as .resources, but calls itself .rResources in its own manifest.
renamePackage("simplifier.core.r4.rResources", "simplifier.core.r4.resources"),
],
indexEntry: [
// shareablecodesystem's recursive concept.concept breaks the schema generator.
excludeCanonical({
package: { name: "hl7.fhir.r5.core", version: "5.0.0" },
url: "http://hl7.org/fhir/StructureDefinition/shareablecodesystem",
reason: "CodeSystem.concept.concept recursion breaks generation",
}),
],
fhirResource: [
// Material.sdtcExpirationTime points at .../IVL_TS; the datatype is published
// at .../IVL-TS. Scoped to Material, because IVL-TS's own `type` field carries
// the underscore spelling legitimately — an unscoped replace would break it.
inResource("http://hl7.org/cda/stds/core/StructureDefinition/Material", [
replaceText(
"http://hl7.org/cda/stds/core/StructureDefinition/IVL_TS",
"http://hl7.org/cda/stds/core/StructureDefinition/IVL-TS",
),
]),
// bundle-type is missing codes the R5 bundle profiles pin — including
// "bundle", batch-bundle's typo, which is not a legal code anywhere.
ensureCodes("http://hl7.org/fhir/bundle-type", ["bundle", "subscription-notification"]),
],
}
Trois points méritent d'être connus avant d'en écrire un.
L'emplacement d'un correctif est important. Le manifeste, l'index et les corps de ressources sont lus à des moments différents ; une faute de frappe dans le manifeste doit donc être corrigée dans packageJson — au moment où les ressources sont lues, le graphe de dépendances est déjà construit.
Ciblez un correctif sur son objet. L'encapsuler dans inPackage ou inResource le restreint à un seul paquet ou à une seule canonique. C'est facultatif, mais généralement judicieux. Le ensureCodes ci-dessus s'applique sans portée restreinte, ce qui convient — il ne touche que le CodeSystem qu'il nomme.
L'exclusion supprime plus que la sortie. Une canonique exclue disparaît également de la résolution, de sorte que tout ce qui en est dérivé doit être exclu avec elle.
Codegen effectue deux de ces opérations pour vous :
- Les défauts HL7 connus sont corrigés par défaut.
builtinPatchesles contient et les applique à chaque compilation, afin que personne ne les redécouvre. Désactivez-les ou remplacez-les si vous n'êtes pas d'accord. - Un paquet livré sans index en reçoit un. Il est construit à partir d'un balayage de répertoire, automatiquement. Un index qui existe mais est incorrect est laissé tel quel avec un avertissement jusqu'à ce que vous demandiez
packageIndex: "recover"— reconstruire quelque chose qu'un éditeur a bien livré est une hypothèse plus importante à faire en votre nom.
Vivre avec les correctifs
Un correctif modifie les données de quelqu'un d'autre, donc chaque correction appliquée apparaît dans le rapport de génération — chaque index récupéré et chaque exclusion, accompagnés de la reason déclarée par leur correctif. « Pourquoi mon SDK généré diffère-t-il du paquet publié ? » a donc toujours une réponse, que n'importe qui peut vérifier. C'est là le véritable intérêt de déclarer une correction plutôt que de l'effectuer manuellement : une archive modifiée ne signale rien et un paquet bifurqué n'explique rien, alors qu'un correctif doit indiquer ce qu'il a fait et pourquoi. Trois règles font que cela reste utile.
Soumettez quand même un rapport en amont. Un correctif gagne du temps ; ce n'est pas une vraie correction.
Réparez l'entrée, ne la repensez pas. Le correctif bundle-type ci-dessus est la solution économique — il ajoute un code que HL7 n'a jamais défini plutôt que de corriger le patternCode de batch-bundle.
Attendez-vous à supprimer des correctifs. Ces huit exclusions sont devenues inutiles le jour où 5.3.0 a été publié, et codegen ne l'a pas remarqué. Un correctif qui ne fait silencieusement rien ressemble exactement à un qui accomplit un travail important.
Ce que vous savez sur un paquet défectueux appartient à la configuration, où c'est versionné et révisé comme tout le reste — pas dans une copie du paquet que vous maintenez désormais, pas dans le code source de votre générateur, pas dans une page wiki que trois personnes connaissent. Déclarez-le une fois, avec sa raison, et ouvrez le billet en amont pendant que votre compilation reste verte.
Chaque défaut nommé ici a été vérifié en septembre 2026 contre les archives publiées aux versions indiquées. La configuration provient des pipelines d'exemples de codegen, qui régénèrent et vérifient les types contre des registres actifs à chaque poussée.
Pour ce que codegen produit une fois que l'entrée est saine, voir les profils US Core en TypeScript et Type Schema : une approche pragmatique pour construire un SDK FHIR.



