@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/dist/index.js CHANGED
@@ -383,6 +383,9 @@ var isPrimitiveIdentifier = (id) => {
383
383
  var isNestedIdentifier = (id) => {
384
384
  return id?.kind === "nested";
385
385
  };
386
+ var isProfileIdentifier = (id) => {
387
+ return id?.kind === "profile";
388
+ };
386
389
  var isSnapshotProfileIdentifier = (id) => {
387
390
  return id?.kind === "profile-snapshot";
388
391
  };
@@ -426,6 +429,7 @@ var isValueSetTypeSchema = (schema) => {
426
429
  var isSnapshotProfileTypeSchema = (s) => {
427
430
  return s?.identifier.kind === "profile-snapshot";
428
431
  };
432
+ var isTypeDiscriminated = (slicing) => slicing?.discriminator?.some((d) => d.type === "type") ?? false;
429
433
  var extractExtensionDeps = (ext) => [
430
434
  ...ext.valueFieldTypes ?? [],
431
435
  ...ext.profile ? [ext.profile] : [],
@@ -892,6 +896,18 @@ var pyTypeFromIdentifier = (id) => {
892
896
  if (prim !== void 0) return prim;
893
897
  return deriveResourceName(id);
894
898
  };
899
+ var pyReferenceTypeParam = (field, tsIndex) => {
900
+ if (!field.reference || field.reference.resource.length === 0) return void 0;
901
+ const isFamilyType = (ref) => {
902
+ const schema = tsIndex.resolveType(ref);
903
+ if (!schema || !("typeFamily" in schema)) return false;
904
+ return (schema.typeFamily?.resources?.length ?? 0) > 0;
905
+ };
906
+ const resolved = field.reference.resource.map((ref) => tsIndex.findLastSpecializationByIdentifier(ref));
907
+ if (resolved.some(isFamilyType)) return void 0;
908
+ const names = [...new Set(resolved.map((ref) => ref.name))];
909
+ return `Literal[${names.map((n) => JSON.stringify(n)).join(", ")}]`;
910
+ };
895
911
  var groupByPackages = (typeSchemas) => {
896
912
  const grouped = {};
897
913
  for (const ts of typeSchemas) {
@@ -1083,6 +1099,54 @@ var populateGeneric = (schemas, resolveType) => {
1083
1099
  schema.dependencies = concatIdentifiers(schema.dependencies, constraints) ?? schema.dependencies;
1084
1100
  }
1085
1101
  };
1102
+ var choiceInstances = (fields, declName) => Object.entries(fields).filter(
1103
+ (e) => isChoiceInstanceField(e[1]) && e[1].choiceOf === declName
1104
+ );
1105
+ var choiceUniverse = (fields, baseFields, declName, declField) => {
1106
+ const baseDecl = baseFields[declName];
1107
+ const baseChoices = isChoiceDeclarationField(baseDecl) ? baseDecl.choices : [];
1108
+ return [
1109
+ .../* @__PURE__ */ new Set([...baseChoices, ...declField.choices, ...choiceInstances(fields, declName).map(([name]) => name)])
1110
+ ];
1111
+ };
1112
+ var declaredChoiceVariants = (fields, declName) => {
1113
+ const declaration = fields[declName];
1114
+ const fromDeclaration = isChoiceDeclarationField(declaration) && !declaration.excluded ? declaration.choices : [];
1115
+ const fromInstances = choiceInstances(fields, declName).filter(([, field]) => !field.excluded).map(([name]) => name);
1116
+ return [.../* @__PURE__ */ new Set([...fromDeclaration, ...fromInstances])];
1117
+ };
1118
+ var resolvePermittedChoiceVariants = (universe, baseDeclaration, constraintSchemas, declName, logger) => {
1119
+ let permitted = new Set(isChoiceDeclarationField(baseDeclaration) ? baseDeclaration.choices : universe);
1120
+ for (const schema of constraintSchemas.slice().reverse()) {
1121
+ const fields = schema.fields;
1122
+ if (!fields) continue;
1123
+ const reintroduced = declaredChoiceVariants(fields, declName).filter((name) => !permitted.has(name));
1124
+ if (reintroduced.length > 0)
1125
+ logger?.dryWarn(
1126
+ "#nonMonotonicChoice",
1127
+ `Profile '${schema.identifier.name}' declares choice variant(s) ${reintroduced.join(", ")} of '${declName}' that an ancestor prohibits; they stay prohibited`
1128
+ );
1129
+ permitted = applyChoiceConstraints(permitted, fields, declName);
1130
+ }
1131
+ return permitted;
1132
+ };
1133
+ var applyChoiceConstraints = (permitted, fields, declName) => {
1134
+ const result = new Set(permitted);
1135
+ const instances = choiceInstances(fields, declName);
1136
+ for (const [name, field] of instances) {
1137
+ if (field.excluded) result.delete(name);
1138
+ }
1139
+ const declaration = fields[declName];
1140
+ if (isChoiceDeclarationField(declaration)) {
1141
+ if (declaration.excluded) return /* @__PURE__ */ new Set();
1142
+ const declared2 = new Set(declaration.choices);
1143
+ return new Set([...result].filter((name) => declared2.has(name)));
1144
+ }
1145
+ const declared = instances.filter(([, field]) => !field.excluded).map(([name]) => name);
1146
+ if (declared.length === 0) return result;
1147
+ const declaredSet = new Set(declared);
1148
+ return new Set([...result].filter((name) => declaredSet.has(name)));
1149
+ };
1086
1150
  var mkTypeSchemaIndex = (schemas, {
1087
1151
  register,
1088
1152
  logger,
@@ -1196,28 +1260,29 @@ var mkTypeSchemaIndex = (schemas, {
1196
1260
  if (isNestedTypeSchema(resolved)) return findLastSpecializationByIdentifier(resolved.base);
1197
1261
  return findLastSpecialization(resolved).identifier;
1198
1262
  };
1199
- const narrowMergedChoiceDeclarations = (mergedFields, constraintSchemas) => {
1263
+ const narrowMergedChoiceDeclarations = (mergedFields, constraintSchemas, baseFields = {}) => {
1200
1264
  const result = { ...mergedFields };
1201
- for (const [declName, declField] of Object.entries(result)) {
1202
- if (!isChoiceDeclarationField(declField) || declField.excluded) continue;
1203
- for (const cSchema of constraintSchemas) {
1204
- const sFields = cSchema.fields;
1205
- if (!sFields) continue;
1206
- if (sFields[declName] && isChoiceDeclarationField(sFields[declName])) continue;
1207
- const instancesInSchema = Object.entries(sFields).filter(([_, f]) => isChoiceInstanceField(f) && f.choiceOf === declName).map(([name]) => name);
1208
- if (instancesInSchema.length === 0) continue;
1209
- const allowed = new Set(instancesInSchema);
1210
- result[declName] = { ...declField, choices: declField.choices.filter((c) => allowed.has(c)) };
1211
- break;
1212
- }
1213
- }
1214
- for (const [declName, declField] of Object.entries(result)) {
1265
+ const declNames = /* @__PURE__ */ new Set([...Object.keys(result), ...Object.keys(baseFields)]);
1266
+ for (const declName of declNames) {
1267
+ const declField = result[declName] ?? baseFields[declName];
1215
1268
  if (!isChoiceDeclarationField(declField)) continue;
1216
- const permitted = new Set(declField.excluded ? [] : declField.choices);
1217
- const prohibited = Object.entries(result).filter(
1218
- (e) => isChoiceInstanceField(e[1]) && e[1].choiceOf === declName
1219
- ).filter(([name]) => !permitted.has(name)).map(([name]) => name);
1220
- if (prohibited.length > 0) result[declName] = { ...declField, prohibited };
1269
+ const restated = result[declName] !== void 0;
1270
+ if (!restated && choiceInstances(result, declName).length === 0) continue;
1271
+ const universe = choiceUniverse(result, baseFields, declName, declField);
1272
+ const permitted = resolvePermittedChoiceVariants(
1273
+ universe,
1274
+ baseFields[declName],
1275
+ constraintSchemas,
1276
+ declName,
1277
+ logger
1278
+ );
1279
+ const prohibited = universe.filter((name) => !permitted.has(name));
1280
+ if (!restated && prohibited.length === 0) continue;
1281
+ result[declName] = {
1282
+ ...declField,
1283
+ choices: universe.filter((name) => permitted.has(name)),
1284
+ ...prohibited.length > 0 ? { prohibited } : {}
1285
+ };
1221
1286
  }
1222
1287
  return result;
1223
1288
  };
@@ -1228,8 +1293,10 @@ var mkTypeSchemaIndex = (schemas, {
1228
1293
  if (!nonConstraintSchema)
1229
1294
  throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
1230
1295
  const mergedFields = {};
1296
+ const mergedSlicing = {};
1231
1297
  for (const anySchema of constraintSchemas.slice().reverse()) {
1232
1298
  const schema2 = anySchema;
1299
+ if (schema2.slicing) Object.assign(mergedSlicing, schema2.slicing);
1233
1300
  if (!schema2.fields) continue;
1234
1301
  for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
1235
1302
  const merged = mergedFields[fieldName] ? { ...mergedFields[fieldName], ...fieldConstraints } : { ...fieldConstraints };
@@ -1239,7 +1306,11 @@ var mkTypeSchemaIndex = (schemas, {
1239
1306
  mergedFields[fieldName] = merged;
1240
1307
  }
1241
1308
  }
1242
- const narrowedFields = narrowMergedChoiceDeclarations(mergedFields, constraintSchemas);
1309
+ const narrowedFields = narrowMergedChoiceDeclarations(
1310
+ mergedFields,
1311
+ constraintSchemas,
1312
+ nonConstraintSchema.fields
1313
+ );
1243
1314
  const dependencies = Object.values(
1244
1315
  Object.fromEntries(
1245
1316
  constraintSchemas.flatMap((s) => s.dependencies ?? []).map((dep) => [dep.url, dep])
@@ -1256,6 +1327,7 @@ var mkTypeSchemaIndex = (schemas, {
1256
1327
  ...schema,
1257
1328
  base: nonConstraintSchema.identifier,
1258
1329
  fields: narrowedFields,
1330
+ slicing: Object.keys(mergedSlicing).length > 0 ? mergedSlicing : void 0,
1259
1331
  dependencies,
1260
1332
  extensions: mergedExtensions.length > 0 ? mergedExtensions : void 0
1261
1333
  };
@@ -1281,6 +1353,7 @@ var mkTypeSchemaIndex = (schemas, {
1281
1353
  base: flat.base,
1282
1354
  description: flat.description,
1283
1355
  fields: flatFields,
1356
+ slicing: flat.slicing,
1284
1357
  inheritedRequiredFields: inheritedRequiredFields.length > 0 ? inheritedRequiredFields : void 0,
1285
1358
  extensions: flat.extensions,
1286
1359
  dependencies: flat.dependencies,
@@ -1625,10 +1698,10 @@ var generateGenericExtensionSetter = (w, ext, className, baseName, targetPath, e
1625
1698
  };
1626
1699
 
1627
1700
  // src/api/writer-generator/python/profile-slices.ts
1628
- var collectRequiredSliceNames = (field) => {
1629
- if (!field.array || !field.slicing?.slices) return void 0;
1630
- if (field.slicing.discriminator?.some((d) => d.type === "type")) return void 0;
1631
- const names = Object.entries(field.slicing.slices).filter(([_, s]) => s.min !== void 0 && s.min >= 1 && s.match && Object.keys(s.match).length > 0).map(([name]) => name);
1701
+ var collectRequiredSliceNames = (field, fieldSlicing) => {
1702
+ if (!field.array || !fieldSlicing?.slices) return void 0;
1703
+ if (isTypeDiscriminated(fieldSlicing)) return void 0;
1704
+ const names = Object.entries(fieldSlicing.slices).filter(([_, s]) => s.min !== void 0 && s.min >= 1 && s.match && Object.keys(s.match).length > 0).map(([name]) => name);
1632
1705
  return names.length > 0 ? names : void 0;
1633
1706
  };
1634
1707
  var generateStaticSliceFields = (w, sliceDefs) => {
@@ -1660,8 +1733,8 @@ var normalizeMatchForPython = (tsIndex, match, schema) => {
1660
1733
  }
1661
1734
  return result;
1662
1735
  };
1663
- var extractTypeDiscriminatorResource = (isTypeDiscriminated, rawMatch) => {
1664
- if (!isTypeDiscriminated || !rawMatch) return void 0;
1736
+ var extractTypeDiscriminatorResource = (isTypeDiscriminated2, rawMatch) => {
1737
+ if (!isTypeDiscriminated2 || !rawMatch) return void 0;
1665
1738
  for (const val of Object.values(rawMatch)) {
1666
1739
  if (val !== null && typeof val === "object" && !Array.isArray(val)) {
1667
1740
  const rt = val.resourceType;
@@ -1672,8 +1745,9 @@ var extractTypeDiscriminatorResource = (isTypeDiscriminated, rawMatch) => {
1672
1745
  };
1673
1746
  var collectSliceDefs = (tsIndex, flatProfile) => {
1674
1747
  const pkgName = flatProfile.identifier.package;
1675
- return Object.entries(flatProfile.fields).flatMap(([fieldName, field]) => {
1676
- if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) return [];
1748
+ return Object.entries(flatProfile.slicing ?? {}).flatMap(([fieldName, fieldSlicing]) => {
1749
+ const field = flatProfile.fields[fieldName];
1750
+ if (!isNotChoiceDeclarationField(field) || !fieldSlicing.slices || !field.type) return [];
1677
1751
  const choiceBaseNames = /* @__PURE__ */ new Set();
1678
1752
  const baseSchema = tsIndex.resolveType(field.type);
1679
1753
  if (baseSchema && "fields" in baseSchema && baseSchema.fields) {
@@ -1681,16 +1755,16 @@ var collectSliceDefs = (tsIndex, flatProfile) => {
1681
1755
  if (isChoiceDeclarationField(f)) choiceBaseNames.add(n);
1682
1756
  }
1683
1757
  }
1684
- return Object.entries(field.slicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
1758
+ return Object.entries(fieldSlicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
1685
1759
  const matchFields = Object.keys(slice.match ?? {});
1686
1760
  const required = (slice.required ?? []).filter(
1687
1761
  (name) => !matchFields.includes(name) && !choiceBaseNames.has(name)
1688
1762
  );
1689
1763
  const cc = slice.elements ? tsIndex.constrainedChoice(pkgName, field.type, slice.elements) : void 0;
1690
1764
  const constrainedChoice = cc && !isPrimitiveIdentifier(cc.variantType) ? cc : void 0;
1691
- const isTypeDiscriminated = field.slicing?.discriminator?.some((d) => d.type === "type") ?? false;
1765
+ const typeDiscriminated = isTypeDiscriminated(fieldSlicing);
1692
1766
  const typeDiscriminatorResource = extractTypeDiscriminatorResource(
1693
- isTypeDiscriminated,
1767
+ typeDiscriminated,
1694
1768
  slice.match
1695
1769
  );
1696
1770
  return {
@@ -1703,7 +1777,7 @@ var collectSliceDefs = (tsIndex, flatProfile) => {
1703
1777
  constrainedChoice,
1704
1778
  elementTypeName: field.type && !isPrimitiveIdentifier(field.type) ? pyTypeFromIdentifier(field.type) : void 0,
1705
1779
  elementTypeId: field.type && !isPrimitiveIdentifier(field.type) ? field.type : void 0,
1706
- isTypeDiscriminated,
1780
+ isTypeDiscriminated: typeDiscriminated,
1707
1781
  typeDiscriminatorResource,
1708
1782
  nameCandidates: slice.nameCandidates
1709
1783
  };
@@ -1840,25 +1914,46 @@ var generateSliceSetters = (w, className, sliceDefs, sliceBaseNames) => {
1840
1914
  };
1841
1915
 
1842
1916
  // src/api/writer-generator/python/profile-factory.ts
1843
- var fieldPyType = (field, resolveRef) => {
1917
+ var fieldPyType = (field, resolveRef, tsIndex) => {
1844
1918
  const resolved = resolveRef ? resolveRef(field.type) : field.type;
1845
- const base = pyTypeFromIdentifier(resolved);
1919
+ let base = pyTypeFromIdentifier(resolved);
1846
1920
  if (base === "str" && field.enum && !field.enum.isOpen && field.enum.values.length > 0) {
1847
1921
  const literal = `Literal[${field.enum.values.map((v) => JSON.stringify(v)).join(", ")}]`;
1848
1922
  return field.array ? `list[${literal}]` : literal;
1849
1923
  }
1924
+ if (base === "Reference" && tsIndex) {
1925
+ const typeParam = pyReferenceTypeParam(field, tsIndex);
1926
+ if (typeParam) base = `Reference[${typeParam}]`;
1927
+ }
1850
1928
  return field.array ? `list[${base}]` : base;
1851
1929
  };
1852
- var tryPromoteChoice = (field, fields, params, promotedChoices) => {
1930
+ var pyReferenceComment = (field) => {
1931
+ const urls = (field.reference?.profiles ?? []).map((profile) => profile.url);
1932
+ return urls.length > 0 ? ` # ${urls.join(", ")}` : void 0;
1933
+ };
1934
+ var tryPromoteChoice = (field, fields, params, promotedChoices, tsIndex) => {
1853
1935
  if (!isChoiceDeclarationField(field) || !field.required || field.choices.length !== 1) return;
1854
1936
  const choiceName = field.choices[0];
1855
1937
  if (!choiceName) return;
1856
1938
  const choiceField = fields[choiceName];
1857
1939
  if (!choiceField || !isChoiceInstanceField(choiceField)) return;
1858
- const pyType = pyTypeFromIdentifier(choiceField.type) + (choiceField.array ? "[]" : "");
1859
- params.push({ name: choiceName, pyType, typeId: choiceField.type });
1940
+ const pyType = pyChoiceInstanceType(choiceField, tsIndex);
1941
+ params.push({
1942
+ name: choiceName,
1943
+ pyType,
1944
+ typeId: choiceField.type,
1945
+ refComment: pyReferenceComment(choiceField)
1946
+ });
1860
1947
  promotedChoices.add(choiceName);
1861
1948
  };
1949
+ var pyChoiceInstanceType = (field, tsIndex) => {
1950
+ let base = pyTypeFromIdentifier(field.type);
1951
+ if (base === "Reference") {
1952
+ const typeParam = pyReferenceTypeParam(field, tsIndex);
1953
+ if (typeParam) base = `Reference[${typeParam}]`;
1954
+ }
1955
+ return base + (field.array ? "[]" : "");
1956
+ };
1862
1957
  var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, coveredNames) => {
1863
1958
  const covered = new Set(coveredNames);
1864
1959
  const baseSchema = tsIndex.resolveType(flatProfile.base);
@@ -1869,8 +1964,8 @@ var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, cover
1869
1964
  if (isChoiceInstanceField(field)) continue;
1870
1965
  if (isChoiceDeclarationField(field)) continue;
1871
1966
  if (isNotChoiceDeclarationField(field) && field.type) {
1872
- const pyType = fieldPyType(field, resolveRef);
1873
- params.push({ name, pyType, typeId: field.type });
1967
+ const pyType = fieldPyType(field, resolveRef, tsIndex);
1968
+ params.push({ name, pyType, typeId: field.type, refComment: pyReferenceComment(field) });
1874
1969
  }
1875
1970
  }
1876
1971
  };
@@ -1899,32 +1994,32 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
1899
1994
  continue;
1900
1995
  }
1901
1996
  if (isChoiceDeclarationField(field)) {
1902
- tryPromoteChoice(field, fields, params, promotedChoices);
1997
+ tryPromoteChoice(field, fields, params, promotedChoices, tsIndex);
1903
1998
  continue;
1904
1999
  }
1905
2000
  if (field.valueConstraint) {
1906
2001
  const value = JSON.stringify(field.valueConstraint.value);
1907
2002
  autoFields.push({ name, value: field.array ? `[${value}]` : value });
1908
2003
  if (isNotChoiceDeclarationField(field) && field.type) {
1909
- const pyType = fieldPyType(field, resolveRef);
1910
- autoAccessors.push({ name, pyType, typeId: field.type });
2004
+ const pyType = fieldPyType(field, resolveRef, tsIndex);
2005
+ autoAccessors.push({ name, pyType, typeId: field.type, refComment: pyReferenceComment(field) });
1911
2006
  }
1912
2007
  continue;
1913
2008
  }
1914
2009
  if (isNotChoiceDeclarationField(field)) {
1915
- const sliceNames = collectRequiredSliceNames(field);
2010
+ const sliceNames = collectRequiredSliceNames(field, flatProfile.slicing?.[name]);
1916
2011
  if (sliceNames) {
1917
2012
  if (field.type) {
1918
- const pyType = fieldPyType(field, resolveRef);
2013
+ const pyType = fieldPyType(field, resolveRef, tsIndex);
1919
2014
  sliceAutoFields.push({ name, pyType, typeId: field.type, sliceNames });
1920
- autoAccessors.push({ name, pyType, typeId: field.type });
2015
+ autoAccessors.push({ name, pyType, typeId: field.type, refComment: pyReferenceComment(field) });
1921
2016
  }
1922
2017
  continue;
1923
2018
  }
1924
2019
  }
1925
2020
  if (field.required) {
1926
- const pyType = fieldPyType(field, resolveRef);
1927
- params.push({ name, pyType, typeId: field.type });
2021
+ const pyType = fieldPyType(field, resolveRef, tsIndex);
2022
+ params.push({ name, pyType, typeId: field.type, refComment: pyReferenceComment(field) });
1928
2023
  }
1929
2024
  }
1930
2025
  collectBaseRequiredParams(tsIndex, flatProfile, resolveRef, params, [
@@ -1936,9 +2031,15 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
1936
2031
  const choiceAccessors = [];
1937
2032
  for (const [name, field] of pendingChoiceInstances) {
1938
2033
  if (promotedChoices.has(name)) continue;
1939
- const pyType = pyTypeFromIdentifier(field.type) + (field.array ? "[]" : "");
2034
+ const pyType = pyChoiceInstanceType(field, tsIndex);
1940
2035
  const choiceSiblings = (choiceGroups.get(name) ?? []).filter((s) => s !== name && !promotedChoices.has(s));
1941
- choiceAccessors.push({ name, pyType, typeId: field.type, choiceSiblings });
2036
+ choiceAccessors.push({
2037
+ name,
2038
+ pyType,
2039
+ typeId: field.type,
2040
+ choiceSiblings,
2041
+ refComment: pyReferenceComment(field)
2042
+ });
1942
2043
  }
1943
2044
  return { autoFields, sliceAutoFields, params, accessors: [...autoAccessors, ...choiceAccessors] };
1944
2045
  };
@@ -2021,12 +2122,12 @@ var generateFieldAccessors = (w, className, factoryInfo, extSliceMethodBaseNames
2021
2122
  for (const p of factoryInfo.params) {
2022
2123
  const fieldName = pyFieldName(p.name, fmt);
2023
2124
  const methodSuffix = pySnakeName(p.name);
2024
- w.line(`def get_${methodSuffix}(self) -> ${p.pyType} | None:`);
2125
+ w.line(`def get_${methodSuffix}(self) -> ${p.pyType} | None:${p.refComment ?? ""}`);
2025
2126
  w.indentBlock(() => {
2026
2127
  w.line(`return cast('${p.pyType} | None', getattr(self._resource, ${JSON.stringify(fieldName)}, None))`);
2027
2128
  });
2028
2129
  w.line();
2029
- w.line(`def set_${methodSuffix}(self, value: ${p.pyType}) -> "${className}":`);
2130
+ w.line(`def set_${methodSuffix}(self, value: ${p.pyType}) -> "${className}":${p.refComment ?? ""}`);
2030
2131
  w.indentBlock(() => {
2031
2132
  w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, value)`);
2032
2133
  w.line("return self");
@@ -2037,12 +2138,12 @@ var generateFieldAccessors = (w, className, factoryInfo, extSliceMethodBaseNames
2037
2138
  const methodSuffix = pySnakeName(a.name);
2038
2139
  if (extSliceMethodBaseNames.has(methodSuffix)) continue;
2039
2140
  const fieldName = pyFieldName(a.name, fmt);
2040
- w.line(`def get_${methodSuffix}(self) -> ${a.pyType} | None:`);
2141
+ w.line(`def get_${methodSuffix}(self) -> ${a.pyType} | None:${a.refComment ?? ""}`);
2041
2142
  w.indentBlock(() => {
2042
2143
  w.line(`return cast('${a.pyType} | None', getattr(self._resource, ${JSON.stringify(fieldName)}, None))`);
2043
2144
  });
2044
2145
  w.line();
2045
- w.line(`def set_${methodSuffix}(self, value: ${a.pyType}) -> "${className}":`);
2146
+ w.line(`def set_${methodSuffix}(self, value: ${a.pyType}) -> "${className}":${a.refComment ?? ""}`);
2046
2147
  w.indentBlock(() => {
2047
2148
  if (a.choiceSiblings?.length) {
2048
2149
  for (const sibling of a.choiceSiblings) {
@@ -2057,94 +2158,125 @@ var generateFieldAccessors = (w, className, factoryInfo, extSliceMethodBaseNames
2057
2158
  };
2058
2159
 
2059
2160
  // src/api/writer-generator/python/profile-validation.ts
2060
- var collectValidateBody = (flatProfile, resolveRef, errorLines, warningLines, formatName2) => {
2161
+ var pushListValidation = (lines, target, fn, args, items) => {
2162
+ const argList = ["self._resource", "profile_name", ...args].join(", ");
2163
+ lines.push(
2164
+ `${target}.extend(`,
2165
+ ` ${fn}(${argList}, [`,
2166
+ ` ${items.map((i) => JSON.stringify(i)).join(",")}`,
2167
+ "]))"
2168
+ );
2169
+ };
2170
+ var collectValidateBody = (flatProfile, tsIndex, errorLines, warningLines, formatName2) => {
2061
2171
  const helpers = /* @__PURE__ */ new Set();
2062
2172
  const fields = flatProfile.fields;
2063
2173
  for (const [name, field] of Object.entries(fields)) {
2064
2174
  const pyName = pyFieldName(name, formatName2);
2065
- if (isChoiceInstanceField(field)) {
2066
- collectProhibitedChoiceValidation(fields, name, pyName, helpers, errorLines);
2067
- continue;
2068
- }
2175
+ if (isChoiceInstanceField(field)) continue;
2069
2176
  if (isChoiceDeclarationField(field)) {
2070
2177
  if (field.required) {
2071
2178
  helpers.add("validate_choice_required");
2072
2179
  const pyChoices = field.choices.map((c) => pyFieldName(c, formatName2));
2073
- errorLines.push(
2074
- `errors.extend(validate_choice_required(self._resource, profile_name, ${JSON.stringify(pyChoices)}))`
2075
- );
2180
+ pushListValidation(errorLines, "errors", "validate_choice_required", [], pyChoices);
2181
+ }
2182
+ if (field.prohibited?.length) {
2183
+ helpers.add("validate_choice_prohibited");
2184
+ const pyProhibited = field.prohibited.map((c) => pyFieldName(c, formatName2));
2185
+ pushListValidation(errorLines, "errors", "validate_choice_prohibited", [], pyProhibited);
2076
2186
  }
2077
2187
  continue;
2078
2188
  }
2079
- if (field.excluded) {
2080
- helpers.add("validate_excluded");
2081
- errorLines.push(
2082
- `errors.extend(validate_excluded(self._resource, profile_name, ${JSON.stringify(pyName)}))`
2083
- );
2084
- continue;
2085
- }
2086
- if (field.required) {
2087
- helpers.add("validate_required");
2088
- errorLines.push(
2089
- `errors.extend(validate_required(self._resource, profile_name, ${JSON.stringify(pyName)}))`
2189
+ collectRegularFieldValidation(
2190
+ field,
2191
+ flatProfile.slicing?.[name],
2192
+ pyName,
2193
+ helpers,
2194
+ errorLines,
2195
+ warningLines,
2196
+ tsIndex,
2197
+ formatName2
2198
+ );
2199
+ }
2200
+ for (const inheritedName of flatProfile.inheritedRequiredFields ?? []) {
2201
+ helpers.add("validate_required");
2202
+ errorLines.push(
2203
+ `errors.extend(validate_required(self._resource, profile_name, ${JSON.stringify(pyFieldName(inheritedName, formatName2))}))`
2204
+ );
2205
+ }
2206
+ return helpers;
2207
+ };
2208
+ var collectRegularFieldValidation = (field, fieldSlicing, pyName, helpers, errorLines, warningLines, tsIndex, formatName2) => {
2209
+ if (field.excluded) {
2210
+ helpers.add("validate_excluded");
2211
+ errorLines.push(`errors.extend(validate_excluded(self._resource, profile_name, ${JSON.stringify(pyName)}))`);
2212
+ return;
2213
+ }
2214
+ if (field.required) {
2215
+ helpers.add("validate_required");
2216
+ errorLines.push(`errors.extend(validate_required(self._resource, profile_name, ${JSON.stringify(pyName)}))`);
2217
+ }
2218
+ if (field.valueConstraint) {
2219
+ helpers.add("validate_fixed_value");
2220
+ const value = JSON.stringify(field.valueConstraint.value);
2221
+ errorLines.push(
2222
+ `errors.extend(validate_fixed_value(self._resource, profile_name, ${JSON.stringify(pyName)}, ${value}))`
2223
+ );
2224
+ }
2225
+ if (isNotChoiceDeclarationField(field)) {
2226
+ if (field.enum) {
2227
+ helpers.add("validate_enum");
2228
+ const target = field.enum.isOpen ? warningLines : errorLines;
2229
+ const listName = field.enum.isOpen ? "warnings" : "errors";
2230
+ pushListValidation(target, listName, "validate_enum", [JSON.stringify(pyName)], field.enum.values);
2231
+ }
2232
+ if (field.mustSupport && !field.required) {
2233
+ helpers.add("validate_must_support");
2234
+ warningLines.push(
2235
+ `warnings.extend(validate_must_support(self._resource, profile_name, ${JSON.stringify(pyName)}))`
2090
2236
  );
2091
2237
  }
2092
- if (field.valueConstraint) {
2093
- helpers.add("validate_fixed_value");
2094
- const value = JSON.stringify(field.valueConstraint.value);
2095
- errorLines.push(
2096
- `errors.extend(validate_fixed_value(self._resource, profile_name, ${JSON.stringify(pyName)}, ${value}))`
2097
- );
2238
+ if (field.reference && field.reference.resource.length > 0) {
2239
+ helpers.add("validate_reference");
2240
+ const allowed = field.reference.resource.map((ref) => tsIndex.findLastSpecializationByIdentifier(ref).name);
2241
+ pushListValidation(errorLines, "errors", "validate_reference", [JSON.stringify(pyName)], allowed);
2098
2242
  }
2099
- if (isNotChoiceDeclarationField(field)) {
2100
- if (field.enum) {
2101
- helpers.add("validate_enum");
2102
- const target = field.enum.isOpen ? warningLines : errorLines;
2103
- const listName = field.enum.isOpen ? "warnings" : "errors";
2104
- target.push(
2105
- `${listName}.extend(validate_enum(self._resource, profile_name, ${JSON.stringify(pyName)}, ${JSON.stringify(field.enum.values)}))`
2106
- );
2107
- }
2108
- if (field.mustSupport && !field.required) {
2109
- helpers.add("validate_must_support");
2110
- warningLines.push(
2111
- `warnings.extend(validate_must_support(self._resource, profile_name, ${JSON.stringify(pyName)}))`
2112
- );
2113
- }
2114
- if (field.reference && field.reference.length > 0) {
2115
- helpers.add("validate_reference");
2116
- const allowed = field.reference.map((ref) => resolveRef(ref).name);
2117
- errorLines.push(
2118
- `errors.extend(validate_reference(self._resource, profile_name, ${JSON.stringify(pyName)}, ${JSON.stringify(allowed)}))`
2119
- );
2120
- }
2121
- if (field.slicing?.slices) {
2122
- collectSliceCardinalityValidation(field, pyName, helpers, errorLines);
2123
- }
2243
+ if (fieldSlicing?.slices) {
2244
+ collectSliceValidation(field, fieldSlicing, pyName, helpers, errorLines, tsIndex, formatName2);
2124
2245
  }
2125
2246
  }
2126
- return helpers;
2127
2247
  };
2128
- var collectProhibitedChoiceValidation = (fields, name, pyName, helpers, errorLines) => {
2129
- const field = fields[name];
2130
- if (!field || !isChoiceInstanceField(field)) return;
2131
- const decl = fields[field.choiceOf];
2132
- if (!decl || !isChoiceDeclarationField(decl) || !decl.prohibited?.includes(name)) return;
2133
- helpers.add("validate_excluded");
2134
- errorLines.push(`errors.extend(validate_excluded(self._resource, profile_name, ${JSON.stringify(pyName)}))`);
2135
- };
2136
- var collectSliceCardinalityValidation = (field, name, helpers, errorLines) => {
2137
- if (!field.slicing?.slices) return;
2138
- for (const [sliceName, slice] of Object.entries(field.slicing.slices)) {
2139
- if (slice.min === void 0 && slice.max === void 0) continue;
2248
+ var collectSliceValidation = (field, fieldSlicing, name, helpers, errorLines, tsIndex, formatName2) => {
2249
+ if (!fieldSlicing.slices) return;
2250
+ for (const [sliceName, slice] of Object.entries(fieldSlicing.slices)) {
2140
2251
  const match = slice.match ?? {};
2141
2252
  if (Object.keys(match).length === 0) continue;
2142
- const min = slice.min ?? 0;
2143
- const max = slice.max ?? 0;
2144
- helpers.add("validate_slice_cardinality");
2145
- errorLines.push(
2146
- `errors.extend(validate_slice_cardinality(self._resource, profile_name, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${min}, ${max}))`
2147
- );
2253
+ if (slice.min !== void 0 || slice.max !== void 0) {
2254
+ const min = slice.min ?? 0;
2255
+ const max = slice.max ?? 0;
2256
+ helpers.add("validate_slice_cardinality");
2257
+ errorLines.push(
2258
+ `errors.extend(validate_slice_cardinality(self._resource, profile_name, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${min}, ${max}))`
2259
+ );
2260
+ }
2261
+ const sliceRequiredFields = [];
2262
+ const matchKeys = new Set(Object.keys(match));
2263
+ for (const rf of slice.required ?? []) {
2264
+ if (!matchKeys.has(rf)) sliceRequiredFields.push(pyFieldName(rf, formatName2));
2265
+ }
2266
+ if (field.type && slice.elements) {
2267
+ const cc = tsIndex.constrainedChoice(field.type.package, field.type, slice.elements);
2268
+ if (cc) sliceRequiredFields.push(pyFieldName(cc.variant, formatName2));
2269
+ }
2270
+ if (sliceRequiredFields.length > 0) {
2271
+ helpers.add("validate_slice_fields");
2272
+ pushListValidation(
2273
+ errorLines,
2274
+ "errors",
2275
+ "validate_slice_fields",
2276
+ [JSON.stringify(name), JSON.stringify(match), JSON.stringify(sliceName)],
2277
+ sliceRequiredFields
2278
+ );
2279
+ }
2148
2280
  }
2149
2281
  };
2150
2282
 
@@ -2387,13 +2519,7 @@ var generateProfileModule = (w, tsIndex, flatProfile) => {
2387
2519
  const resolvedNames = resolveProfileMethodBaseNames(extensions, sliceDefs);
2388
2520
  const errorLines = [];
2389
2521
  const warningLines = [];
2390
- const validationHelpers = collectValidateBody(
2391
- flatProfile,
2392
- tsIndex.findLastSpecializationByIdentifier,
2393
- errorLines,
2394
- warningLines,
2395
- w.nameFormatFunction
2396
- );
2522
+ const validationHelpers = collectValidateBody(flatProfile, tsIndex, errorLines, warningLines, w.nameFormatFunction);
2397
2523
  const helperImports = collectHelperImports(isResourceBase, factoryInfo, sliceDefs, extensions, validationHelpers);
2398
2524
  const typeImports = collectTypeImports(
2399
2525
  w.opts.rootPackageName,
@@ -2500,7 +2626,8 @@ var AVAILABLE_STRING_FORMATS = {
2500
2626
  var MAX_IMPORT_LINE_LENGTH = 100;
2501
2627
  var GENERIC_FIELD_REWRITES = {
2502
2628
  Coding: { code: "T" },
2503
- CodeableConcept: { coding: "Coding[T]" }
2629
+ CodeableConcept: { coding: "Coding[T]" },
2630
+ Reference: { type: "T" }
2504
2631
  };
2505
2632
  var leafOf2 = (path) => path[path.length - 1] ?? "";
2506
2633
  var collectResourceGenericTypeVars = (schema) => {
@@ -2897,6 +3024,11 @@ var Python = class extends Writer {
2897
3024
  fieldType = `Literal[${s}]`;
2898
3025
  }
2899
3026
  }
3027
+ if (fieldType === "Reference" && "reference" in field && field.reference) {
3028
+ assert4(this.tsIndex !== void 0);
3029
+ const typeParam = pyReferenceTypeParam(field, this.tsIndex);
3030
+ if (typeParam) fieldType = `Reference[${typeParam}]`;
3031
+ }
2900
3032
  if (field.array) {
2901
3033
  fieldType = `PyList[${fieldType}]`;
2902
3034
  }
@@ -3048,6 +3180,18 @@ var Python = class extends Writer {
3048
3180
  }
3049
3181
  };
3050
3182
 
3183
+ // src/typeschema/collision-order.ts
3184
+ var compareStrings = (left, right) => {
3185
+ if (left < right) return -1;
3186
+ if (left > right) return 1;
3187
+ return 0;
3188
+ };
3189
+ var compareCollisionSources = (left, right) => compareStrings(left.sourcePackage, right.sourcePackage) || compareStrings(left.sourceCanonical, right.sourceCanonical);
3190
+ var collisionSourcesKey = (sources) => JSON.stringify(
3191
+ [...sources].sort(compareCollisionSources).map(({ sourcePackage, sourceCanonical }) => [sourcePackage, sourceCanonical])
3192
+ );
3193
+ var compareCollisionVariants = (left, right) => right.sources.length - left.sources.length || compareStrings(collisionSourcesKey(left.sources), collisionSourcesKey(right.sources)) || compareStrings(left.schemaHash, right.schemaHash);
3194
+
3051
3195
  // src/typeschema/skip-hack.ts
3052
3196
  var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
3053
3197
  var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
@@ -3360,9 +3504,9 @@ var assignRecommendedBaseNames = (profile) => {
3360
3504
  key: `ext:${ext.url}:${ext.path}`,
3361
3505
  candidates: ext.nameCandidates.candidates
3362
3506
  }));
3363
- const sliceEntries = Object.entries(profile.fields ?? {}).flatMap(([fieldName, field]) => {
3364
- if (!("slicing" in field) || !field.slicing?.slices) return [];
3365
- return Object.entries(field.slicing.slices).map(([sliceName, slice]) => ({
3507
+ const sliceEntries = Object.entries(profile.slicing ?? {}).flatMap(([fieldName, fieldSlicing]) => {
3508
+ if (!fieldSlicing.slices) return [];
3509
+ return Object.entries(fieldSlicing.slices).map(([sliceName, slice]) => ({
3366
3510
  key: `slice:${fieldName}:${sliceName}`,
3367
3511
  candidates: slice.nameCandidates.candidates
3368
3512
  }));
@@ -3376,9 +3520,9 @@ var assignRecommendedBaseNames = (profile) => {
3376
3520
  const key = `ext:${ext.url}:${ext.path}`;
3377
3521
  if (resolved[key]) ext.nameCandidates.recommended = resolved[key];
3378
3522
  }
3379
- for (const [fieldName, field] of Object.entries(profile.fields ?? {})) {
3380
- if (!("slicing" in field) || !field.slicing?.slices) continue;
3381
- for (const [sliceName, slice] of Object.entries(field.slicing.slices)) {
3523
+ for (const [fieldName, fieldSlicing] of Object.entries(profile.slicing ?? {})) {
3524
+ if (!fieldSlicing.slices) continue;
3525
+ for (const [sliceName, slice] of Object.entries(fieldSlicing.slices)) {
3382
3526
  const key = `slice:${fieldName}:${sliceName}`;
3383
3527
  if (resolved[key]) slice.nameCandidates.recommended = resolved[key];
3384
3528
  }
@@ -3585,8 +3729,8 @@ var registerFromManager = async (manager, { logger, focusedPackages }) => {
3585
3729
  if (!sd.package_name) {
3586
3730
  return {
3587
3731
  ...sd,
3588
- package_name: pkgIndex.pkg.name,
3589
- package_version: pkgIndex.pkg.version
3732
+ package_name: r.pkg.name,
3733
+ package_version: r.pkg.version
3590
3734
  };
3591
3735
  }
3592
3736
  return sd;
@@ -3715,6 +3859,7 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
3715
3859
  }
3716
3860
  function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
3717
3861
  const fields = {};
3862
+ const slicing = {};
3718
3863
  const genealogy = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
3719
3864
  const elemGenealogy = resolveFsElementGenealogy(genealogy, parentPath);
3720
3865
  const allKeys = /* @__PURE__ */ new Set();
@@ -3733,8 +3878,10 @@ function transformNestedElements(register, fhirSchema, parentPath, elements, log
3733
3878
  } else {
3734
3879
  fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
3735
3880
  }
3881
+ const fieldSlicing = buildSlicing(key, elemSnapshot);
3882
+ if (fieldSlicing) slicing[key] = fieldSlicing;
3736
3883
  }
3737
- return fields;
3884
+ return { fields, slicing: Object.keys(slicing).length > 0 ? slicing : void 0 };
3738
3885
  }
3739
3886
  function mkNestedTypes(register, fhirSchema, logger) {
3740
3887
  if (!fhirSchema.elements) return void 0;
@@ -3764,11 +3911,12 @@ function mkNestedTypes(register, fhirSchema, logger) {
3764
3911
  name: baseName,
3765
3912
  url: baseUrl
3766
3913
  };
3767
- const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger);
3914
+ const { fields, slicing } = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger);
3768
3915
  const nestedType = {
3769
3916
  identifier,
3770
3917
  base,
3771
- fields
3918
+ fields,
3919
+ slicing
3772
3920
  };
3773
3921
  nestedTypes.push(nestedType);
3774
3922
  }
@@ -3851,12 +3999,27 @@ function isExcluded(register, fhirSchema, path) {
3851
3999
  }
3852
4000
  var buildReferences = (register, fhirSchema, element) => {
3853
4001
  if (!element.refers) return void 0;
3854
- return element.refers.map((ref) => {
4002
+ const resource = [];
4003
+ const profiles = [];
4004
+ const seen = /* @__PURE__ */ new Set();
4005
+ for (const ref of element.refers) {
3855
4006
  const curl = register.ensureSpecializationCanonicalUrl(ref);
3856
4007
  const fs6 = register.resolveFs(fhirSchema.package_meta, curl);
3857
4008
  if (!fs6) throw new Error(`Failed to resolve fs for ${curl}`);
3858
- return mkIdentifier(fs6);
3859
- });
4009
+ const id = mkIdentifier(fs6);
4010
+ let resolved = id;
4011
+ if (isProfileIdentifier(id)) {
4012
+ profiles.push(id);
4013
+ const baseFs = register.resolveFsSpecializations(fs6.package_meta, fs6.url)[0];
4014
+ if (!baseFs) throw new Error(`Failed to resolve base specialization for ${curl}`);
4015
+ resolved = mkIdentifier(baseFs);
4016
+ }
4017
+ if (!seen.has(resolved.url)) {
4018
+ seen.add(resolved.url);
4019
+ resource.push(resolved);
4020
+ }
4021
+ }
4022
+ return { resource, profiles: profiles.length > 0 ? profiles : void 0 };
3860
4023
  };
3861
4024
  var extractSliceFieldNames = (schema) => {
3862
4025
  const required = /* @__PURE__ */ new Set();
@@ -4082,7 +4245,6 @@ var mkField = (register, fhirSchema, path, element, logger, rawElement) => {
4082
4245
  array: element.array || false,
4083
4246
  min: element.min,
4084
4247
  max: element.max,
4085
- slicing: buildSlicing(path[path.length - 1] ?? "", element),
4086
4248
  choices: element.choices,
4087
4249
  choiceOf: element.choiceOf,
4088
4250
  binding,
@@ -4097,8 +4259,7 @@ function mkNestedField(register, fhirSchema, path, element) {
4097
4259
  type: nestedIdentifier,
4098
4260
  array: element.array || false,
4099
4261
  required: isRequired(register, fhirSchema, path),
4100
- excluded: isExcluded(register, fhirSchema, path),
4101
- slicing: buildSlicing(path[path.length - 1] ?? "", element)
4262
+ excluded: isExcluded(register, fhirSchema, path)
4102
4263
  };
4103
4264
  }
4104
4265
 
@@ -4237,8 +4398,9 @@ var extractProfileExtensions = (register, fhirSchema, logger) => {
4237
4398
 
4238
4399
  // src/typeschema/core/transformer.ts
4239
4400
  function mkFields(register, fhirSchema, parentPath, elements, logger) {
4240
- if (!elements) return void 0;
4401
+ if (!elements) return {};
4241
4402
  const fields = {};
4403
+ const slicing = {};
4242
4404
  for (const key of register.getAllElementKeys(elements)) {
4243
4405
  const path = [...parentPath, key];
4244
4406
  const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
@@ -4255,8 +4417,10 @@ function mkFields(register, fhirSchema, parentPath, elements, logger) {
4255
4417
  } else {
4256
4418
  fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger, elements[key]);
4257
4419
  }
4420
+ const fieldSlicing = buildSlicing(key, elemSnapshot);
4421
+ if (fieldSlicing) slicing[key] = fieldSlicing;
4258
4422
  }
4259
- return fields;
4423
+ return { fields, slicing: Object.keys(slicing).length > 0 ? slicing : void 0 };
4260
4424
  }
4261
4425
  function extractFieldDependencies(fields) {
4262
4426
  const deps = [];
@@ -4317,7 +4481,7 @@ function transformFhirSchema(register, fhirSchema, logger) {
4317
4481
  assert4(!isNestedIdentifier(baseId), `Unexpected nested base for ${fhirSchema.url}`);
4318
4482
  base = baseId;
4319
4483
  }
4320
- const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
4484
+ const { fields, slicing } = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
4321
4485
  const nested = mkNestedTypes(register, fhirSchema, logger);
4322
4486
  const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
4323
4487
  if (fhirSchema.derivation === "constraint") {
@@ -4330,6 +4494,7 @@ function transformFhirSchema(register, fhirSchema, logger) {
4330
4494
  identifier: identifier2,
4331
4495
  base,
4332
4496
  fields,
4497
+ slicing,
4333
4498
  nested,
4334
4499
  description: fhirSchema.description,
4335
4500
  dependencies: concatIdentifiers(rawDeps, extensionDeps),
@@ -4356,6 +4521,7 @@ function transformFhirSchema(register, fhirSchema, logger) {
4356
4521
  identifier,
4357
4522
  base,
4358
4523
  fields,
4524
+ slicing,
4359
4525
  nested,
4360
4526
  description: fhirSchema.description,
4361
4527
  dependencies: extractDependencies(identifier, base, fields, nested),
@@ -4377,7 +4543,11 @@ var deduplicateSchemas = (schemasWithSources, resolveCollisions, logger) => {
4377
4543
  const schemas = [];
4378
4544
  const collisions = {};
4379
4545
  for (const versions of Object.values(groups)) {
4380
- const sorted = Object.values(versions).sort((a, b) => b.sources.length - a.sources.length);
4546
+ const sorted = Object.entries(versions).map(([schemaHash, version]) => ({
4547
+ ...version,
4548
+ schemaHash,
4549
+ sources: [...version.sources].sort(compareCollisionSources)
4550
+ })).sort(compareCollisionVariants);
4381
4551
  const best = sorted[0];
4382
4552
  if (!best) continue;
4383
4553
  if (sorted.length > 1) {
@@ -4602,6 +4772,10 @@ var treeShakeTypeSchema = (schema, rule, _logger) => {
4602
4772
  if (isProfileTypeSchema(schema) && rule.ignoreExtensions) {
4603
4773
  mutableIgnoreExtensions(schema, rule.ignoreExtensions);
4604
4774
  }
4775
+ if (schema.slicing) {
4776
+ const kept = Object.fromEntries(Object.entries(schema.slicing).filter(([name]) => schema.fields?.[name]));
4777
+ schema.slicing = Object.keys(kept).length > 0 ? kept : void 0;
4778
+ }
4605
4779
  if (schema.nested) {
4606
4780
  const usedTypes = /* @__PURE__ */ new Set();
4607
4781
  const collectUsedNestedTypes = (s) => {
@@ -4630,16 +4804,36 @@ var treeShakeTypeSchema = (schema, rule, _logger) => {
4630
4804
  }
4631
4805
  return schema;
4632
4806
  };
4633
- var treeShake = (tsIndex, treeShake2) => {
4807
+ var collectReferenceTargets = (schema) => {
4808
+ if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) return [];
4809
+ const fieldSets = [schema.fields, ...(schema.nested ?? []).map((n) => n.fields)];
4810
+ return fieldSets.flatMap(
4811
+ (fields) => Object.values(fields ?? {}).filter(isNotChoiceDeclarationField).flatMap((f) => [...f.reference?.resource ?? [], ...f.reference?.profiles ?? []]).filter((id) => !isNestedIdentifier(id))
4812
+ );
4813
+ };
4814
+ var treeShake = (tsIndex, treeShake2, defaults) => {
4634
4815
  const focusedSchemas = [];
4816
+ const followedSchemas = [];
4635
4817
  for (const [pkgId, requires] of Object.entries(treeShake2)) {
4636
4818
  for (const [url, rule] of Object.entries(requires)) {
4637
4819
  const schema = tsIndex.resolveByUrl(pkgId, url);
4638
4820
  if (!schema || isNestedTypeSchema(schema)) throw new Error(`Schema not found for ${pkgId} ${url}`);
4639
4821
  const shaked2 = treeShakeTypeSchema(schema, rule);
4640
4822
  focusedSchemas.push(shaked2);
4823
+ if (rule.followReferences ?? defaults?.followReferences) {
4824
+ for (const refId of collectReferenceTargets(shaked2)) {
4825
+ const refSchema = tsIndex.resolve(refId);
4826
+ if (!refSchema)
4827
+ throw new Error(`Reference target ${JSON.stringify(refId)} not found for ${pkgId} ${url}`);
4828
+ followedSchemas.push(refSchema);
4829
+ }
4830
+ }
4641
4831
  }
4642
4832
  }
4833
+ const rootIds = new Set(focusedSchemas.map((s) => JSON.stringify(s.identifier)));
4834
+ for (const schema of followedSchemas) {
4835
+ if (!rootIds.has(JSON.stringify(schema.identifier))) focusedSchemas.push(schema);
4836
+ }
4643
4837
  const collectDeps = (schemas, acc) => {
4644
4838
  if (schemas.length === 0) return Object.values(acc);
4645
4839
  for (const schema of schemas) {
@@ -4678,7 +4872,8 @@ var normalizeFileName = (str) => {
4678
4872
  };
4679
4873
  var typeSchemaToJson = (ts, pretty) => {
4680
4874
  const pkgPath = normalizeFileName(ts.identifier.package);
4681
- const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`);
4875
+ const suffix = isSnapshotProfileTypeSchema(ts) ? ".snapshot" : "";
4876
+ const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`) + suffix;
4682
4877
  const baseName = Path5.join(pkgPath, name);
4683
4878
  return {
4684
4879
  filename: baseName,
@@ -4712,17 +4907,19 @@ var IntrospectionWriter = class extends FileSystemWriter {
4712
4907
  this.logger()?.info(`IntrospectionWriter: Type tree written to ${this.opts.typeTree}`);
4713
4908
  }
4714
4909
  if (this.opts.typeSchemas) {
4715
- if (Path5.extname(this.opts.typeSchemas) === ".ndjson") {
4716
- await this.writeNdjson(tsIndex.schemas, this.opts.typeSchemas, typeSchemaToJson);
4910
+ const tsOpts = typeof this.opts.typeSchemas === "string" ? { target: this.opts.typeSchemas } : this.opts.typeSchemas;
4911
+ const schemas = tsOpts.profileSnapshots ? [...tsIndex.schemas, ...tsIndex.collectSnapshotProfiles()] : tsIndex.schemas;
4912
+ if (Path5.extname(tsOpts.target) === ".ndjson") {
4913
+ await this.writeNdjson(schemas, tsOpts.target, typeSchemaToJson);
4717
4914
  } else {
4718
- const items = tsIndex.schemas.map((ts) => typeSchemaToJson(ts, true));
4915
+ const items = schemas.map((ts) => typeSchemaToJson(ts, true));
4719
4916
  const seenFilenames = /* @__PURE__ */ new Set();
4720
4917
  const dedupedItems = items.filter((item) => {
4721
4918
  if (seenFilenames.has(item.filename)) return false;
4722
4919
  seenFilenames.add(item.filename);
4723
4920
  return true;
4724
4921
  });
4725
- this.cd(this.opts.typeSchemas, () => {
4922
+ this.cd(tsOpts.target, () => {
4726
4923
  for (const { filename, genContent } of dedupedItems) {
4727
4924
  const fileName = `${filename}.json`;
4728
4925
  this.cd(Path5.dirname(fileName), () => {
@@ -4753,15 +4950,15 @@ var IntrospectionWriter = class extends FileSystemWriter {
4753
4950
  }
4754
4951
  });
4755
4952
  }
4756
- this.logger()?.info(
4757
- `IntrospectionWriter: ${tsIndex.schemas.length} TypeSchema written to ${this.opts.typeSchemas}`
4758
- );
4953
+ this.logger()?.info(`IntrospectionWriter: ${schemas.length} TypeSchema written to ${tsOpts.target}`);
4759
4954
  }
4955
+ const indexUrls = new Set(tsIndex.schemas.map((ts) => ts.identifier.url));
4760
4956
  if (this.opts.fhirSchemas && tsIndex.register) {
4761
4957
  const outputPath = this.opts.fhirSchemas;
4762
4958
  const allFs = tsIndex.register.allFs();
4763
4959
  const seenUrls = /* @__PURE__ */ new Set();
4764
4960
  const fhirSchemas = allFs.filter((fs6) => {
4961
+ if (!indexUrls.has(fs6.url)) return false;
4765
4962
  if (seenUrls.has(fs6.url)) return false;
4766
4963
  seenUrls.add(fs6.url);
4767
4964
  return true;
@@ -4781,6 +4978,7 @@ var IntrospectionWriter = class extends FileSystemWriter {
4781
4978
  const allSd = tsIndex.register.allSd();
4782
4979
  const seenUrls = /* @__PURE__ */ new Set();
4783
4980
  const structureDefinitions = allSd.filter((sd) => {
4981
+ if (!indexUrls.has(sd.url)) return false;
4784
4982
  if (seenUrls.has(sd.url)) return false;
4785
4983
  seenUrls.add(sd.url);
4786
4984
  return true;
@@ -4879,11 +5077,15 @@ var generatePackageSection = (lines, pkgName, treeShakePkg, promotedCanonicals)
4879
5077
  var groupCollisionVersions = (entries, resolution) => {
4880
5078
  const uniqueSchemas = /* @__PURE__ */ new Map();
4881
5079
  for (const entry of entries) {
4882
- const key = JSON.stringify(entry.typeSchema);
5080
+ const key = hashSchema(entry.typeSchema);
4883
5081
  if (!uniqueSchemas.has(key)) uniqueSchemas.set(key, []);
4884
5082
  uniqueSchemas.get(key)?.push(entry);
4885
5083
  }
4886
- const sorted = [...uniqueSchemas.values()].sort((a, b) => b.length - a.length);
5084
+ const sorted = [...uniqueSchemas.entries()].map(([schemaHash, group]) => ({
5085
+ entries: [...group].sort(compareCollisionSources),
5086
+ schemaHash,
5087
+ sources: group
5088
+ })).sort(compareCollisionVariants).map((group) => group.entries);
4887
5089
  const markVersion = (group, i) => {
4888
5090
  if (resolution)
4889
5091
  return group.some(
@@ -5783,9 +5985,21 @@ var resolveFieldTsType = (schemaName, tsName, field, resolveRef, genericFieldMap
5783
5985
  if (field.type.name === "CodeableConcept") return `CodeableConcept<${tsEnumType(field.enum)}>`;
5784
5986
  return tsEnumType(field.enum);
5785
5987
  }
5786
- if (field.reference && field.reference.length > 0) {
5787
- const resolved = field.reference.map((ref) => resolveRef ? resolveRef(ref) : ref);
5788
- const references = resolved.map((ref) => isFamilyType?.(ref) ? `string /* ${ref.name} */` : `"${ref.name}"`).join(" | ");
5988
+ if (field.reference && field.reference.resource.length > 0) {
5989
+ const profilesByResource = {};
5990
+ if (resolveRef) {
5991
+ for (const profile of field.reference.profiles ?? []) {
5992
+ const base = resolveRef(profile).name;
5993
+ (profilesByResource[base] ??= []).push(profile.url);
5994
+ }
5995
+ }
5996
+ const references = field.reference.resource.map((original) => {
5997
+ const ref = resolveRef ? resolveRef(original) : original;
5998
+ if (isFamilyType?.(ref)) return `string /* ${ref.name} */`;
5999
+ const profiles = profilesByResource[ref.name];
6000
+ if (profiles) return `"${ref.name}" /* ${profiles.join(", ")} */`;
6001
+ return `"${ref.name}"`;
6002
+ }).join(" | ");
5789
6003
  return `Reference<${references}>`;
5790
6004
  }
5791
6005
  if (isPrimitiveIdentifier(field.type)) return resolvePrimitiveType(field.type.name);
@@ -5834,9 +6048,10 @@ var valueFieldToTsType = (valueField) => {
5834
6048
  };
5835
6049
  var collectSubExtensionSlices = (extProfile) => {
5836
6050
  const extensionField = extProfile.fields.extension;
5837
- if (!extensionField || isChoiceDeclarationField(extensionField) || !extensionField.slicing?.slices) return [];
6051
+ const extensionSlicing = extProfile.slicing?.extension;
6052
+ if (!extensionField || isChoiceDeclarationField(extensionField) || !extensionSlicing?.slices) return [];
5838
6053
  const result = [];
5839
- for (const [sliceName, slice] of Object.entries(extensionField.slicing.slices)) {
6054
+ for (const [sliceName, slice] of Object.entries(extensionSlicing.slices)) {
5840
6055
  const valueField = extractValueField(slice.elements);
5841
6056
  if (!valueField) continue;
5842
6057
  const tsType = valueFieldToTsType(valueField);
@@ -6174,10 +6389,11 @@ var extractResourceTypeFromMatch = (match) => {
6174
6389
  };
6175
6390
  var collectTypesFromSlices = (tsIndex, snapshot, addType) => {
6176
6391
  const pkgName = snapshot.identifier.package;
6177
- for (const field of Object.values(snapshot.fields)) {
6178
- if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) continue;
6179
- const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
6180
- for (const slice of Object.values(field.slicing.slices)) {
6392
+ for (const [fieldName, fieldSlicing] of Object.entries(snapshot.slicing ?? {})) {
6393
+ const field = snapshot.fields[fieldName];
6394
+ if (!isNotChoiceDeclarationField(field) || !fieldSlicing.slices || !field.type) continue;
6395
+ const isTypeDisc = isTypeDiscriminated(fieldSlicing);
6396
+ for (const slice of Object.values(fieldSlicing.slices)) {
6181
6397
  if (Object.keys(slice.match ?? {}).length > 0) {
6182
6398
  addType(field.type);
6183
6399
  const cc = slice.elements ? tsIndex.constrainedChoice(pkgName, field.type, slice.elements) : void 0;
@@ -6195,11 +6411,10 @@ var collectTypesFromSlices = (tsIndex, snapshot, addType) => {
6195
6411
  }
6196
6412
  }
6197
6413
  };
6198
- var collectRequiredSliceNames2 = (field) => {
6199
- if (!field.array || !field.slicing?.slices) return void 0;
6200
- const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
6201
- if (isTypeDisc) return void 0;
6202
- const names = Object.entries(field.slicing.slices).filter(([_, s]) => {
6414
+ var collectRequiredSliceNames2 = (field, fieldSlicing) => {
6415
+ if (!field.array || !fieldSlicing?.slices) return void 0;
6416
+ if (isTypeDiscriminated(fieldSlicing)) return void 0;
6417
+ const names = Object.entries(fieldSlicing.slices).filter(([_, s]) => {
6203
6418
  if (s.min === void 0 || s.min < 1 || !s.match || Object.keys(s.match).length === 0) return false;
6204
6419
  const matchKeys = new Set(Object.keys(s.match));
6205
6420
  const requiredBeyondMatch = (s.required ?? []).filter((name) => !matchKeys.has(name));
@@ -6207,13 +6422,14 @@ var collectRequiredSliceNames2 = (field) => {
6207
6422
  }).map(([name]) => name);
6208
6423
  return names.length > 0 ? names : void 0;
6209
6424
  };
6210
- var collectSliceDefs2 = (tsIndex, snapshot) => Object.entries(snapshot.fields).filter(([_, field]) => isNotChoiceDeclarationField(field) && field.slicing?.slices).flatMap(([fieldName, field]) => {
6211
- if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) return [];
6425
+ var collectSliceDefs2 = (tsIndex, snapshot) => Object.entries(snapshot.slicing ?? {}).flatMap(([fieldName, fieldSlicing]) => {
6426
+ const field = snapshot.fields[fieldName];
6427
+ if (!isNotChoiceDeclarationField(field) || !fieldSlicing.slices || !field.type) return [];
6212
6428
  const baseType = tsTypeFromIdentifier(field.type);
6213
6429
  const pkgName = snapshot.identifier.package;
6214
6430
  const choiceBaseNames = collectChoiceBaseNames(tsIndex, field.type);
6215
- const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
6216
- return Object.entries(field.slicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
6431
+ const isTypeDisc = isTypeDiscriminated(fieldSlicing);
6432
+ return Object.entries(fieldSlicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
6217
6433
  const matchFields = Object.keys(slice.match ?? {});
6218
6434
  const required = (slice.required ?? []).filter(
6219
6435
  (name) => !matchFields.includes(name) && !choiceBaseNames.has(name)
@@ -6387,7 +6603,7 @@ var generateSliceGetters2 = (w, sliceDefs, snapshot) => {
6387
6603
  };
6388
6604
 
6389
6605
  // src/api/writer-generator/typescript/profile-validation.ts
6390
- var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef, canonicalUrlExpr, tsIndex) => {
6606
+ var collectRegularFieldValidation2 = (errors, warnings, name, field, resolveRef, canonicalUrlExpr, tsIndex, fieldSlicing) => {
6391
6607
  if (field.excluded) {
6392
6608
  errors.push(`...validateExcluded(res, profileName, ${JSON.stringify(name)})`);
6393
6609
  return;
@@ -6403,12 +6619,12 @@ var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef,
6403
6619
  }
6404
6620
  if (field.mustSupport && !field.required)
6405
6621
  warnings.push(`...validateMustSupport(res, profileName, ${JSON.stringify(name)})`);
6406
- if (field.reference && field.reference.length > 0)
6622
+ if (field.reference && field.reference.resource.length > 0)
6407
6623
  errors.push(
6408
- `...validateReference(res, profileName, ${JSON.stringify(name)}, ${JSON.stringify(field.reference.map((ref) => resolveRef(ref).name))})`
6624
+ `...validateReference(res, profileName, ${JSON.stringify(name)}, ${JSON.stringify(field.reference.resource.map((ref) => resolveRef(ref).name))})`
6409
6625
  );
6410
- if (field.slicing?.slices) {
6411
- for (const [sliceName, slice] of Object.entries(field.slicing.slices)) {
6626
+ if (fieldSlicing?.slices) {
6627
+ for (const [sliceName, slice] of Object.entries(fieldSlicing.slices)) {
6412
6628
  const match = slice.match ?? {};
6413
6629
  if (Object.keys(match).length === 0) continue;
6414
6630
  if (slice.min !== void 0 || slice.max !== void 0) {
@@ -6446,25 +6662,23 @@ var generateValidateMethod2 = (w, tsIndex, snapshot) => {
6446
6662
  const errors = [];
6447
6663
  const warnings = [];
6448
6664
  for (const [name, field] of Object.entries(fields)) {
6449
- if (isChoiceInstanceField(field)) {
6450
- const decl = fields[field.choiceOf];
6451
- if (decl && isChoiceDeclarationField(decl) && decl.prohibited?.includes(name))
6452
- errors.push(`...validateExcluded(res, profileName, ${JSON.stringify(name)})`);
6453
- continue;
6454
- }
6665
+ if (isChoiceInstanceField(field)) continue;
6455
6666
  if (isChoiceDeclarationField(field)) {
6456
6667
  if (field.required)
6457
6668
  errors.push(`...validateChoiceRequired(res, profileName, ${JSON.stringify(field.choices)})`);
6669
+ if (field.prohibited?.length)
6670
+ errors.push(`...validateChoiceProhibited(res, profileName, ${JSON.stringify(field.prohibited)})`);
6458
6671
  continue;
6459
6672
  }
6460
- collectRegularFieldValidation(
6673
+ collectRegularFieldValidation2(
6461
6674
  errors,
6462
6675
  warnings,
6463
6676
  name,
6464
6677
  field,
6465
6678
  tsIndex.findLastSpecializationByIdentifier,
6466
6679
  canonicalUrlExpr,
6467
- tsIndex
6680
+ tsIndex,
6681
+ snapshot.slicing?.[name]
6468
6682
  );
6469
6683
  }
6470
6684
  for (const inheritedName of snapshot.inheritedRequiredFields ?? []) {
@@ -6558,7 +6772,7 @@ var collectProfileFactoryInfo2 = (tsIndex, snapshot) => {
6558
6772
  continue;
6559
6773
  }
6560
6774
  if (isNotChoiceDeclarationField(field)) {
6561
- const sliceNames = collectRequiredSliceNames2(field);
6775
+ const sliceNames = collectRequiredSliceNames2(field, snapshot.slicing?.[name]);
6562
6776
  if (sliceNames) {
6563
6777
  if (field.type) {
6564
6778
  const tsType = fieldTsType(field, resolveRef, isFamilyType);
@@ -6658,6 +6872,7 @@ var generateProfileHelpersImport = (w, tsIndex, snapshot, sliceDefs, factoryInfo
6658
6872
  "validateEnum",
6659
6873
  "validateReference",
6660
6874
  "validateChoiceRequired",
6875
+ "validateChoiceProhibited",
6661
6876
  "validateMustSupport"
6662
6877
  );
6663
6878
  if (imports.length > 0) {
@@ -7680,6 +7895,10 @@ var APIBuilder = class {
7680
7895
  assert4(this.options.typeSchema.treeShake === void 0, "treeShake option is already set");
7681
7896
  this.options.typeSchema.treeShake = cfg.treeShake;
7682
7897
  }
7898
+ if (cfg.treeShakeDefaults) {
7899
+ assert4(this.options.typeSchema.treeShakeDefaults === void 0, "treeShakeDefaults option is already set");
7900
+ this.options.typeSchema.treeShakeDefaults = cfg.treeShakeDefaults;
7901
+ }
7683
7902
  if (cfg.promoteLogical) {
7684
7903
  assert4(this.options.typeSchema.promoteLogical === void 0, "promoteLogical option is already set");
7685
7904
  this.options.typeSchema.promoteLogical = cfg.promoteLogical;
@@ -7754,7 +7973,12 @@ var APIBuilder = class {
7754
7973
  };
7755
7974
  const tsIndexOpts = { register, irReport, logger: tsLogger };
7756
7975
  let tsIndex = mkTypeSchemaIndex(typeSchemas, tsIndexOpts);
7757
- if (this.options.typeSchema?.treeShake) tsIndex = treeShake(tsIndex, this.options.typeSchema.treeShake);
7976
+ if (this.options.typeSchema?.treeShake)
7977
+ tsIndex = treeShake(
7978
+ tsIndex,
7979
+ this.options.typeSchema.treeShake,
7980
+ this.options.typeSchema.treeShakeDefaults
7981
+ );
7758
7982
  if (this.options.typeSchema?.promoteLogical)
7759
7983
  tsIndex = promoteLogical(tsIndex, this.options.typeSchema.promoteLogical);
7760
7984
  tsLogger.printTagSummary();