@atomic-ehr/codegen 0.0.10 → 0.0.11-canary.20260408122114.4af415a

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
@@ -1482,11 +1482,31 @@ var Python = class extends Writer {
1482
1482
  }
1483
1483
  generateFields(schema, schemaName) {
1484
1484
  const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
1485
+ const withExtensions = this.shouldAddPrimitiveExtensions(schema);
1485
1486
  for (const [fieldName, field] of sortedFields) {
1486
1487
  if ("choices" in field && field.choices) continue;
1487
1488
  const fieldInfo = this.buildFieldInfo(fieldName, field, schemaName);
1488
1489
  this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
1490
+ if (withExtensions && "type" in field && isPrimitiveIdentifier(field.type)) {
1491
+ this.addPrimitiveExtensionField(fieldName, field.array ?? false);
1492
+ }
1493
+ }
1494
+ }
1495
+ shouldAddPrimitiveExtensions(schema) {
1496
+ if (!this.opts.primitiveTypeExtension) return false;
1497
+ if (!isSpecializationTypeSchema(schema)) return false;
1498
+ for (const field of Object.values(schema.fields ?? {})) {
1499
+ if ("choices" in field && field.choices) continue;
1500
+ if ("type" in field && isPrimitiveIdentifier(field.type)) return true;
1489
1501
  }
1502
+ return false;
1503
+ }
1504
+ addPrimitiveExtensionField(fieldName, isArray) {
1505
+ const pyFieldName = this.nameFormatFunction(`${fieldName}Extension`);
1506
+ const alias = `_${fieldName}`;
1507
+ const typeExpr = isArray ? "PyList[Element | None] | None" : "Element | None";
1508
+ const aliasSpec = `alias="${alias}", serialization_alias="${alias}"`;
1509
+ this.line(`${pyFieldName}: ${typeExpr} = Field(None, ${aliasSpec})`);
1490
1510
  }
