@atomic-ehr/codegen 0.0.17 → 0.0.18-canary.20260727072946.f724a66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +28 -1
- package/assets/api/writer-generator/python/profile_helpers.py +52 -18
- package/assets/api/writer-generator/typescript/profile-helpers.ts +11 -0
- package/dist/cli/index.js +8 -8
- package/dist/index.d.ts +14 -2
- package/dist/index.js +420 -196
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -279,7 +279,7 @@ Tree shaking optimizes the generated output by including only the resources you
|
|
|
279
279
|
})
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
-
This feature automatically resolves and includes all dependencies (
|
|
282
|
+
This feature automatically resolves and includes all dependencies (field 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. Reference targets are not dependencies: a field typed `Reference<"Patient">` does not pull the `Patient` type into the output, since the reference is rendered as a string literal.
|
|
283
283
|
|
|
284
284
|
##### Field-Level Tree Shaking
|
|
285
285
|
|
|
@@ -311,6 +311,33 @@ Beyond resource-level filtering, tree shaking supports fine-grained field select
|
|
|
311
311
|
|
|
312
312
|
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.
|
|
313
313
|
|
|
314
|
+
##### Following Reference Targets
|
|
315
|
+
|
|
316
|
+
To also generate types for a schema's reference targets — both the base resources (`reference.resource`) and the target profiles (`reference.profiles`) — enable `followReferences` on the rule (default: `false`):
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
.typeSchema({
|
|
320
|
+
treeShake: {
|
|
321
|
+
"kbv.basis": {
|
|
322
|
+
"https://fhir.kbv.de/StructureDefinition/KBV_PR_Base_Condition_Diagnosis": {
|
|
323
|
+
followReferences: true
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
With this rule, `Condition.subject` targeting `KBV_PR_Base_Patient` pulls the Patient profile (and the `Patient`/`Group` resources) into the generated output, including their regular dependencies. Following is **non-transitive**: the pulled-in types keep their own reference targets as plain string literals, so a single flag cannot drag in the whole package. If a followed target is also listed as a tree-shake root, the root's rule wins.
|
|
331
|
+
|
|
332
|
+
To enable it for every tree-shake root at once, use `treeShakeDefaults` (a rule's own `followReferences` still wins over the default):
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
.typeSchema({
|
|
336
|
+
treeShake: { ... },
|
|
337
|
+
treeShakeDefaults: { followReferences: true }
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
314
341
|
#### Logical Model Promotion
|
|
315
342
|
|
|
316
343
|
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.
|
|
@@ -35,7 +35,7 @@ T = TypeVar("T")
|
|
|
35
35
|
# ---------------------------------------------------------------------------
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def is_record(value:
|
|
38
|
+
def is_record(value: object) -> TypeGuard[MutableMapping[str, Any]]:
|
|
39
39
|
"""True when ``value`` is a non-None mapping (dict-like, not a list)."""
|
|
40
40
|
return isinstance(value, MutableMapping)
|
|
41
41
|
|
|
@@ -87,14 +87,14 @@ def apply_slice_match(input_obj: Mapping[str, Any], match: Mapping[str, Any]) ->
|
|
|
87
87
|
return result
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def _get_key(obj:
|
|
90
|
+
def _get_key(obj: object, key: str) -> Any:
|
|
91
91
|
"""Retrieve ``key`` from a dict-like or Pydantic-model-like object."""
|
|
92
92
|
if is_record(obj):
|
|
93
93
|
return obj.get(key)
|
|
94
94
|
return getattr(obj, key, None)
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
def _model_get(value:
|
|
97
|
+
def _model_get(value: object, key: str) -> Any:
|
|
98
98
|
"""Get an attribute from a Pydantic model by Python name or field alias.
|
|
99
99
|
|
|
100
100
|
Pydantic stores fields as snake_case attributes but slice match dicts use
|
|
@@ -112,7 +112,7 @@ def _model_get(value: Any, key: str) -> Any:
|
|
|
112
112
|
return None
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
def matches_value(value:
|
|
115
|
+
def matches_value(value: object, match: object) -> bool:
|
|
116
116
|
"""Recursively test whether ``value`` structurally contains everything in
|
|
117
117
|
``match``. Lists use "every match item has a corresponding value item"
|
|
118
118
|
semantics; mappings are matched key-by-key; primitives use ``==``.
|
|
@@ -151,7 +151,7 @@ def matches_value(value: Any, match: Any) -> bool:
|
|
|
151
151
|
return bool(value == match)
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
def is_extension(value:
|
|
154
|
+
def is_extension(value: object, url: str | None = None) -> bool:
|
|
155
155
|
"""True when ``value`` looks like a raw FHIR Extension (has a ``url``).
|
|
156
156
|
When ``url`` is given, also checks the URL matches.
|
|
157
157
|
Works with both plain dicts and Pydantic model instances."""
|
|
@@ -161,7 +161,7 @@ def is_extension(value: Any, url: str | None = None) -> bool:
|
|
|
161
161
|
return url is None or bool(ext_url == url)
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def get_extension_value(ext:
|
|
164
|
+
def get_extension_value(ext: object | None, field: str) -> Any:
|
|
165
165
|
"""Read a single typed value field from an Extension dict or Pydantic model,
|
|
166
166
|
returning ``None`` when the extension itself is absent or the field is not set."""
|
|
167
167
|
if ext is None:
|
|
@@ -194,7 +194,7 @@ def push_extension(target: Any, ext: Any) -> None:
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
def extract_complex_extension(
|
|
197
|
-
extension:
|
|
197
|
+
extension: object | None,
|
|
198
198
|
config: Sequence[Mapping[str, Any]],
|
|
199
199
|
) -> dict[str, Any] | None:
|
|
200
200
|
"""Read a complex (nested) FHIR extension into a plain key/value dict.
|
|
@@ -327,7 +327,7 @@ def ensure_profile(resource: Any, canonical_url: str) -> None:
|
|
|
327
327
|
profiles.append(canonical_url)
|
|
328
328
|
|
|
329
329
|
|
|
330
|
-
def set_array_slice(lst: MutableSequence[Any], match: Mapping[str, Any], value:
|
|
330
|
+
def set_array_slice(lst: MutableSequence[Any], match: Mapping[str, Any], value: object) -> None:
|
|
331
331
|
"""Find or insert a slice element. If an element matching ``match``
|
|
332
332
|
already exists it is replaced in place; otherwise ``value`` is appended."""
|
|
333
333
|
for i, item in enumerate(lst):
|
|
@@ -357,7 +357,7 @@ def get_array_slices(lst: Sequence[Any] | None, match: Mapping[str, Any]) -> lis
|
|
|
357
357
|
def set_array_slices(
|
|
358
358
|
lst: MutableSequence[Any],
|
|
359
359
|
match: Mapping[str, Any],
|
|
360
|
-
values: Sequence[
|
|
360
|
+
values: Sequence[object],
|
|
361
361
|
) -> None:
|
|
362
362
|
"""Remove all elements matching ``match``, then append ``values``."""
|
|
363
363
|
indices = [i for i, item in enumerate(lst) if matches_value(item, match)]
|
|
@@ -374,13 +374,13 @@ def set_array_slices(
|
|
|
374
374
|
# ---------------------------------------------------------------------------
|
|
375
375
|
|
|
376
376
|
|
|
377
|
-
def _get_field(res:
|
|
377
|
+
def _get_field(res: object, field: str) -> Any:
|
|
378
378
|
if isinstance(res, Mapping):
|
|
379
379
|
return res.get(field)
|
|
380
380
|
return getattr(res, field, None)
|
|
381
381
|
|
|
382
382
|
|
|
383
|
-
def validate_required(res:
|
|
383
|
+
def validate_required(res: object, profile_name: str, field: str) -> list[str]:
|
|
384
384
|
"""Checks that ``field`` is present (not ``None``)."""
|
|
385
385
|
return (
|
|
386
386
|
[f"{profile_name}: required field '{field}' is missing"]
|
|
@@ -389,7 +389,7 @@ def validate_required(res: Any, profile_name: str, field: str) -> list[str]:
|
|
|
389
389
|
)
|
|
390
390
|
|
|
391
391
|
|
|
392
|
-
def validate_must_support(res:
|
|
392
|
+
def validate_must_support(res: object, profile_name: str, field: str) -> list[str]:
|
|
393
393
|
"""Checks that a must-support field is populated (warning, not error)."""
|
|
394
394
|
return (
|
|
395
395
|
[f"{profile_name}: must-support field '{field}' is not populated"]
|
|
@@ -398,7 +398,7 @@ def validate_must_support(res: Any, profile_name: str, field: str) -> list[str]:
|
|
|
398
398
|
)
|
|
399
399
|
|
|
400
400
|
|
|
401
|
-
def validate_excluded(res:
|
|
401
|
+
def validate_excluded(res: object, profile_name: str, field: str) -> list[str]:
|
|
402
402
|
"""Checks that ``field`` is absent."""
|
|
403
403
|
return (
|
|
404
404
|
[f"{profile_name}: field '{field}' must not be present"]
|
|
@@ -407,7 +407,7 @@ def validate_excluded(res: Any, profile_name: str, field: str) -> list[str]:
|
|
|
407
407
|
)
|
|
408
408
|
|
|
409
409
|
|
|
410
|
-
def validate_fixed_value(res:
|
|
410
|
+
def validate_fixed_value(res: object, profile_name: str, field: str, expected: object) -> list[str]:
|
|
411
411
|
"""Checks that ``field`` structurally contains the expected fixed value."""
|
|
412
412
|
actual = _get_field(res, field)
|
|
413
413
|
return (
|
|
@@ -418,7 +418,7 @@ def validate_fixed_value(res: Any, profile_name: str, field: str, expected: Any)
|
|
|
418
418
|
|
|
419
419
|
|
|
420
420
|
def validate_slice_cardinality(
|
|
421
|
-
res:
|
|
421
|
+
res: object,
|
|
422
422
|
profile_name: str,
|
|
423
423
|
field: str,
|
|
424
424
|
match: Mapping[str, Any],
|
|
@@ -444,14 +444,48 @@ def validate_slice_cardinality(
|
|
|
444
444
|
return errors
|
|
445
445
|
|
|
446
446
|
|
|
447
|
-
def
|
|
447
|
+
def validate_slice_fields(
|
|
448
|
+
res: object,
|
|
449
|
+
profile_name: str,
|
|
450
|
+
field: str,
|
|
451
|
+
match: Mapping[str, Any],
|
|
452
|
+
slice_name: str,
|
|
453
|
+
required_fields: Sequence[str],
|
|
454
|
+
) -> list[str]:
|
|
455
|
+
"""Validates required fields within matched slice elements. For each array
|
|
456
|
+
item matching the discriminator, checks that the listed fields are present."""
|
|
457
|
+
items = _get_field(res, field) or []
|
|
458
|
+
if not isinstance(items, Iterable):
|
|
459
|
+
items = []
|
|
460
|
+
errors: list[str] = []
|
|
461
|
+
for item in items:
|
|
462
|
+
if not matches_value(item, match):
|
|
463
|
+
continue
|
|
464
|
+
for rf in required_fields:
|
|
465
|
+
if _get_field(item, rf) is None:
|
|
466
|
+
errors.append(f"{profile_name}.{field}[{slice_name}].{rf} is required")
|
|
467
|
+
return errors
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def validate_choice_required(res: object, profile_name: str, choices: Sequence[str]) -> list[str]:
|
|
448
471
|
"""Checks that at least one of the listed choice-type variants is present."""
|
|
449
472
|
if any(_get_field(res, c) is not None for c in choices):
|
|
450
473
|
return []
|
|
451
474
|
return [f"{profile_name}: at least one of {', '.join(choices)} is required"]
|
|
452
475
|
|
|
453
476
|
|
|
454
|
-
def
|
|
477
|
+
def validate_choice_prohibited(res: object, profile_name: str, prohibited: Sequence[str]) -> list[str]:
|
|
478
|
+
"""Checks that none of the listed prohibited choice-type variants is present.
|
|
479
|
+
E.g. a profile narrowing ``value[x]`` to ``value_quantity`` prohibits every
|
|
480
|
+
other variant."""
|
|
481
|
+
return [
|
|
482
|
+
f"{profile_name}: field '{c}' must not be present"
|
|
483
|
+
for c in prohibited
|
|
484
|
+
if _get_field(res, c) is not None
|
|
485
|
+
]
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def validate_enum(res: object, profile_name: str, field: str, allowed: Sequence[str]) -> list[str]:
|
|
455
489
|
"""Checks that the value of ``field`` has a code within ``allowed``.
|
|
456
490
|
Handles plain strings, Coding, and CodeableConcept."""
|
|
457
491
|
value = _get_field(res, field)
|
|
@@ -483,7 +517,7 @@ def validate_enum(res: Any, profile_name: str, field: str, allowed: Sequence[str
|
|
|
483
517
|
return []
|
|
484
518
|
|
|
485
519
|
|
|
486
|
-
def validate_reference(res:
|
|
520
|
+
def validate_reference(res: object, profile_name: str, field: str, allowed: Sequence[str]) -> list[str]:
|
|
487
521
|
"""Checks that a Reference field points to one of the ``allowed`` resource
|
|
488
522
|
types. Extracts the type from the ``reference`` string (the part before
|
|
489
523
|
the first ``/``)."""
|
|
@@ -437,6 +437,17 @@ export const validateChoiceRequired = (res: object, profileName: string, choices
|
|
|
437
437
|
: [`${profileName}: at least one of ${choices.join(", ")} is required`];
|
|
438
438
|
};
|
|
439
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Checks that none of the listed prohibited choice-type variants is present.
|
|
442
|
+
* E.g. a profile narrowing `value[x]` to `valueAddress` prohibits every other variant.
|
|
443
|
+
*/
|
|
444
|
+
export const validateChoiceProhibited = (res: object, profileName: string, prohibited: string[]): string[] => {
|
|
445
|
+
const rec = res as Record<string, unknown>;
|
|
446
|
+
return prohibited
|
|
447
|
+
.filter((c) => rec[c] !== undefined)
|
|
448
|
+
.map((c) => `${profileName}: field '${c}' must not be present`);
|
|
449
|
+
};
|
|
450
|
+
|
|
440
451
|
/**
|
|
441
452
|
* Checks that the value of `field` has a code within `allowed`.
|
|
442
453
|
* Handles plain strings, Coding objects, and CodeableConcept objects.
|
package/dist/cli/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import T from'picocolors';import Dn from'yargs';import {hideBin}from'yargs/helpers';import {mkdir,writeFile}from'fs/promises';import {dirname}from'path';import Pe from'assert';import {createHash}from'crypto';import {CanonicalManager}from'@atomic-ehr/fhir-canonical-manager';import*as he from'@atomic-ehr/fhirschema';import {isStructureDefinition}from'@atomic-ehr/fhirschema';var ie=e=>{console.log(),console.log(T.cyan(T.bold(`\u2501\u2501\u2501 ${e} \u2501\u2501\u2501`)));},re=(e,n,i)=>{let t=e;if(n&&(t+=` ${T.gray(`(${n}ms)`)}`),console.log(`${T.green("")} ${t}`),i)for(let[r,o]of Object.entries(i))console.log(T.gray(` ${r}: ${o}`));},F=(e,n="\u2022")=>{for(let i of e)console.log(T.gray(` ${n} ${i}`));};var oe={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4};function k(e={}){let n=e.prefix??"",i=new Set(e.suppressTags??[]),t={},r=[],o=new Set,a=e.level??"INFO",s=p=>oe[p]>=oe[a],c={DEBUG:p=>p,INFO:p=>p,WARN:T.yellow,ERROR:T.red,SILENT:p=>p},l=(p,m,f,y)=>{let h=n?`${n}: `:"",S=y?` ${T.dim(`(${y})`)}`:"";return c[p](`${m} ${h}${f}`)+S},d=(p,m,f,y=false)=>{r.push({level:p,tag:f,message:m,suppressed:y,prefix:n,timestamp:Date.now()});},u=(p,m,f,y=false)=>(...h)=>{let S=h.length===2?h[0]:void 0,x=h.length===2?h[1]:h[0];S&&(t[S]=(t[S]??0)+1);let C=S!==void 0&&i.has(S);if(d(p,x,S,C),!C&&s(p)){if(y){let N=`${p}::${S??""}::${x}`;if(o.has(N))return;o.add(N);}f(l(p,m,x,S));}},g={warn:u("WARN","!",console.warn),dryWarn:u("WARN","!",console.warn,true),info:u("INFO","i",console.log),error:u("ERROR","X",console.error),debug:u("DEBUG","D",console.log),fork(p,m){let f=n?`${n}/${p}`:p,y=[...i,...m?.suppressTags??[]];return k({prefix:f,suppressTags:y,level:m?.level??a})},as(){return g},tagCounts(){return t},printTagSummary(){let p=Object.entries(t);if(p.length===0)return;let m=n?`${n}: `:"",f=p.filter(([h])=>!i.has(h)),y=p.filter(([h])=>i.has(h));if(f.length>0){let h=f.reduce((x,[,C])=>x+C,0),S=f.map(([x,C])=>`${x}: ${C}`).join(", ");console.warn(T.yellow(`! ${m}${h} warnings (${S})`));}if(y.length>0){let h=y.reduce((x,[,C])=>x+C,0),S=y.map(([x,C])=>`${x}: ${C}`).join(", ");console.log(T.dim(`i ${m}${h} suppressed (${S})`));}},buffer(){return r},bufferClear(){r.length=0;}};return g}var b="Use CodeableReference which is not provided by FHIR R4.",Oe="Use Availability which is not provided by FHIR R4.",A={"hl7.fhir.uv.extensions.r4":{"http://hl7.org/fhir/StructureDefinition/biologicallyderivedproduct-manipulation":b,"http://hl7.org/fhir/StructureDefinition/biologicallyderivedproduct-processing":b,"http://hl7.org/fhir/StructureDefinition/extended-contact-availability":Oe,"http://hl7.org/fhir/StructureDefinition/immunization-procedure":b,"http://hl7.org/fhir/StructureDefinition/specimen-additive":b,"http://hl7.org/fhir/StructureDefinition/workflow-barrier":b,"http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor":b,"http://hl7.org/fhir/StructureDefinition/workflow-reason":b},"hl7.fhir.r5.core#5.0.0":{"http://hl7.org/fhir/StructureDefinition/shareablecodesystem":"FIXME: CodeSystem.concept.concept defined by ElementReference. FHIR Schema generator output broken value in it, so we just skip it for now.","http://hl7.org/fhir/StructureDefinition/publishablecodesystem":"Uses R5-only base types not available in R4 generation."}};function v(e,n){let i=`${e.name}#${e.version}`,t=A[i]?.[n];if(t)return {shouldSkip:true,reason:t};let r=A[e.name]?.[n];return r?{shouldSkip:true,reason:r}:{shouldSkip:false}}var R=e=>`${e.name}#${e.version}`,ae=e=>`${e.name}@${e.version}`,se=e=>{let n=JSON.stringify(e);return createHash("sha256").update(n).digest("hex").slice(0,16)},ce=(e,n)=>{let i=e.derivation==="constraint"?"constraint":"specialization";return {...e,derivation:i,kind:e.kind,package_meta:e.package_meta||n,name:e.name,url:e.url,base:e.base}};var G=e=>e?.kind==="nested";var E=(...e)=>{let n=e.filter(t=>t!==void 0).flatMap(t=>t.map(r=>[r.url,r]));return n.length===0?void 0:Object.values(Object.fromEntries(n)).sort((t,r)=>t.url.localeCompare(r.url))};var le=e=>[...e.valueFieldTypes??[],...e.profile?[e.profile]:[],...e.subExtensions?.flatMap(n=>n.valueFieldType?[n.valueFieldType]:[])??[]];var pe=(e,n)=>{if(!e.url)throw new Error("ValueSet must have a URL");if(!e.name)throw new Error("ValueSet must have a name");return {...e,package_meta:e.package_meta||n,name:e.name,url:e.url}};function z(e){let n=e.split("|")[0];return n||e}function Be(e){return e.split("|")[1]}var Me=e=>({package:e.package_meta.name,version:e.package_meta.version,name:e.name,url:e.url});function I(e){let n=Me(e);return e.derivation==="constraint"?{kind:"profile",...n}:e.kind==="primitive-type"?{kind:"primitive-type",...n}:e.kind==="complex-type"?{kind:"complex-type",...n}:e.kind==="resource"?{kind:"resource",...n}:e.kind==="logical"?{kind:"logical",...n}:{kind:"resource",...n}}var je=/[-_]/,_e=/^[a-zA-Z0-9_-]{20,}$/,Ae=e=>{let n=e.split("/"),i=n[n.length-1];return i&&i.length>0?i.split(je).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join(""):e};function L(e,n,i){let t=z(i),r=Ae(t),o={package_meta:{name:"missing_valuesets",version:Be(t)||"0.0.0"},id:i},a=e.resolveVs(n,t)||o,s=a?.id&&!_e.test(a.id)?a.id:r;return {kind:"value-set",package:a.package_meta.name,version:a.package_meta.version,name:s,url:t}}function H(e,n,i){let t=i.binding?.bindingName,r=n.join("."),[o,a,s]=t?[{name:"shared",version:"1.0.0"},t,`urn:fhir:binding:${t}`]:[e.package_meta,`${e.name}.${r}_binding`,`${e.url}#${r}_binding`];return {kind:"binding",package:o.name,version:o.version,name:a,url:s}}function q(e,n,i,t){let r=z(i)||i,o=e.resolveVs(n,r);if(o)return ze(e,o)}function ze(e,n,i){if(n.expansion?.contains)return n.expansion.contains.filter(r=>r.code!==void 0).map(r=>(Pe(r.code),{code:r.code,display:r.display,system:r.system}));let t=[];if(n.compose?.include){for(let r of n.compose.include)if(r.concept)for(let o of r.concept)t.push({system:r.system,code:o.code,display:o.display});else if(r.system&&!r.filter)try{let o=e.resolveAny(r.system);if(o?.concept){let a=(s,c)=>{for(let l of s)t.push({system:c,code:l.code,display:l.display}),l.concept&&a(l.concept,c);};a(o.concept,r.system);}}catch{}}return t.length>0?t:void 0}var de=100,qe=new Set(["UNK"]),W=new Set(["code","Coding","CodeableConcept","CodeableReference","Quantity","string","uri","Duration"]);function K(e,n,i,t){if(!i.binding)return;let r=i.binding.strength,o=i.binding.valueSet;if(!o)return;if(!W.has(i.type??"")){t?.dryWarn("#binding",`eld-11: Binding on non-bindable type '${i.type}' (valueSet: ${o})`);return}if(!(r==="required"||r==="extensible"||r==="preferred"))return;let s=q(e,n.package_meta,o);if(!s||s.length===0)return;let c=s.map(d=>d.code).filter(d=>d&&typeof d=="string"&&d.trim().length>0),l=c.length===1?c[0]:void 0;if(l&&qe.has(l)){t?.dryWarn("#placeholderValueSet",`Value set ${o} only expands to placeholder code '${l}'; skipping enum generation.`);return}if(c.length>de){t?.dryWarn("#largeValueSet",`Value set ${o} has ${c.length} which is more than ${de} codes, which may cause issues with code generation.`);return}if(c.length!==0)return {isOpen:r!=="required",values:c}}function We(e,n,i,t,r){if(!t.binding?.valueSet)return;let o=H(n,i,t),a=L(e,n.package_meta,t.binding.valueSet),s=K(e,n,t,r);return {identifier:o,valueset:a,strength:t.binding.strength,enum:s,dependencies:[a]}}function me(e,n,i){let t=new Set;if(!n.elements)return [];let r=[];function o(c,l){for(let[d,u]of Object.entries(c)){let g=[...l,d],p=g.join("."),m=e.resolveElementSnapshot(n,g);if(!t.has(p)){if(t.add(p),m.binding){let f=We(e,n,g,m,i);f&&r.push(f);}u.elements&&o(u.elements,g);}}}o(n.elements,[]),r.sort((c,l)=>c.identifier.name.localeCompare(l.identifier.name));let a=[],s=new Set;for(let c of r)s.has(c.identifier.url)||(s.add(c.identifier.url),a.push(c));return a}var Ke=/(?<=[a-z])(?=[A-Z])|[-_.\s]/,Ye=e=>e.split(Ke).filter(Boolean);var Je=e=>{if(e.length===0)throw new Error("Empty string");return e[0]?.toUpperCase()+e.substring(1).toLowerCase()},ue=e=>{if(e.length===0)throw new Error("Empty string");let[n,...i]=Ye(e);return [n?.toLowerCase(),...i.map(Je)].join("")};var Y=e=>!e||e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1);var Ze=e=>{let n=e.replace(/\[x\]/g,"").replace(/[- :.]/g,"_");return n?Y(n):""},w=e=>{let n=e.replace(/\[x\]/g,"").replace(/:/g,"_");return n?Y(ue(n)):""},Qe=(e,n)=>{let i=w(e)||"Extension",t=n.split(".").filter(a=>a&&a!=="extension").join("_"),o=`${t?w(t):""}${i}`;return [i,o,`${o}Extension`]},Xe=(e,n)=>{let i=Ze(n)||"Slice",r=`${w(e)||"Field"}${i}`;return [i,r,`${r}Slice`]},en=(e,n,i)=>e.reduce((t,r)=>{let o=r.candidates[n]??"";return t[o]=(t[o]??0)+1,i.has(o)&&(t[o]=(t[o]??0)+1),t},{}),nn=(e,n)=>{let i=e[0]?.candidates.length??0,t=(r,o)=>{if(r.length===0||o>=i)return {};let a=en(r,o,n),s=o>=i-1,[c,l]=r.reduce(([d,u],g)=>{let p=g.candidates[o]??"";return (a[p]??0)>1&&!s?[d,[...u,g]]:[{...d,[g.key]:p},u]},[{},[]]);return {...c,...t(l,o+1)}};return t(e,0)},fe=e=>({candidates:Qe(e.name,e.path),recommended:""}),ge=(e,n)=>({candidates:Xe(e,n),recommended:""}),ye=e=>{let n=(e.extensions??[]).filter(a=>a.url).map(a=>({key:`ext:${a.url}:${a.path}`,candidates:a.nameCandidates.candidates})),i=Object.entries(e.fields??{}).flatMap(([a,s])=>!("slicing"in s)||!s.slicing?.slices?[]:Object.entries(s.slicing.slices).map(([c,l])=>({key:`slice:${a}:${c}`,candidates:l.nameCandidates.candidates}))),t=new Set(Object.keys(e.fields??{}).map(w)),r=[...n,...i];if(r.length===0)return;let o=nn(r,t);for(let a of e.extensions??[]){if(!a.url)continue;let s=`ext:${a.url}:${a.path}`;o[s]&&(a.nameCandidates.recommended=o[s]);}for(let[a,s]of Object.entries(e.fields??{}))if(!(!("slicing"in s)||!s.slicing?.slices))for(let[c,l]of Object.entries(s.slicing.slices)){let d=`slice:${a}:${c}`;o[d]&&(l.nameCandidates.recommended=o[d]);}};var J=e=>e!==null&&typeof e=="object"&&e.resourceType==="CodeSystem";var U=e=>e!==null&&typeof e=="object"&&e.resourceType==="ValueSet";var rn=/^[a-zA-Z0-9]+$/,on=async(e,n)=>{let i=await e.packageJson(n.name);if(!i)return [];let t=i.dependencies;return t!==void 0?Object.entries(t).map(([r,o])=>({name:r,version:o})):[]},Se=e=>({pkg:e,canonicalResolution:{},fhirSchemas:{},valueSets:{}}),Re=async(e,n,i,t,r)=>{let o=R(n);if(r?.info(`${" ".repeat(i*2)}+ ${o}`),t[o])return t[o];let a=Se(n);t[o]=a;for(let c of await e.search({package:n})){let l=c.url;if(!l||!(isStructureDefinition(c)||U(c)||J(c)))continue;let d=l;a.canonicalResolution[d]&&r?.dryWarn("#duplicateCanonical",`Duplicate canonical URL: ${d} at ${o}.`),a.canonicalResolution[d]=[{deep:i,pkg:n,pkgId:o,resource:c}];}let s=await on(e,n);for(let c of s){let{canonicalResolution:l}=await Re(e,c,i+1,t,r);for(let[d,u]of Object.entries(l)){let g=d;a.canonicalResolution[g]=[...a.canonicalResolution[g]||[],...u];}}for(let c of Object.values(a.canonicalResolution))c.sort((l,d)=>l.deep-d.deep);return a},an=(e,n)=>{for(let{pkg:i,canonicalResolution:t}of Object.values(e)){let r=R(i);if(!e[r])throw new Error(`Package ${r} not found`);let o=0;n?.info(`FHIR Schema conversion for '${R(i)}' begins...`);for(let[a,s]of Object.entries(t)){let c=s[0];if(!c)throw new Error("Resource not found");let l=c.resource,d=c.pkg;if(isStructureDefinition(l)){let u=he.translate(l),g=ce(u,d);o++,e[r].fhirSchemas[g.url]=g;}if(U(l)){let u=pe(l,d);e[r].valueSets[u.url]=u;}}n?.info(`FHIR Schema conversion for '${R(i)}' completed: ${o} successful`);}},sn=(e,n,i)=>{let t=Object.values(e).flatMap(r=>r.canonicalResolution[n]);if(!t)throw new Error(`No canonical resolution found for ${n} in any package`);return t[0]?.resource},cn=async(e,{logger:n,focusedPackages:i})=>{let t=i??await e.packages(),r={};for(let p of t)await Re(e,p,0,r,n);an(r,n);let o=(p,m)=>{let f=r[R(p)];if(f){let y=f.canonicalResolution[m]?.[0];if(y)return r[y.pkgId]?.fhirSchemas[m]}for(let y of Object.values(r)){let h=y.fhirSchemas[m];if(h&&h.package_meta.name===p.name)return h}for(let y of Object.values(r)){let h=y.fhirSchemas[m];if(h)return h}},a=(p,m)=>{let f=r[R(p)];if(f){let y=f.canonicalResolution[m]?.[0];if(y)return r[y.pkgId]?.valueSets[m]}for(let y of Object.values(r)){let h=y.valueSets[m];if(h&&h.package_meta.name===p.name)return h}for(let y of Object.values(r)){let h=y.valueSets[m];if(h)return h}},s=p=>(p.includes("|")&&(p=p.split("|")[0]),rn.test(p)?`http://hl7.org/fhir/StructureDefinition/${p}`:p),c=(p,m)=>{let f=o(p,m);if(f===void 0)throw new Error(`Failed to resolve FHIR Schema: '${m}'`);let y=[f];for(;f?.base;){let h=f.package_meta,S=s(f.base);if(f=o(h,S),f===void 0)throw new Error(`Failed to resolve FHIR Schema base for '${m}'. Problem: '${S}' from '${R(h)}'`);y.push(f);}return y},l=(p,m)=>c(p,m).filter(f=>f.derivation==="specialization"),d=(p,m)=>{let f=c(p.package_meta,p.url),y=O(f,m);return Z(y)},u=p=>{let m=new Set;for(let[f,y]of Object.entries(p)){m.add(f);for(let h of y?.choices||[])p[h]||m.add(h);}return Array.from(m)},g;return {testAppendFs(p){let m=R(p.package_meta);r[m]||(r[m]=Se(p.package_meta)),r[m].fhirSchemas[p.url]=p,g=void 0;},resolveFs:o,resolveFsGenealogy:c,resolveFsSpecializations:l,ensureSpecializationCanonicalUrl:s,resolveSd:(p,m)=>{let f=r[R(p)]?.canonicalResolution[m]?.[0]?.resource;if(isStructureDefinition(f))return f},allSd:()=>Object.values(r).flatMap(p=>Object.values(p.canonicalResolution).flatMap(m=>m.map(f=>{let y=f.resource;return y.package_name?y:{...y,package_name:p.pkg.name,package_version:p.pkg.version}}))).filter(p=>isStructureDefinition(p)).sort((p,m)=>p.url.localeCompare(m.url)),allFs:()=>Object.values(r).flatMap(p=>Object.values(p.fhirSchemas)),allVs:()=>Object.values(r).flatMap(p=>Object.values(p.valueSets)),resolveVs:a,resolveAny:p=>sn(r,p),resolveElementSnapshot:d,getAllElementKeys:u,resolver:r,resolutionTree:()=>{if(g)return g;let p={};for(let[m,f]of Object.entries(r)){let y=f.pkg.name;p[y]={};for(let[h,S]of Object.entries(f.canonicalResolution)){let x=h;p[y][x]=[];for(let C of S)p[y][x].push({deep:C.deep,pkg:C.pkg});}}return g=p,p}}},xe=async(e,n)=>{let i=e.map(ae);n?.logger?.info(`Loading FHIR packages: ${i.join(", ")}`);let t=CanonicalManager({packages:i,workingDir:".codegen-cache/canonical-manager-cache",registry:n.registry||void 0});return await t.init(),await cn(t,{...n,focusedPackages:e})},O=(e,n)=>{let[i,...t]=n;return i===void 0?[]:e.map(r=>{if(!r.elements)return;let o=r.elements?.[i];for(let a of t)o=o?.elements?.[a];return o}).filter(r=>r!==void 0)};function Z(e){let n=e.reverse(),i=Object.assign({},...n);return i.elements=void 0,i}var Ce=(e,n,i)=>{let t=e.resolveFsSpecializations(n.package_meta,n.url),r=O(t,i),o=Z(r).type,a;if(o){let s=e.ensureSpecializationCanonicalUrl(o),l=e.resolveFsGenealogy(n.package_meta,s).flatMap(d=>Object.keys(d.elements??{}));l.length>0&&(a=new Set(l));}for(let s of r)if(!(!s.elements||Object.keys(s.elements).length===0)&&!(a&&!Object.keys(s.elements).some(c=>!a.has(c))))return true;return false},Q=(e,n,i,t,r)=>t.type==="BackboneElement"?true:!r?.elements||r.choiceOf!==void 0?false:Ce(e,n,i),ln=e=>e.elements?new Set(X(e,[],e.elements).filter(([n,i])=>i.elements&&Object.keys(i.elements).length>0).map(([n])=>n.join("."))):new Set;function V(e,n,i){let t={},r=n.derivation==="constraint"?e.resolveFsSpecializations(n.package_meta,n.url):e.resolveFsGenealogy(n.package_meta,n.url);for(let d of [...r].reverse()){let u=ln(d);for(let g of u)t[g]=`${d.url}#${g}`;}let o=i.join("."),a=t[o]??`${n.url}#${o}`,s=a.split("#")[0],l=e.resolveFs(n.package_meta,s)?.package_meta??n.package_meta;return {kind:"nested",package:l.name,version:l.version,name:o,url:a}}function X(e,n,i){let t=[];for(let[r,o]of Object.entries(i)){let a=[...n,r];o.elements&&o.choiceOf===void 0&&t.push([a,o]),o.elements&&t.push(...X(e,a,o.elements));}return t}function pn(e,n,i,t,r){let o={},a=e.resolveFsGenealogy(n.package_meta,n.url),s=O(a,i),c=new Set;for(let l of s)if(l.elements)for(let d of Object.keys(l.elements))c.add(d);for(let l of c){let d=[...i,l],u=e.resolveElementSnapshot(n,d);Q(e,n,d,u,t[l])?o[l]=M(e,n,d,u):o[l]=B(e,n,d,u,r);}return o}function ke(e,n,i){if(!n.elements)return;let t=X(n,[],n.elements).filter(([o,a])=>!a.elements||Object.keys(a.elements).length===0?false:a.type!=="BackboneElement"?Ce(e,n,o):true),r=[];for(let[o,a]of t){let s=V(e,n,o),c;a.type==="BackboneElement"||!a.type?c="BackboneElement":c=a.type;let l=e.ensureSpecializationCanonicalUrl(c),d=e.resolveFs(n.package_meta,l);if(!d)throw new Error(`Could not resolve base type ${c}`);let u={kind:"complex-type",package:d.package_meta.name,version:d.package_meta.version,name:c,url:l},g=pn(e,n,o,a.elements??{},i),p={identifier:s,base:u,fields:g};r.push(p);}return r.sort((o,a)=>o.identifier.url.localeCompare(a.identifier.url)),r.length===0?void 0:r}function Ie(e){let n=[];for(let i of e){i.base&&n.push(i.base);for(let t of Object.values(i.fields||{}))"type"in t&&t.type&&n.push(t.type),"binding"in t&&t.binding&&n.push(t.binding);}return n}var dn=new Set(["Availability","CodeableReference","ExtendedContactDetail","MonetaryComponent","RatioRange","VirtualServiceDetail"]),mn=(e,n)=>{let i=e.resolver[R(n)];if(!i)return false;for(let t of Object.values(i.canonicalResolution))for(let r of t)if(r.pkg.name==="hl7.fhir.r4.core")return true;return false},un=(e,n,i)=>!dn.has(i)||!mn(e,n)?"":`
|
|
2
|
+
import F from'picocolors';import $n from'yargs';import {hideBin}from'yargs/helpers';import {mkdir,writeFile}from'fs/promises';import {dirname}from'path';import we from'assert';import {createHash}from'crypto';import {CanonicalManager}from'@atomic-ehr/fhir-canonical-manager';import*as Ie from'@atomic-ehr/fhirschema';import {isStructureDefinition}from'@atomic-ehr/fhirschema';var se=e=>{console.log(),console.log(F.cyan(F.bold(`\u2501\u2501\u2501 ${e} \u2501\u2501\u2501`)));},ae=(e,n,i)=>{let t=e;if(n&&(t+=` ${F.gray(`(${n}ms)`)}`),console.log(`${F.green("")} ${t}`),i)for(let[r,o]of Object.entries(i))console.log(F.gray(` ${r}: ${o}`));},T=(e,n="\u2022")=>{for(let i of e)console.log(F.gray(` ${n} ${i}`));};var ce={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4};function I(e={}){let n=e.prefix??"",i=new Set(e.suppressTags??[]),t={},r=[],o=new Set,s=e.level??"INFO",a=d=>ce[d]>=ce[s],c={DEBUG:d=>d,INFO:d=>d,WARN:F.yellow,ERROR:F.red,SILENT:d=>d},l=(d,u,f,y)=>{let h=n?`${n}: `:"",S=y?` ${F.dim(`(${y})`)}`:"";return c[d](`${u} ${h}${f}`)+S},p=(d,u,f,y=false)=>{r.push({level:d,tag:f,message:u,suppressed:y,prefix:n,timestamp:Date.now()});},m=(d,u,f,y=false)=>(...h)=>{let S=h.length===2?h[0]:void 0,x=h.length===2?h[1]:h[0];S&&(t[S]=(t[S]??0)+1);let C=S!==void 0&&i.has(S);if(p(d,x,S,C),!C&&a(d)){if(y){let N=`${d}::${S??""}::${x}`;if(o.has(N))return;o.add(N);}f(l(d,u,x,S));}},g={warn:m("WARN","!",console.warn),dryWarn:m("WARN","!",console.warn,true),info:m("INFO","i",console.log),error:m("ERROR","X",console.error),debug:m("DEBUG","D",console.log),fork(d,u){let f=n?`${n}/${d}`:d,y=[...i,...u?.suppressTags??[]];return I({prefix:f,suppressTags:y,level:u?.level??s})},as(){return g},tagCounts(){return t},printTagSummary(){let d=Object.entries(t);if(d.length===0)return;let u=n?`${n}: `:"",f=d.filter(([h])=>!i.has(h)),y=d.filter(([h])=>i.has(h));if(f.length>0){let h=f.reduce((x,[,C])=>x+C,0),S=f.map(([x,C])=>`${x}: ${C}`).join(", ");console.warn(F.yellow(`! ${u}${h} warnings (${S})`));}if(y.length>0){let h=y.reduce((x,[,C])=>x+C,0),S=y.map(([x,C])=>`${x}: ${C}`).join(", ");console.log(F.dim(`i ${u}${h} suppressed (${S})`));}},buffer(){return r},bufferClear(){r.length=0;}};return g}var L=(e,n)=>e<n?-1:e>n?1:0,z=(e,n)=>L(e.sourcePackage,n.sourcePackage)||L(e.sourceCanonical,n.sourceCanonical),le=e=>JSON.stringify([...e].sort(z).map(({sourcePackage:n,sourceCanonical:i})=>[n,i])),pe=(e,n)=>n.sources.length-e.sources.length||L(le(e.sources),le(n.sources))||L(e.schemaHash,n.schemaHash);var b="Use CodeableReference which is not provided by FHIR R4.",_e="Use Availability which is not provided by FHIR R4.",q={"hl7.fhir.uv.extensions.r4":{"http://hl7.org/fhir/StructureDefinition/biologicallyderivedproduct-manipulation":b,"http://hl7.org/fhir/StructureDefinition/biologicallyderivedproduct-processing":b,"http://hl7.org/fhir/StructureDefinition/extended-contact-availability":_e,"http://hl7.org/fhir/StructureDefinition/immunization-procedure":b,"http://hl7.org/fhir/StructureDefinition/specimen-additive":b,"http://hl7.org/fhir/StructureDefinition/workflow-barrier":b,"http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor":b,"http://hl7.org/fhir/StructureDefinition/workflow-reason":b},"hl7.fhir.r5.core#5.0.0":{"http://hl7.org/fhir/StructureDefinition/shareablecodesystem":"FIXME: CodeSystem.concept.concept defined by ElementReference. FHIR Schema generator output broken value in it, so we just skip it for now.","http://hl7.org/fhir/StructureDefinition/publishablecodesystem":"Uses R5-only base types not available in R4 generation."}};function v(e,n){let i=`${e.name}#${e.version}`,t=q[i]?.[n];if(t)return {shouldSkip:true,reason:t};let r=q[e.name]?.[n];return r?{shouldSkip:true,reason:r}:{shouldSkip:false}}var R=e=>`${e.name}#${e.version}`,de=e=>`${e.name}@${e.version}`,me=e=>{let n=JSON.stringify(e);return createHash("sha256").update(n).digest("hex").slice(0,16)},ue=(e,n)=>{let i=e.derivation==="constraint"?"constraint":"specialization";return {...e,derivation:i,kind:e.kind,package_meta:e.package_meta||n,name:e.name,url:e.url,base:e.base}};var W=e=>e?.kind==="nested",fe=e=>e?.kind==="profile";var E=(...e)=>{let n=e.filter(t=>t!==void 0).flatMap(t=>t.map(r=>[r.url,r]));return n.length===0?void 0:Object.values(Object.fromEntries(n)).sort((t,r)=>t.url.localeCompare(r.url))};var ge=e=>[...e.valueFieldTypes??[],...e.profile?[e.profile]:[],...e.subExtensions?.flatMap(n=>n.valueFieldType?[n.valueFieldType]:[])??[]];var ye=(e,n)=>{if(!e.url)throw new Error("ValueSet must have a URL");if(!e.name)throw new Error("ValueSet must have a name");return {...e,package_meta:e.package_meta||n,name:e.name,url:e.url}};function K(e){let n=e.split("|")[0];return n||e}function Ge(e){return e.split("|")[1]}var ze=e=>({package:e.package_meta.name,version:e.package_meta.version,name:e.name,url:e.url});function k(e){let n=ze(e);return e.derivation==="constraint"?{kind:"profile",...n}:e.kind==="primitive-type"?{kind:"primitive-type",...n}:e.kind==="complex-type"?{kind:"complex-type",...n}:e.kind==="resource"?{kind:"resource",...n}:e.kind==="logical"?{kind:"logical",...n}:{kind:"resource",...n}}var qe=/[-_]/,We=/^[a-zA-Z0-9_-]{20,}$/,Ke=e=>{let n=e.split("/"),i=n[n.length-1];return i&&i.length>0?i.split(qe).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join(""):e};function H(e,n,i){let t=K(i),r=Ke(t),o={package_meta:{name:"missing_valuesets",version:Ge(t)||"0.0.0"},id:i},s=e.resolveVs(n,t)||o,a=s?.id&&!We.test(s.id)?s.id:r;return {kind:"value-set",package:s.package_meta.name,version:s.package_meta.version,name:a,url:t}}function w(e,n,i){let t=i.binding?.bindingName,r=n.join("."),[o,s,a]=t?[{name:"shared",version:"1.0.0"},t,`urn:fhir:binding:${t}`]:[e.package_meta,`${e.name}.${r}_binding`,`${e.url}#${r}_binding`];return {kind:"binding",package:o.name,version:o.version,name:s,url:a}}function Y(e,n,i,t){let r=K(i)||i,o=e.resolveVs(n,r);if(o)return Je(e,o)}function Je(e,n,i){if(n.expansion?.contains)return n.expansion.contains.filter(r=>r.code!==void 0).map(r=>(we(r.code),{code:r.code,display:r.display,system:r.system}));let t=[];if(n.compose?.include){for(let r of n.compose.include)if(r.concept)for(let o of r.concept)t.push({system:r.system,code:o.code,display:o.display});else if(r.system&&!r.filter)try{let o=e.resolveAny(r.system);if(o?.concept){let s=(a,c)=>{for(let l of a)t.push({system:c,code:l.code,display:l.display}),l.concept&&s(l.concept,c);};s(o.concept,r.system);}}catch{}}return t.length>0?t:void 0}var he=100,Ze=new Set(["UNK"]),J=new Set(["code","Coding","CodeableConcept","CodeableReference","Quantity","string","uri","Duration"]);function Z(e,n,i,t){if(!i.binding)return;let r=i.binding.strength,o=i.binding.valueSet;if(!o)return;if(!J.has(i.type??"")){t?.dryWarn("#binding",`eld-11: Binding on non-bindable type '${i.type}' (valueSet: ${o})`);return}if(!(r==="required"||r==="extensible"||r==="preferred"))return;let a=Y(e,n.package_meta,o);if(!a||a.length===0)return;let c=a.map(p=>p.code).filter(p=>p&&typeof p=="string"&&p.trim().length>0),l=c.length===1?c[0]:void 0;if(l&&Ze.has(l)){t?.dryWarn("#placeholderValueSet",`Value set ${o} only expands to placeholder code '${l}'; skipping enum generation.`);return}if(c.length>he){t?.dryWarn("#largeValueSet",`Value set ${o} has ${c.length} which is more than ${he} codes, which may cause issues with code generation.`);return}if(c.length!==0)return {isOpen:r!=="required",values:c}}function Qe(e,n,i,t,r){if(!t.binding?.valueSet)return;let o=w(n,i,t),s=H(e,n.package_meta,t.binding.valueSet),a=Z(e,n,t,r);return {identifier:o,valueset:s,strength:t.binding.strength,enum:a,dependencies:[s]}}function Se(e,n,i){let t=new Set;if(!n.elements)return [];let r=[];function o(c,l){for(let[p,m]of Object.entries(c)){let g=[...l,p],d=g.join("."),u=e.resolveElementSnapshot(n,g);if(!t.has(d)){if(t.add(d),u.binding){let f=Qe(e,n,g,u,i);f&&r.push(f);}m.elements&&o(m.elements,g);}}}o(n.elements,[]),r.sort((c,l)=>c.identifier.name.localeCompare(l.identifier.name));let s=[],a=new Set;for(let c of r)a.has(c.identifier.url)||(a.add(c.identifier.url),s.push(c));return s}var Xe=/(?<=[a-z])(?=[A-Z])|[-_.\s]/,en=e=>e.split(Xe).filter(Boolean);var nn=e=>{if(e.length===0)throw new Error("Empty string");return e[0]?.toUpperCase()+e.substring(1).toLowerCase()},Re=e=>{if(e.length===0)throw new Error("Empty string");let[n,...i]=en(e);return [n?.toLowerCase(),...i.map(nn)].join("")};var Q=e=>!e||e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1);var tn=e=>{let n=e.replace(/\[x\]/g,"").replace(/[- :.]/g,"_");return n?Q(n):""},U=e=>{let n=e.replace(/\[x\]/g,"").replace(/:/g,"_");return n?Q(Re(n)):""},rn=(e,n)=>{let i=U(e)||"Extension",t=n.split(".").filter(s=>s&&s!=="extension").join("_"),o=`${t?U(t):""}${i}`;return [i,o,`${o}Extension`]},on=(e,n)=>{let i=tn(n)||"Slice",r=`${U(e)||"Field"}${i}`;return [i,r,`${r}Slice`]},sn=(e,n,i)=>e.reduce((t,r)=>{let o=r.candidates[n]??"";return t[o]=(t[o]??0)+1,i.has(o)&&(t[o]=(t[o]??0)+1),t},{}),an=(e,n)=>{let i=e[0]?.candidates.length??0,t=(r,o)=>{if(r.length===0||o>=i)return {};let s=sn(r,o,n),a=o>=i-1,[c,l]=r.reduce(([p,m],g)=>{let d=g.candidates[o]??"";return (s[d]??0)>1&&!a?[p,[...m,g]]:[{...p,[g.key]:d},m]},[{},[]]);return {...c,...t(l,o+1)}};return t(e,0)},xe=e=>({candidates:rn(e.name,e.path),recommended:""}),Ce=(e,n)=>({candidates:on(e,n),recommended:""}),ke=e=>{let n=(e.extensions??[]).filter(s=>s.url).map(s=>({key:`ext:${s.url}:${s.path}`,candidates:s.nameCandidates.candidates})),i=Object.entries(e.slicing??{}).flatMap(([s,a])=>a.slices?Object.entries(a.slices).map(([c,l])=>({key:`slice:${s}:${c}`,candidates:l.nameCandidates.candidates})):[]),t=new Set(Object.keys(e.fields??{}).map(U)),r=[...n,...i];if(r.length===0)return;let o=an(r,t);for(let s of e.extensions??[]){if(!s.url)continue;let a=`ext:${s.url}:${s.path}`;o[a]&&(s.nameCandidates.recommended=o[a]);}for(let[s,a]of Object.entries(e.slicing??{}))if(a.slices)for(let[c,l]of Object.entries(a.slices)){let p=`slice:${s}:${c}`;o[p]&&(l.nameCandidates.recommended=o[p]);}};var X=e=>e!==null&&typeof e=="object"&&e.resourceType==="CodeSystem";var $=e=>e!==null&&typeof e=="object"&&e.resourceType==="ValueSet";var ln=/^[a-zA-Z0-9]+$/,pn=async(e,n)=>{let i=await e.packageJson(n.name);if(!i)return [];let t=i.dependencies;return t!==void 0?Object.entries(t).map(([r,o])=>({name:r,version:o})):[]},be=e=>({pkg:e,canonicalResolution:{},fhirSchemas:{},valueSets:{}}),Fe=async(e,n,i,t,r)=>{let o=R(n);if(r?.info(`${" ".repeat(i*2)}+ ${o}`),t[o])return t[o];let s=be(n);t[o]=s;for(let c of await e.search({package:n})){let l=c.url;if(!l||!(isStructureDefinition(c)||$(c)||X(c)))continue;let p=l;s.canonicalResolution[p]&&r?.dryWarn("#duplicateCanonical",`Duplicate canonical URL: ${p} at ${o}.`),s.canonicalResolution[p]=[{deep:i,pkg:n,pkgId:o,resource:c}];}let a=await pn(e,n);for(let c of a){let{canonicalResolution:l}=await Fe(e,c,i+1,t,r);for(let[p,m]of Object.entries(l)){let g=p;s.canonicalResolution[g]=[...s.canonicalResolution[g]||[],...m];}}for(let c of Object.values(s.canonicalResolution))c.sort((l,p)=>l.deep-p.deep);return s},dn=(e,n)=>{for(let{pkg:i,canonicalResolution:t}of Object.values(e)){let r=R(i);if(!e[r])throw new Error(`Package ${r} not found`);let o=0;n?.info(`FHIR Schema conversion for '${R(i)}' begins...`);for(let[s,a]of Object.entries(t)){let c=a[0];if(!c)throw new Error("Resource not found");let l=c.resource,p=c.pkg;if(isStructureDefinition(l)){let m=Ie.translate(l),g=ue(m,p);o++,e[r].fhirSchemas[g.url]=g;}if($(l)){let m=ye(l,p);e[r].valueSets[m.url]=m;}}n?.info(`FHIR Schema conversion for '${R(i)}' completed: ${o} successful`);}},mn=(e,n,i)=>{let t=Object.values(e).flatMap(r=>r.canonicalResolution[n]);if(!t)throw new Error(`No canonical resolution found for ${n} in any package`);return t[0]?.resource},un=async(e,{logger:n,focusedPackages:i})=>{let t=i??await e.packages(),r={};for(let d of t)await Fe(e,d,0,r,n);dn(r,n);let o=(d,u)=>{let f=r[R(d)];if(f){let y=f.canonicalResolution[u]?.[0];if(y)return r[y.pkgId]?.fhirSchemas[u]}for(let y of Object.values(r)){let h=y.fhirSchemas[u];if(h&&h.package_meta.name===d.name)return h}for(let y of Object.values(r)){let h=y.fhirSchemas[u];if(h)return h}},s=(d,u)=>{let f=r[R(d)];if(f){let y=f.canonicalResolution[u]?.[0];if(y)return r[y.pkgId]?.valueSets[u]}for(let y of Object.values(r)){let h=y.valueSets[u];if(h&&h.package_meta.name===d.name)return h}for(let y of Object.values(r)){let h=y.valueSets[u];if(h)return h}},a=d=>(d.includes("|")&&(d=d.split("|")[0]),ln.test(d)?`http://hl7.org/fhir/StructureDefinition/${d}`:d),c=(d,u)=>{let f=o(d,u);if(f===void 0)throw new Error(`Failed to resolve FHIR Schema: '${u}'`);let y=[f];for(;f?.base;){let h=f.package_meta,S=a(f.base);if(f=o(h,S),f===void 0)throw new Error(`Failed to resolve FHIR Schema base for '${u}'. Problem: '${S}' from '${R(h)}'`);y.push(f);}return y},l=(d,u)=>c(d,u).filter(f=>f.derivation==="specialization"),p=(d,u)=>{let f=c(d.package_meta,d.url),y=O(f,u);return ee(y)},m=d=>{let u=new Set;for(let[f,y]of Object.entries(d)){u.add(f);for(let h of y?.choices||[])d[h]||u.add(h);}return Array.from(u)},g;return {testAppendFs(d){let u=R(d.package_meta);r[u]||(r[u]=be(d.package_meta)),r[u].fhirSchemas[d.url]=d,g=void 0;},resolveFs:o,resolveFsGenealogy:c,resolveFsSpecializations:l,ensureSpecializationCanonicalUrl:a,resolveSd:(d,u)=>{let f=r[R(d)]?.canonicalResolution[u]?.[0]?.resource;if(isStructureDefinition(f))return f},allSd:()=>Object.values(r).flatMap(d=>Object.values(d.canonicalResolution).flatMap(u=>u.map(f=>{let y=f.resource;return y.package_name?y:{...y,package_name:f.pkg.name,package_version:f.pkg.version}}))).filter(d=>isStructureDefinition(d)).sort((d,u)=>d.url.localeCompare(u.url)),allFs:()=>Object.values(r).flatMap(d=>Object.values(d.fhirSchemas)),allVs:()=>Object.values(r).flatMap(d=>Object.values(d.valueSets)),resolveVs:s,resolveAny:d=>mn(r,d),resolveElementSnapshot:p,getAllElementKeys:m,resolver:r,resolutionTree:()=>{if(g)return g;let d={};for(let[u,f]of Object.entries(r)){let y=f.pkg.name;d[y]={};for(let[h,S]of Object.entries(f.canonicalResolution)){let x=h;d[y][x]=[];for(let C of S)d[y][x].push({deep:C.deep,pkg:C.pkg});}}return g=d,d}}},Te=async(e,n)=>{let i=e.map(de);n?.logger?.info(`Loading FHIR packages: ${i.join(", ")}`);let t=CanonicalManager({packages:i,workingDir:".codegen-cache/canonical-manager-cache",registry:n.registry||void 0});return await t.init(),await un(t,{...n,focusedPackages:e})},O=(e,n)=>{let[i,...t]=n;return i===void 0?[]:e.map(r=>{if(!r.elements)return;let o=r.elements?.[i];for(let s of t)o=o?.elements?.[s];return o}).filter(r=>r!==void 0)};function ee(e){let n=e.reverse(),i=Object.assign({},...n);return i.elements=void 0,i}var ve=(e,n,i)=>{let t=e.resolveFsSpecializations(n.package_meta,n.url),r=O(t,i),o=ee(r).type,s;if(o){let a=e.ensureSpecializationCanonicalUrl(o),l=e.resolveFsGenealogy(n.package_meta,a).flatMap(p=>Object.keys(p.elements??{}));l.length>0&&(s=new Set(l));}for(let a of r)if(!(!a.elements||Object.keys(a.elements).length===0)&&!(s&&!Object.keys(a.elements).some(c=>!s.has(c))))return true;return false},ne=(e,n,i,t,r)=>t.type==="BackboneElement"?true:!r?.elements||r.choiceOf!==void 0?false:ve(e,n,i),fn=e=>e.elements?new Set(te(e,[],e.elements).filter(([n,i])=>i.elements&&Object.keys(i.elements).length>0).map(([n])=>n.join("."))):new Set;function B(e,n,i){let t={},r=n.derivation==="constraint"?e.resolveFsSpecializations(n.package_meta,n.url):e.resolveFsGenealogy(n.package_meta,n.url);for(let p of [...r].reverse()){let m=fn(p);for(let g of m)t[g]=`${p.url}#${g}`;}let o=i.join("."),s=t[o]??`${n.url}#${o}`,a=s.split("#")[0],l=e.resolveFs(n.package_meta,a)?.package_meta??n.package_meta;return {kind:"nested",package:l.name,version:l.version,name:o,url:s}}function te(e,n,i){let t=[];for(let[r,o]of Object.entries(i)){let s=[...n,r];o.elements&&o.choiceOf===void 0&&t.push([s,o]),o.elements&&t.push(...te(e,s,o.elements));}return t}function gn(e,n,i,t,r){let o={},s={},a=e.resolveFsGenealogy(n.package_meta,n.url),c=O(a,i),l=new Set;for(let p of c)if(p.elements)for(let m of Object.keys(p.elements))l.add(m);for(let p of l){let m=[...i,p],g=e.resolveElementSnapshot(n,m);ne(e,n,m,g,t[p])?o[p]=_(e,n,m,g):o[p]=j(e,n,m,g,r);let d=M(p,g);d&&(s[p]=d);}return {fields:o,slicing:Object.keys(s).length>0?s:void 0}}function Ee(e,n,i){if(!n.elements)return;let t=te(n,[],n.elements).filter(([o,s])=>!s.elements||Object.keys(s.elements).length===0?false:s.type!=="BackboneElement"?ve(e,n,o):true),r=[];for(let[o,s]of t){let a=B(e,n,o),c;s.type==="BackboneElement"||!s.type?c="BackboneElement":c=s.type;let l=e.ensureSpecializationCanonicalUrl(c),p=e.resolveFs(n.package_meta,l);if(!p)throw new Error(`Could not resolve base type ${c}`);let m={kind:"complex-type",package:p.package_meta.name,version:p.package_meta.version,name:c,url:l},{fields:g,slicing:d}=gn(e,n,o,s.elements??{},i),u={identifier:a,base:m,fields:g,slicing:d};r.push(u);}return r.sort((o,s)=>o.identifier.url.localeCompare(s.identifier.url)),r.length===0?void 0:r}function Pe(e){let n=[];for(let i of e){i.base&&n.push(i.base);for(let t of Object.values(i.fields||{}))"type"in t&&t.type&&n.push(t.type),"binding"in t&&t.binding&&n.push(t.binding);}return n}var yn=new Set(["Availability","CodeableReference","ExtendedContactDetail","MonetaryComponent","RatioRange","VirtualServiceDetail"]),hn=(e,n)=>{let i=e.resolver[R(n)];if(!i)return false;for(let t of Object.values(i.canonicalResolution))for(let r of t)if(r.pkg.name==="hl7.fhir.r4.core")return true;return false},Sn=(e,n,i)=>!yn.has(i)||!hn(e,n)?"":`
|
|
3
3
|
hint: '${i}' is an R5+ type and is not available when generating against R4.
|
|
4
|
-
Either skip this canonical via skip-hack.ts, or upgrade the target to R5.`;function
|
|
5
|
-
package: ${
|
|
4
|
+
Either skip this canonical via skip-hack.ts, or upgrade the target to R5.`;function Ne(e,n,i){let t=i[i.length-1];if(!t)throw new Error(`Internal error: fieldName is missing for path ${i.join("/")}`);let r=i.slice(0,-1),o=e.resolveFsGenealogy(n.package_meta,n.url).flatMap(s=>{if(r.length===0)return s.required||[];if(!s.elements)return [];let a=s;for(let c of r)a=a?.elements?.[c];return a?.required||[]});return new Set(o).has(t)}function De(e,n,i){let t=i[i.length-1];if(!t)throw new Error(`Internal error: fieldName is missing for path ${i.join("/")}`);let r=i.slice(0,-1),o=e.resolveFsGenealogy(n.package_meta,n.url).flatMap(s=>{if(r.length===0)return s.excluded||[];if(!s.elements)return [];let a=s;for(let c of r)a=a?.elements?.[c];return a?.excluded||[]});return new Set(o).has(t)}var Rn=(e,n,i)=>{if(!i.refers)return;let t=[],r=[],o=new Set;for(let s of i.refers){let a=e.ensureSpecializationCanonicalUrl(s),c=e.resolveFs(n.package_meta,a);if(!c)throw new Error(`Failed to resolve fs for ${a}`);let l=k(c),p=l;if(fe(l)){r.push(l);let m=e.resolveFsSpecializations(c.package_meta,c.url)[0];if(!m)throw new Error(`Failed to resolve base specialization for ${a}`);p=k(m);}o.has(p.url)||(o.add(p.url),t.push(p));}return {resource:t,profiles:r.length>0?r:void 0}},xn=e=>{let n=new Set,i=new Set;if(e.required)for(let r of e.required)n.add(r);if(e.excluded)for(let r of e.excluded)i.add(r);if(e.elements)for(let[r,o]of Object.entries(e.elements))o.min!==void 0&&o.min>0&&n.add(r);let t=e.elements?Object.keys(e.elements):void 0;return {required:n.size>0?Array.from(n):void 0,excluded:i.size>0?Array.from(i):void 0,elements:t&&t.length>0?t:void 0}},Cn=e=>!e||typeof e=="object"&&Object.keys(e).length===0,A=(e,n,i)=>{let t=e;for(let o=0;o<n.length-1;o++){let s=n[o];(!t[s]||typeof t[s]!="object")&&(t[s]={}),t=t[s];}let r=n[n.length-1];t[r]=i;},kn=(e,n)=>{let i=e;for(let t of n)if(i&&typeof i=="object"&&!Array.isArray(i))i=i[t];else return;return i},Le=(e,n,i,t,r)=>{if(i>=n.length||!e.elements)return;let o=n[i],s=e.elements[o];if(s){if(i===n.length-1&&s.fixed?.value!==void 0){A(t,n,s.fixed.value);return}if(s.slicing?.slices){r.add(n.slice(0,i+1).join("."));let a=n.slice(i+1);for(let c of Object.values(s.slicing.slices)){if(!c.min||c.min<1||!c.match||typeof c.match!="object")continue;let l=c.match;if(Object.keys(l).length!==0)if(a.length>0){let p=kn(l,a);p!==void 0&&A(t,n,p);}else A(t,n.slice(0,i+1),l);}return}Le(s,n,i+1,t,r);}},In=(e,n,i)=>{if(e==="$this")return;let t=e.split("."),r=n;for(let s of t)if(r=r?.elements?.[s],!r)return;let o=r.type;!o||o.includes("/")||A(i,t,{resourceType:o});},bn=(e,n)=>{if(!n||!e||e.length===0)return;let i={},t=new Set;for(let r of e)if(r.type==="type")In(r.path,n,i);else {if(!n.elements)continue;let o=r.path.split(".");Le(n,o,0,i,t);}if(Object.keys(i).length!==0){for(let r of t){let o=r.split("."),s=i;for(let c=0;c<o.length-1;c++){let l=s[o[c]];if(!l||typeof l!="object"||Array.isArray(l))break;s=l;}let a=o[o.length-1];s[a]&&typeof s[a]=="object"&&!Array.isArray(s[a])&&(s[a]=[s[a]]);}return i}},M=(e,n)=>{let i=n.slicing;if(!i)return;let t={};for(let[r,o]of Object.entries(i.slices??{})){if(!o)continue;let{required:s,excluded:a,elements:c}=o.schema?xn(o.schema):{};t[r]={min:o.min,max:o.max,match:Cn(o.match)?bn(i.discriminator??[],o.schema):o.match,required:s,excluded:a,elements:c,nameCandidates:Ce(e,r)};}return {discriminator:i.discriminator??[],rules:i.rules,ordered:i.ordered,slices:Object.keys(t).length>0?t:void 0}};function P(e,n,i,t,r){if(t.elementReference){let o=t.elementReference.slice(1).filter((s,a)=>a%2===1);return B(e,n,o)}if(t.type){let o=e.ensureSpecializationCanonicalUrl(t.type),s=e.resolveFs(n.package_meta,o);if(!s){let a=R(n.package_meta),c=i.join("."),l=Sn(e,n.package_meta,t.type);throw new Error(`Could not resolve field type:
|
|
5
|
+
package: ${a}
|
|
6
6
|
schema: ${n.url}
|
|
7
7
|
field: ${c}
|
|
8
|
-
type: ${t.type}${l}`)}return
|
|
9
|
-
`),await writeFile(
|
|
8
|
+
type: ${t.type}${l}`)}return k(s)}t.choices||n.derivation!=="constraint"&&r?.dryWarn("#fieldTypeNotFound",`Can't recognize element type: <${n.url}>.${i.join(".")} (pkg: '${R(n.package_meta)}'): missing type info`);}var j=(e,n,i,t,r,o)=>{let s,a;t.binding&&(s=w(n,i,t),J.has(t.type??"")&&(a=Z(e,n,t,r)));let c=P(e,n,i,t,r);c||r?.dryWarn("#fieldTypeNotFound",`Field type not found for '${n.url}#${i.join(".")}' (${n.derivation})`);let l;t.pattern?l={kind:"pattern",type:t.pattern.type,value:t.pattern.value}:t.fixed&&(l={kind:"fixed",type:t.fixed.type,value:t.fixed.value});let p=o??t;if(!l&&p.elements?.coding?.slicing?.slices){let m=p.elements.coding.slicing.slices,g=Object.values(m);g.length>0&&g.every(u=>u.min!==void 0&&u.min>=1&&u.match&&typeof u.match=="object"&&Object.keys(u.match).length>0)&&(l={kind:"fixed",type:"CodeableConcept",value:{coding:g.flatMap(f=>f.match?[f.match]:[])}});}return {type:c,required:Ne(e,n,i),excluded:De(e,n,i),reference:Rn(e,n,t),array:t.array||false,min:t.min,max:t.max,choices:t.choices,choiceOf:t.choiceOf,binding:s,enum:a,valueConstraint:l,mustSupport:t.mustSupport}};function _(e,n,i,t){return {type:B(e,n,i),array:t.array||false,required:Ne(e,n,i),excluded:De(e,n,i)}}var Fn=(e,n,i,t)=>{let r=e.resolveFs(n.package_meta,i);if(!r?.elements)return;let o=[];for(let[s,a]of Object.entries(r.elements)){if(a.choiceOf!=="value"&&!s.startsWith("value"))continue;let c=P(e,r,[s],a,t);c&&o.push(c);}return E(o)},Tn=(e,n,i)=>{let t=[];if(!n.elements)return t;for(let[r,o]of Object.entries(n.elements)){if(!r.startsWith("extension:"))continue;let s=r.split(":")[1];if(!s)continue;let a;for(let[c,l]of Object.entries(o.elements??{}))if(!(l.choiceOf!=="value"&&!c.startsWith("value"))&&(a=P(e,n,[r,c],l,i),a))break;t.push({name:s,url:o.url??s,valueFieldType:a,min:o.min,max:o.max!==void 0?String(o.max):void 0});}return t},vn=(e,n,i)=>{let t=[],o=n.elements?.extension?.slicing?.slices;if(!o||typeof o!="object")return t;for(let[s,a]of Object.entries(o)){let c=a,l=c.schema;if(!l)continue;let p;for(let[m,g]of Object.entries(l.elements??{})){let d=g;if(!(d.choiceOf!=="value"&&!m.startsWith("value"))&&(p=P(e,n,[m],d,i),p))break}t.push({name:s,url:c.match?.url??s,valueFieldType:p,min:l._required?1:l.min??0,max:l.max!==void 0?String(l.max):l.array?"*":"1"});}return t},En=(e,n,i,t)=>{let r=e.resolveFs(n.package_meta,i);if(!r?.elements)return;let o=Tn(e,r,t),s=vn(e,r,t),a=[...o,...s];return a.length>0?a:void 0},He=(e,n,i)=>{let t=[],r=(c,l,p)=>{let m=p.url,g=m?Fn(e,n,m,i):void 0,d=m?En(e,n,m,i):void 0;if(!m){let S=n.elements?.extension?.slicing?.slices?.[l]?.schema;if(S){m=S.elements?.url?.fixed?.value??l;for(let[x,C]of Object.entries(S.elements??{})){let N=C;if(N.choiceOf==="value"||x.startsWith("value")){let oe=P(e,n,[x],N,i);if(oe){g=[oe];break}}}}}let u=d&&d.length>0,f=m?e.resolveFs(n.package_meta,m):void 0,y=f?k(f):void 0,h=[...c,"extension"].join(".");t.push({name:l,path:h,url:m,profile:y,min:p.min,max:p.max!==void 0?String(p.max):void 0,mustSupport:p.mustSupport,valueFieldTypes:g,subExtensions:d,isComplex:u,nameCandidates:xe({name:l,path:h})});},o=(c,l)=>{if(l.extensions)for(let[p,m]of Object.entries(l.extensions))r(c,p,m);if(l.elements)for(let[p,m]of Object.entries(l.elements))o([...c,p],m);};o([],n);let s=new Set,a=t.filter(c=>{let l=`${c.url}:${c.path}`;return s.has(l)?false:(s.add(l),true)});return a.length===0?void 0:a};function Pn(e,n,i,t,r){if(!t)return {};let o={},s={};for(let a of e.getAllElementKeys(t)){let c=[...i,a],l=e.resolveElementSnapshot(n,c),p=l.type?e.ensureSpecializationCanonicalUrl(l.type):void 0;if(p&&v(n.package_meta,p).shouldSkip){r?.warn("#skipCanonical",`Skipping field ${c} for ${p} due to skip hack ${v(n.package_meta,p).reason}`);continue}ne(e,n,c,l,t[a])?o[a]=_(e,n,c,l):o[a]=j(e,n,c,l,r,t[a]);let m=M(a,l);m&&(s[a]=m);}return {fields:o,slicing:Object.keys(s).length>0?s:void 0}}function Nn(e){let n=[];for(let i of Object.values(e))"type"in i&&i.type&&n.push(i.type),"binding"in i&&i.binding&&n.push(i.binding);return n}async function $e(e,n,i){if(!n.url)throw new Error("ValueSet URL is required");let t=H(e,n.package_meta,n.url),r=Y(e,n.package_meta,n.url);return {identifier:t,description:n.description,concept:r,compose:r?void 0:n.compose}}var Ve=(e,n,i)=>{let t=[];return e&&t.push(e),n&&t.push(...Nn(n)),i&&t.push(...Pe(i)),t},Ue=(e,n,i,t)=>{let o=Ve(n,i,t).filter(s=>!(s.url===e.url||W(s)));return E(o)},Dn=(e,n,i,t)=>{let o=Ve(n,i,t).filter(s=>s.url!==e.url);return E(o)};function Oe(e,n,i){let t;if(n.base){let p=e.resolveFs(n.package_meta,e.ensureSpecializationCanonicalUrl(n.base));if(!p)throw new Error(`Base resource not found '${n.base}' for <${n.url}> from ${R(n.package_meta)}`);let m=k(p);we(!W(m),`Unexpected nested base for ${n.url}`),t=m;}let{fields:r,slicing:o}=Pn(e,n,[],n.elements,i),s=Ee(e,n,i),a=Se(e,n,i);if(n.derivation==="constraint"){let p=k(n);if(!t)throw new Error(`Profile ${n.url} must have a base type`);let m=He(e,n,i),g=m?.flatMap(ge),d=Dn(p,t,r,s),u={identifier:p,base:t,fields:r,slicing:o,nested:s,description:n.description,dependencies:E(d,g),extensions:m};return ke(u),[u,...a]}if(n.kind==="primitive-type"){let p=k(n);return we(t,`Primitive type ${n.url} must have a base type`),[{identifier:p,description:n.description,base:t,dependencies:Ue(p,t,r,s)},...a]}let c=k(n);return [{identifier:c,base:t,fields:r,slicing:o,nested:s,description:n.description,dependencies:Ue(c,t,r,s),typeFamily:void 0},...a]}var Ln=(e,n,i)=>{let t={};for(let s of e){let a=`${s.schema.identifier.url}|${s.schema.identifier.package}`,c=me(s.schema);t[a]??={},t[a][c]??={typeSchema:s.schema,sources:[]},t[a][c].sources.push(s);}let r=[],o={};for(let s of Object.values(t)){let a=Object.entries(s).map(([l,p])=>({...p,schemaHash:l,sources:[...p.sources].sort(z)})).sort(pe),c=a[0];if(c)if(a.length>1){let l=c.typeSchema.identifier.url,p=c.typeSchema.identifier.package;i?.dryWarn("#duplicateSchema",`'${l}' from '${p}' has ${a.length} versions`),r.push(c.typeSchema);o[p]??={},o[p][l]=a.flatMap(g=>g.sources.map(d=>({typeSchema:g.typeSchema,sourcePackage:d.sourcePackage,sourceCanonical:d.sourceCanonical})));}else r.push(c.typeSchema);}return {schemas:r,collisions:o}},Be=async(e,n,i)=>{let t=[];for(let r of e.allFs()){let o=R(r.package_meta),s=v(r.package_meta,r.url);if(s.shouldSkip){i?.dryWarn("#skipCanonical",`Skip ${r.url} from ${o}. Reason: ${s.reason}`);continue}for(let a of Oe(e,r,i))t.push({schema:a,sourcePackage:o,sourceCanonical:r.url});}for(let r of e.allVs())t.push({schema:await $e(e,r),sourcePackage:R(r.package_meta),sourceCanonical:r.url});return Ln(t,n,i)};var Me={command:"generate <packages..>",describe:"Generate TypeSchema files from FHIR packages",builder:{packages:{type:"string",array:true,demandOption:true,describe:"FHIR packages to process (e.g., hl7.fhir.r4.core@4.0.1)"},output:{alias:"o",type:"string",describe:"Output file or directory",default:"./schemas.ndjson"},format:{alias:"f",type:"string",choices:["ndjson","json"],default:"ndjson",describe:"Output format for TypeSchema files"},treeshake:{alias:"t",type:"string",array:true,describe:"Only generate TypeSchemas for specific ResourceTypes (treeshaking)"},singleFile:{alias:"s",type:"boolean",default:false,describe:"Generate single TypeSchema file instead of multiple files (NDJSON format)"},verbose:{alias:"v",type:"boolean",default:false,describe:"Enable verbose output"},registry:{alias:"r",type:"string",describe:"Custom FHIR package registry URL (default: https://fs.get-ig.org/pkgs/)"}},handler:async e=>{let n=I({prefix:"TypeSchema"});try{n.info("Generating TypeSchema from FHIR packages"),n.info(`Packages: ${e.packages.join(", ")}`),n.info(`Output: ${e.output}`);let i=e.singleFile?"ndjson":e.format;n.debug(`Format: ${i}${e.singleFile&&e.format==="json"?" (forced from json due to singleFile)":""}`),e.treeshake&&e.treeshake.length>0&&n.info(`Treeshaking enabled for ResourceTypes: ${e.treeshake.join(", ")}`),e.singleFile&&n.info("Single file output enabled (NDJSON format)"),e.registry&&n.info(`Using custom registry: ${e.registry}`);let t=Date.now(),r=e.packages.map(p=>{if(p.includes("@")){let m=p.lastIndexOf("@");return {name:p.slice(0,m),version:p.slice(m+1)||"latest"}}return {name:p,version:"latest"}});n.info(`Processing packages: ${r.map(p=>`${p.name}@${p.version}`).join(", ")}`);let o=await Te(r,{logger:n,registry:e.registry,focusedPackages:r}),{schemas:s}=await Be(o,void 0,n);if(s.length===0)throw new Error("No schemas were generated from the specified packages");let a=e.output;if(!a)throw new Error("Output format not specified");await mkdir(dirname(a),{recursive:!0});let c;i==="json"?c=JSON.stringify(s,null,2):c=s.map(p=>JSON.stringify(p)).join(`
|
|
9
|
+
`),await writeFile(a,c,"utf-8");let l=Date.now()-t;if(ae(`Generated ${s.length} TypeSchema definitions`,l,{schemas:s.length}),n.info(`Output: ${a}`),e.verbose){n.debug("Generated schemas:");let p=s.map(m=>`${m.identifier?.name||"Unknown"} (${m.identifier?.kind||"unknown"})`);T(p);}}catch(i){n.error(`Failed to generate TypeSchema: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}}};var ie=I({prefix:"typeschema"}),je={command:"typeschema [subcommand]",describe:"TypeSchema operations - generate, validate and merge schemas",builder:e=>e.command(Me).help().example("$0 typeschema generate hl7.fhir.r4.core@4.0.1","Generate TypeSchema from FHIR R4 core package"),handler:e=>{if(!e.subcommand&&e._.length===1){ie.info("Available typeschema subcommands:"),T(["generate Generate TypeSchema files from FHIR packages"]),console.log(`
|
|
10
10
|
Use 'atomic-codegen typeschema <subcommand> --help' for more information about a subcommand.`),console.log(`
|
|
11
|
-
Examples:`),
|
|
12
|
-
Use 'atomic-codegen typeschema <subcommand> --help' for more information about a subcommand.`),process.exit(1));}};var
|
|
11
|
+
Examples:`),T(["atomic-codegen typeschema generate hl7.fhir.r4.core@4.0.1 -o schemas.ndjson","atomic-codegen typeschema validate schemas.ndjson","atomic-codegen typeschema merge schema1.ndjson schema2.ndjson -o merged.ndjson"]);return}e.subcommand&&!["generate","validate","merge"].includes(e.subcommand)&&(ie.error(`Unknown typeschema subcommand: ${e.subcommand}`),ie.info("Available typeschema subcommands:"),T(["generate Generate TypeSchema files from FHIR packages","validate Validate TypeSchema files for correctness and consistency","merge Merge multiple TypeSchema files into a single file"]),console.log(`
|
|
12
|
+
Use 'atomic-codegen typeschema <subcommand> --help' for more information about a subcommand.`),process.exit(1));}};var G=I({prefix:"cli"});async function On(e){let n=e.logLevel??(e.debug||e.verbose?"DEBUG":"INFO");G=I({prefix:"cli",level:n});}function Bn(){return $n(hideBin(process.argv)).scriptName("atomic-codegen").usage("$0 <command> [options]").middleware(On).command(je).option("verbose",{alias:"v",type:"boolean",description:"Enable verbose output",default:false,global:true}).option("debug",{alias:"d",type:"boolean",description:"Enable debug output with detailed logging",default:false,global:true}).option("log-level",{alias:"l",type:"string",choices:["DEBUG","INFO","WARN","ERROR","SILENT"],description:"Set the log level (default: INFO)",global:true}).demandCommand(0).middleware(e=>{e._.length===0&&(se("Welcome to Atomic Codegen!"),console.log("Available commands:"),console.log(" typeschema Generate, validate and merge TypeSchema files"),console.log(`
|
|
13
13
|
Use 'atomic-codegen <command> --help' for more information about a command.`),console.log(`
|
|
14
14
|
Quick examples:`),console.log(" atomic-codegen typeschema generate hl7.fhir.r4.core@4.0.1 -o schemas.ndjson"),console.log(`
|
|
15
|
-
Use 'atomic-codegen --help' to see all options.`),process.exit(0));}).help().version("0.1.0").example("$0 typeschema generate hl7.fhir.r4.core@4.0.1 -o schemas.ndjson","Generate TypeSchemas from FHIR package").fail((e,n,i)=>{
|
|
15
|
+
Use 'atomic-codegen --help' to see all options.`),process.exit(0));}).help().version("0.1.0").example("$0 typeschema generate hl7.fhir.r4.core@4.0.1 -o schemas.ndjson","Generate TypeSchemas from FHIR package").fail((e,n,i)=>{G.error(n?n.message:e),G.error("Use --help for usage information"),process.exit(1);}).wrap(Math.min(120,process.stdout.columns||80))}async function re(){await Bn().parseAsync();}import.meta.main&&re().catch(e=>{G.error(String(e)),process.exit(1);});re().catch(e=>{console.error("CLI Error:",e instanceof Error?e.message:e),process.exit(1);});
|
package/dist/index.d.ts
CHANGED
|
@@ -871,15 +871,22 @@ type CollisionResolution = {
|
|
|
871
871
|
type ResolveCollisionsConf = Record<string, CollisionResolution>;
|
|
872
872
|
type IrConf = {
|
|
873
873
|
treeShake?: TreeShakeConf;
|
|
874
|
+
/** Rule defaults applied to every treeShake root; a rule's own value wins. */
|
|
875
|
+
treeShakeDefaults?: TreeShakeDefaults;
|
|
874
876
|
promoteLogical?: LogicalPromotionConf;
|
|
875
877
|
resolveCollisions?: ResolveCollisionsConf;
|
|
876
878
|
};
|
|
879
|
+
type TreeShakeDefaults = Pick<TreeShakeRule, "followReferences">;
|
|
877
880
|
type LogicalPromotionConf = Record<PkgName$1, CanonicalUrl[]>;
|
|
878
881
|
type TreeShakeConf = Record<string, Record<string, TreeShakeRule>>;
|
|
879
882
|
type TreeShakeRule = {
|
|
880
883
|
ignoreFields?: string[];
|
|
881
884
|
selectFields?: string[];
|
|
882
885
|
ignoreExtensions?: string[];
|
|
886
|
+
/** Also generate types for the schema's reference targets (base resources
|
|
887
|
+
* and target profiles). Non-transitive: followed types keep their own
|
|
888
|
+
* reference targets as plain string literals. Default: false. */
|
|
889
|
+
followReferences?: boolean;
|
|
883
890
|
};
|
|
884
891
|
|
|
885
892
|
type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR" | "SILENT";
|
|
@@ -913,7 +920,7 @@ type LoggerOptions<T extends string> = {
|
|
|
913
920
|
level?: LogLevel;
|
|
914
921
|
};
|
|
915
922
|
|
|
916
|
-
type CodegenTag = "#binding" | "#largeValueSet" | "#placeholderValueSet" | "#fieldTypeNotFound" | "#skipCanonical" | "#duplicateSchema" | "#duplicateCanonical" | "#resolveBase" | "#resolveCollisionMiss";
|
|
923
|
+
type CodegenTag = "#binding" | "#largeValueSet" | "#placeholderValueSet" | "#fieldTypeNotFound" | "#skipCanonical" | "#duplicateSchema" | "#duplicateCanonical" | "#resolveBase" | "#resolveCollisionMiss" | "#nonMonotonicChoice";
|
|
917
924
|
type CodegenLog = Log<CodegenTag>;
|
|
918
925
|
type CodegenLogManager = LogManager<CodegenTag>;
|
|
919
926
|
declare const mkCodegenLogger: (opts?: LoggerOptions<CodegenTag>) => LogManager<CodegenTag>;
|
|
@@ -1006,7 +1013,12 @@ interface PythonGeneratorOptions extends WriterOptions {
|
|
|
1006
1013
|
}
|
|
1007
1014
|
|
|
1008
1015
|
interface IntrospectionWriterOptions extends FileSystemWriterOptions {
|
|
1009
|
-
|
|
1016
|
+
/** if target is .ndjson -- put in one file, else -- split into separated files;
|
|
1017
|
+
* profileSnapshots additionally exports flattened profile snapshots next to profiles as <name>.snapshot.json */
|
|
1018
|
+
typeSchemas?: string | {
|
|
1019
|
+
target: string;
|
|
1020
|
+
profileSnapshots?: boolean;
|
|
1021
|
+
};
|
|
1010
1022
|
typeTree?: string /** .json or .yaml file */;
|
|
1011
1023
|
fhirSchemas?: string /** if .ndjson -- put in one file, else -- split into separated files*/;
|
|
1012
1024
|
structureDefinitions?: string /** if .ndjson -- put in one file, else -- split into separated files*/;
|