@atomic-ehr/codegen 0.0.9 → 0.0.10-canary.20260330084405.5054c4a

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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import pc from 'picocolors';
2
- import assert3 from 'assert';
2
+ import assert4 from 'assert';
3
3
  import * as fs from 'fs';
4
4
  import fs__default from 'fs';
5
5
  import * as Path5 from 'path';
@@ -138,9 +138,6 @@ var uppercaseFirstLetter = (str) => {
138
138
  var uppercaseFirstLetterOfEach = (strings) => {
139
139
  return strings.map((str) => uppercaseFirstLetter(str));
140
140
  };
141
- var typeSchemaInfo = (schema) => {
142
- return `<${schema.identifier.url}> from ${schema.identifier.package}#${schema.identifier.version}`;
143
- };
144
141
  var FileSystemWriter = class {
145
142
  opts;
146
143
  currentDir;
@@ -362,8 +359,11 @@ var hashSchema = (schema) => {
362
359
  return createHash("sha256").update(json).digest("hex").slice(0, 16);
363
360
  };
364
361
  var enrichFHIRSchema = (schema, packageMeta2) => {
362
+ const derivation = schema.derivation === "constraint" ? "constraint" : "specialization";
365
363
  return {
366
364
  ...schema,
365
+ derivation,
366
+ kind: schema.kind,
367
367
  package_meta: schema.package_meta || packageMeta2,
368
368
  name: schema.name,
369
369
  url: schema.url,
@@ -382,15 +382,15 @@ var isPrimitiveIdentifier = (id) => {
382
382
  var isNestedIdentifier = (id) => {
383
383
  return id?.kind === "nested";
384
384
  };
385
- var isProfileIdentifier = (id) => {
386
- return id?.kind === "profile";
387
- };
388
385
  var concatIdentifiers = (...sources) => {
389
386
  const entries = sources.filter((s) => s !== void 0).flatMap((s) => s.map((id) => [id.url, id]));
390
387
  if (entries.length === 0) return void 0;
391
388
  const deduped = Object.values(Object.fromEntries(entries));
392
389
  return deduped.sort((a, b) => a.url.localeCompare(b.url));
393
390
  };
391
+ var isNestedTypeSchema = (schema) => {
392
+ return schema !== void 0 && isNestedIdentifier(schema.identifier);
393
+ };
394
394
  var isSpecializationTypeSchema = (schema) => {
395
395
  return schema?.identifier.kind === "resource" || schema?.identifier.kind === "complex-type" || schema?.identifier.kind === "logical";
396
396
  };
@@ -409,12 +409,17 @@ var isLogicalTypeSchema = (schema) => {
409
409
  var isProfileTypeSchema = (schema) => {
410
410
  return schema?.identifier.kind === "profile";
411
411
  };
412
- function isBindingSchema(schema) {
412
+ var isBindingSchema = (schema) => {
413
413
  return schema?.identifier.kind === "binding";
414
- }
415
- function isValueSetTypeSchema(schema) {
414
+ };
415
+ var isValueSetTypeSchema = (schema) => {
416
416
  return schema?.identifier.kind === "value-set";
417
- }
417
+ };
418
+ var extractExtensionDeps = (ext) => [
419
+ ...ext.valueFieldTypes ?? [],
420
+ ...ext.profile ? [ext.profile] : [],
421
+ ...ext.subExtensions?.flatMap((sub) => sub.valueFieldType ? [sub.valueFieldType] : []) ?? []
422
+ ];
418
423
  var isNotChoiceDeclarationField = (field) => {
419
424
  if (!field) return false;
420
425
  return field.choices === void 0;
@@ -832,51 +837,36 @@ var sortAsDeclarationSequence = (schemas) => {
832
837
  const sorted = topologicalSort(graph);
833
838
  return sorted.map((name) => schemas.find((schema) => schema.identifier.name === name)).filter(Boolean);
834
839
  };
835
- var resourceRelatives = (schemas) => {
836
- const regularSchemas = schemas.filter(
837
- (e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e) || isComplexTypeTypeSchema(e)
838
- );
839
- const directPairs = [];
840
- const childrenByParent = /* @__PURE__ */ new Map();
841
- for (const schema of regularSchemas) {
842
- if (schema.base) {
843
- directPairs.push({ parent: schema.base, child: schema.identifier });
844
- const parentName = schema.base.name;
845
- let children = childrenByParent.get(parentName);
846
- if (!children) {
847
- children = [];
848
- childrenByParent.set(parentName, children);
849
- }
850
- children.push(schema.identifier);
851
- }
852
- }
853
- const transitiveCache = /* @__PURE__ */ new Map();
854
- const getTransitiveChildren = (parentName) => {
855
- const cached = transitiveCache.get(parentName);
856
- if (cached) return cached;
857
- const directChildren = childrenByParent.get(parentName) ?? [];
858
- const result = [...directChildren];
859
- for (const child of directChildren) {
860
- result.push(...getTransitiveChildren(child.name));
861
- }
862
- transitiveCache.set(parentName, result);
840
+ var populateTypeFamily = (schemas) => {
841
+ const directChildrenByParent = {};
842
+ for (const schema of schemas) {
843
+ if (!isSpecializationTypeSchema(schema) || !schema.base) continue;
844
+ const parentUrl = schema.base.url;
845
+ if (!directChildrenByParent[parentUrl]) directChildrenByParent[parentUrl] = [];
846
+ directChildrenByParent[parentUrl].push(schema.identifier);
847
+ }
848
+ const transitiveCache = {};
849
+ const getTransitiveChildren = (parentUrl) => {
850
+ if (transitiveCache[parentUrl]) return transitiveCache[parentUrl];
851
+ const direct = directChildrenByParent[parentUrl] ?? [];
852
+ const result = [...direct];
853
+ for (const child of direct) {
854
+ result.push(...getTransitiveChildren(child.url));
855
+ }
856
+ transitiveCache[parentUrl] = result;
863
857
  return result;
864
858
  };
865
- const seen = /* @__PURE__ */ new Set();
866
- const allPairs = [];
867
- for (const pair of directPairs) {
868
- const key = `${pair.parent.name}|${pair.child.name}`;
869
- seen.add(key);
870
- allPairs.push(pair);
871
- for (const transitiveChild of getTransitiveChildren(pair.child.name)) {
872
- const transitiveKey = `${pair.parent.name}|${transitiveChild.name}`;
873
- if (!seen.has(transitiveKey)) {
874
- seen.add(transitiveKey);
875
- allPairs.push({ parent: pair.parent, child: transitiveChild });
876
- }
877
- }
859
+ for (const schema of schemas) {
860
+ if (!isSpecializationTypeSchema(schema)) continue;
861
+ const allChildren = getTransitiveChildren(schema.identifier.url);
862
+ if (allChildren.length === 0) continue;
863
+ const resources = allChildren.filter(isResourceIdentifier);
864
+ const complexTypes = allChildren.filter(isComplexTypeIdentifier);
865
+ const family = {};
866
+ if (resources.length > 0) family.resources = resources;
867
+ if (complexTypes.length > 0) family.complexTypes = complexTypes;
868
+ if (Object.keys(family).length > 0) schema.typeFamily = family;
878
869
  }
879
- return allPairs;
880
870
  };
881
871
  var mkTypeSchemaIndex = (schemas, {
882
872
  register,
@@ -910,9 +900,12 @@ var mkTypeSchemaIndex = (schemas, {
910
900
  for (const schema of schemas) {
911
901
  append(schema);
912
902
  }
913
- const relations = resourceRelatives(schemas);
903
+ populateTypeFamily(schemas);
914
904
  const resolve6 = (id) => {
915
- if (id.kind === "nested") return nestedIndex[id.url]?.[id.package];
905
+ return index[id.url]?.[id.package];
906
+ };
907
+ const resolveType = (id) => {
908
+ if (isNestedIdentifier(id)) return nestedIndex[id.url]?.[id.package];
916
909
  return index[id.url]?.[id.package];
917
910
  };
918
911
  const resolveByUrl = (pkgName, url) => {
@@ -924,6 +917,7 @@ var mkTypeSchemaIndex = (schemas, {
924
917
  }
925
918
  }
926
919
  if (index[url]?.[pkgName]) return index[url]?.[pkgName];
920
+ if (nestedIndex[url]?.[pkgName]) return nestedIndex[url]?.[pkgName];
927
921
  logger?.dryWarn(`Type '${url}' not found in '${pkgName}'`);
928
922
  if (index[url]) {
929
923
  const anyPkg = Object.keys(index[url])[0];
@@ -932,11 +926,15 @@ var mkTypeSchemaIndex = (schemas, {
932
926
  return index[url]?.[anyPkg];
933
927
  }
934
928
  }
929
+ if (nestedIndex[url]) {
930
+ const anyPkg = Object.keys(nestedIndex[url])[0];
931
+ if (anyPkg) {
932
+ logger?.dryWarn(`Type '${url}' fallback to package ${anyPkg}`);
933
+ return nestedIndex[url]?.[anyPkg];
934
+ }
935
+ }
935
936
  return void 0;
936
937
  };
937
- const resourceChildren = (id) => {
938
- return relations.filter((relative) => relative.parent.name === id.name).map((relative) => relative.child);
939
- };
940
938
  const tryHierarchy = (schema) => {
941
939
  const res = [];
942
940
  let cur = schema;
@@ -944,6 +942,7 @@ var mkTypeSchemaIndex = (schemas, {
944
942
  res.push(cur);
945
943
  const base = cur.base;
946
944
  if (base === void 0) break;
945
+ if (isNestedIdentifier(base)) break;
947
946
  const resolved = resolve6(base);
948
947
  if (!resolved) {
949
948
  logger?.warn(
@@ -971,9 +970,35 @@ var mkTypeSchemaIndex = (schemas, {
971
970
  return nonConstraintSchema;
972
971
  };
973
972
  const findLastSpecializationByIdentifier = (id) => {
974
- const schema = resolve6(id);
975
- if (!schema) return id;
976
- return findLastSpecialization(schema).identifier;
973
+ const resolved = resolveType(id);
974
+ if (!resolved) return id;
975
+ if (isNestedTypeSchema(resolved)) return findLastSpecializationByIdentifier(resolved.base);
976
+ return findLastSpecialization(resolved).identifier;
977
+ };
978
+ const narrowMergedChoiceDeclarations = (mergedFields, constraintSchemas) => {
979
+ const result = { ...mergedFields };
980
+ for (const [declName, declField] of Object.entries(result)) {
981
+ if (!isChoiceDeclarationField(declField) || declField.excluded) continue;
982
+ for (const cSchema of constraintSchemas) {
983
+ const sFields = cSchema.fields;
984
+ if (!sFields) continue;
985
+ if (sFields[declName] && isChoiceDeclarationField(sFields[declName])) continue;
986
+ const instancesInSchema = Object.entries(sFields).filter(([_, f]) => isChoiceInstanceField(f) && f.choiceOf === declName).map(([name]) => name);
987
+ if (instancesInSchema.length === 0) continue;
988
+ const allowed = new Set(instancesInSchema);
989
+ result[declName] = { ...declField, choices: declField.choices.filter((c) => allowed.has(c)) };
990
+ break;
991
+ }
992
+ }
993
+ for (const [declName, declField] of Object.entries(result)) {
994
+ if (!isChoiceDeclarationField(declField)) continue;
995
+ const permitted = new Set(declField.excluded ? [] : declField.choices);
996
+ const prohibited = Object.entries(result).filter(
997
+ (e) => isChoiceInstanceField(e[1]) && e[1].choiceOf === declName
998
+ ).filter(([name]) => !permitted.has(name)).map(([name]) => name);
999
+ if (prohibited.length > 0) result[declName] = { ...declField, prohibited };
1000
+ }
1001
+ return result;
977
1002
  };
978
1003
  const flatProfile = (schema) => {
979
1004
  const hierarchySchemas = hierarchy(schema);
@@ -996,6 +1021,7 @@ var mkTypeSchemaIndex = (schemas, {
996
1021
  }
997
1022
  }
998
1023
  }
1024
+ const narrowedFields = narrowMergedChoiceDeclarations(mergedFields, constraintSchemas);
999
1025
  const dependencies = Object.values(
1000
1026
  Object.fromEntries(
1001
1027
  constraintSchemas.flatMap((s) => s.dependencies ?? []).map((dep) => [dep.url, dep])
@@ -1011,7 +1037,7 @@ var mkTypeSchemaIndex = (schemas, {
1011
1037
  return {
1012
1038
  ...schema,
1013
1039
  base: nonConstraintSchema.identifier,
1014
- fields: mergedFields,
1040
+ fields: narrowedFields,
1015
1041
  dependencies,
1016
1042
  extensions: mergedExtensions.length > 0 ? mergedExtensions : void 0
1017
1043
  };
@@ -1069,7 +1095,6 @@ var mkTypeSchemaIndex = (schemas, {
1069
1095
  };
1070
1096
  return {
1071
1097
  _schemaIndex: index,
1072
- _relations: relations,
1073
1098
  schemas,
1074
1099
  schemasByPackage: groupByPackages(schemas),
1075
1100
  register,
@@ -1078,8 +1103,8 @@ var mkTypeSchemaIndex = (schemas, {
1078
1103
  collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
1079
1104
  collectProfiles: () => schemas.filter(isProfileTypeSchema),
1080
1105
  resolve: resolve6,
1106
+ resolveType,
1081
1107
  resolveByUrl,
1082
- resourceChildren,
1083
1108
  tryHierarchy,
1084
1109
  hierarchy,
1085
1110
  findLastSpecialization,
@@ -1373,8 +1398,7 @@ var Python = class extends Writer {
1373
1398
  return names;
1374
1399
  }
1375
1400
  shouldImportResourceFamily(resource) {
1376
- assert3(this.tsIndex !== void 0);
1377
- return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
1401
+ return resource.identifier.kind === "resource" && (resource.typeFamily?.resources?.length ?? 0) > 0;
1378
1402
  }
1379
1403
  generateExportsDeclaration(packageComplexTypes, allResourceNames) {
1380
1404
  this.squareBlock(["__all__", "="], () => {
@@ -1426,11 +1450,11 @@ var Python = class extends Writer {
1426
1450
  this.line("pass");
1427
1451
  return;
1428
1452
  }
1429
- if (schema.identifier.kind === "resource") {
1453
+ if (isResourceTypeSchema(schema)) {
1430
1454
  this.generateResourceTypeField(schema);
1431
1455
  }
1432
1456
  this.generateFields(schema, schema.identifier.name);
1433
- if (schema.identifier.kind === "resource") {
1457
+ if (isResourceTypeSchema(schema)) {
1434
1458
  this.generateResourceMethods(schema);
1435
1459
  }
1436
1460
  }
@@ -1439,8 +1463,7 @@ var Python = class extends Writer {
1439
1463
  this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
1440
1464
  }
1441
1465
  generateResourceTypeField(schema) {
1442
- assert3(this.tsIndex !== void 0);
1443
- const hasChildren = this.tsIndex.resourceChildren(schema.identifier).length > 0;
1466
+ const hasChildren = (schema.typeFamily?.resources?.length ?? 0) > 0;
1444
1467
  if (hasChildren) {
1445
1468
  this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
1446
1469
  } else {
@@ -1602,7 +1625,7 @@ var Python = class extends Writer {
1602
1625
  this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
1603
1626
  }
1604
1627
  generateResourceFamilies(packageResources) {
1605
- assert3(this.tsIndex !== void 0);
1628
+ assert4(this.tsIndex !== void 0);
1606
1629
  const packages = (
1607
1630
  //this.helper.getPackages(packageResources, this.opts.rootPackageName);
1608
1631
  Object.keys(groupByPackages(packageResources)).map(
@@ -1611,7 +1634,7 @@ var Python = class extends Writer {
1611
1634
  );
1612
1635
  const families = {};
1613
1636
  for (const resource of this.tsIndex.collectResources()) {
1614
- const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
1637
+ const children = (resource.typeFamily?.resources ?? []).map((c) => c.name);
1615
1638
  if (children.length > 0) {
1616
1639
  const familyName = `${resource.identifier.name}Family`;
1617
1640
  families[familyName] = children;
@@ -1730,22 +1753,20 @@ function getVersionFromUrl(url) {
1730
1753
  const version = url.split("|")[1];
1731
1754
  return version;
1732
1755
  }
1733
- function determineKind(fhirSchema) {
1734
- if (fhirSchema.derivation === "constraint") return "profile";
1735
- if (fhirSchema.kind === "primitive-type") return "primitive-type";
1736
- if (fhirSchema.kind === "complex-type") return "complex-type";
1737
- if (fhirSchema.kind === "resource") return "resource";
1738
- if (fhirSchema.kind === "logical") return "logical";
1739
- return "resource";
1740
- }
1756
+ var identifierBase = (fhirSchema) => ({
1757
+ package: fhirSchema.package_meta.name,
1758
+ version: fhirSchema.package_meta.version,
1759
+ name: fhirSchema.name,
1760
+ url: fhirSchema.url
1761
+ });
1741
1762
  function mkIdentifier(fhirSchema) {
1742
- return {
1743
- kind: determineKind(fhirSchema),
1744
- package: fhirSchema.package_meta.name,
1745
- version: fhirSchema.package_meta.version,
1746
- name: fhirSchema.name,
1747
- url: fhirSchema.url
1748
- };
1763
+ const fields = identifierBase(fhirSchema);
1764
+ if (fhirSchema.derivation === "constraint") return { kind: "profile", ...fields };
1765
+ if (fhirSchema.kind === "primitive-type") return { kind: "primitive-type", ...fields };
1766
+ if (fhirSchema.kind === "complex-type") return { kind: "complex-type", ...fields };
1767
+ if (fhirSchema.kind === "resource") return { kind: "resource", ...fields };
1768
+ if (fhirSchema.kind === "logical") return { kind: "logical", ...fields };
1769
+ return { kind: "resource", ...fields };
1749
1770
  }
1750
1771
  var getValueSetName = (url) => {
1751
1772
  const urlParts = url.split("/");
@@ -1797,7 +1818,7 @@ function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
1797
1818
  function extractValueSetConcepts(register, valueSet, _logger) {
1798
1819
  if (valueSet.expansion?.contains) {
1799
1820
  return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
1800
- assert3(item.code);
1821
+ assert4(item.code);
1801
1822
  return {
1802
1823
  code: item.code,
1803
1824
  display: item.display,
@@ -2455,12 +2476,29 @@ var collectDiscriminatorValue = (schema, segments, index, result) => {
2455
2476
  }
2456
2477
  collectDiscriminatorValue(element, segments, index + 1, result);
2457
2478
  };
2479
+ var computeTypeDiscriminatorMatch = (path, schema, result) => {
2480
+ if (path === "$this") return;
2481
+ const segments = path.split(".");
2482
+ let elem = schema;
2483
+ for (const seg of segments) {
2484
+ elem = elem?.elements?.[seg];
2485
+ if (!elem) return;
2486
+ }
2487
+ const typeName = elem.type;
2488
+ if (!typeName || typeName.includes("/")) return;
2489
+ setNestedValue(result, segments, { resourceType: typeName });
2490
+ };
2458
2491
  var computeMatchFromSchema = (discriminators, schema) => {
2459
- if (!schema?.elements || !discriminators || discriminators.length === 0) return void 0;
2492
+ if (!schema || !discriminators || discriminators.length === 0) return void 0;
2460
2493
  const result = {};
2461
2494
  for (const disc of discriminators) {
2462
- const segments = disc.path.split(".");
2463
- collectDiscriminatorValue(schema, segments, 0, result);
2495
+ if (disc.type === "type") {
2496
+ computeTypeDiscriminatorMatch(disc.path, schema, result);
2497
+ } else {
2498
+ if (!schema.elements) continue;
2499
+ const segments = disc.path.split(".");
2500
+ collectDiscriminatorValue(schema, segments, 0, result);
2501
+ }
2464
2502
  }
2465
2503
  return Object.keys(result).length > 0 ? result : void 0;
2466
2504
  };
@@ -2481,7 +2519,7 @@ var buildSlicing = (element) => {
2481
2519
  };
2482
2520
  }
2483
2521
  return {
2484
- discriminator: slicing.discriminator,
2522
+ discriminator: slicing.discriminator ?? [],
2485
2523
  rules: slicing.rules,
2486
2524
  ordered: slicing.ordered,
2487
2525
  slices: Object.keys(slices).length > 0 ? slices : void 0
@@ -2579,16 +2617,16 @@ function mkNestedField(register, fhirSchema, path, element) {
2579
2617
  }
2580
2618
 
2581
2619
  // src/typeschema/core/profile-extensions.ts
2582
- var extractExtensionValueTypes = (register, fhirSchema, extensionUrl, logger) => {
2620
+ var extractExtensionValueFieldTypes = (register, fhirSchema, extensionUrl, logger) => {
2583
2621
  const extensionSchema = register.resolveFs(fhirSchema.package_meta, extensionUrl);
2584
2622
  if (!extensionSchema?.elements) return void 0;
2585
- const valueTypes = [];
2623
+ const valueFieldTypes = [];
2586
2624
  for (const [key, element] of Object.entries(extensionSchema.elements)) {
2587
2625
  if (element.choiceOf !== "value" && !key.startsWith("value")) continue;
2588
2626
  const fieldType = buildFieldType(register, extensionSchema, [key], element, logger);
2589
- if (fieldType) valueTypes.push(fieldType);
2627
+ if (fieldType) valueFieldTypes.push(fieldType);
2590
2628
  }
2591
- return concatIdentifiers(valueTypes);
2629
+ return concatIdentifiers(valueFieldTypes);
2592
2630
  };
2593
2631
  var extractLegacySubExtensions = (register, extensionSchema, logger) => {
2594
2632
  const subExtensions = [];
@@ -2606,14 +2644,14 @@ var extractLegacySubExtensions = (register, extensionSchema, logger) => {
2606
2644
  subExtensions.push({
2607
2645
  name: sliceName,
2608
2646
  url: element.url ?? sliceName,
2609
- valueType,
2647
+ valueFieldType: valueType,
2610
2648
  min: element.min,
2611
2649
  max: element.max !== void 0 ? String(element.max) : void 0
2612
2650
  });
2613
2651
  }
2614
2652
  return subExtensions;
2615
2653
  };
2616
- var extractSlicingSubExtensions = (extensionSchema) => {
2654
+ var extractSlicingSubExtensions = (register, extensionSchema, logger) => {
2617
2655
  const subExtensions = [];
2618
2656
  const extensionElement = extensionSchema.elements?.extension;
2619
2657
  const slices = extensionElement?.slicing?.slices;
@@ -2626,21 +2664,13 @@ var extractSlicingSubExtensions = (extensionSchema) => {
2626
2664
  for (const [elemKey, elemValue] of Object.entries(schema.elements ?? {})) {
2627
2665
  const elem = elemValue;
2628
2666
  if (elem.choiceOf !== "value" && !elemKey.startsWith("value")) continue;
2629
- if (elem.type) {
2630
- valueType = {
2631
- kind: "complex-type",
2632
- package: extensionSchema.package_meta.name,
2633
- version: extensionSchema.package_meta.version,
2634
- name: elem.type,
2635
- url: `http://hl7.org/fhir/StructureDefinition/${elem.type}`
2636
- };
2637
- break;
2638
- }
2667
+ valueType = buildFieldType(register, extensionSchema, [elemKey], elem, logger);
2668
+ if (valueType) break;
2639
2669
  }
2640
2670
  subExtensions.push({
2641
2671
  name: sliceName,
2642
2672
  url: slice.match?.url ?? sliceName,
2643
- valueType,
2673
+ valueFieldType: valueType,
2644
2674
  min: schema._required ? 1 : schema.min ?? 0,
2645
2675
  // biome-ignore lint/style/noNestedTernary : okay here
2646
2676
  max: schema.max !== void 0 ? String(schema.max) : schema.array ? "*" : "1"
@@ -2652,7 +2682,7 @@ var extractSubExtensions = (register, fhirSchema, extensionUrl, logger) => {
2652
2682
  const extensionSchema = register.resolveFs(fhirSchema.package_meta, extensionUrl);
2653
2683
  if (!extensionSchema?.elements) return void 0;
2654
2684
  const legacySubs = extractLegacySubExtensions(register, extensionSchema, logger);
2655
- const slicingSubs = extractSlicingSubExtensions(extensionSchema);
2685
+ const slicingSubs = extractSlicingSubExtensions(register, extensionSchema, logger);
2656
2686
  const subExtensions = [...legacySubs, ...slicingSubs];
2657
2687
  return subExtensions.length > 0 ? subExtensions : void 0;
2658
2688
  };
@@ -2660,38 +2690,36 @@ var extractProfileExtensions = (register, fhirSchema, logger) => {
2660
2690
  const extensions = [];
2661
2691
  const addExtensionEntry = (path, name, schema) => {
2662
2692
  let url = schema.url;
2663
- let valueTypes = url ? extractExtensionValueTypes(register, fhirSchema, url, logger) : void 0;
2693
+ let valueFieldTypes = url ? extractExtensionValueFieldTypes(register, fhirSchema, url, logger) : void 0;
2664
2694
  const subExtensions = url ? extractSubExtensions(register, fhirSchema, url, logger) : void 0;
2665
2695
  if (!url) {
2666
2696
  const sliceSchema = fhirSchema.elements?.extension?.slicing?.slices?.[name]?.schema;
2667
2697
  if (sliceSchema) {
2668
2698
  url = sliceSchema.elements?.url?.fixed?.value ?? name;
2669
- for (const [_elemKey, elemValue] of Object.entries(sliceSchema.elements ?? {})) {
2699
+ for (const [elemKey, elemValue] of Object.entries(sliceSchema.elements ?? {})) {
2670
2700
  const elem = elemValue;
2671
- if (elem.choiceOf === "value" && elem.type) {
2672
- valueTypes = [
2673
- {
2674
- kind: "complex-type",
2675
- package: fhirSchema.package_meta.name,
2676
- version: fhirSchema.package_meta.version,
2677
- name: elem.type,
2678
- url: `http://hl7.org/fhir/StructureDefinition/${elem.type}`
2679
- }
2680
- ];
2681
- break;
2701
+ if (elem.choiceOf === "value" || elemKey.startsWith("value")) {
2702
+ const ft = buildFieldType(register, fhirSchema, [elemKey], elem, logger);
2703
+ if (ft) {
2704
+ valueFieldTypes = [ft];
2705
+ break;
2706
+ }
2682
2707
  }
2683
2708
  }
2684
2709
  }
2685
2710
  }
2686
2711
  const isComplex = subExtensions && subExtensions.length > 0;
2712
+ const extFs = url ? register.resolveFs(fhirSchema.package_meta, url) : void 0;
2713
+ const profile = extFs ? mkIdentifier(extFs) : void 0;
2687
2714
  extensions.push({
2688
2715
  name,
2689
2716
  path: [...path, "extension"].join("."),
2690
2717
  url,
2718
+ profile,
2691
2719
  min: schema.min,
2692
2720
  max: schema.max !== void 0 ? String(schema.max) : void 0,
2693
2721
  mustSupport: schema.mustSupport,
2694
- valueTypes,
2722
+ valueFieldTypes,
2695
2723
  subExtensions,
2696
2724
  isComplex
2697
2725
  });
@@ -2765,22 +2793,28 @@ async function transformValueSet(register, valueSet, logger) {
2765
2793
  compose: !concept ? valueSet.compose : void 0
2766
2794
  };
2767
2795
  }
2768
- function extractDependencies(identifier, base, fields, nestedTypes) {
2796
+ var collectRawDeps = (base, fields, nestedTypes) => {
2769
2797
  const deps = [];
2770
2798
  if (base) deps.push(base);
2771
2799
  if (fields) deps.push(...extractFieldDependencies(fields));
2772
2800
  if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
2773
- const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
2801
+ return deps;
2802
+ };
2803
+ var extractDependencies = (identifier, base, fields, nestedTypes) => {
2804
+ const deps = collectRawDeps(base, fields, nestedTypes);
2774
2805
  const filtered = deps.filter((dep) => {
2775
2806
  if (dep.url === identifier.url) return false;
2776
- if (isProfileIdentifier(identifier)) return true;
2777
- if (!isNestedIdentifier(dep)) return true;
2778
- return !localNestedTypeUrls.has(dep.url);
2807
+ if (isNestedIdentifier(dep)) return false;
2808
+ return true;
2779
2809
  });
2780
2810
  return concatIdentifiers(filtered);
2781
- }
2782
- function transformFhirSchemaResource(register, fhirSchema, logger) {
2783
- const identifier = mkIdentifier(fhirSchema);
2811
+ };
2812
+ var extractProfileDependencies = (identifier, base, fields, nestedTypes) => {
2813
+ const deps = collectRawDeps(base, fields, nestedTypes);
2814
+ const filtered = deps.filter((dep) => dep.url !== identifier.url);
2815
+ return concatIdentifiers(filtered);
2816
+ };
2817
+ function transformFhirSchema(register, fhirSchema, logger) {
2784
2818
  let base;
2785
2819
  if (fhirSchema.base) {
2786
2820
  const baseFs = register.resolveFs(
@@ -2791,31 +2825,60 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
2791
2825
  throw new Error(
2792
2826
  `Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
2793
2827
  );
2794
- base = mkIdentifier(baseFs);
2828
+ const baseId = mkIdentifier(baseFs);
2829
+ assert4(!isNestedIdentifier(baseId), `Unexpected nested base for ${fhirSchema.url}`);
2830
+ base = baseId;
2795
2831
  }
2796
2832
  const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
2797
2833
  const nested = mkNestedTypes(register, fhirSchema, logger);
2798
- const extensions = fhirSchema.derivation === "constraint" ? extractProfileExtensions(register, fhirSchema, logger) : void 0;
2799
- const extensionDeps = extensions?.flatMap((ext) => ext.valueTypes ?? []) ?? [];
2800
- const dependencies = extractDependencies(identifier, base, fields, nested);
2801
- const typeSchema = {
2834
+ const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
2835
+ if (fhirSchema.derivation === "constraint") {
2836
+ const identifier2 = mkIdentifier(fhirSchema);
2837
+ if (!base) throw new Error(`Profile ${fhirSchema.url} must have a base type`);
2838
+ const extensions = extractProfileExtensions(register, fhirSchema, logger);
2839
+ const extensionDeps = extensions?.flatMap(extractExtensionDeps);
2840
+ const rawDeps = extractProfileDependencies(identifier2, base, fields, nested);
2841
+ return [
2842
+ {
2843
+ identifier: identifier2,
2844
+ base,
2845
+ fields,
2846
+ nested,
2847
+ description: fhirSchema.description,
2848
+ dependencies: concatIdentifiers(rawDeps, extensionDeps),
2849
+ extensions
2850
+ },
2851
+ ...bindingSchemas
2852
+ ];
2853
+ }
2854
+ if (fhirSchema.kind === "primitive-type") {
2855
+ const identifier2 = mkIdentifier(fhirSchema);
2856
+ assert4(base, `Primitive type ${fhirSchema.url} must have a base type`);
2857
+ return [
2858
+ {
2859
+ identifier: identifier2,
2860
+ description: fhirSchema.description,
2861
+ base,
2862
+ dependencies: extractDependencies(identifier2, base, fields, nested)
2863
+ },
2864
+ ...bindingSchemas
2865
+ ];
2866
+ }
2867
+ const identifier = mkIdentifier(fhirSchema);
2868
+ const schema = {
2802
2869
  identifier,
2803
2870
  base,
2804
2871
  fields,
2805
2872
  nested,
2806
2873
  description: fhirSchema.description,
2807
- dependencies: concatIdentifiers(dependencies, extensionDeps),
2808
- extensions
2874
+ dependencies: extractDependencies(identifier, base, fields, nested),
2875
+ typeFamily: void 0
2809
2876
  };
2810
- const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
2811
- return [typeSchema, ...bindingSchemas];
2812
- }
2813
- async function transformFhirSchema(register, fhirSchema, logger) {
2814
- return transformFhirSchemaResource(register, fhirSchema, logger);
2877
+ return [schema, ...bindingSchemas];
2815
2878
  }
2816
2879
 
2817
2880
  // src/typeschema/index.ts
2818
- var deduplicateSchemas = (schemasWithSources, logger) => {
2881
+ var deduplicateSchemas = (schemasWithSources, resolveCollisions, logger) => {
2819
2882
  const groups = {};
2820
2883
  for (const item of schemasWithSources) {
2821
2884
  const key = `${item.schema.identifier.url}|${item.schema.identifier.package}`;
@@ -2830,11 +2893,30 @@ var deduplicateSchemas = (schemasWithSources, logger) => {
2830
2893
  const sorted = Object.values(versions).sort((a, b) => b.sources.length - a.sources.length);
2831
2894
  const best = sorted[0];
2832
2895
  if (!best) continue;
2833
- schemas.push(best.typeSchema);
2834
2896
  if (sorted.length > 1) {
2835
- const pkg = best.typeSchema.identifier.package;
2836
2897
  const url = best.typeSchema.identifier.url;
2837
- logger?.dryWarn("#duplicateSchema", `'${url}' from '${pkg}' has ${sorted.length} versions`);
2898
+ const pkg = best.typeSchema.identifier.package;
2899
+ const preferredCanonical = resolveCollisions?.[url];
2900
+ if (preferredCanonical) {
2901
+ const allSources = sorted.flatMap((v) => v.sources);
2902
+ const match = sorted.find(
2903
+ (v) => v.sources.some(
2904
+ (s) => s.sourceCanonical === preferredCanonical.canonical && s.sourcePackage === preferredCanonical.package
2905
+ )
2906
+ );
2907
+ if (match) {
2908
+ schemas.push(match.typeSchema);
2909
+ } else {
2910
+ logger?.dryWarn(
2911
+ "#resolveCollisionMiss",
2912
+ `'${url}': preferred source '${preferredCanonical.canonical}' from '${preferredCanonical.package}' not found among variants: ${allSources.map((s) => `${s.sourceCanonical} (${s.sourcePackage})`).join(", ")}`
2913
+ );
2914
+ schemas.push(best.typeSchema);
2915
+ }
2916
+ } else {
2917
+ logger?.dryWarn("#duplicateSchema", `'${url}' from '${pkg}' has ${sorted.length} versions`);
2918
+ schemas.push(best.typeSchema);
2919
+ }
2838
2920
  collisions[pkg] ??= {};
2839
2921
  collisions[pkg][url] = sorted.flatMap(
2840
2922
  (v) => v.sources.map((s) => ({
@@ -2843,11 +2925,13 @@ var deduplicateSchemas = (schemasWithSources, logger) => {
2843
2925
  sourceCanonical: s.sourceCanonical
2844
2926
  }))
2845
2927
  );
2928
+ } else {
2929
+ schemas.push(best.typeSchema);
2846
2930
  }
2847
2931
  }
2848
2932
  return { schemas, collisions };
2849
2933
  };
2850
- var generateTypeSchemas = async (register, logger) => {
2934
+ var generateTypeSchemas = async (register, resolveCollisions, logger) => {
2851
2935
  const schemasWithSources = [];
2852
2936
  for (const fhirSchema of register.allFs()) {
2853
2937
  const pkgId = packageMetaToFhir(fhirSchema.package_meta);
@@ -2856,7 +2940,7 @@ var generateTypeSchemas = async (register, logger) => {
2856
2940
  logger?.dryWarn("#skipCanonical", `Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipCheck.reason}`);
2857
2941
  continue;
2858
2942
  }
2859
- for (const schema of await transformFhirSchema(register, fhirSchema, logger)) {
2943
+ for (const schema of transformFhirSchema(register, fhirSchema, logger)) {
2860
2944
  schemasWithSources.push({
2861
2945
  schema,
2862
2946
  sourcePackage: pkgId,
@@ -2871,7 +2955,7 @@ var generateTypeSchemas = async (register, logger) => {
2871
2955
  sourceCanonical: vsSchema.url
2872
2956
  });
2873
2957
  }
2874
- return deduplicateSchemas(schemasWithSources, logger);
2958
+ return deduplicateSchemas(schemasWithSources, resolveCollisions, logger);
2875
2959
  };
2876
2960
 
2877
2961
  // src/typeschema/ir/logic-promotion.ts
@@ -2884,7 +2968,7 @@ var promoteLogical = (tsIndex, promotes) => {
2884
2968
  tsIndex.schemas.map((schema) => {
2885
2969
  const promo = promoteSets[schema.identifier.package]?.has(schema.identifier.url);
2886
2970
  if (!promo) return void 0;
2887
- if (schema.identifier.kind !== "logical")
2971
+ if (!isLogicalTypeSchema(schema))
2888
2972
  throw new Error(`Unexpected schema kind: ${JSON.stringify(schema.identifier)}`);
2889
2973
  return [identifierToString(schema.identifier), { ...schema.identifier, kind: "resource" }];
2890
2974
  }).filter((e) => e !== void 0)
@@ -2943,14 +3027,14 @@ var mutableSelectFields = (schema, selectFields) => {
2943
3027
  }
2944
3028
  for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) {
2945
3029
  const choices = instances ?? declaration;
2946
- assert3(choices);
3030
+ assert4(choices);
2947
3031
  for (const choiceInstanceName of choices) {
2948
3032
  const field = schema.fields?.[choiceInstanceName];
2949
- assert3(field);
3033
+ assert4(field);
2950
3034
  selectedFields[choiceInstanceName] = field;
2951
3035
  }
2952
3036
  const decl = schema.fields?.[choiceName];
2953
- assert3(decl);
3037
+ assert4(decl);
2954
3038
  selectedFields[choiceName] = { ...decl, choices };
2955
3039
  }
2956
3040
  schema.fields = selectedFields;
@@ -2967,7 +3051,7 @@ var mutableIgnoreFields = (schema, ignoreFields) => {
2967
3051
  }
2968
3052
  if (isChoiceInstanceField(field)) {
2969
3053
  const choiceDeclaration = schema.fields[field.choiceOf];
2970
- assert3(isChoiceDeclarationField(choiceDeclaration));
3054
+ assert4(isChoiceDeclarationField(choiceDeclaration));
2971
3055
  choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName);
2972
3056
  if (choiceDeclaration.choices.length === 0) {
2973
3057
  delete schema.fields[field.choiceOf];
@@ -2977,6 +3061,15 @@ var mutableIgnoreFields = (schema, ignoreFields) => {
2977
3061
  }
2978
3062
  }
2979
3063
  };
3064
+ var mutableIgnoreExtensions = (schema, ignoreExtensions) => {
3065
+ if (!schema.extensions) return;
3066
+ for (const url of ignoreExtensions) {
3067
+ if (!schema.extensions.some((ext) => ext.url === url))
3068
+ throw new Error(`Extension ${url} not found in profile ${schema.identifier.url}`);
3069
+ }
3070
+ schema.extensions = schema.extensions.filter((ext) => !ext.url || !ignoreExtensions.includes(ext.url));
3071
+ if (schema.extensions.length === 0) schema.extensions = void 0;
3072
+ };
2980
3073
  var mutableFillReport = (report, tsIndex, shakedIndex) => {
2981
3074
  const packages = Object.keys(tsIndex.schemasByPackage);
2982
3075
  const shakedPackages = Object.keys(shakedIndex.schemasByPackage);
@@ -2985,15 +3078,15 @@ var mutableFillReport = (report, tsIndex, shakedIndex) => {
2985
3078
  for (const [pkgName, shakedSchemas] of Object.entries(shakedIndex.schemasByPackage)) {
2986
3079
  if (skippedPackages.includes(pkgName)) continue;
2987
3080
  const tsSchemas = tsIndex.schemasByPackage[pkgName];
2988
- assert3(tsSchemas);
3081
+ assert4(tsSchemas);
2989
3082
  report.packages[pkgName] = {
2990
3083
  skippedCanonicals: tsSchemas.filter((schema) => !shakedSchemas.includes(schema)).map((schema) => schema.identifier.url).sort(),
2991
3084
  canonicals: Object.fromEntries(
2992
3085
  shakedSchemas.map((shakedSchema) => {
2993
3086
  const schema = tsIndex.resolve(shakedSchema.identifier);
2994
- assert3(schema);
3087
+ assert4(schema);
2995
3088
  if (!isSpecializationTypeSchema(schema)) return void 0;
2996
- assert3(isSpecializationTypeSchema(shakedSchema));
3089
+ assert4(isSpecializationTypeSchema(shakedSchema));
2997
3090
  if (!schema.fields) return void 0;
2998
3091
  if (!shakedSchema.fields) {
2999
3092
  return [shakedSchema.identifier.url, Object.keys(schema.fields)];
@@ -3018,6 +3111,9 @@ var treeShakeTypeSchema = (schema, rule, _logger) => {
3018
3111
  if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
3019
3112
  mutableIgnoreFields(schema, rule.ignoreFields);
3020
3113
  }
3114
+ if (isProfileTypeSchema(schema) && rule.ignoreExtensions) {
3115
+ mutableIgnoreExtensions(schema, rule.ignoreExtensions);
3116
+ }
3021
3117
  if (schema.nested) {
3022
3118
  const usedTypes = /* @__PURE__ */ new Set();
3023
3119
  const collectUsedNestedTypes = (s) => {
@@ -3026,7 +3122,7 @@ var treeShakeTypeSchema = (schema, rule, _logger) => {
3026
3122
  if (!usedTypes.has(url)) {
3027
3123
  usedTypes.add(url);
3028
3124
  const nestedTypeDef = schema.nested?.find((f2) => f2.identifier.url === url);
3029
- assert3(nestedTypeDef);
3125
+ assert4(nestedTypeDef);
3030
3126
  collectUsedNestedTypes(nestedTypeDef);
3031
3127
  }
3032
3128
  });
@@ -3034,7 +3130,16 @@ var treeShakeTypeSchema = (schema, rule, _logger) => {
3034
3130
  collectUsedNestedTypes(schema);
3035
3131
  schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url));
3036
3132
  }
3037
- schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
3133
+ if (isProfileTypeSchema(schema)) {
3134
+ const extDeps = schema.extensions?.flatMap(extractExtensionDeps);
3135
+ schema.dependencies = concatIdentifiers(
3136
+ extractProfileDependencies(schema.identifier, schema.base, schema.fields, schema.nested),
3137
+ extDeps
3138
+ );
3139
+ } else {
3140
+ assert4(!isNestedIdentifier(schema.identifier));
3141
+ schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
3142
+ }
3038
3143
  return schema;
3039
3144
  };
3040
3145
  var treeShake = (tsIndex, treeShake2) => {
@@ -3042,7 +3147,7 @@ var treeShake = (tsIndex, treeShake2) => {
3042
3147
  for (const [pkgId, requires] of Object.entries(treeShake2)) {
3043
3148
  for (const [url, rule] of Object.entries(requires)) {
3044
3149
  const schema = tsIndex.resolveByUrl(pkgId, url);
3045
- if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`);
3150
+ if (!schema || isNestedTypeSchema(schema)) throw new Error(`Schema not found for ${pkgId} ${url}`);
3046
3151
  const shaked2 = treeShakeTypeSchema(schema, rule);
3047
3152
  focusedSchemas.push(shaked2);
3048
3153
  }
@@ -3057,6 +3162,7 @@ var treeShake = (tsIndex, treeShake2) => {
3057
3162
  if (isSpecializationTypeSchema(schema) || isProfileTypeSchema(schema)) {
3058
3163
  if (!schema.dependencies) continue;
3059
3164
  schema.dependencies.forEach((dep) => {
3165
+ if (isNestedIdentifier(dep)) return;
3060
3166
  const depSchema = tsIndex.resolve(dep);
3061
3167
  if (!depSchema)
3062
3168
  throw new Error(
@@ -3065,13 +3171,6 @@ var treeShake = (tsIndex, treeShake2) => {
3065
3171
  const id = JSON.stringify(depSchema.identifier);
3066
3172
  if (!acc[id]) newSchemas.push(depSchema);
3067
3173
  });
3068
- if (schema.nested) {
3069
- for (const nest of schema.nested) {
3070
- if (isNestedIdentifier(nest.identifier)) continue;
3071
- const id = JSON.stringify(nest.identifier);
3072
- if (!acc[id]) newSchemas.push(nest);
3073
- }
3074
- }
3075
3174
  }
3076
3175
  }
3077
3176
  return collectDeps(newSchemas, acc);
@@ -3149,7 +3248,7 @@ var IntrospectionWriter = class extends FileSystemWriter {
3149
3248
  for (const [canonical, entries] of Object.entries(canonicals)) {
3150
3249
  if (entries.length <= 1) continue;
3151
3250
  const firstEntry = entries[0];
3152
- assert3(firstEntry);
3251
+ assert4(firstEntry);
3153
3252
  const name = normalizeFileName(
3154
3253
  `${firstEntry.typeSchema.identifier.name}(${extractNameFromCanonical(canonical)})`
3155
3254
  );
@@ -3289,31 +3388,42 @@ var generatePackageSection = (lines, pkgName, treeShakePkg, promotedCanonicals)
3289
3388
  lines.push("");
3290
3389
  }
3291
3390
  };
3292
- var generateCollisionVersionLines = (entries) => {
3391
+ var groupCollisionVersions = (entries, resolution) => {
3293
3392
  const uniqueSchemas = /* @__PURE__ */ new Map();
3294
3393
  for (const entry of entries) {
3295
3394
  const key = JSON.stringify(entry.typeSchema);
3296
3395
  if (!uniqueSchemas.has(key)) uniqueSchemas.set(key, []);
3297
3396
  uniqueSchemas.get(key)?.push(entry);
3298
3397
  }
3299
- const versionLines = [];
3300
- const sortedVersions = [...uniqueSchemas.values()].sort((a, b) => b.length - a.length);
3398
+ const sorted = [...uniqueSchemas.values()].sort((a, b) => b.length - a.length);
3399
+ const markVersion = (group, i) => {
3400
+ if (resolution)
3401
+ return group.some(
3402
+ (e) => e.sourceCanonical === resolution.canonical && e.sourcePackage === resolution.package
3403
+ ) ? "selected" : void 0;
3404
+ return i === 0 ? "auto" : void 0;
3405
+ };
3406
+ return sorted.map((group, i) => ({ entries: group, mark: markVersion(group, i) }));
3407
+ };
3408
+ var versionMarkLabel = { selected: " (selected)", auto: " (auto)" };
3409
+ var generateCollisionVersionLines = (versions) => {
3301
3410
  let version = 1;
3302
- for (const schemaEntries of sortedVersions) {
3303
- const sourceList = schemaEntries.map((e) => {
3411
+ return versions.map((v) => {
3412
+ const sourceList = v.entries.map((e) => {
3304
3413
  const name = extractNameFromCanonical(e.sourceCanonical) ?? e.sourceCanonical;
3305
3414
  return `${name} (${e.sourcePackage})`;
3306
3415
  }).join(", ");
3307
- versionLines.push(` - Version ${version++}: ${sourceList}`);
3308
- }
3309
- return versionLines;
3416
+ const mark = v.mark ? versionMarkLabel[v.mark] : "";
3417
+ return ` - Version ${version++}${mark}: ${sourceList}`;
3418
+ });
3310
3419
  };
3311
- var generateCollisionsSection = (lines, collisions) => {
3420
+ var generateCollisionsSection = (lines, collisions, resolveCollisions) => {
3312
3421
  if (!collisions) return;
3313
3422
  lines.push("## Schema Collisions", "");
3314
3423
  lines.push("The following canonicals have multiple schema versions with different content.");
3315
3424
  lines.push("To inspect collision versions, export TypeSchemas using `.introspection({ typeSchemas: 'path' })`");
3316
3425
  lines.push("and check `<pkg>/collisions/<name>/1.json, 2.json, ...` files.", "");
3426
+ const allCollisions = [];
3317
3427
  const collisionPackages = Object.keys(collisions).sort();
3318
3428
  for (const pkgName of collisionPackages) {
3319
3429
  const collisionsPkg = collisions[pkgName];
@@ -3326,13 +3436,34 @@ var generateCollisionsSection = (lines, collisions) => {
3326
3436
  if (sortedEntries.length > 0) {
3327
3437
  lines.push(`### \`${pkgName}\``, "");
3328
3438
  for (const [canonical, entries] of sortedEntries) {
3329
- const versionLines = generateCollisionVersionLines(entries);
3330
- lines.push(`- \`${canonical}\` (${versionLines.length} versions)`);
3439
+ const versions = groupCollisionVersions(entries, resolveCollisions?.[canonical]);
3440
+ const versionLines = generateCollisionVersionLines(versions);
3441
+ lines.push(`- \`${canonical}\` (${versions.length} versions)`);
3331
3442
  lines.push(...versionLines);
3443
+ if (entries[0]) allCollisions.push({ url: canonical, firstSource: entries[0] });
3332
3444
  }
3333
3445
  lines.push("");
3334
3446
  }
3335
3447
  }
3448
+ if (allCollisions.length > 0) {
3449
+ const unresolved = allCollisions.filter((c) => !resolveCollisions?.[c.url]);
3450
+ if (unresolved.length > 0) {
3451
+ lines.push("### Suggested `resolveCollisions` config", "");
3452
+ lines.push("Add to `.typeSchema({ resolveCollisions: { ... } })` to resolve remaining collisions:", "");
3453
+ lines.push("```typescript");
3454
+ lines.push(".typeSchema({");
3455
+ lines.push(" resolveCollisions: {");
3456
+ for (const { url, firstSource } of unresolved) {
3457
+ lines.push(` "${url}": {`);
3458
+ lines.push(` package: "${firstSource.sourcePackage}",`);
3459
+ lines.push(` canonical: "${firstSource.sourceCanonical}",`);
3460
+ lines.push(" },");
3461
+ }
3462
+ lines.push(" },");
3463
+ lines.push("})");
3464
+ lines.push("```", "");
3465
+ }
3466
+ }
3336
3467
  };
3337
3468
  var generateIrReportReadme = (report) => {
3338
3469
  const lines = ["# IR Report", ""];
@@ -3360,7 +3491,7 @@ var generateIrReportReadme = (report) => {
3360
3491
  );
3361
3492
  }
3362
3493
  if (hasCollisions) {
3363
- generateCollisionsSection(lines, report.collisions);
3494
+ generateCollisionsSection(lines, report.collisions, report.resolveCollisions);
3364
3495
  }
3365
3496
  return lines.join("\n");
3366
3497
  };
@@ -3654,7 +3785,7 @@ var ViewModelFactory = class {
3654
3785
  throw new Error(`Unknown type ${typeRef.kind}`);
3655
3786
  }
3656
3787
  _createForComplexType(typeRef, cache, nestedIn) {
3657
- const type = this.tsIndex.resolve(typeRef);
3788
+ const type = this.tsIndex.resolveType(typeRef);
3658
3789
  if (!type) {
3659
3790
  throw new Error(`ComplexType ${typeRef.name} not found`);
3660
3791
  }
@@ -3666,7 +3797,7 @@ var ViewModelFactory = class {
3666
3797
  return res;
3667
3798
  }
3668
3799
  _createForResource(typeRef, cache, nestedIn) {
3669
- const type = this.tsIndex.resolve(typeRef);
3800
+ const type = this.tsIndex.resolveType(typeRef);
3670
3801
  if (!type) {
3671
3802
  throw new Error(`Resource ${typeRef.name} not found`);
3672
3803
  }
@@ -3678,11 +3809,13 @@ var ViewModelFactory = class {
3678
3809
  return res;
3679
3810
  }
3680
3811
  _createChildrenFor(typeRef, cache, nestedIn) {
3681
- if (isComplexTypeIdentifier(typeRef)) {
3682
- return this.tsIndex.resourceChildren(typeRef).filter(isComplexTypeIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
3812
+ const schema = this.tsIndex.resolveType(typeRef);
3813
+ if (!schema) return [];
3814
+ if (isComplexTypeTypeSchema(schema)) {
3815
+ return (schema.typeFamily?.complexTypes ?? []).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
3683
3816
  }
3684
- if (isResourceIdentifier(typeRef)) {
3685
- return this.tsIndex.resourceChildren(typeRef).filter(isResourceIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
3817
+ if (isResourceTypeSchema(schema)) {
3818
+ return (schema.typeFamily?.resources ?? []).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
3686
3819
  }
3687
3820
  return [];
3688
3821
  }
@@ -3691,7 +3824,7 @@ var ViewModelFactory = class {
3691
3824
  let parentRef = "base" in base ? base.base : void 0;
3692
3825
  while (parentRef) {
3693
3826
  parents.push(this._createFor(parentRef, cache, void 0));
3694
- const parent = this.tsIndex.resolve(parentRef);
3827
+ const parent = this.tsIndex.resolveType(parentRef);
3695
3828
  parentRef = parent && "base" in parent ? parent.base : void 0;
3696
3829
  }
3697
3830
  return parents;
@@ -4151,7 +4284,8 @@ var rewriteFieldTypeDefs = {
4151
4284
  Reference: { reference: () => "`${T}/${string}`" },
4152
4285
  CodeableConcept: { coding: () => "Coding<T>" }
4153
4286
  };
4154
- var resolveFieldTsType = (schemaName, tsName, field, resolveRef) => {
4287
+ var resolveFieldTsType = (schemaName, tsName, field, resolveRef, genericFieldMap) => {
4288
+ if (genericFieldMap?.[tsName]) return genericFieldMap[tsName];
4155
4289
  const rewriteFieldType = rewriteFieldTypeDefs[schemaName]?.[tsName];
4156
4290
  if (rewriteFieldType) return rewriteFieldType();
4157
4291
  if (field.enum) {
@@ -4325,7 +4459,7 @@ var generateComplexExtensionSetter = (w, info) => {
4325
4459
  w.curlyBlock(["public", setMethodName, `(input: ${inputTypeName}): this`], () => {
4326
4460
  w.line("const subExtensions: Extension[] = []");
4327
4461
  for (const sub of ext.subExtensions ?? []) {
4328
- const valueField = sub.valueType ? tsValueFieldName(sub.valueType) : "value";
4462
+ const valueField = sub.valueFieldType ? tsValueFieldName(sub.valueFieldType) : "value";
4329
4463
  if (sub.max === "*") {
4330
4464
  w.curlyBlock(["if", `(input.${sub.name})`], () => {
4331
4465
  w.curlyBlock(["for", `(const item of input.${sub.name})`], () => {
@@ -4362,7 +4496,7 @@ var generateComplexExtensionGetter = (w, info) => {
4362
4496
  const inputType = extProfileHasFlatInput && extProfileInfo ? `${extProfileInfo.className}Flat` : inputTypeName;
4363
4497
  generateExtensionGetterOverloads(w, ext, targetPath, getMethodName, inputType, extProfileInfo, () => {
4364
4498
  const configItems = (ext.subExtensions ?? []).map((sub) => {
4365
- const valueField = sub.valueType ? tsValueFieldName(sub.valueType) : "value";
4499
+ const valueField = sub.valueFieldType ? tsValueFieldName(sub.valueFieldType) : "value";
4366
4500
  const isArray = sub.max === "*";
4367
4501
  return `{ name: "${sub.url}", valueField: "${valueField}", isArray: ${isArray} }`;
4368
4502
  });
@@ -4372,7 +4506,7 @@ var generateComplexExtensionGetter = (w, info) => {
4372
4506
  };
4373
4507
  var generateSingleValueExtensionSetter = (w, tsIndex, info) => {
4374
4508
  const { ext, setMethodName, targetPath, extProfileInfo } = info;
4375
- const firstValueType = ext.valueTypes?.[0];
4509
+ const firstValueType = ext.valueFieldTypes?.[0];
4376
4510
  if (!firstValueType) return;
4377
4511
  const valueType = tsTypeFromIdentifier(firstValueType);
4378
4512
  const valueField = tsValueFieldName(firstValueType);
@@ -4408,7 +4542,7 @@ var generateSingleValueExtensionSetter = (w, tsIndex, info) => {
4408
4542
  };
4409
4543
  var generateSingleValueExtensionGetter = (w, info) => {
4410
4544
  const { ext, getMethodName, targetPath, extProfileInfo } = info;
4411
- const firstValueType = ext.valueTypes?.[0];
4545
+ const firstValueType = ext.valueFieldTypes?.[0];
4412
4546
  if (!firstValueType) return;
4413
4547
  const valueType = tsTypeFromIdentifier(firstValueType);
4414
4548
  const valueField = tsValueFieldName(firstValueType);
@@ -4462,7 +4596,7 @@ var generateExtensionMethods = (w, tsIndex, flatProfile, extensionBaseNames) =>
4462
4596
  generateComplexExtensionSetter(w, info);
4463
4597
  w.line();
4464
4598
  generateComplexExtensionGetter(w, info);
4465
- } else if (ext.valueTypes?.length === 1 && ext.valueTypes[0]) {
4599
+ } else if (ext.valueFieldTypes?.length === 1 && ext.valueFieldTypes[0]) {
4466
4600
  generateSingleValueExtensionSetter(w, tsIndex, info);
4467
4601
  w.line();
4468
4602
  generateSingleValueExtensionGetter(w, info);
@@ -4480,21 +4614,21 @@ var collectTypesFromExtensions = (tsIndex, flatProfile, addType) => {
4480
4614
  if (ext.isComplex && ext.subExtensions) {
4481
4615
  needsExtensionType = true;
4482
4616
  for (const sub of ext.subExtensions) {
4483
- if (!sub.valueType) continue;
4617
+ if (!sub.valueFieldType) continue;
4484
4618
  const resolvedType = tsIndex.resolveByUrl(
4485
4619
  flatProfile.identifier.package,
4486
- sub.valueType.url
4620
+ sub.valueFieldType.url
4487
4621
  );
4488
- addType(resolvedType?.identifier ?? sub.valueType);
4622
+ addType(resolvedType?.identifier ?? sub.valueFieldType);
4489
4623
  }
4490
- } else if (ext.valueTypes && ext.valueTypes.length === 1) {
4624
+ } else if (ext.valueFieldTypes && ext.valueFieldTypes.length === 1) {
4491
4625
  needsExtensionType = true;
4492
- if (ext.valueTypes[0]) {
4626
+ if (ext.valueFieldTypes[0]) {
4493
4627
  const resolvedType = tsIndex.resolveByUrl(
4494
4628
  flatProfile.identifier.package,
4495
- ext.valueTypes[0].url
4629
+ ext.valueFieldTypes[0].url
4496
4630
  );
4497
- addType(resolvedType?.identifier ?? ext.valueTypes[0]);
4631
+ addType(resolvedType?.identifier ?? ext.valueFieldTypes[0]);
4498
4632
  }
4499
4633
  } else {
4500
4634
  needsExtensionType = true;
@@ -4517,7 +4651,7 @@ var collectTypesFromFlatInput = (tsIndex, flatProfile, addType) => {
4517
4651
  // src/api/writer-generator/typescript/profile-slices.ts
4518
4652
  var collectChoiceBaseNames = (tsIndex, typeId) => {
4519
4653
  const names = /* @__PURE__ */ new Set();
4520
- const schema = tsIndex.resolve(typeId);
4654
+ const schema = tsIndex.resolveType(typeId);
4521
4655
  if (schema && "fields" in schema && schema.fields) {
4522
4656
  for (const [name, f] of Object.entries(schema.fields)) {
4523
4657
  if (isChoiceDeclarationField(f)) names.add(name);
@@ -4525,15 +4659,35 @@ var collectChoiceBaseNames = (tsIndex, typeId) => {
4525
4659
  }
4526
4660
  return names;
4527
4661
  };
4662
+ var extractResourceTypeFromMatch = (match) => {
4663
+ for (const value of Object.values(match)) {
4664
+ if (typeof value !== "object" || value === null) continue;
4665
+ const obj = value;
4666
+ if (typeof obj.resourceType === "string") return obj.resourceType;
4667
+ const nested = extractResourceTypeFromMatch(obj);
4668
+ if (nested) return nested;
4669
+ }
4670
+ return void 0;
4671
+ };
4528
4672
  var collectTypesFromSlices = (tsIndex, flatProfile, addType) => {
4529
4673
  const pkgName = flatProfile.identifier.package;
4530
4674
  for (const field of Object.values(flatProfile.fields ?? {})) {
4531
4675
  if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) continue;
4676
+ const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
4532
4677
  for (const slice of Object.values(field.slicing.slices)) {
4533
4678
  if (Object.keys(slice.match ?? {}).length > 0) {
4534
4679
  addType(field.type);
4535
4680
  const cc = slice.elements ? tsIndex.constrainedChoice(pkgName, field.type, slice.elements) : void 0;
4536
4681
  if (cc) addType(cc.variantType);
4682
+ if (isTypeDisc && slice.match) {
4683
+ const resourceTypeName = extractResourceTypeFromMatch(slice.match);
4684
+ if (resourceTypeName) {
4685
+ const resourceSchema = tsIndex.schemas.find(
4686
+ (s) => s.identifier.name === resourceTypeName && s.identifier.kind === "resource"
4687
+ );
4688
+ if (resourceSchema) addType(resourceSchema.identifier);
4689
+ }
4690
+ }
4537
4691
  }
4538
4692
  }
4539
4693
  }
@@ -4548,6 +4702,7 @@ var collectSliceDefs = (tsIndex, flatProfile) => Object.entries(flatProfile.fiel
4548
4702
  const baseType = tsTypeFromIdentifier(field.type);
4549
4703
  const pkgName = flatProfile.identifier.package;
4550
4704
  const choiceBaseNames = collectChoiceBaseNames(tsIndex, field.type);
4705
+ const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
4551
4706
  return Object.entries(field.slicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
4552
4707
  const matchFields = Object.keys(slice.match ?? {});
4553
4708
  const required = (slice.required ?? []).filter(
@@ -4555,15 +4710,19 @@ var collectSliceDefs = (tsIndex, flatProfile) => Object.entries(flatProfile.fiel
4555
4710
  );
4556
4711
  const cc = slice.elements ? tsIndex.constrainedChoice(pkgName, field.type, slice.elements) : void 0;
4557
4712
  const constrainedChoice = cc && !isPrimitiveIdentifier(cc.variantType) ? cc : void 0;
4713
+ const resourceType = isTypeDisc ? extractResourceTypeFromMatch(slice.match ?? {}) : void 0;
4714
+ const typedBaseType = resourceType ? `${baseType}<${resourceType}>` : baseType;
4558
4715
  return {
4559
4716
  fieldName,
4560
4717
  baseType,
4718
+ typedBaseType,
4561
4719
  sliceName,
4562
4720
  match: slice.match ?? {},
4563
4721
  required,
4564
4722
  excluded: slice.excluded ?? [],
4565
4723
  array: Boolean(field.array),
4566
- constrainedChoice
4724
+ constrainedChoice,
4725
+ typeDiscriminator: isTypeDisc
4567
4726
  };
4568
4727
  });
4569
4728
  });
@@ -4577,7 +4736,7 @@ var generateSliceSetters = (w, sliceDefs, flatProfile, sliceBaseNames) => {
4577
4736
  const matchRef = `${profileClassName}.${tsSliceStaticName(sliceDef.sliceName)}SliceMatch`;
4578
4737
  const tsField = tsFieldName(sliceDef.fieldName);
4579
4738
  const fieldAccess = tsGet("this.resource", tsField);
4580
- const baseType = sliceDef.baseType;
4739
+ const baseType = sliceDef.typedBaseType;
4581
4740
  const inputOptional = sliceDef.required.length === 0;
4582
4741
  const unionType = `${typeName} | ${baseType}`;
4583
4742
  const paramSignature = inputOptional ? `(input?: ${unionType}): this` : `(input: ${unionType}): this`;
@@ -4621,7 +4780,7 @@ var generateSliceGetters = (w, sliceDefs, flatProfile, sliceBaseNames) => {
4621
4780
  const matchKeys = JSON.stringify(Object.keys(sliceDef.match));
4622
4781
  const tsField = tsFieldName(sliceDef.fieldName);
4623
4782
  const fieldAccess = tsGet("this.resource", tsField);
4624
- const baseType = sliceDef.baseType;
4783
+ const baseType = sliceDef.typedBaseType;
4625
4784
  const defaultReturn = defaultMode === "raw" ? baseType : typeName;
4626
4785
  w.lineSM(`public ${getMethodName}(mode: 'flat'): ${typeName} | undefined`);
4627
4786
  w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType} | undefined`);
@@ -4641,8 +4800,14 @@ var generateSliceGetters = (w, sliceDefs, flatProfile, sliceBaseNames) => {
4641
4800
  w.line(`const item = ${fieldAccess}`);
4642
4801
  w.line("if (!item || !matchesValue(item, match)) return undefined");
4643
4802
  }
4644
- w.line("if (mode === 'raw') return item");
4645
- if (sliceDef.constrainedChoice) {
4803
+ if (sliceDef.typeDiscriminator) {
4804
+ w.line(`if (mode === 'raw') return item as ${baseType}`);
4805
+ } else {
4806
+ w.line("if (mode === 'raw') return item");
4807
+ }
4808
+ if (sliceDef.typeDiscriminator) {
4809
+ w.line(`return item as ${typeName}`);
4810
+ } else if (sliceDef.constrainedChoice) {
4646
4811
  const cc = sliceDef.constrainedChoice;
4647
4812
  w.line(`return unwrapSliceChoice<${typeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)})`);
4648
4813
  } else {
@@ -4699,7 +4864,12 @@ var generateValidateMethod = (w, tsIndex, flatProfile) => {
4699
4864
  const errors = [];
4700
4865
  const warnings = [];
4701
4866
  for (const [name, field] of Object.entries(fields)) {
4702
- if (isChoiceInstanceField(field)) continue;
4867
+ if (isChoiceInstanceField(field)) {
4868
+ const decl = fields[field.choiceOf];
4869
+ if (decl && isChoiceDeclarationField(decl) && decl.prohibited?.includes(name))
4870
+ errors.push(`...validateExcluded(res, profileName, ${JSON.stringify(name)})`);
4871
+ continue;
4872
+ }
4703
4873
  if (isChoiceDeclarationField(field)) {
4704
4874
  if (field.required)
4705
4875
  errors.push(`...validateChoiceRequired(res, profileName, ${JSON.stringify(field.choices)})`);
@@ -4812,7 +4982,7 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
4812
4982
  };
4813
4983
  var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, coveredNames) => {
4814
4984
  const covered = new Set(coveredNames);
4815
- const baseSchema = tsIndex.resolve(flatProfile.base);
4985
+ const baseSchema = tsIndex.resolveType(flatProfile.base);
4816
4986
  if (!baseSchema || !("fields" in baseSchema) || !baseSchema.fields) return;
4817
4987
  for (const [name, field] of Object.entries(baseSchema.fields)) {
4818
4988
  if (covered.has(name)) continue;
@@ -4829,78 +4999,20 @@ var generateProfileIndexFile = (w, tsIndex, initialProfiles) => {
4829
4999
  if (initialProfiles.length === 0) return;
4830
5000
  w.cd("profiles", () => {
4831
5001
  w.cat("index.ts", () => {
4832
- const profiles = initialProfiles.map((profile) => {
5002
+ const exports$1 = /* @__PURE__ */ new Map();
5003
+ for (const profile of initialProfiles) {
4833
5004
  const className = tsProfileClassName(profile);
4834
- const resourceName = tsResourceName(profile.identifier);
4835
- const overrides = detectFieldOverrides(tsIndex, profile);
4836
- let typeExport;
4837
- if (overrides.size > 0) typeExport = resourceName;
4838
- return [profile, className, typeExport];
4839
- });
4840
- if (profiles.length === 0) return;
4841
- const classExports = /* @__PURE__ */ new Map();
4842
- const typeExports = /* @__PURE__ */ new Map();
4843
- for (const [profile, className, typeName] of profiles) {
4844
5005
  const moduleName = tsProfileModuleName(tsIndex, profile);
4845
- if (!classExports.has(className)) {
4846
- classExports.set(className, `export { ${className} } from "./${moduleName}"`);
4847
- }
4848
- if (typeName && !typeExports.has(typeName) && !classExports.has(typeName)) {
4849
- typeExports.set(typeName, `export type { ${typeName} } from "./${moduleName}"`);
5006
+ if (!exports$1.has(className)) {
5007
+ exports$1.set(className, `export { ${className} } from "./${moduleName}"`);
4850
5008
  }
4851
5009
  }
4852
- const allExports = [...classExports.values(), ...typeExports.values()].sort();
4853
- for (const exp of allExports) {
5010
+ for (const exp of [...exports$1.values()].sort()) {
4854
5011
  w.lineSM(exp);
4855
5012
  }
4856
5013
  });
4857
5014
  });
4858
5015
  };
4859
- var tsTypeForProfileField = (tsIndex, flatProfile, fieldName, field) => {
4860
- if (!isNotChoiceDeclarationField(field)) {
4861
- throw new Error(`Choice declaration fields not supported for '${fieldName}'`);
4862
- }
4863
- let tsType;
4864
- if (field.enum) {
4865
- if (field.type?.name === "Coding") {
4866
- tsType = `Coding<${tsEnumType(field.enum)}>`;
4867
- } else if (field.type?.name === "CodeableConcept") {
4868
- tsType = `CodeableConcept<${tsEnumType(field.enum)}>`;
4869
- } else {
4870
- tsType = tsEnumType(field.enum);
4871
- }
4872
- } else if (field.reference && field.reference.length > 0) {
4873
- const specialization = tsIndex.findLastSpecialization(flatProfile);
4874
- if (!isSpecializationTypeSchema(specialization))
4875
- throw new Error(`Invalid specialization for ${flatProfile.identifier}`);
4876
- const sField = specialization.fields?.[fieldName];
4877
- if (sField === void 0 || isChoiceDeclarationField(sField) || sField.reference === void 0)
4878
- throw new Error(`Invalid field declaration for ${fieldName}`);
4879
- const sRefs = sField.reference.map((e) => e.name);
4880
- const references = field.reference.map((ref) => {
4881
- const resRef = tsIndex.findLastSpecializationByIdentifier(ref);
4882
- if (resRef.name !== ref.name) {
4883
- return `"${resRef.name}" /*${ref.name}*/`;
4884
- }
4885
- return `'${ref.name}'`;
4886
- }).join(" | ");
4887
- if (sRefs.length === 1 && sRefs[0] === "Resource" && references !== '"Resource"') {
4888
- const cleanRefs = references.replace(/\/\*[^*]*\*\//g, "").trim();
4889
- tsType = `Reference<"Resource" /* ${cleanRefs} */ >`;
4890
- } else {
4891
- tsType = `Reference<${references}>`;
4892
- }
4893
- } else if (isPrimitiveIdentifier(field.type)) {
4894
- tsType = resolvePrimitiveType(field.type.name);
4895
- } else if (isNestedIdentifier(field.type)) {
4896
- tsType = tsResourceName(field.type);
4897
- } else if (field.type === void 0) {
4898
- throw new Error(`Undefined type for '${fieldName}' field at ${typeSchemaInfo(flatProfile)}`);
4899
- } else {
4900
- tsType = field.type.name;
4901
- }
4902
- return tsType;
4903
- };
4904
5016
  var generateProfileHelpersImport = (w, tsIndex, flatProfile, sliceDefs, factoryInfo) => {
4905
5017
  const extensions = flatProfile.extensions ?? [];
4906
5018
  const hasMeta = tsIndex.isWithMetaField(flatProfile);
@@ -4914,7 +5026,7 @@ var generateProfileHelpersImport = (w, tsIndex, flatProfile, sliceDefs, factoryI
4914
5026
  imports.push("applySliceMatch", "matchesValue", "setArraySlice", "getArraySlice", "ensureSliceDefaults");
4915
5027
  if (extensions.some((ext) => ext.path.split(".").some((s) => s !== "extension"))) imports.push("ensurePath");
4916
5028
  if (extensions.some((ext) => ext.isComplex && ext.subExtensions)) imports.push("extractComplexExtension");
4917
- if (sliceDefs.length > 0) imports.push("stripMatchKeys");
5029
+ if (sliceDefs.some((s) => !s.typeDiscriminator)) imports.push("stripMatchKeys");
4918
5030
  if (sliceDefs.some((s) => s.constrainedChoice)) imports.push("wrapSliceChoice", "unwrapSliceChoice");
4919
5031
  if (extensions.some((ext) => ext.url)) imports.push("isExtension", "getExtensionValue", "pushExtension");
4920
5032
  if (Object.keys(flatProfile.fields ?? {}).length > 0)
@@ -4933,7 +5045,7 @@ var generateProfileHelpersImport = (w, tsIndex, flatProfile, sliceDefs, factoryI
4933
5045
  w.line();
4934
5046
  }
4935
5047
  };
4936
- var generateProfileImports = (w, tsIndex, flatProfile, overrides) => {
5048
+ var generateProfileImports = (w, tsIndex, flatProfile) => {
4937
5049
  const usedTypes = /* @__PURE__ */ new Map();
4938
5050
  const getModulePath = (typeId) => {
4939
5051
  if (isNestedIdentifier(typeId)) {
@@ -4952,7 +5064,6 @@ var generateProfileImports = (w, tsIndex, flatProfile, overrides) => {
4952
5064
  addType(flatProfile.base);
4953
5065
  collectTypesFromSlices(tsIndex, flatProfile, addType);
4954
5066
  const needsExtensionType = collectTypesFromExtensions(tsIndex, flatProfile, addType);
4955
- for (const { typeId } of overrides.values()) addType(typeId);
4956
5067
  collectTypesFromFlatInput(tsIndex, flatProfile, addType);
4957
5068
  const factoryInfo = collectProfileFactoryInfo(tsIndex, flatProfile);
4958
5069
  for (const param of factoryInfo.params) addType(param.typeId);
@@ -5198,7 +5309,7 @@ var generateInlineExtensionInputTypes = (w, tsIndex, flatProfile) => {
5198
5309
  const typeName = tsExtensionFlatTypeName(tsProfileName, ext.name);
5199
5310
  w.curlyBlock(["export", "type", typeName, "="], () => {
5200
5311
  for (const sub of ext.subExtensions ?? []) {
5201
- const tsType = sub.valueType ? tsTypeFromIdentifier(sub.valueType) : "unknown";
5312
+ const tsType = sub.valueFieldType ? tsTypeFromIdentifier(sub.valueFieldType) : "unknown";
5202
5313
  const isArray = sub.max === "*";
5203
5314
  const isRequired2 = sub.min !== void 0 && sub.min > 0;
5204
5315
  w.lineSM(`${sub.name}${isRequired2 ? "" : "?"}: ${tsType}${isArray ? "[]" : ""}`);
@@ -5212,7 +5323,7 @@ var generateSliceInputTypes = (w, flatProfile, sliceDefs) => {
5212
5323
  const tsProfileName = tsResourceName(flatProfile.identifier);
5213
5324
  for (const sliceDef of sliceDefs) {
5214
5325
  const typeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
5215
- const matchFields = Object.keys(sliceDef.match);
5326
+ const matchFields = sliceDef.typeDiscriminator ? [] : Object.keys(sliceDef.match);
5216
5327
  const allExcluded = [.../* @__PURE__ */ new Set([...sliceDef.excluded, ...matchFields])];
5217
5328
  if (sliceDef.constrainedChoice) {
5218
5329
  const cc = sliceDef.constrainedChoice;
@@ -5223,12 +5334,13 @@ var generateSliceInputTypes = (w, flatProfile, sliceDefs) => {
5223
5334
  }
5224
5335
  const excludedNames = allExcluded.map((name) => JSON.stringify(name));
5225
5336
  const requiredNames = sliceDef.required.map((name) => JSON.stringify(name));
5226
- let typeExpr = sliceDef.baseType;
5337
+ const baseType = sliceDef.typedBaseType;
5338
+ let typeExpr = baseType;
5227
5339
  if (excludedNames.length > 0) {
5228
5340
  typeExpr = `Omit<${typeExpr}, ${excludedNames.join(" | ")}>`;
5229
5341
  }
5230
5342
  if (requiredNames.length > 0) {
5231
- typeExpr = `${typeExpr} & Required<Pick<${sliceDef.baseType}, ${requiredNames.join(" | ")}>>`;
5343
+ typeExpr = `${typeExpr} & Required<Pick<${baseType}, ${requiredNames.join(" | ")}>>`;
5232
5344
  }
5233
5345
  if (sliceDef.constrainedChoice) {
5234
5346
  typeExpr = `${typeExpr} & ${tsTypeFromIdentifier(sliceDef.constrainedChoice.variantType)}`;
@@ -5343,57 +5455,6 @@ var generateProfileClass = (w, tsIndex, flatProfile) => {
5343
5455
  });
5344
5456
  w.line();
5345
5457
  };
5346
- var detectFieldOverrides = (tsIndex, flatProfile) => {
5347
- const overrides = /* @__PURE__ */ new Map();
5348
- const specialization = tsIndex.findLastSpecialization(flatProfile);
5349
- if (!isSpecializationTypeSchema(specialization)) return overrides;
5350
- const referenceUrl = "http://hl7.org/fhir/StructureDefinition/Reference";
5351
- const referenceSchema = tsIndex.resolveByUrl(flatProfile.identifier.package, referenceUrl);
5352
- for (const [fieldName, pField] of Object.entries(flatProfile.fields ?? {})) {
5353
- if (!isNotChoiceDeclarationField(pField)) continue;
5354
- const sField = specialization.fields?.[fieldName];
5355
- if (!sField || isChoiceDeclarationField(sField)) continue;
5356
- if (pField.reference && sField.reference && pField.reference.length < sField.reference.length) {
5357
- if (!referenceSchema) continue;
5358
- const references = pField.reference.map((ref) => {
5359
- const resRef = tsIndex.findLastSpecializationByIdentifier(ref);
5360
- if (resRef.name !== ref.name) {
5361
- return `"${resRef.name}"`;
5362
- }
5363
- return `"${ref.name}"`;
5364
- }).join(" | ");
5365
- overrides.set(fieldName, {
5366
- profileType: `Reference<${references}>`,
5367
- required: pField.required ?? false,
5368
- array: pField.array ?? false,
5369
- typeId: referenceSchema.identifier
5370
- });
5371
- } else if (pField.required && !sField.required) {
5372
- const tsType = tsTypeForProfileField(tsIndex, flatProfile, fieldName, pField);
5373
- overrides.set(fieldName, {
5374
- profileType: tsType,
5375
- required: true,
5376
- array: pField.array ?? false,
5377
- typeId: pField.type
5378
- });
5379
- }
5380
- }
5381
- return overrides;
5382
- };
5383
- var generateProfileOverrideInterface = (w, flatProfile, overrides) => {
5384
- if (overrides.size === 0) return;
5385
- const tsProfileName = tsResourceName(flatProfile.identifier);
5386
- const tsBaseResourceName = tsResourceName(flatProfile.base);
5387
- w.curlyBlock(["export", "interface", tsProfileName, "extends", tsBaseResourceName], () => {
5388
- for (const [fieldName, override] of overrides) {
5389
- const tsField = tsFieldName(fieldName);
5390
- const optionalSymbol = override.required ? "" : "?";
5391
- const arraySymbol = override.array ? "[]" : "";
5392
- w.lineSM(`${tsField}${optionalSymbol}: ${override.profileType}${arraySymbol}`);
5393
- }
5394
- });
5395
- w.line();
5396
- };
5397
5458
 
5398
5459
  // src/api/writer-generator/typescript/writer.ts
5399
5460
  var resolveTsAssets = (fn) => {
@@ -5488,14 +5549,6 @@ var TypeScript = class extends Writer {
5488
5549
  name: tsResourceName(dep),
5489
5550
  dep
5490
5551
  });
5491
- } else if (isNestedIdentifier(dep)) {
5492
- const ndep = { ...dep };
5493
- ndep.name = tsNameFromCanonical(dep.url);
5494
- imports.push({
5495
- tsPackage: `${importPrefix}${tsModulePath(ndep)}`,
5496
- name: tsResourceName(dep),
5497
- dep
5498
- });
5499
5552
  } else {
5500
5553
  skipped.push(dep);
5501
5554
  }
@@ -5537,11 +5590,32 @@ var TypeScript = class extends Writer {
5537
5590
  const genericTypes = ["Reference", "Coding", "CodeableConcept"];
5538
5591
  if (genericTypes.includes(schema.identifier.name)) {
5539
5592
  name = `${schema.identifier.name}<T extends string = string>`;
5540
- } else if (schema.identifier.kind === "nested") {
5541
- name = tsResourceName(schema.identifier);
5542
5593
  } else {
5543
5594
  name = tsResourceName(schema.identifier);
5544
5595
  }
5596
+ const typeFamilyFields = [];
5597
+ for (const [fieldName, field] of Object.entries(schema.fields ?? {})) {
5598
+ if (isChoiceDeclarationField(field) || !field.type) continue;
5599
+ const fieldTypeSchema = tsIndex.resolveType(field.type);
5600
+ if (isSpecializationTypeSchema(fieldTypeSchema) && (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0) {
5601
+ typeFamilyFields.push({ fieldName: tsFieldName(fieldName), familyTypeName: field.type.name });
5602
+ }
5603
+ }
5604
+ const genericFieldMap = {};
5605
+ if (!genericTypes.includes(schema.identifier.name) && typeFamilyFields.length > 0) {
5606
+ const [first, ...rest] = typeFamilyFields;
5607
+ if (first && rest.length === 0) {
5608
+ genericFieldMap[first.fieldName] = "T";
5609
+ name += `<T extends ${first.familyTypeName} = ${first.familyTypeName}>`;
5610
+ } else {
5611
+ const params = typeFamilyFields.map((tf) => {
5612
+ const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`;
5613
+ genericFieldMap[tf.fieldName] = paramName;
5614
+ return `${paramName} extends ${tf.familyTypeName} = ${tf.familyTypeName}`;
5615
+ });
5616
+ name += `<${params.join(", ")}>`;
5617
+ }
5618
+ }
5545
5619
  let extendsClause;
5546
5620
  if (schema.base) extendsClause = `extends ${tsNameFromCanonical(schema.base.url)}`;
5547
5621
  this.debugComment(schema.identifier);
@@ -5551,8 +5625,7 @@ var TypeScript = class extends Writer {
5551
5625
  }
5552
5626
  this.curlyBlock(["export", "interface", name, extendsClause], () => {
5553
5627
  if (isResourceTypeSchema(schema)) {
5554
- const possibleResourceTypes = [schema.identifier];
5555
- possibleResourceTypes.push(...tsIndex.resourceChildren(schema.identifier));
5628
+ const possibleResourceTypes = [schema.identifier, ...schema.typeFamily?.resources ?? []];
5556
5629
  const openSetSuffix = this.opts.openResourceTypeSet && possibleResourceTypes.length > 1 ? " | string" : "";
5557
5630
  this.lineSM(
5558
5631
  `resourceType: ${possibleResourceTypes.sort((a, b) => a.name.localeCompare(b.name)).map((e) => `"${e.name}"`).join(" | ")}${openSetSuffix}`
@@ -5566,7 +5639,7 @@ var TypeScript = class extends Writer {
5566
5639
  if (!field.type) continue;
5567
5640
  this.debugComment(fieldName, ":", field);
5568
5641
  const tsName = tsFieldName(fieldName);
5569
- const tsType = resolveFieldTsType(schema.identifier.name, tsName, field);
5642
+ const tsType = resolveFieldTsType(schema.identifier.name, tsName, field, void 0, genericFieldMap);
5570
5643
  const optionalSymbol = field.required ? "" : "?";
5571
5644
  const arraySymbol = field.array ? "[]" : "";
5572
5645
  this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${arraySymbol}`);
@@ -5610,13 +5683,11 @@ var TypeScript = class extends Writer {
5610
5683
  this.cat(`${tsProfileModuleFileName(tsIndex, schema)}`, () => {
5611
5684
  this.generateDisclaimer();
5612
5685
  const flatProfile = tsIndex.flatProfile(schema);
5613
- const overrides = detectFieldOverrides(tsIndex, flatProfile);
5614
- generateProfileImports(this, tsIndex, flatProfile, overrides);
5615
- generateProfileOverrideInterface(this, flatProfile, overrides);
5686
+ generateProfileImports(this, tsIndex, flatProfile);
5616
5687
  generateProfileClass(this, tsIndex, flatProfile);
5617
5688
  });
5618
5689
  });
5619
- } else if (["complex-type", "resource", "logical"].includes(schema.identifier.kind)) {
5690
+ } else if (isSpecializationTypeSchema(schema)) {
5620
5691
  this.cat(`${tsModuleFileName(schema.identifier)}`, () => {
5621
5692
  this.generateDisclaimer();
5622
5693
  this.generateDependenciesImports(tsIndex, schema);
@@ -5707,8 +5778,6 @@ var APIBuilder = class {
5707
5778
  outputDir: "./generated",
5708
5779
  cleanOutput: true,
5709
5780
  throwException: false,
5710
- treeShake: void 0,
5711
- promoteLogical: void 0,
5712
5781
  registry: void 0,
5713
5782
  dropCanonicalManagerCache: false
5714
5783
  };
@@ -5885,13 +5954,18 @@ var APIBuilder = class {
5885
5954
  return this;
5886
5955
  }
5887
5956
  typeSchema(cfg) {
5957
+ this.options.typeSchema ??= {};
5888
5958
  if (cfg.treeShake) {
5889
- assert3(this.options.treeShake === void 0, "treeShake option is already set");
5890
- this.options.treeShake = cfg.treeShake;
5959
+ assert4(this.options.typeSchema.treeShake === void 0, "treeShake option is already set");
5960
+ this.options.typeSchema.treeShake = cfg.treeShake;
5891
5961
  }
5892
5962
  if (cfg.promoteLogical) {
5893
- assert3(this.options.promoteLogical === void 0, "promoteLogical option is already set");
5894
- this.options.promoteLogical = cfg.promoteLogical;
5963
+ assert4(this.options.typeSchema.promoteLogical === void 0, "promoteLogical option is already set");
5964
+ this.options.typeSchema.promoteLogical = cfg.promoteLogical;
5965
+ }
5966
+ if (cfg.resolveCollisions) {
5967
+ assert4(this.options.typeSchema.resolveCollisions === void 0, "resolveCollisions option is already set");
5968
+ this.options.typeSchema.resolveCollisions = cfg.resolveCollisions;
5895
5969
  }
5896
5970
  this.irReport({});
5897
5971
  return this;
@@ -5948,15 +6022,20 @@ var APIBuilder = class {
5948
6022
  });
5949
6023
  }
5950
6024
  const tsLogger = this.logger.fork("ts");
5951
- const { schemas: typeSchemas, collisions } = await generateTypeSchemas(register, tsLogger);
5952
- const tsIndexOpts = {
6025
+ const { schemas: typeSchemas, collisions } = await generateTypeSchemas(
5953
6026
  register,
5954
- logger: tsLogger,
5955
- irReport: Object.keys(collisions).length > 0 ? { collisions } : {}
6027
+ this.options.typeSchema?.resolveCollisions,
6028
+ tsLogger
6029
+ );
6030
+ const irReport = {
6031
+ resolveCollisions: this.options.typeSchema?.resolveCollisions,
6032
+ collisions
5956
6033
  };
6034
+ const tsIndexOpts = { register, irReport, logger: tsLogger };
5957
6035
  let tsIndex = mkTypeSchemaIndex(typeSchemas, tsIndexOpts);
5958
- if (this.options.treeShake) tsIndex = treeShake(tsIndex, this.options.treeShake);
5959
- if (this.options.promoteLogical) tsIndex = promoteLogical(tsIndex, this.options.promoteLogical);
6036
+ if (this.options.typeSchema?.treeShake) tsIndex = treeShake(tsIndex, this.options.typeSchema.treeShake);
6037
+ if (this.options.typeSchema?.promoteLogical)
6038
+ tsIndex = promoteLogical(tsIndex, this.options.typeSchema.promoteLogical);
5960
6039
  tsLogger.printTagSummary();
5961
6040
  this.logger.debug(`Executing ${this.generators.length} generators`);
5962
6041
  await this.executeGenerators(result, tsIndex);