1491
1511
  buildFieldInfo(fieldName, field, schemaName) {
1492
1512
  const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
@@ -1571,6 +1591,18 @@ var Python = class extends Writer {
1571
1591
  if (!schema.dependencies || schema.dependencies.length === 0) return;
1572
1592
  this.importComplexTypeDependencies(schema.dependencies);
1573
1593
  this.importResourceDependencies(schema.dependencies);
1594
+ this.importElementIfNeeded(schema);
1595
+ }
1596
+ importElementIfNeeded(schema) {
1597
+ if (!this.shouldAddPrimitiveExtensions(schema)) return;
1598
+ if (schema.identifier.name === "Element") return;
1599
+ if (schema.dependencies?.find((d) => d.name === "Element")) return;
1600
+ assert4(this.tsIndex !== void 0);
1601
+ const elementUrl = "http://hl7.org/fhir/StructureDefinition/Element";
1602
+ const element = this.tsIndex.resolveByUrl(schema.identifier.package, elementUrl);
1603
+ if (!element) return;
1604
+ const pyPackage = this.pyPackage(element.identifier);
1605
+ this.pyImportFrom(pyPackage, "Element");
1574
1606
  }
1575
1607
  importComplexTypeDependencies(dependencies) {
1576
1608
  const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
@@ -2450,7 +2482,7 @@ var navigateMatch = (match, remainingPath) => {
2450
2482
  }
2451
2483
  return value;
2452
2484
  };
2453
- var collectDiscriminatorValue = (schema, segments, index, result) => {
2485
+ var collectDiscriminatorValue = (schema, segments, index, result, arrayPaths) => {
2454
2486
  if (index >= segments.length || !schema.elements) return;
2455
2487
  const segment = segments[index];
2456
2488
  const element = schema.elements[segment];
@@ -2460,6 +2492,7 @@ var collectDiscriminatorValue = (schema, segments, index, result) => {
2460
2492
  return;
2461
2493
  }
2462
2494
  if (element.slicing?.slices) {
2495
+ arrayPaths.add(segments.slice(0, index + 1).join("."));
2463
2496
  const remainingSegments = segments.slice(index + 1);
2464
2497
  for (const subSlice of Object.values(element.slicing.slices)) {
2465
2498
  if (!subSlice.min || subSlice.min < 1 || !subSlice.match || typeof subSlice.match !== "object") continue;
@@ -2474,7 +2507,7 @@ var collectDiscriminatorValue = (schema, segments, index, result) => {
2474
2507
  }
2475
2508
  return;
2476
2509
  }
2477
- collectDiscriminatorValue(element, segments, index + 1, result);
2510
+ collectDiscriminatorValue(element, segments, index + 1, result, arrayPaths);
2478
2511
  };
2479
2512
  var computeTypeDiscriminatorMatch = (path, schema, result) => {
2480
2513
  if (path === "$this") return;
@@ -2491,16 +2524,31 @@ var computeTypeDiscriminatorMatch = (path, schema, result) => {
2491
2524
  var computeMatchFromSchema = (discriminators, schema) => {
2492
2525
  if (!schema || !discriminators || discriminators.length === 0) return void 0;
2493
2526
  const result = {};
2527
+ const arrayPaths = /* @__PURE__ */ new Set();
2494
2528
  for (const disc of discriminators) {
2495
2529
  if (disc.type === "type") {
2496
2530
  computeTypeDiscriminatorMatch(disc.path, schema, result);
2497
2531
  } else {
2498
2532
  if (!schema.elements) continue;
2499
2533
  const segments = disc.path.split(".");
2500
- collectDiscriminatorValue(schema, segments, 0, result);
2534
+ collectDiscriminatorValue(schema, segments, 0, result, arrayPaths);
2501
2535
  }
2502
2536
  }
2503
- return Object.keys(result).length > 0 ? result : void 0;
2537
+ if (Object.keys(result).length === 0) return void 0;
2538
+ for (const path of arrayPaths) {
2539
+ const segments = path.split(".");
2540
+ let target = result;
2541
+ for (let i = 0; i < segments.length - 1; i++) {
2542
+ const v = target[segments[i]];
2543
+ if (!v || typeof v !== "object" || Array.isArray(v)) break;
2544
+ target = v;
2545
+ }
2546
+ const key = segments[segments.length - 1];
2547
+ if (target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
2548
+ target[key] = [target[key]];
2549
+ }
2550
+ }
2551
+ return result;
2504
2552
  };
2505
2553
  var buildSlicing = (element) => {
2506
2554
  const slicing = element.slicing;
@@ -4221,6 +4269,9 @@ var tsProfileClassName = (schema) => {
4221
4269
  var tsSliceFlatTypeName = (profileName, fieldName, sliceName) => {
4222
4270
  return `${uppercaseFirstLetter(profileName)}_${uppercaseFirstLetter(normalizeTsName(fieldName))}_${uppercaseFirstLetter(normalizeTsName(sliceName))}SliceFlat`;
4223
4271
  };
4272
+ var tsSliceFlatAllTypeName = (profileName, fieldName, sliceName) => {
4273
+ return `${uppercaseFirstLetter(profileName)}_${uppercaseFirstLetter(normalizeTsName(fieldName))}_${uppercaseFirstLetter(normalizeTsName(sliceName))}SliceFlatAll`;
4274
+ };
4224
4275
  var tsExtensionFlatTypeName = (profileName, extensionName) => {
4225
4276
  return `${uppercaseFirstLetter(profileName)}_${uppercaseFirstLetter(normalizeTsName(extensionName))}Flat`;
4226
4277
  };
@@ -4284,7 +4335,7 @@ var rewriteFieldTypeDefs = {
4284
4335
  Reference: { reference: () => "`${T}/${string}`" },
4285
4336
  CodeableConcept: { coding: () => "Coding<T>" }
4286
4337
  };
4287
- var resolveFieldTsType = (schemaName, tsName, field, resolveRef, genericFieldMap) => {
4338
+ var resolveFieldTsType = (schemaName, tsName, field, resolveRef, genericFieldMap, isFamilyType) => {
4288
4339
  if (genericFieldMap?.[tsName]) return genericFieldMap[tsName];
4289
4340
  const rewriteFieldType = rewriteFieldTypeDefs[schemaName]?.[tsName];
4290
4341
  if (rewriteFieldType) return rewriteFieldType();
@@ -4294,14 +4345,15 @@ var resolveFieldTsType = (schemaName, tsName, field, resolveRef, genericFieldMap
4294
4345
  return tsEnumType(field.enum);
4295
4346
  }
4296
4347
  if (field.reference && field.reference.length > 0) {
4297
- const references = field.reference.map((ref) => resolveRef ? resolveRef(ref) : ref).map((ref) => `"${ref.name}"`).join(" | ");
4348
+ const resolved = field.reference.map((ref) => resolveRef ? resolveRef(ref) : ref);
4349
+ const references = resolved.map((ref) => isFamilyType?.(ref) ? `string /* ${ref.name} */` : `"${ref.name}"`).join(" | ");
4298
4350
  return `Reference<${references}>`;
4299
4351
  }
4300
4352
  if (isPrimitiveIdentifier(field.type)) return resolvePrimitiveType(field.type.name);
4301
4353
  if (isNestedIdentifier(field.type)) return tsResourceName(field.type);
4302
4354
  return field.type.name;
4303
4355
  };
4304
- var fieldTsType = (field, resolveRef) => resolveFieldTsType("", "", field, resolveRef) + (field.array ? "[]" : "");
4356
+ var fieldTsType = (field, resolveRef, isFamilyType) => resolveFieldTsType("", "", field, resolveRef, void 0, isFamilyType) + (field.array ? "[]" : "");
4305
4357
  var tsTypeFromIdentifier = (id) => {
4306
4358
  if (isNestedIdentifier(id)) return tsResourceName(id);
4307
4359
  if (isPrimitiveIdentifier(id)) return resolvePrimitiveType(id.name);
@@ -4370,21 +4422,22 @@ var resolveExtensionProfile = (tsIndex, pkgName, url) => {
4370
4422
  const flatProfile = tsIndex.flatProfile(schema);
4371
4423
  return { className, modulePath, flatProfile };
4372
4424
  };
4373
- var generateRawExtensionBody = (w, ext, targetPath, paramName = "input") => {
4425
+ var generateRawExtensionBody = (w, ext, targetPath, paramName = "input", useUpsert = false) => {
4374
4426
  w.line(
4375
4427
  `if (${paramName}.url !== ${JSON.stringify(ext.url)}) throw new Error(\`Expected extension url '${ext.url}', got '\${${paramName}.url}'\`)`
4376
4428
  );
4377
- generateExtensionPush(w, targetPath, paramName);
4429
+ generateExtensionPush(w, targetPath, paramName, useUpsert);
4378
4430
  };
4379
- var generateExtensionPush = (w, targetPath, extExpr) => {
4431
+ var generateExtensionPush = (w, targetPath, extExpr, useUpsert = false) => {
4432
+ const fn = useUpsert ? "upsertExtension" : "pushExtension";
4380
4433
  if (targetPath.length === 0) {
4381
- w.line(`pushExtension(this.resource, ${extExpr})`);
4434
+ w.line(`${fn}(this.resource, ${extExpr})`);
4382
4435
  } else {
4383
4436
  w.line(
4384
4437
  `const target = ensurePath(this.resource as unknown as Record<string, unknown>, ${JSON.stringify(targetPath)})`
4385
4438
  );
4386
4439
  w.line("if (!Array.isArray(target.extension)) target.extension = [] as Extension[]");
4387
- w.line(`pushExtension(target as unknown as { extension?: Extension[] }, ${extExpr})`);
4440
+ w.line(`${fn}(target as unknown as { extension?: Extension[] }, ${extExpr})`);
4388
4441
  }
4389
4442
  };
4390
4443
  var generateExtLookup = (w, ext, targetPath) => {
@@ -4437,6 +4490,7 @@ var generateComplexExtensionSetter = (w, info) => {
4437
4490
  const tsProfileName = tsResourceName(flatProfile.identifier);
4438
4491
  const inputTypeName = tsExtensionFlatTypeName(tsProfileName, ext.name);
4439
4492
  const extProfileHasFlatInput = extProfileInfo ? collectSubExtensionSlices(extProfileInfo.flatProfile).length > 0 : false;
4493
+ const useUpsert = ext.max === "1";
4440
4494
  if (extProfileInfo && extProfileHasFlatInput) {
4441
4495
  const paramType = `${extProfileInfo.className}Flat | ${extProfileInfo.className} | Extension`;
4442
4496
  w.curlyBlock(["public", setMethodName, `(input: ${paramType}): this`], () => {
@@ -4444,14 +4498,19 @@ var generateComplexExtensionSetter = (w, info) => {
4444
4498
  [
4445
4499
  {
4446
4500
  cond: `input instanceof ${extProfileInfo.className}`,
4447
- body: () => generateExtensionPush(w, targetPath, "input.toResource()")
4501
+ body: () => generateExtensionPush(w, targetPath, "input.toResource()", useUpsert)
4448
4502
  },
4449
4503
  {
4450
4504
  cond: "isExtension<Extension>(input)",
4451
- body: () => generateRawExtensionBody(w, ext, targetPath)
4505
+ body: () => generateRawExtensionBody(w, ext, targetPath, "input", useUpsert)
4452
4506
  }
4453
4507
  ],
4454
- () => generateExtensionPush(w, targetPath, `${extProfileInfo.className}.createResource(input)`)
4508
+ () => generateExtensionPush(
4509
+ w,
4510
+ targetPath,
4511
+ `${extProfileInfo.className}.createResource(input)`,
4512
+ useUpsert
4513
+ )
4455
4514
  );
4456
4515
  w.line("return this");
4457
4516
  });
@@ -4474,15 +4533,16 @@ var generateComplexExtensionSetter = (w, info) => {
4474
4533
  });
4475
4534
  }
4476
4535
  }
4536
+ const extLiteral = `{ url: "${ext.url}", extension: subExtensions }`;
4537
+ const fn = useUpsert ? "upsertExtension" : "pushExtension";
4477
4538
  if (targetPath.length === 0) {
4478
- w.line("const list = (this.resource.extension ??= [])");
4479
- w.line(`list.push({ url: "${ext.url}", extension: subExtensions })`);
4539
+ w.line(`${fn}(this.resource, ${extLiteral})`);
4480
4540
  } else {
4481
4541
  w.line(
4482
4542
  `const target = ensurePath(this.resource as unknown as Record<string, unknown>, ${JSON.stringify(targetPath)})`
4483
4543
  );
4484
4544
  w.line("if (!Array.isArray(target.extension)) target.extension = [] as Extension[]");
4485
- w.line(`(target.extension as Extension[]).push({ url: "${ext.url}", extension: subExtensions })`);
4545
+ w.line(`${fn}(target as unknown as { extension?: Extension[] }, ${extLiteral})`);
4486
4546
  }
4487
4547
  w.line("return this");
4488
4548
  });
@@ -4510,32 +4570,33 @@ var generateSingleValueExtensionSetter = (w, tsIndex, info) => {
4510
4570
  if (!firstValueType) return;
4511
4571
  const valueType = tsTypeFromIdentifier(firstValueType);
4512
4572
  const valueField = tsValueFieldName(firstValueType);
4573
+ const useUpsert = ext.max === "1";
4513
4574
  if (extProfileInfo) {
4514
- const paramType = `${extProfileInfo.className} | Extension | ${valueType}`;
4515
- const extHasValueParam = collectProfileFactoryInfo(tsIndex, extProfileInfo.flatProfile).params.some(
4516
- (p) => p.name === valueField
4517
- );
4518
- const elseExpr = extHasValueParam ? `${extProfileInfo.className}.createResource({ ${valueField}: value as ${valueType} })` : `{ url: "${ext.url}", ${valueField}: value as ${valueType} } as Extension`;
4575
+ const extFactoryInfo = collectProfileFactoryInfo(tsIndex, extProfileInfo.flatProfile);
4576
+ const extValueParam = extFactoryInfo.params.find((p) => p.name === valueField);
4577
+ const resolvedValueType = extValueParam?.tsType ?? valueType;
4578
+ const paramType = `${extProfileInfo.className} | Extension | ${resolvedValueType}`;
4579
+ const elseExpr = extValueParam ? `${extProfileInfo.className}.createResource({ ${valueField}: value as ${resolvedValueType} })` : `{ url: "${ext.url}", ${valueField}: value as ${valueType} } as Extension`;
4519
4580
  w.curlyBlock(["public", setMethodName, `(value: ${paramType}): this`], () => {
4520
4581
  w.ifElseChain(
4521
4582
  [
4522
4583
  {
4523
4584
  cond: `value instanceof ${extProfileInfo.className}`,
4524
- body: () => generateExtensionPush(w, targetPath, "value.toResource()")
4585
+ body: () => generateExtensionPush(w, targetPath, "value.toResource()", useUpsert)
4525
4586
  },
4526
4587
  {
4527
4588
  cond: "isExtension(value)",
4528
- body: () => generateRawExtensionBody(w, ext, targetPath, "value")
4589
+ body: () => generateRawExtensionBody(w, ext, targetPath, "value", useUpsert)
4529
4590
  }
4530
4591
  ],
4531
- () => generateExtensionPush(w, targetPath, elseExpr)
4592
+ () => generateExtensionPush(w, targetPath, elseExpr, useUpsert)
4532
4593
  );
4533
4594
  w.line("return this");
4534
4595
  });
4535
4596
  } else {
4536
4597
  w.curlyBlock(["public", setMethodName, `(value: ${valueType}): this`], () => {
4537
4598
  const extLiteral = `{ url: "${ext.url}", ${valueField}: value } as Extension`;
4538
- generateExtensionPush(w, targetPath, extLiteral);
4599
+ generateExtensionPush(w, targetPath, extLiteral, useUpsert);
4539
4600
  w.line("return this");
4540
4601
  });
4541
4602
  }
@@ -4552,15 +4613,16 @@ var generateSingleValueExtensionGetter = (w, info) => {
4552
4613
  };
4553
4614
  var generateGenericExtensionSetter = (w, info) => {
4554
4615
  const { ext, setMethodName, targetPath } = info;
4616
+ const useUpsert = ext.max === "1";
4555
4617
  w.curlyBlock(["public", setMethodName, `(value: Omit<Extension, "url"> | Extension): this`], () => {
4556
4618
  w.ifElseChain(
4557
4619
  [
4558
4620
  {
4559
4621
  cond: "isExtension(value)",
4560
- body: () => generateRawExtensionBody(w, ext, targetPath, "value")
4622
+ body: () => generateRawExtensionBody(w, ext, targetPath, "value", useUpsert)
4561
4623
  }
4562
4624
  ],
4563
- () => generateExtensionPush(w, targetPath, `{ url: "${ext.url}", ...value } as Extension`)
4625
+ () => generateExtensionPush(w, targetPath, `{ url: "${ext.url}", ...value } as Extension`, useUpsert)
4564
4626
  );
4565
4627
  w.line("return this");
4566
4628
  });
@@ -4694,7 +4756,14 @@ var collectTypesFromSlices = (tsIndex, flatProfile, addType) => {
4694
4756
  };
4695
4757
  var collectRequiredSliceNames = (field) => {
4696
4758
  if (!field.array || !field.slicing?.slices) return void 0;
4697
- 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);
4759
+ const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
4760
+ if (isTypeDisc) return void 0;
4761
+ const names = Object.entries(field.slicing.slices).filter(([_, s]) => {
4762
+ if (s.min === void 0 || s.min < 1 || !s.match || Object.keys(s.match).length === 0) return false;
4763
+ const matchKeys = new Set(Object.keys(s.match));
4764
+ const requiredBeyondMatch = (s.required ?? []).filter((name) => !matchKeys.has(name));
4765
+ return requiredBeyondMatch.length === 0;
4766
+ }).map(([name]) => name);
4698
4767
  return names.length > 0 ? names : void 0;
4699
4768
  };
4700
4769
  var collectSliceDefs = (tsIndex, flatProfile) => Object.entries(flatProfile.fields ?? {}).filter(([_, field]) => isNotChoiceDeclarationField(field) && field.slicing?.slices).flatMap(([fieldName, field]) => {
@@ -4722,7 +4791,8 @@ var collectSliceDefs = (tsIndex, flatProfile) => Object.entries(flatProfile.fiel
4722
4791
  excluded: slice.excluded ?? [],
4723
4792
  array: Boolean(field.array),
4724
4793
  constrainedChoice,
4725
- typeDiscriminator: isTypeDisc
4794
+ typeDiscriminator: isTypeDisc,
4795
+ max: slice.max ?? 0
4726
4796
  };
4727
4797
  });
4728
4798
  });
@@ -4732,39 +4802,61 @@ var generateSliceSetters = (w, sliceDefs, flatProfile, sliceBaseNames) => {
4732
4802
  for (const sliceDef of sliceDefs) {
4733
4803
  const baseName = tsResolvedSliceBaseName(sliceBaseNames, sliceDef.fieldName, sliceDef.sliceName);
4734
4804
  const methodName = `set${baseName}`;
4735
- const typeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
4805
+ const inputTypeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
4736
4806
  const matchRef = `${profileClassName}.${tsSliceStaticName(sliceDef.sliceName)}SliceMatch`;
4737
4807
  const tsField = tsFieldName(sliceDef.fieldName);
4738
4808
  const fieldAccess = tsGet("this.resource", tsField);
4739
4809
  const baseType = sliceDef.typedBaseType;
4740
- const inputOptional = sliceDef.required.length === 0;
4741
- const unionType = `${typeName} | ${baseType}`;
4742
- const paramSignature = inputOptional ? `(input?: ${unionType}): this` : `(input: ${unionType}): this`;
4743
- w.curlyBlock(["public", methodName, paramSignature], () => {
4744
- w.line(`const match = ${matchRef}`);
4745
- w.curlyBlock(["if", "(input && matchesValue(input, match))"], () => {
4810
+ const isUnbounded = sliceDef.array && (sliceDef.max === 0 || sliceDef.max === void 0);
4811
+ if (isUnbounded) {
4812
+ const unionType = `(${inputTypeName} | ${baseType})[]`;
4813
+ const paramSignature = `(input: ${unionType}): this`;
4814
+ w.curlyBlock(["public", methodName, paramSignature], () => {
4815
+ w.line(`const match = ${matchRef}`);
4816
+ w.line(`const arr = ${fieldAccess} ??= []`);
4817
+ if (sliceDef.constrainedChoice) {
4818
+ const cc = sliceDef.constrainedChoice;
4819
+ w.line(
4820
+ `const values = input.map(item => matchesValue(item, match) ? item as ${baseType} : applySliceMatch<${baseType}>(wrapSliceChoice<${baseType}>(item, ${JSON.stringify(cc.variant)}), match))`
4821
+ );
4822
+ } else {
4823
+ w.line(
4824
+ `const values = input.map(item => matchesValue(item, match) ? item as ${baseType} : applySliceMatch<${baseType}>(item, match))`
4825
+ );
4826
+ }
4827
+ w.line("setArraySliceAll(arr, match, values)");
4828
+ w.line("return this");
4829
+ });
4830
+ } else {
4831
+ const inputOptional = sliceDef.required.length === 0;
4832
+ const unionType = `${inputTypeName} | ${baseType}`;
4833
+ const paramSignature = inputOptional ? `(input?: ${unionType}): this` : `(input: ${unionType}): this`;
4834
+ w.curlyBlock(["public", methodName, paramSignature], () => {
4835
+ w.line(`const match = ${matchRef}`);
4836
+ w.curlyBlock(["if", "(input && matchesValue(input, match))"], () => {
4837
+ if (sliceDef.array) {
4838
+ w.line(`setArraySlice(${fieldAccess} ??= [], match, input as ${baseType})`);
4839
+ } else {
4840
+ w.line(`${fieldAccess} = input as ${baseType}`);
4841
+ }
4842
+ w.line("return this");
4843
+ });
4844
+ const inputExpr = inputOptional ? "input ?? {}" : "input";
4845
+ if (sliceDef.constrainedChoice) {
4846
+ const cc = sliceDef.constrainedChoice;
4847
+ w.line(`const wrapped = wrapSliceChoice<${baseType}>(${inputExpr}, ${JSON.stringify(cc.variant)})`);
4848
+ w.line(`const value = applySliceMatch<${baseType}>(wrapped, match)`);
4849
+ } else {
4850
+ w.line(`const value = applySliceMatch<${baseType}>(${inputExpr}, match)`);
4851
+ }
4746
4852
  if (sliceDef.array) {
4747
- w.line(`setArraySlice(${fieldAccess} ??= [], match, input as ${baseType})`);
4853
+ w.line(`setArraySlice(${fieldAccess} ??= [], match, value)`);
4748
4854
  } else {
4749
- w.line(`${fieldAccess} = input as ${baseType}`);
4855
+ w.line(`${fieldAccess} = value`);
4750
4856
  }
4751
4857
  w.line("return this");
4752
4858
  });
4753
- const inputExpr = inputOptional ? "input ?? {}" : "input";
4754
- if (sliceDef.constrainedChoice) {
4755
- const cc = sliceDef.constrainedChoice;
4756
- w.line(`const wrapped = wrapSliceChoice<${baseType}>(${inputExpr}, ${JSON.stringify(cc.variant)})`);
4757
- w.line(`const value = applySliceMatch<${baseType}>(wrapped, match)`);
4758
- } else {
4759
- w.line(`const value = applySliceMatch<${baseType}>(${inputExpr}, match)`);
4760
- }
4761
- if (sliceDef.array) {
4762
- w.line(`setArraySlice(${fieldAccess} ??= [], match, value)`);
4763
- } else {
4764
- w.line(`${fieldAccess} = value`);
4765
- }
4766
- w.line("return this");
4767
- });
4859
+ }
4768
4860
  w.line();
4769
4861
  }
4770
4862
  };
@@ -4775,52 +4867,85 @@ var generateSliceGetters = (w, sliceDefs, flatProfile, sliceBaseNames) => {
4775
4867
  for (const sliceDef of sliceDefs) {
4776
4868
  const baseName = tsResolvedSliceBaseName(sliceBaseNames, sliceDef.fieldName, sliceDef.sliceName);
4777
4869
  const getMethodName = `get${baseName}`;
4778
- const typeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
4870
+ const flatTypeName = tsSliceFlatAllTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
4779
4871
  const matchRef = `${profileClassName}.${tsSliceStaticName(sliceDef.sliceName)}SliceMatch`;
4780
4872
  const matchKeys = JSON.stringify(Object.keys(sliceDef.match));
4781
4873
  const tsField = tsFieldName(sliceDef.fieldName);
4782
4874
  const fieldAccess = tsGet("this.resource", tsField);
4783
4875
  const baseType = sliceDef.typedBaseType;
4784
- const defaultReturn = defaultMode === "raw" ? baseType : typeName;
4785
- w.lineSM(`public ${getMethodName}(mode: 'flat'): ${typeName} | undefined`);
4786
- w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType} | undefined`);
4787
- w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`);
4788
- w.curlyBlock(
4789
- [
4790
- "public",
4791
- getMethodName,
4792
- `(mode: 'flat' | 'raw' = '${defaultMode}'): ${typeName} | ${baseType} | undefined`
4793
- ],
4794
- () => {
4795
- w.line(`const match = ${matchRef}`);
4796
- if (sliceDef.array) {
4797
- w.line(`const item = getArraySlice(${fieldAccess}, match)`);
4798
- w.line("if (!item) return undefined");
4799
- } else {
4800
- w.line(`const item = ${fieldAccess}`);
4801
- w.line("if (!item || !matchesValue(item, match)) return undefined");
4802
- }
4803
- if (sliceDef.typeDiscriminator) {
4804
- w.line(`if (mode === 'raw') return item as ${baseType}`);
4805
- } else {
4806
- w.line("if (mode === 'raw') return item");
4876
+ const isUnbounded = sliceDef.array && (sliceDef.max === 0 || sliceDef.max === void 0);
4877
+ if (isUnbounded) {
4878
+ const defaultReturn = defaultMode === "raw" ? `${baseType}[]` : `${flatTypeName}[]`;
4879
+ w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName}[] | undefined`);
4880
+ w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType}[] | undefined`);
4881
+ w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`);
4882
+ w.curlyBlock(
4883
+ [
4884
+ "public",
4885
+ getMethodName,
4886
+ `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[] | undefined`
4887
+ ],
4888
+ () => {
4889
+ w.line(`const match = ${matchRef}`);
4890
+ w.line(`const items = getArraySliceAll(${fieldAccess}, match)`);
4891
+ w.line("if (items.length === 0) return undefined");
4892
+ if (sliceDef.typeDiscriminator) {
4893
+ w.line(`if (mode === 'raw') return items as ${baseType}[]`);
4894
+ } else {
4895
+ w.line("if (mode === 'raw') return items");
4896
+ }
4897
+ if (sliceDef.constrainedChoice) {
4898
+ const cc = sliceDef.constrainedChoice;
4899
+ w.line(
4900
+ `return items.map(item => unwrapSliceChoice<${flatTypeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)}))`
4901
+ );
4902
+ } else {
4903
+ w.line(`return items as unknown as ${flatTypeName}[]`);
4904
+ }
4807
4905
  }
4808
- if (sliceDef.typeDiscriminator) {
4809
- w.line(`return item as ${typeName}`);
4810
- } else if (sliceDef.constrainedChoice) {
4811
- const cc = sliceDef.constrainedChoice;
4812
- w.line(`return unwrapSliceChoice<${typeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)})`);
4813
- } else {
4814
- w.line(`return stripMatchKeys<${typeName}>(item, ${matchKeys})`);
4906
+ );
4907
+ } else {
4908
+ const defaultReturn = defaultMode === "raw" ? baseType : flatTypeName;
4909
+ w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName} | undefined`);
4910
+ w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType} | undefined`);
4911
+ w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`);
4912
+ w.curlyBlock(
4913
+ [
4914
+ "public",
4915
+ getMethodName,
4916
+ `(mode: 'flat' | 'raw' = '${defaultMode}'): ${flatTypeName} | ${baseType} | undefined`
4917
+ ],
4918
+ () => {
4919
+ w.line(`const match = ${matchRef}`);
4920
+ if (sliceDef.array) {
4921
+ w.line(`const item = getArraySlice(${fieldAccess}, match)`);
4922
+ w.line("if (!item) return undefined");
4923
+ } else {
4924
+ w.line(`const item = ${fieldAccess}`);
4925
+ w.line("if (!item || !matchesValue(item, match)) return undefined");
4926
+ }
4927
+ if (sliceDef.typeDiscriminator) {
4928
+ w.line(`if (mode === 'raw') return item as ${baseType}`);
4929
+ } else {
4930
+ w.line("if (mode === 'raw') return item");
4931
+ }
4932
+ if (sliceDef.constrainedChoice) {
4933
+ const cc = sliceDef.constrainedChoice;
4934
+ w.line(
4935
+ `return unwrapSliceChoice<${flatTypeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)})`
4936
+ );
4937
+ } else {
4938
+ w.line(`return item as unknown as ${flatTypeName}`);
4939
+ }
4815
4940
  }
4816
- }
4817
- );
4941
+ );
4942
+ }
4818
4943
  w.line();
4819
4944
  }
4820
4945
  };
4821
4946
 
4822
4947
  // src/api/writer-generator/typescript/profile-validation.ts
4823
- var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef, canonicalUrlExpr) => {
4948
+ var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef, canonicalUrlExpr, tsIndex) => {
4824
4949
  if (field.excluded) {
4825
4950
  errors.push(`...validateExcluded(res, profileName, ${JSON.stringify(name)})`);
4826
4951
  return;
@@ -4842,14 +4967,29 @@ var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef,
4842
4967
  );
4843
4968
  if (field.slicing?.slices) {
4844
4969
  for (const [sliceName, slice] of Object.entries(field.slicing.slices)) {
4845
- if (slice.min === void 0 && slice.max === void 0) continue;
4846
4970
  const match = slice.match ?? {};
4847
4971
  if (Object.keys(match).length === 0) continue;
4848
- const min = slice.min ?? 0;
4849
- const max = slice.max ?? 0;
4850
- errors.push(
4851
- `...validateSliceCardinality(res, profileName, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${min}, ${max})`
4852
- );
4972
+ if (slice.min !== void 0 || slice.max !== void 0) {
4973
+ const min = slice.min ?? 0;
4974
+ const max = slice.max ?? 0;
4975
+ errors.push(
4976
+ `...validateSliceCardinality(res, profileName, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${min}, ${max})`
4977
+ );
4978
+ }
4979
+ const sliceRequiredFields = [];
4980
+ const matchKeys = new Set(Object.keys(match));
4981
+ for (const rf of slice.required ?? []) {
4982
+ if (!matchKeys.has(rf)) sliceRequiredFields.push(rf);
4983
+ }
4984
+ if (tsIndex && field.type && slice.elements) {
4985
+ const cc = tsIndex.constrainedChoice(field.type.package, field.type, slice.elements);
4986
+ if (cc) sliceRequiredFields.push(cc.variant);
4987
+ }
4988
+ if (sliceRequiredFields.length > 0) {
4989
+ errors.push(
4990
+ `...validateSliceFields(res, profileName, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${JSON.stringify(sliceRequiredFields)})`
4991
+ );
4992
+ }
4853
4993
  }
4854
4994
  }
4855
4995
  };
@@ -4881,7 +5021,8 @@ var generateValidateMethod = (w, tsIndex, flatProfile) => {
4881
5021
  name,
4882
5022
  field,
4883
5023
  tsIndex.findLastSpecializationByIdentifier,
4884
- canonicalUrlExpr
5024
+ canonicalUrlExpr,
5025
+ tsIndex
4885
5026
  );
4886
5027
  }
4887
5028
  const emitArray = (label, exprs) => {
@@ -4913,24 +5054,31 @@ var collectChoiceAccessors = (flatProfile, promotedChoices) => {
4913
5054
  }
4914
5055
  return accessors;
4915
5056
  };
4916
- var tryPromoteChoice = (field, fields, params, promotedChoices) => {
5057
+ var tryPromoteChoice = (field, fields, params, promotedChoices, resolveRef, isFamilyType) => {
4917
5058
  if (!isChoiceDeclarationField(field) || !field.required || field.choices.length !== 1) return;
4918
5059
  const choiceName = field.choices[0];
4919
5060
  if (!choiceName) return;
4920
5061
  const choiceField = fields[choiceName];
4921
5062
  if (!choiceField || !isChoiceInstanceField(choiceField)) return;
4922
- const tsType = tsTypeFromIdentifier(choiceField.type) + (choiceField.array ? "[]" : "");
5063
+ const tsType = fieldTsType(choiceField, resolveRef, isFamilyType);
4923
5064
  params.push({ name: choiceName, tsType, typeId: choiceField.type });
4924
5065
  promotedChoices.add(choiceName);
4925
5066
  };
5067
+ var mkIsFamilyType = (tsIndex) => (ref) => {
5068
+ const schema = tsIndex.resolveType(ref);
5069
+ if (!schema || !("typeFamily" in schema)) return false;
5070
+ return (schema.typeFamily?.resources?.length ?? 0) > 0;
5071
+ };
4926
5072
  var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
4927
5073
  const autoFields = [];
4928
5074
  const sliceAutoFields = [];
4929
5075
  const params = [];
4930
5076
  const autoAccessors = [];
5077
+ const fixedFields = /* @__PURE__ */ new Set();
4931
5078
  const fields = flatProfile.fields ?? {};
4932
5079
  const promotedChoices = /* @__PURE__ */ new Set();
4933
5080
  const resolveRef = tsIndex.findLastSpecializationByIdentifier;
5081
+ const isFamilyType = mkIsFamilyType(tsIndex);
4934
5082
  if (isResourceIdentifier(flatProfile.base)) {
4935
5083
  autoFields.push({ name: "resourceType", value: JSON.stringify(flatProfile.base.name) });
4936
5084
  }
@@ -4938,14 +5086,15 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
4938
5086
  if (field.excluded) continue;
4939
5087
  if (isChoiceInstanceField(field)) continue;
4940
5088
  if (isChoiceDeclarationField(field)) {
4941
- tryPromoteChoice(field, fields, params, promotedChoices);
5089
+ tryPromoteChoice(field, fields, params, promotedChoices, resolveRef, isFamilyType);
4942
5090
  continue;
4943
5091
  }
4944
5092
  if (field.valueConstraint) {
4945
5093
  const value = JSON.stringify(field.valueConstraint.value);
4946
5094
  autoFields.push({ name, value: field.array ? `[${value}]` : value });
5095
+ fixedFields.add(name);
4947
5096
  if (isNotChoiceDeclarationField(field) && field.type) {
4948
- const tsType = fieldTsType(field, resolveRef);
5097
+ const tsType = fieldTsType(field, resolveRef, isFamilyType);
4949
5098
  autoAccessors.push({ name, tsType, typeId: field.type });
4950
5099
  }
4951
5100
  continue;
@@ -4954,7 +5103,7 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
4954
5103
  const sliceNames = collectRequiredSliceNames(field);
4955
5104
  if (sliceNames) {
4956
5105
  if (field.type) {
4957
- const tsType = fieldTsType(field, resolveRef);
5106
+ const tsType = fieldTsType(field, resolveRef, isFamilyType);
4958
5107
  sliceAutoFields.push({
4959
5108
  name,
4960
5109
  tsType,
@@ -4967,20 +5116,27 @@ var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
4967
5116
  }
4968
5117
  }
4969
5118
  if (field.required) {
4970
- const tsType = fieldTsType(field, resolveRef);
5119
+ const tsType = fieldTsType(field, resolveRef, isFamilyType);
4971
5120
  params.push({ name, tsType, typeId: field.type });
4972
5121
  }
4973
5122
  }
4974
- collectBaseRequiredParams(tsIndex, flatProfile, resolveRef, params, [
4975
- ...autoFields.map((f) => f.name),
4976
- ...sliceAutoFields.map((f) => f.name),
4977
- ...params.map((f) => f.name),
4978
- ...promotedChoices
4979
- ]);
5123
+ collectBaseRequiredParams(
5124
+ tsIndex,
5125
+ flatProfile,
5126
+ resolveRef,
5127
+ params,
5128
+ [
5129
+ ...autoFields.map((f) => f.name),
5130
+ ...sliceAutoFields.map((f) => f.name),
5131
+ ...params.map((f) => f.name),
5132
+ ...promotedChoices
5133
+ ],
5134
+ isFamilyType
5135
+ );
4980
5136
  const accessors = [...autoAccessors, ...collectChoiceAccessors(flatProfile, promotedChoices)];
4981
- return { autoFields, sliceAutoFields, params, accessors };
5137
+ return { autoFields, sliceAutoFields, params, accessors, fixedFields };
4982
5138
  };
4983
- var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, coveredNames) => {
5139
+ var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, coveredNames, isFamilyType) => {
4984
5140
  const covered = new Set(coveredNames);
4985
5141
  const baseSchema = tsIndex.resolveType(flatProfile.base);
4986
5142
  if (!baseSchema || !("fields" in baseSchema) || !baseSchema.fields) return;
@@ -4990,7 +5146,7 @@ var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, cover
4990
5146
  if (isChoiceInstanceField(field)) continue;
4991
5147
  if (isChoiceDeclarationField(field)) continue;
4992
5148
  if (isNotChoiceDeclarationField(field) && field.type) {
4993
- const tsType = fieldTsType(field, resolveRef);
5149
+ const tsType = fieldTsType(field, resolveRef, isFamilyType);
4994
5150
  params.push({ name, tsType, typeId: field.type });
4995
5151
  }
4996
5152
  }
@@ -5018,23 +5174,27 @@ var generateProfileHelpersImport = (w, tsIndex, flatProfile, sliceDefs, factoryI
5018
5174
  const hasMeta = tsIndex.isWithMetaField(flatProfile);
5019
5175
  const canonicalUrl = flatProfile.identifier.url;
5020
5176
  const imports = [];
5021
- if (!isPrimitiveIdentifier(flatProfile.base)) imports.push("buildResource");
5022
5177
  if (flatProfile.base.name === "Extension" && !!canonicalUrl && collectSubExtensionSlices(flatProfile).length > 0)
5023
5178
  imports.push("isRawExtensionInput");
5024
5179
  if (canonicalUrl && hasMeta) imports.push("ensureProfile");
5025
5180
  if (sliceDefs.length > 0 || factoryInfo.sliceAutoFields.length > 0)
5026
5181
  imports.push("applySliceMatch", "matchesValue", "setArraySlice", "getArraySlice", "ensureSliceDefaults");
5182
+ const hasUnboundedSlice = sliceDefs.some((s) => s.array && (s.max === 0 || s.max === void 0));
5183
+ if (hasUnboundedSlice) imports.push("setArraySliceAll", "getArraySliceAll");
5027
5184
  if (extensions.some((ext) => ext.path.split(".").some((s) => s !== "extension"))) imports.push("ensurePath");
5028
5185
  if (extensions.some((ext) => ext.isComplex && ext.subExtensions)) imports.push("extractComplexExtension");
5029
- if (sliceDefs.some((s) => !s.typeDiscriminator)) imports.push("stripMatchKeys");
5030
5186
  if (sliceDefs.some((s) => s.constrainedChoice)) imports.push("wrapSliceChoice", "unwrapSliceChoice");
5031
- if (extensions.some((ext) => ext.url)) imports.push("isExtension", "getExtensionValue", "pushExtension");
5187
+ if (extensions.some((ext) => ext.url)) {
5188
+ imports.push("isExtension", "getExtensionValue", "pushExtension");
5189
+ if (extensions.some((ext) => ext.url && ext.max === "1")) imports.push("upsertExtension");
5190
+ }
5032
5191
  if (Object.keys(flatProfile.fields ?? {}).length > 0)
5033
5192
  imports.push(
5034
5193
  "validateRequired",
5035
5194
  "validateExcluded",
5036
5195
  "validateFixedValue",
5037
5196
  "validateSliceCardinality",
5197
+ "validateSliceFields",
5038
5198
  "validateEnum",
5039
5199
  "validateReference",
5040
5200
  "validateChoiceRequired",
@@ -5109,7 +5269,17 @@ var generateProfileImports = (w, tsIndex, flatProfile) => {
5109
5269
  var generateStaticSliceFields = (w, sliceDefs) => {
5110
5270
  for (const sliceDef of sliceDefs) {
5111
5271
  const staticName = `${tsSliceStaticName(sliceDef.sliceName)}SliceMatch`;
5112
- w.lineSM(`private static readonly ${staticName}: Record<string, unknown> = ${JSON.stringify(sliceDef.match)}`);
5272
+ const json = JSON.stringify(sliceDef.match);
5273
+ const prefix = `private static readonly ${staticName}: Record<string, unknown> = `;
5274
+ if (prefix.length + json.length <= (w.opts.lineWidth ?? 120)) {
5275
+ w.lineSM(`${prefix}${json}`);
5276
+ } else {
5277
+ w.curlyBlock([prefix.trimEnd()], () => {
5278
+ for (const [key, value] of Object.entries(sliceDef.match)) {
5279
+ w.line(`${JSON.stringify(key)}: ${JSON.stringify(value)},`);
5280
+ }
5281
+ });
5282
+ }
5113
5283
  }
5114
5284
  if (sliceDefs.length > 0) w.line();
5115
5285
  };
@@ -5147,6 +5317,28 @@ var generateFactoryMethods = (w, tsIndex, flatProfile, factoryInfo) => {
5147
5317
  if (hasMeta) {
5148
5318
  w.lineSM(`ensureProfile(resource, ${profileClassName}.canonicalUrl)`);
5149
5319
  }
5320
+ if (flatProfile.base.name === "Extension" && flatProfile.identifier.url) {
5321
+ w.lineSM(`resource.url = ${profileClassName}.canonicalUrl`);
5322
+ }
5323
+ const applyAutoFields = factoryInfo.autoFields.filter((f) => f.name !== "resourceType");
5324
+ if (applyAutoFields.length > 0) {
5325
+ w.curlyBlock(["Object.assign(resource,"], () => {
5326
+ for (const f of applyAutoFields) {
5327
+ w.line(`${f.name}: ${f.value},`);
5328
+ }
5329
+ }, [")"]);
5330
+ }
5331
+ for (const f of factoryInfo.sliceAutoFields) {
5332
+ const matchRefs = f.sliceNames.map((s) => `${profileClassName}.${tsSliceStaticName(s)}SliceMatch`);
5333
+ w.line(`resource.${f.name} = ensureSliceDefaults(`);
5334
+ w.indentBlock(() => {
5335
+ w.line(`[...(resource.${f.name} ?? [])],`);
5336
+ for (const ref of matchRefs) {
5337
+ w.line(`${ref},`);
5338
+ }
5339
+ });
5340
+ w.lineSM(")");
5341
+ }
5150
5342
  w.lineSM(`return new ${profileClassName}(resource)`);
5151
5343
  });
5152
5344
  w.line();
@@ -5209,7 +5401,7 @@ var generateFactoryMethods = (w, tsIndex, flatProfile, factoryInfo) => {
5209
5401
  }
5210
5402
  w.line();
5211
5403
  const extensionVar = extSliceField ? "extensionWithDefaults" : "resolvedExtensions";
5212
- w.curlyBlock([`const resource = buildResource<${tsBaseResourceName}>(`], () => {
5404
+ w.curlyBlock([`const resource: ${tsBaseResourceName} =`], () => {
5213
5405
  for (const f of allFields) {
5214
5406
  if (f.name === "extension") continue;
5215
5407
  w.line(`${f.name}: ${f.value},`);
@@ -5218,7 +5410,7 @@ var generateFactoryMethods = (w, tsIndex, flatProfile, factoryInfo) => {
5218
5410
  if (hasMeta) {
5219
5411
  w.line(`meta: { profile: [${profileClassName}.canonicalUrl] },`);
5220
5412
  }
5221
- }, [")"]);
5413
+ });
5222
5414
  w.lineSM("return resource");
5223
5415
  });
5224
5416
  w.line();
@@ -5245,22 +5437,21 @@ var generateFactoryMethods = (w, tsIndex, flatProfile, factoryInfo) => {
5245
5437
  if (isPrimitiveIdentifier(flatProfile.base)) {
5246
5438
  w.lineSM(`const resource = undefined as unknown as ${tsBaseResourceName}`);
5247
5439
  } else {
5248
- w.curlyBlock([`const resource = buildResource<${tsBaseResourceName}>(`], () => {
5440
+ w.curlyBlock([`const resource: ${tsBaseResourceName} =`], () => {
5249
5441
  for (const f of allFields) {
5250
5442
  w.line(`${f.name}: ${f.value},`);
5251
5443
  }
5252
5444
  if (hasMeta) {
5253
5445
  w.line(`meta: { profile: [${profileClassName}.canonicalUrl] },`);
5254
5446
  }
5255
- }, [")"]);
5447
+ });
5256
5448
  }
5257
5449
  w.lineSM("return resource");
5258
5450
  });
5259
5451
  w.line();
5260
5452
  w.curlyBlock(["static", "create", `(${paramSignature})`, `: ${profileClassName}`], () => {
5261
- w.lineSM(
5262
- `return ${profileClassName}.apply(${profileClassName}.createResource(${hasParams ? "args" : ""}))`
5263
- );
5453
+ w.lineSM(`const resource = ${profileClassName}.createResource(${hasParams ? "args" : ""})`);
5454
+ w.lineSM(`return ${profileClassName}.apply(resource)`);
5264
5455
  });
5265
5456
  }
5266
5457
  w.line();
@@ -5291,11 +5482,13 @@ var generateFieldAccessors = (w, factoryInfo, extSliceMethodBaseNames) => {
5291
5482
  w.lineSM(`return ${tsGet("this.resource", fieldAccess)} as ${a.tsType} | undefined`);
5292
5483
  });
5293
5484
  w.line();
5294
- w.curlyBlock([`set${methodBaseName}`, `(value: ${a.tsType})`, ": this"], () => {
5295
- w.lineSM(`Object.assign(this.resource, { ${fieldAccess}: value })`);
5296
- w.lineSM("return this");
5297
- });
5298
- w.line();
5485
+ if (!factoryInfo.fixedFields.has(a.name)) {
5486
+ w.curlyBlock([`set${methodBaseName}`, `(value: ${a.tsType})`, ": this"], () => {
5487
+ w.lineSM(`Object.assign(this.resource, { ${fieldAccess}: value })`);
5488
+ w.lineSM("return this");
5489
+ });
5490
+ w.line();
5491
+ }
5299
5492
  }
5300
5493
  };
5301
5494
  var generateInlineExtensionInputTypes = (w, tsIndex, flatProfile) => {
@@ -5318,11 +5511,23 @@ var generateInlineExtensionInputTypes = (w, tsIndex, flatProfile) => {
5318
5511
  w.line();
5319
5512
  }
5320
5513
  };
5514
+ var valueToTypeLiteral = (value) => {
5515
+ if (value === null || value === void 0) return "undefined";
5516
+ if (typeof value === "string") return JSON.stringify(value);
5517
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
5518
+ if (Array.isArray(value)) return `[${value.map(valueToTypeLiteral).join(", ")}]`;
5519
+ if (typeof value === "object") {
5520
+ const entries = Object.entries(value).map(([k, v]) => `${k}: ${valueToTypeLiteral(v)}`).join("; ");
5521
+ return `{ ${entries} }`;
5522
+ }
5523
+ return "unknown";
5524
+ };
5321
5525
  var generateSliceInputTypes = (w, flatProfile, sliceDefs) => {
5322
5526
  if (sliceDefs.length === 0) return;
5323
5527
  const tsProfileName = tsResourceName(flatProfile.identifier);
5324
5528
  for (const sliceDef of sliceDefs) {
5325
- const typeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
5529
+ const inputTypeName = tsSliceFlatTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
5530
+ const flatTypeName = tsSliceFlatAllTypeName(tsProfileName, sliceDef.fieldName, sliceDef.sliceName);
5326
5531
  const matchFields = sliceDef.typeDiscriminator ? [] : Object.keys(sliceDef.match);
5327
5532
  const allExcluded = [.../* @__PURE__ */ new Set([...sliceDef.excluded, ...matchFields])];
5328
5533
  if (sliceDef.constrainedChoice) {
@@ -5335,19 +5540,32 @@ var generateSliceInputTypes = (w, flatProfile, sliceDefs) => {
5335
5540
  const excludedNames = allExcluded.map((name) => JSON.stringify(name));
5336
5541
  const requiredNames = sliceDef.required.map((name) => JSON.stringify(name));
5337
5542
  const baseType = sliceDef.typedBaseType;
5338
- let typeExpr = baseType;
5543
+ let inputTypeExpr = baseType;
5339
5544
  if (excludedNames.length > 0) {
5340
- typeExpr = `Omit<${typeExpr}, ${excludedNames.join(" | ")}>`;
5545
+ inputTypeExpr = `Omit<${inputTypeExpr}, ${excludedNames.join(" | ")}>`;
5341
5546
  }
5342
5547
  if (requiredNames.length > 0) {
5343
- typeExpr = `${typeExpr} & Required<Pick<${baseType}, ${requiredNames.join(" | ")}>>`;
5548
+ inputTypeExpr = `${inputTypeExpr} & Required<Pick<${baseType}, ${requiredNames.join(" | ")}>>`;
5344
5549
  }
5345
5550
  if (sliceDef.constrainedChoice) {
5346
- typeExpr = `${typeExpr} & ${tsTypeFromIdentifier(sliceDef.constrainedChoice.variantType)}`;
5551
+ inputTypeExpr = `${inputTypeExpr} & ${tsTypeFromIdentifier(sliceDef.constrainedChoice.variantType)}`;
5552
+ }
5553
+ w.lineSM(`export type ${inputTypeName} = ${inputTypeExpr}`);
5554
+ const safeMatchEntries = matchFields.length > 0 && !sliceDef.constrainedChoice ? matchFields.filter((key) => {
5555
+ const v = sliceDef.match[key];
5556
+ return Array.isArray(v) || typeof v !== "object" || v === null;
5557
+ }).map((key) => ({ key, typeLiteral: valueToTypeLiteral(sliceDef.match[key]) })) : [];
5558
+ if (safeMatchEntries.length > 0) {
5559
+ w.curlyBlock([`export type ${flatTypeName} = ${inputTypeName} &`], () => {
5560
+ for (const entry of safeMatchEntries) {
5561
+ w.lineSM(`readonly ${entry.key}: ${entry.typeLiteral}`);
5562
+ }
5563
+ });
5564
+ } else {
5565
+ w.lineSM(`export type ${flatTypeName} = ${inputTypeName}`);
5347
5566
  }
5348
- w.lineSM(`export type ${typeName} = ${typeExpr}`);
5567
+ w.line();
5349
5568
  }
5350
- w.line();
5351
5569
  };
5352
5570
  var generateRawType = (w, flatProfile, factoryInfo) => {
5353
5571
  const hasParams = factoryInfo.params.length > 0 || factoryInfo.sliceAutoFields.length > 0;
@@ -5585,7 +5803,7 @@ var TypeScript = class extends Writer {
5585
5803
  const typeExpr = isArray ? "(Element | null)[]" : "Element";
5586
5804
  this.lineSM(`${extFieldName}?: ${typeExpr}`);
5587
5805
  }
5588
- generateType(tsIndex, schema) {
5806
+ generateType(tsIndex, schema, isFamilyType) {
5589
5807
  let name;
5590
5808
  const genericTypes = ["Reference", "Coding", "CodeableConcept"];
5591
5809
  if (genericTypes.includes(schema.identifier.name)) {
@@ -5639,7 +5857,14 @@ var TypeScript = class extends Writer {
5639
5857
  if (!field.type) continue;
5640
5858
  this.debugComment(fieldName, ":", field);
5641
5859
  const tsName = tsFieldName(fieldName);
5642
- const tsType = resolveFieldTsType(schema.identifier.name, tsName, field, void 0, genericFieldMap);
5860
+ const tsType = resolveFieldTsType(
5861
+ schema.identifier.name,
5862
+ tsName,
5863
+ field,
5864
+ void 0,
5865
+ genericFieldMap,
5866
+ isFamilyType
5867
+ );
5643
5868
  const optionalSymbol = field.required ? "" : "?";
5644
5869
  const arraySymbol = field.array ? "[]" : "";
5645
5870
  this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${arraySymbol}`);
@@ -5669,10 +5894,10 @@ var TypeScript = class extends Writer {
5669
5894
  );
5670
5895
  });
5671
5896
  }
5672
- generateNestedTypes(tsIndex, schema) {
5897
+ generateNestedTypes(tsIndex, schema, isFamilyType) {
5673
5898
  if (schema.nested) {
5674
5899
  for (const subtype of schema.nested) {
5675
- this.generateType(tsIndex, subtype);
5900
+ this.generateType(tsIndex, subtype, isFamilyType);
5676
5901
  this.line();
5677
5902
  }
5678
5903
  }
@@ -5688,17 +5913,18 @@ var TypeScript = class extends Writer {
5688
5913
  });
5689
5914
  });
5690
5915
  } else if (isSpecializationTypeSchema(schema)) {
5916
+ const isFamilyType = mkIsFamilyType(tsIndex);
5691
5917
  this.cat(`${tsModuleFileName(schema.identifier)}`, () => {
5692
5918
  this.generateDisclaimer();
5693
5919
  this.generateDependenciesImports(tsIndex, schema);
5694
5920
  this.generateComplexTypeReexports(schema);
5695
- this.generateNestedTypes(tsIndex, schema);
5921
+ this.generateNestedTypes(tsIndex, schema, isFamilyType);
5696
5922
  this.comment(
5697
5923
  "CanonicalURL:",
5698
5924
  schema.identifier.url,
5699
5925
  `(pkg: ${packageMetaToFhir(packageMeta(schema))})`
5700
5926
  );
5701
- this.generateType(tsIndex, schema);
5927
+ this.generateType(tsIndex, schema, isFamilyType);
5702
5928
  this.generateResourceTypePredicate(schema);
5703
5929
  });
5704
5930
  } else {
@@ -5881,7 +6107,8 @@ var APIBuilder = class {
5881
6107
  const defaultPyOpts = {
5882
6108
  ...defaultWriterOpts,
5883
6109
  rootPackageName: "fhir_types",
5884
- fieldFormat: "snake_case"
6110
+ fieldFormat: "snake_case",
6111
+ primitiveTypeExtension: false
5885
6112
  };
5886
6113
  const opts = {
5887
6114
  ...defaultPyOpts,