babelfhir-ts 1.0.25 → 1.0.26

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.
Files changed (65) hide show
  1. package/README.md +7 -7
  2. package/out/generator/index.js +10 -15
  3. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionAppointment.ts +94 -0
  4. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionAppointmentClass.ts +194 -0
  5. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckIn.ts +29 -0
  6. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInClass.ts +194 -0
  7. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInNoCode.ts +29 -0
  8. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInNoCodeClass.ts +194 -0
  9. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDataFlow.ts +51 -0
  10. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDataFlowClass.ts +194 -0
  11. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDevice.ts +67 -0
  12. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDeviceClass.ts +199 -0
  13. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/RandomSupport.ts +26 -0
  14. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/ValueSet-AdmissionFlowIds.ts +38 -0
  15. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/ValueSet-AdmissionReasonCodeGroup.ts +42 -0
  16. package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/index.ts +18 -0
  17. package/out/generator/temp/1761673242558-4u3in1qziv2/package/.index.db +0 -0
  18. package/out/generator/temp/1761673242558-4u3in1qziv2/package/.index.json +234 -0
  19. package/out/generator/temp/1761673242558-4u3in1qziv2/package/CodeSystem-admission-flow-ids.json +1 -0
  20. package/out/generator/temp/1761673242558-4u3in1qziv2/package/CodeSystem-admission-reason-codes.json +1 -0
  21. package/out/generator/temp/1761673242558-4u3in1qziv2/package/ImplementationGuide-pink.admission.json +1 -0
  22. package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionCheckIn.json +1 -0
  23. package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionCheckInNoCode.json +1 -0
  24. package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionUrl.json +1 -0
  25. package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionUuid.json +1 -0
  26. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionAppointment.json +1 -0
  27. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionCheckIn.json +1 -0
  28. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionCheckInNoCode.json +1 -0
  29. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDataFlow.json +1 -0
  30. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDevice.json +1 -0
  31. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDocumentMetadata.json +1 -0
  32. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormData.json +1 -0
  33. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormUrl.json +1 -0
  34. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormUuid.json +1 -0
  35. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionIntegrationPoints.json +1 -0
  36. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionPatient.json +1 -0
  37. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionPatientData.json +1 -0
  38. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionRoutingRule.json +1 -0
  39. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-LocationTomlConfiguration.json +1 -0
  40. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-Pink-Location-With-Toml.json +1 -0
  41. package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-PinkLocation.json +1 -0
  42. package/out/generator/temp/1761673242558-4u3in1qziv2/package/ValueSet-AdmissionFlowIdsValueSet.json +1 -0
  43. package/out/generator/temp/1761673242558-4u3in1qziv2/package/ValueSet-AdmissionReasonCodeGroup.json +1 -0
  44. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/AllergyIntolerance-Example-PenicillinAllergy.json +1 -0
  45. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Appointment-Example-AdmissionAppointment.json +1 -0
  46. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Condition-Example-DiabetesCondition.json +1 -0
  47. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Device-Example-Surgery-Room.json +1 -0
  48. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/DocumentReference-Example-PreOperativeEvaluation.json +1 -0
  49. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Location-Example-Location-With-Toml.json +1 -0
  50. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Patient-Example-Patient.json +1 -0
  51. package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/QuestionnaireResponse-Example-AdmissionFormResponse.json +1 -0
  52. package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/spec.internals +313 -0
  53. package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/validation-oo.json +1 -0
  54. package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/validation-summary.json +1 -0
  55. package/out/generator/temp/1761673242558-4u3in1qziv2/package/package.json +23 -0
  56. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionAppointment.sch +22 -0
  57. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionCheckIn.sch +18 -0
  58. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionCheckInNoCode.sch +18 -0
  59. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionDevice.sch +12 -0
  60. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionFormUrl.sch +18 -0
  61. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionFormUuid.sch +18 -0
  62. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionPatient.sch +12 -0
  63. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-Pink-Location-With-Toml.sch +26 -0
  64. package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-PinkLocation.sch +12 -0
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -78,11 +78,11 @@ Install `fhirpath` in your app if you plan to call `.validate()` or use the gene
78
78
 
79
79
  ### Module resolution
80
80
 
81
- Generated packages use **`moduleResolution: "node16"`** in their `tsconfig.json` to ensure proper ESM compatibility. This works with all modern TypeScript setups including:
81
+ Generated packages work with all modern TypeScript setups:
82
82
 
83
83
  - **Node.js 18+** with `"type": "module"` in `package.json`
84
84
  - **Bundlers** (Vite, esbuild, Webpack) with ESM output
85
- - **TypeScript 5.0+** with `node16`, `nodenext`, or `bundler` module resolution
85
+ - **TypeScript 5.0+** with any module resolution (`node16`, `nodenext`, or `bundler`)
86
86
 
87
87
  #### FHIR type imports
88
88
 
@@ -92,13 +92,13 @@ Generated code imports base FHIR types from `"fhir/r4"`:
92
92
  import { Appointment, Extension, CodeableConcept } from "fhir/r4";
93
93
  ```
94
94
 
95
- Since `@types/fhir` doesn't provide a `package.json` `exports` field, BabelFHIR-TS includes an **ambient module declaration** (`fhir-r4.d.ts`) in every generated package. This declaration file:
95
+ Since `@types/fhir` doesn't provide a `package.json` `exports` field, BabelFHIR-TS includes an **ambient module declaration** (`fhir-r4.d.ts`) in every generated package. The generated `index.d.ts` references this file:
96
96
 
97
- - Maps `"fhir/r4"` imports to the actual `@types/fhir` type definitions
98
- - Enables strict `node16`/`nodenext` module resolution without path configuration
99
- - Is automatically picked up by TypeScript—no `tsconfig.json` changes needed in your project
97
+ ```ts
98
+ /// <reference path="./fhir-r4.d.ts" />
99
+ ```
100
100
 
101
- > **Note**: If you're using `moduleResolution: "bundler"` in your own project, the ambient module declaration is still compatible and won't cause any issues.
101
+ This ensures TypeScript can resolve `fhir/r4` imports automatically without any configuration in your project's `tsconfig.json`.
102
102
 
103
103
  ## CLI reference
104
104
 
@@ -91,6 +91,15 @@ async function compileTypeScriptToJS(dir) {
91
91
  }
92
92
  catch { /* ignore */ }
93
93
  if (code === 0) {
94
+ // Add triple-slash directive to index.d.ts after compilation
95
+ const indexDtsPath = path.join(dir, 'index.d.ts');
96
+ if (fs.existsSync(indexDtsPath)) {
97
+ const content = fs.readFileSync(indexDtsPath, 'utf-8');
98
+ if (!content.startsWith('/// <reference')) {
99
+ const newContent = `/// <reference path="./fhir-r4.d.ts" />\n${content}`;
100
+ fs.writeFileSync(indexDtsPath, newContent, 'utf-8');
101
+ }
102
+ }
94
103
  // Remove .ts files after successful compilation
95
104
  const files = fs.readdirSync(dir, { recursive: true, withFileTypes: true });
96
105
  for (const file of files) {
@@ -462,27 +471,13 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
462
471
  const generatedPackageJsonPath = path.join(outputDir, 'package.json');
463
472
  fs.writeFileSync(generatedPackageJsonPath, JSON.stringify(generatedPackageJson, null, 2));
464
473
  console.log(`Created package.json in generated folder: ${packageName}-generated@${packageVersion}`);
465
- // Create a tsconfig.json for Node16/NodeNext module resolution
466
- // This ensures fhir/r4 imports are resolved via the ambient module declaration
467
- const generatedTsConfig = {
468
- compilerOptions: {
469
- moduleResolution: 'node16',
470
- module: 'Node16',
471
- target: 'ES2020',
472
- strict: false,
473
- skipLibCheck: true,
474
- esModuleInterop: true
475
- }
476
- };
477
- const generatedTsConfigPath = path.join(outputDir, 'tsconfig.json');
478
- fs.writeFileSync(generatedTsConfigPath, JSON.stringify(generatedTsConfig, null, 2));
479
474
  // Copy fhir-r4.d.ts ambient module declaration to support fhir/r4 imports
480
475
  // The file is at out/fhir-r4.d.ts (root of compiled output)
481
476
  const fhirR4DtsSource = path.join(path.dirname(path.dirname(fileURLToPath(import.meta.url))), 'fhir-r4.d.ts');
482
477
  const fhirR4DtsDest = path.join(outputDir, 'fhir-r4.d.ts');
483
478
  if (fs.existsSync(fhirR4DtsSource)) {
484
479
  fs.copyFileSync(fhirR4DtsSource, fhirR4DtsDest);
485
- console.log('Copied fhir-r4.d.ts ambient module declaration for Node16/NodeNext support');
480
+ console.log('Copied fhir-r4.d.ts ambient module declaration');
486
481
  }
487
482
  else {
488
483
  console.warn('Warning: fhir-r4.d.ts not found. Run: npm run generate:fhir-module');
@@ -0,0 +1,94 @@
1
+ import { Appointment, Extension } from "fhir/r4";
2
+
3
+ export interface AdmissionCheckInNoCode extends Extension { url: 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckInNoCode' }
4
+
5
+ export interface AdmissionCheckIn extends Extension { url: 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckIn' }
6
+
7
+ export interface AdmissionFormUrl extends Extension { url: 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionFormUrl' }
8
+
9
+ export interface AdmissionFormUuid extends Extension { url: 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionFormUuid' }
10
+
11
+ export interface AdmissionAppointment extends Appointment {
12
+ start: string;
13
+
14
+ extension?: (Extension | AdmissionFormUuid | AdmissionFormUrl | AdmissionCheckIn | AdmissionCheckInNoCode)[];
15
+ }
16
+
17
+
18
+ import fhirpath from "fhirpath";
19
+
20
+ export async function validateAdmissionAppointment(resource: AdmissionAppointment): Promise<{ errors: string[], warnings: string[] }> {
21
+ const errors: string[] = [];
22
+ const warnings: string[] = [];
23
+
24
+ const result0 = await Promise.resolve(fhirpath.evaluate(resource, "start.exists() = end.exists()", { resource }));
25
+ if (!result0.every(Boolean)) {
26
+ errors.push("Constraint violation: Either start and end are specified, or neither");
27
+ }
28
+
29
+ const result1 = await Promise.resolve(fhirpath.evaluate(resource, "(start.exists() and end.exists()) or (status in ('proposed' | 'cancelled' | 'waitlist'))", { resource }));
30
+ if (!result1.every(Boolean)) {
31
+ errors.push("Constraint violation: Only proposed or cancelled appointments can be missing start/end dates");
32
+ }
33
+
34
+ const result2 = await Promise.resolve(fhirpath.evaluate(resource, "Appointment.cancelationReason.exists() implies (Appointment.status='no-show' or Appointment.status='cancelled')", { resource }));
35
+ if (!result2.every(Boolean)) {
36
+ errors.push("Constraint violation: Cancelation reason is only used for appointments that have been cancelled, or no-show");
37
+ }
38
+
39
+ const result3 = await Promise.resolve(fhirpath.evaluate(resource, "contained.contained.empty()", { resource }));
40
+ if (!result3.every(Boolean)) {
41
+ errors.push("Constraint violation: If the resource is contained in another resource, it SHALL NOT contain nested Resources");
42
+ }
43
+
44
+ const result4 = await Promise.resolve(fhirpath.evaluate(resource, "contained.where((('#'+id in (%resource.descendants().reference | %resource.descendants().as(canonical) | %resource.descendants().as(uri) | %resource.descendants().as(url))) or descendants().where(reference = '#').exists() or descendants().where(as(canonical) = '#').exists() or descendants().where(as(canonical) = '#').exists()).not()).trace('unmatched', id).empty()", { resource }));
45
+ if (!result4.every(Boolean)) {
46
+ errors.push("Constraint violation: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource or SHALL refer to the containing resource");
47
+ }
48
+
49
+ const result5 = await Promise.resolve(fhirpath.evaluate(resource, "contained.meta.versionId.empty() and contained.meta.lastUpdated.empty()", { resource }));
50
+ if (!result5.every(Boolean)) {
51
+ errors.push("Constraint violation: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated");
52
+ }
53
+
54
+ const result6 = await Promise.resolve(fhirpath.evaluate(resource, "contained.meta.security.empty()", { resource }));
55
+ if (!result6.every(Boolean)) {
56
+ errors.push("Constraint violation: If a resource is contained in another resource, it SHALL NOT have a security label");
57
+ }
58
+
59
+ const result7 = await Promise.resolve(fhirpath.evaluate(resource, "text.`div`.exists()", { resource }));
60
+ if (!result7.every(Boolean)) {
61
+ warnings.push("Constraint violation: A resource should have narrative for robust management");
62
+ }
63
+
64
+ const result8 = await Promise.resolve(fhirpath.evaluate(resource, "hasValue() or (children().count() > id.count())", { resource }));
65
+ if (!result8.every(Boolean)) {
66
+ errors.push("Constraint violation: All FHIR elements must have a @value or children");
67
+ }
68
+
69
+ const result9 = await Promise.resolve(fhirpath.evaluate(resource, "type.exists() or actor.exists()", { resource }));
70
+ if (!result9.every(Boolean)) {
71
+ errors.push("Constraint violation: Either the type or actor on the participant SHALL be specified");
72
+ }
73
+
74
+ const result10 = await Promise.resolve(fhirpath.evaluate(resource, "status.exists()", { resource }));
75
+ if (!result10.every(Boolean)) {
76
+ errors.push("Constraint violation: status must be present");
77
+ }
78
+
79
+ const result11 = await Promise.resolve(fhirpath.evaluate(resource, "start.exists()", { resource }));
80
+ if (!result11.every(Boolean)) {
81
+ errors.push("Constraint violation: start must be present");
82
+ }
83
+
84
+ const result12 = await Promise.resolve(fhirpath.evaluate(resource, "participant.exists()", { resource }));
85
+ if (!result12.every(Boolean)) {
86
+ errors.push("Constraint violation: participant must be present");
87
+ }
88
+
89
+ const result13 = await Promise.resolve(fhirpath.evaluate(resource, "participant.status.exists()", { resource }));
90
+ if (!result13.every(Boolean)) {
91
+ errors.push("Constraint violation: participant.status must be present");
92
+ }
93
+ return { errors, warnings };
94
+ }
@@ -0,0 +1,194 @@
1
+
2
+ import { validateAdmissionAppointment, AdmissionAppointment } from "./AdmissionAppointment.js";
3
+ // Only import helpers actually referenced below (dynamic based on required fields)
4
+ import { randomId, setRandomSeed } from "./RandomSupport.js";
5
+
6
+ // Helper emitted only when meta.profile enforcement is applicable (resource profiles)
7
+
8
+
9
+ // Internal helper (emitted per class file) to clone without resorting to 'any'.
10
+ function safeClone<T>(value: T): T {
11
+ // Use native structuredClone if present; otherwise fallback to JSON round-trip.
12
+ // We intentionally avoid casting through any to satisfy no-explicit-any lint rule.
13
+ const g: unknown = globalThis as unknown;
14
+ // Narrow globalThis to an object potentially containing structuredClone.
15
+ if (typeof (g as { structuredClone?: unknown }).structuredClone === 'function') {
16
+ return (g as { structuredClone: <U>(arg: U) => U }).structuredClone(value);
17
+ }
18
+ return JSON.parse(JSON.stringify(value)) as T; // best-effort deep clone
19
+ }
20
+
21
+ export class AdmissionAppointmentClass {
22
+ constructor(private resource: AdmissionAppointment) {
23
+
24
+ }
25
+ /** Validate current resource instance. */
26
+ async validate(): Promise<{ errors: string[], warnings: string[] }> {
27
+ return validateAdmissionAppointment(this.resource);
28
+ }
29
+
30
+ getResource(): AdmissionAppointment {
31
+ return this.resource;
32
+ }
33
+
34
+ setResource(resource: AdmissionAppointment): void {
35
+ this.resource = resource;
36
+
37
+ }
38
+
39
+ /**
40
+ * Returns a plain JSON clone of the underlying resource ONLY if the last validation succeeded with zero errors.
41
+ * Call validate() first; otherwise this method throws to prevent accidental use of invalid / unchecked data.
42
+ */
43
+ async toJSON(): Promise<AdmissionAppointment> {
44
+ return safeClone(this.resource);
45
+ }
46
+
47
+ /**
48
+ * Create an empty instance for parity testing: for true FHIR resources, includes only resourceType; for datatypes, returns an empty object.
49
+ * Note: meta.profile (when applicable) will be ensured by the constructor when wrapping this resource.
50
+ */
51
+ static empty(overrides: Partial<AdmissionAppointment> = {}): AdmissionAppointment {
52
+ // For Resource profiles, include only resourceType. For Extension profiles, include url so the instance is minimally meaningful.
53
+ const base: Record<string, unknown> = {
54
+
55
+
56
+ };
57
+ return { ...(base as unknown as AdmissionAppointment), ...overrides };
58
+ }
59
+
60
+ /**
61
+ * Create a minimal randomized instance of AdmissionAppointment.
62
+ * Populates id, resourceType (if applicable) and primitive required (min>0) top-level fields with placeholders.
63
+ */
64
+ static random(overrides: Partial<AdmissionAppointment> = {}, opts?: { seed?: number }): AdmissionAppointment {
65
+ if (opts?.seed !== undefined) {
66
+ // Direct call (no dynamic require) to ensure deterministic seeding works under ESM.
67
+ setRandomSeed(opts.seed);
68
+ }
69
+ // Start with a Partial to avoid unsafe casting errors; we'll assert at the end.
70
+ const base: Record<string, unknown> = { id: randomId(), };
71
+ // Always include meta.profile with this profile's canonical URL if available & only for resource profiles
72
+ // (no profileUrl provided or profile targets a datatype – meta.profile not applicable)
73
+ // Opportunistic enrichment: add some common fields if absent and the interface suggests a matching resource type name.
74
+ try {
75
+ const maybe: Record<string, unknown> = base as unknown as Record<string, unknown>;
76
+ const baseRes = 'Appointment';
77
+ // Identifier-like profile: ensure system & value
78
+ if (/Identifier/i.test(baseRes)) {
79
+ if (!('system' in maybe)) maybe.system = 'urn:ietf:rfc:3986';
80
+ if (!('value' in maybe)) maybe.value = 'urn:uuid:' + randomId();
81
+ if (!('type' in maybe)) maybe.type = { text: 'Primary Identifier' };
82
+ }
83
+ // Patient / Practitioner names
84
+ if (/Patient|Practitioner/i.test(baseRes)) {
85
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
86
+ if (!('name' in maybe)) maybe.name = [{ family: randomId(), given: [randomId()] }];
87
+ if (!('gender' in maybe)) maybe.gender = 'unknown';
88
+ if (!('birthDate' in maybe)) maybe.birthDate = new Date(Date.now() - 10*365*24*60*60*1000).toISOString().substring(0,10);
89
+ if (!('address' in maybe)) maybe.address = [{ city: 'Example City', postalCode: '12345', country: 'DE' }];
90
+ }
91
+ if (/Condition/i.test(baseRes)) {
92
+ if (!('code' in maybe)) maybe.code = { coding: [{ system: 'http://snomed.info/sct', code: '64572001', display: 'Condition' }], text: 'Example condition' };
93
+ if (!('clinicalStatus' in maybe)) maybe.clinicalStatus = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-clinical', code: 'active' }] };
94
+ if (!('verificationStatus' in maybe)) maybe.verificationStatus = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status', code: 'confirmed' }] };
95
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
96
+ if (!('category' in maybe)) maybe.category = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-category', code: 'encounter-diagnosis' }] }];
97
+ }
98
+ if (/Procedure/i.test(baseRes)) {
99
+ if (!('code' in maybe)) maybe.code = { coding: [{ system: 'http://snomed.info/sct', code: '71388002', display: 'Procedure' }], text: 'Example procedure' };
100
+ if (!('status' in maybe)) maybe.status = 'completed';
101
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
102
+ if (!('performedDateTime' in maybe)) maybe.performedDateTime = new Date().toISOString();
103
+ if (!('category' in maybe)) maybe.category = { coding: [{ system: 'http://snomed.info/sct', code: '387713003' }] };
104
+ }
105
+ if (/Encounter/i.test(baseRes)) {
106
+ if (!('status' in maybe)) maybe.status = 'finished';
107
+ if (!('class' in maybe)) maybe.class = { system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'IMP', display: 'inpatient encounter' };
108
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
109
+ if (!('period' in maybe)) maybe.period = { start: new Date(Date.now() - 7*24*60*60*1000).toISOString(), end: new Date().toISOString() };
110
+ if (!('type' in maybe)) maybe.type = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'IMP' }] }];
111
+ if (!('serviceProvider' in maybe)) maybe.serviceProvider = { reference: 'Organization/' + randomId() };
112
+ }
113
+ if (/Account/i.test(baseRes)) {
114
+ if (!('status' in maybe)) maybe.status = 'active';
115
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'PBILLACCT' }] };
116
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
117
+ if (!('subject' in maybe)) maybe.subject = [{ reference: 'Patient/' + randomId() }];
118
+ }
119
+ if (/Bundle/i.test(baseRes)) {
120
+ if (!('type' in maybe)) maybe.type = 'document';
121
+ if (!('timestamp' in maybe)) maybe.timestamp = new Date().toISOString();
122
+ if (!('identifier' in maybe)) maybe.identifier = { system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() };
123
+ if (!('entry' in maybe)) maybe.entry = [{ fullUrl: 'urn:uuid:' + randomId(), resource: { resourceType: 'Composition', id: randomId(), status: 'final', type: { coding: [{ system: 'http://loinc.org', code: '60591-5' }] }, subject: { reference: 'Patient/' + randomId() }, date: new Date().toISOString(), author: [{ reference: 'Practitioner/' + randomId() }], title: 'Example Document', section: [] } }];
124
+ }
125
+ if (/Composition/i.test(baseRes)) {
126
+ if (!('status' in maybe)) maybe.status = 'final';
127
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://loinc.org', code: '60591-5', display: 'Patient summary Document' }] };
128
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
129
+ if (!('date' in maybe)) maybe.date = new Date().toISOString();
130
+ if (!('author' in maybe)) maybe.author = [{ reference: 'Practitioner/' + randomId() }];
131
+ if (!('title' in maybe)) maybe.title = 'Patient Summary';
132
+ if (!('section' in maybe)) maybe.section = [];
133
+ }
134
+ if (/ValueSet/i.test(baseRes)) {
135
+ if (!('status' in maybe)) maybe.status = 'active';
136
+ if (!('url' in maybe)) maybe.url = 'urn:uuid:' + randomId();
137
+ if (!('name' in maybe)) maybe.name = 'ExampleValueSet';
138
+ if (!('version' in maybe)) maybe.version = '1.0.0';
139
+ if (!('compose' in maybe)) maybe.compose = { include: [{ system: 'http://loinc.org', concept: [{ code: '1234-5', display: 'Example LOINC' }] }] };
140
+ }
141
+ if (/CodeSystem/i.test(baseRes)) {
142
+ if (!('status' in maybe)) maybe.status = 'active';
143
+ if (!('content' in maybe)) maybe.content = 'complete';
144
+ if (!('url' in maybe)) maybe.url = 'urn:uuid:' + randomId();
145
+ if (!('name' in maybe)) maybe.name = 'ExampleCodeSystem';
146
+ if (!('version' in maybe)) maybe.version = '1.0.0';
147
+ if (!('concept' in maybe)) maybe.concept = [{ code: 'example', display: 'Example Code' }];
148
+ }
149
+ if (/RelatedPerson/i.test(baseRes)) {
150
+ if (!('patient' in maybe)) maybe.patient = { reference: 'Patient/' + randomId() };
151
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
152
+ if (!('name' in maybe)) maybe.name = [{ family: randomId(), given: [randomId()] }];
153
+ if (!('relationship' in maybe)) maybe.relationship = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode', code: 'FTH', display: 'father' }] }];
154
+ if (!('address' in maybe)) maybe.address = [{ city: 'Example City', postalCode: '12345', country: 'DE' }];
155
+ }
156
+ if (/DocumentReference/i.test(baseRes)) {
157
+ if (!('status' in maybe)) maybe.status = 'current';
158
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://loinc.org', code: '34133-9' }] };
159
+ if (!('category' in maybe)) maybe.category = [{ coding: [{ system: 'http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category', code: 'clinical-note' }] }];
160
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
161
+ if (!('content' in maybe)) maybe.content = [{ attachment: { contentType: 'application/pdf', data: 'ZXhhbXBsZQ==' } }]; // 'example'
162
+ }
163
+ if (/Binary/i.test(baseRes)) {
164
+ if (!('contentType' in maybe)) maybe.contentType = 'application/pdf';
165
+ if (!('data' in maybe)) maybe.data = 'SGVsbG8gV29ybGQ='; // "Hello World" in base64
166
+ }
167
+ if (/Coverage/i.test(baseRes)) {
168
+ if (!('status' in maybe)) maybe.status = 'active';
169
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'EHCPOL', display: 'extended healthcare' }] };
170
+ if (!('subscriberId' in maybe)) maybe.subscriberId = randomId();
171
+ if (!('beneficiary' in maybe)) maybe.beneficiary = { reference: 'Patient/' + randomId() };
172
+ if (!('payor' in maybe)) maybe.payor = [{ reference: 'Organization/' + randomId() }];
173
+ }
174
+ // Generic narrative for DomainResource derivatives
175
+ if (/DomainResource|Patient|Account|Bundle|Composition|Binary|CodeSystem|Condition|Encounter|Practitioner|Procedure|ValueSet|RelatedPerson|DocumentReference|Coverage/.test(baseRes)) {
176
+ if (!('text' in maybe)) maybe.text = { status: 'generated', div: '<div xmlns="http://www.w3.org/1999/xhtml">Generated</div>' };
177
+ }
178
+ // Extension value fallback: if profile base is Extension ensure a value[x]
179
+ if (/Extension/.test(baseRes)) {
180
+ const hasValue = Object.keys(maybe).some(k => k.startsWith('value'));
181
+ if (!hasValue) maybe['valueString'] = 'value';
182
+ // For Extension profiles, set url to the profile's canonical URL if available
183
+ if (!('url' in maybe)) maybe.url = 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionAppointment';
184
+ }
185
+ } catch { /* enrichment is best-effort */ }
186
+ // Cast through unknown to acknowledge dynamic enrichment
187
+ return { ...(base as unknown as AdmissionAppointment), ...overrides };
188
+ }
189
+
190
+ /** Convenience: returns a class instance wrapping a randomized resource. */
191
+ static randomClass(overrides: Partial<AdmissionAppointment> = {}, opts?: { seed?: number }): AdmissionAppointmentClass {
192
+ return new AdmissionAppointmentClass(this.random(overrides, opts));
193
+ }
194
+ }
@@ -0,0 +1,29 @@
1
+ import { Extension } from "fhir/r4";
2
+
3
+ export interface AdmissionCheckIn extends Extension {
4
+ url: "http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckIn";
5
+ }
6
+
7
+
8
+ import fhirpath from "fhirpath";
9
+
10
+ export async function validateAdmissionCheckIn(resource: AdmissionCheckIn): Promise<{ errors: string[], warnings: string[] }> {
11
+ const errors: string[] = [];
12
+ const warnings: string[] = [];
13
+
14
+ const result0 = await Promise.resolve(fhirpath.evaluate(resource, "hasValue() or (children().count() > id.count())", { resource }));
15
+ if (!result0.every(Boolean)) {
16
+ errors.push("Constraint violation: All FHIR elements must have a @value or children");
17
+ }
18
+
19
+ const result1 = await Promise.resolve(fhirpath.evaluate(resource, "url.exists()", { resource }));
20
+ if (!result1.every(Boolean)) {
21
+ errors.push("Constraint violation: url must be present");
22
+ }
23
+
24
+ const result2 = await Promise.resolve(fhirpath.evaluate(resource, "value.exists()", { resource }));
25
+ if (!result2.every(Boolean)) {
26
+ errors.push("Constraint violation: value must be present");
27
+ }
28
+ return { errors, warnings };
29
+ }
@@ -0,0 +1,194 @@
1
+
2
+ import { validateAdmissionCheckIn, AdmissionCheckIn } from "./AdmissionCheckIn.js";
3
+ // Only import helpers actually referenced below (dynamic based on required fields)
4
+ import { randomId, setRandomSeed, skeletonCodeableConcept } from "./RandomSupport.js";
5
+
6
+ // Helper emitted only when meta.profile enforcement is applicable (resource profiles)
7
+
8
+
9
+ // Internal helper (emitted per class file) to clone without resorting to 'any'.
10
+ function safeClone<T>(value: T): T {
11
+ // Use native structuredClone if present; otherwise fallback to JSON round-trip.
12
+ // We intentionally avoid casting through any to satisfy no-explicit-any lint rule.
13
+ const g: unknown = globalThis as unknown;
14
+ // Narrow globalThis to an object potentially containing structuredClone.
15
+ if (typeof (g as { structuredClone?: unknown }).structuredClone === 'function') {
16
+ return (g as { structuredClone: <U>(arg: U) => U }).structuredClone(value);
17
+ }
18
+ return JSON.parse(JSON.stringify(value)) as T; // best-effort deep clone
19
+ }
20
+
21
+ export class AdmissionCheckInClass {
22
+ constructor(private resource: AdmissionCheckIn) {
23
+
24
+ }
25
+ /** Validate current resource instance. */
26
+ async validate(): Promise<{ errors: string[], warnings: string[] }> {
27
+ return validateAdmissionCheckIn(this.resource);
28
+ }
29
+
30
+ getResource(): AdmissionCheckIn {
31
+ return this.resource;
32
+ }
33
+
34
+ setResource(resource: AdmissionCheckIn): void {
35
+ this.resource = resource;
36
+
37
+ }
38
+
39
+ /**
40
+ * Returns a plain JSON clone of the underlying resource ONLY if the last validation succeeded with zero errors.
41
+ * Call validate() first; otherwise this method throws to prevent accidental use of invalid / unchecked data.
42
+ */
43
+ async toJSON(): Promise<AdmissionCheckIn> {
44
+ return safeClone(this.resource);
45
+ }
46
+
47
+ /**
48
+ * Create an empty instance for parity testing: for true FHIR resources, includes only resourceType; for datatypes, returns an empty object.
49
+ * Note: meta.profile (when applicable) will be ensured by the constructor when wrapping this resource.
50
+ */
51
+ static empty(overrides: Partial<AdmissionCheckIn> = {}): AdmissionCheckIn {
52
+ // For Resource profiles, include only resourceType. For Extension profiles, include url so the instance is minimally meaningful.
53
+ const base: Record<string, unknown> = {
54
+
55
+ url: 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckIn',
56
+ };
57
+ return { ...(base as unknown as AdmissionCheckIn), ...overrides };
58
+ }
59
+
60
+ /**
61
+ * Create a minimal randomized instance of AdmissionCheckIn.
62
+ * Populates id, resourceType (if applicable) and primitive required (min>0) top-level fields with placeholders.
63
+ */
64
+ static random(overrides: Partial<AdmissionCheckIn> = {}, opts?: { seed?: number }): AdmissionCheckIn {
65
+ if (opts?.seed !== undefined) {
66
+ // Direct call (no dynamic require) to ensure deterministic seeding works under ESM.
67
+ setRandomSeed(opts.seed);
68
+ }
69
+ // Start with a Partial to avoid unsafe casting errors; we'll assert at the end.
70
+ const base: Record<string, unknown> = { id: randomId(), url: "http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckIn", valueCodeableConcept: skeletonCodeableConcept(), };
71
+ // Always include meta.profile with this profile's canonical URL if available & only for resource profiles
72
+ // (no profileUrl provided or profile targets a datatype – meta.profile not applicable)
73
+ // Opportunistic enrichment: add some common fields if absent and the interface suggests a matching resource type name.
74
+ try {
75
+ const maybe: Record<string, unknown> = base as unknown as Record<string, unknown>;
76
+ const baseRes = 'Extension';
77
+ // Identifier-like profile: ensure system & value
78
+ if (/Identifier/i.test(baseRes)) {
79
+ if (!('system' in maybe)) maybe.system = 'urn:ietf:rfc:3986';
80
+ if (!('value' in maybe)) maybe.value = 'urn:uuid:' + randomId();
81
+ if (!('type' in maybe)) maybe.type = { text: 'Primary Identifier' };
82
+ }
83
+ // Patient / Practitioner names
84
+ if (/Patient|Practitioner/i.test(baseRes)) {
85
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
86
+ if (!('name' in maybe)) maybe.name = [{ family: randomId(), given: [randomId()] }];
87
+ if (!('gender' in maybe)) maybe.gender = 'unknown';
88
+ if (!('birthDate' in maybe)) maybe.birthDate = new Date(Date.now() - 10*365*24*60*60*1000).toISOString().substring(0,10);
89
+ if (!('address' in maybe)) maybe.address = [{ city: 'Example City', postalCode: '12345', country: 'DE' }];
90
+ }
91
+ if (/Condition/i.test(baseRes)) {
92
+ if (!('code' in maybe)) maybe.code = { coding: [{ system: 'http://snomed.info/sct', code: '64572001', display: 'Condition' }], text: 'Example condition' };
93
+ if (!('clinicalStatus' in maybe)) maybe.clinicalStatus = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-clinical', code: 'active' }] };
94
+ if (!('verificationStatus' in maybe)) maybe.verificationStatus = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status', code: 'confirmed' }] };
95
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
96
+ if (!('category' in maybe)) maybe.category = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/condition-category', code: 'encounter-diagnosis' }] }];
97
+ }
98
+ if (/Procedure/i.test(baseRes)) {
99
+ if (!('code' in maybe)) maybe.code = { coding: [{ system: 'http://snomed.info/sct', code: '71388002', display: 'Procedure' }], text: 'Example procedure' };
100
+ if (!('status' in maybe)) maybe.status = 'completed';
101
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
102
+ if (!('performedDateTime' in maybe)) maybe.performedDateTime = new Date().toISOString();
103
+ if (!('category' in maybe)) maybe.category = { coding: [{ system: 'http://snomed.info/sct', code: '387713003' }] };
104
+ }
105
+ if (/Encounter/i.test(baseRes)) {
106
+ if (!('status' in maybe)) maybe.status = 'finished';
107
+ if (!('class' in maybe)) maybe.class = { system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'IMP', display: 'inpatient encounter' };
108
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
109
+ if (!('period' in maybe)) maybe.period = { start: new Date(Date.now() - 7*24*60*60*1000).toISOString(), end: new Date().toISOString() };
110
+ if (!('type' in maybe)) maybe.type = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'IMP' }] }];
111
+ if (!('serviceProvider' in maybe)) maybe.serviceProvider = { reference: 'Organization/' + randomId() };
112
+ }
113
+ if (/Account/i.test(baseRes)) {
114
+ if (!('status' in maybe)) maybe.status = 'active';
115
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'PBILLACCT' }] };
116
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
117
+ if (!('subject' in maybe)) maybe.subject = [{ reference: 'Patient/' + randomId() }];
118
+ }
119
+ if (/Bundle/i.test(baseRes)) {
120
+ if (!('type' in maybe)) maybe.type = 'document';
121
+ if (!('timestamp' in maybe)) maybe.timestamp = new Date().toISOString();
122
+ if (!('identifier' in maybe)) maybe.identifier = { system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() };
123
+ if (!('entry' in maybe)) maybe.entry = [{ fullUrl: 'urn:uuid:' + randomId(), resource: { resourceType: 'Composition', id: randomId(), status: 'final', type: { coding: [{ system: 'http://loinc.org', code: '60591-5' }] }, subject: { reference: 'Patient/' + randomId() }, date: new Date().toISOString(), author: [{ reference: 'Practitioner/' + randomId() }], title: 'Example Document', section: [] } }];
124
+ }
125
+ if (/Composition/i.test(baseRes)) {
126
+ if (!('status' in maybe)) maybe.status = 'final';
127
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://loinc.org', code: '60591-5', display: 'Patient summary Document' }] };
128
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
129
+ if (!('date' in maybe)) maybe.date = new Date().toISOString();
130
+ if (!('author' in maybe)) maybe.author = [{ reference: 'Practitioner/' + randomId() }];
131
+ if (!('title' in maybe)) maybe.title = 'Patient Summary';
132
+ if (!('section' in maybe)) maybe.section = [];
133
+ }
134
+ if (/ValueSet/i.test(baseRes)) {
135
+ if (!('status' in maybe)) maybe.status = 'active';
136
+ if (!('url' in maybe)) maybe.url = 'urn:uuid:' + randomId();
137
+ if (!('name' in maybe)) maybe.name = 'ExampleValueSet';
138
+ if (!('version' in maybe)) maybe.version = '1.0.0';
139
+ if (!('compose' in maybe)) maybe.compose = { include: [{ system: 'http://loinc.org', concept: [{ code: '1234-5', display: 'Example LOINC' }] }] };
140
+ }
141
+ if (/CodeSystem/i.test(baseRes)) {
142
+ if (!('status' in maybe)) maybe.status = 'active';
143
+ if (!('content' in maybe)) maybe.content = 'complete';
144
+ if (!('url' in maybe)) maybe.url = 'urn:uuid:' + randomId();
145
+ if (!('name' in maybe)) maybe.name = 'ExampleCodeSystem';
146
+ if (!('version' in maybe)) maybe.version = '1.0.0';
147
+ if (!('concept' in maybe)) maybe.concept = [{ code: 'example', display: 'Example Code' }];
148
+ }
149
+ if (/RelatedPerson/i.test(baseRes)) {
150
+ if (!('patient' in maybe)) maybe.patient = { reference: 'Patient/' + randomId() };
151
+ if (!('identifier' in maybe)) maybe.identifier = [{ system: 'urn:ietf:rfc:3986', value: 'urn:uuid:' + randomId() }];
152
+ if (!('name' in maybe)) maybe.name = [{ family: randomId(), given: [randomId()] }];
153
+ if (!('relationship' in maybe)) maybe.relationship = [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode', code: 'FTH', display: 'father' }] }];
154
+ if (!('address' in maybe)) maybe.address = [{ city: 'Example City', postalCode: '12345', country: 'DE' }];
155
+ }
156
+ if (/DocumentReference/i.test(baseRes)) {
157
+ if (!('status' in maybe)) maybe.status = 'current';
158
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://loinc.org', code: '34133-9' }] };
159
+ if (!('category' in maybe)) maybe.category = [{ coding: [{ system: 'http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category', code: 'clinical-note' }] }];
160
+ if (!('subject' in maybe)) maybe.subject = { reference: 'Patient/' + randomId() };
161
+ if (!('content' in maybe)) maybe.content = [{ attachment: { contentType: 'application/pdf', data: 'ZXhhbXBsZQ==' } }]; // 'example'
162
+ }
163
+ if (/Binary/i.test(baseRes)) {
164
+ if (!('contentType' in maybe)) maybe.contentType = 'application/pdf';
165
+ if (!('data' in maybe)) maybe.data = 'SGVsbG8gV29ybGQ='; // "Hello World" in base64
166
+ }
167
+ if (/Coverage/i.test(baseRes)) {
168
+ if (!('status' in maybe)) maybe.status = 'active';
169
+ if (!('type' in maybe)) maybe.type = { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'EHCPOL', display: 'extended healthcare' }] };
170
+ if (!('subscriberId' in maybe)) maybe.subscriberId = randomId();
171
+ if (!('beneficiary' in maybe)) maybe.beneficiary = { reference: 'Patient/' + randomId() };
172
+ if (!('payor' in maybe)) maybe.payor = [{ reference: 'Organization/' + randomId() }];
173
+ }
174
+ // Generic narrative for DomainResource derivatives
175
+ if (/DomainResource|Patient|Account|Bundle|Composition|Binary|CodeSystem|Condition|Encounter|Practitioner|Procedure|ValueSet|RelatedPerson|DocumentReference|Coverage/.test(baseRes)) {
176
+ if (!('text' in maybe)) maybe.text = { status: 'generated', div: '<div xmlns="http://www.w3.org/1999/xhtml">Generated</div>' };
177
+ }
178
+ // Extension value fallback: if profile base is Extension ensure a value[x]
179
+ if (/Extension/.test(baseRes)) {
180
+ const hasValue = Object.keys(maybe).some(k => k.startsWith('value'));
181
+ if (!hasValue) maybe['valueString'] = 'value';
182
+ // For Extension profiles, set url to the profile's canonical URL if available
183
+ if (!('url' in maybe)) maybe.url = 'http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckIn';
184
+ }
185
+ } catch { /* enrichment is best-effort */ }
186
+ // Cast through unknown to acknowledge dynamic enrichment
187
+ return { ...(base as unknown as AdmissionCheckIn), ...overrides };
188
+ }
189
+
190
+ /** Convenience: returns a class instance wrapping a randomized resource. */
191
+ static randomClass(overrides: Partial<AdmissionCheckIn> = {}, opts?: { seed?: number }): AdmissionCheckInClass {
192
+ return new AdmissionCheckInClass(this.random(overrides, opts));
193
+ }
194
+ }
@@ -0,0 +1,29 @@
1
+ import { Extension } from "fhir/r4";
2
+
3
+ export interface AdmissionCheckInNoCode extends Extension {
4
+ url: "http://fhir.pink-protocol.io/StructureDefinition/AdmissionCheckInNoCode";
5
+ }
6
+
7
+
8
+ import fhirpath from "fhirpath";
9
+
10
+ export async function validateAdmissionCheckInNoCode(resource: AdmissionCheckInNoCode): Promise<{ errors: string[], warnings: string[] }> {
11
+ const errors: string[] = [];
12
+ const warnings: string[] = [];
13
+
14
+ const result0 = await Promise.resolve(fhirpath.evaluate(resource, "hasValue() or (children().count() > id.count())", { resource }));
15
+ if (!result0.every(Boolean)) {
16
+ errors.push("Constraint violation: All FHIR elements must have a @value or children");
17
+ }
18
+
19
+ const result1 = await Promise.resolve(fhirpath.evaluate(resource, "url.exists()", { resource }));
20
+ if (!result1.every(Boolean)) {
21
+ errors.push("Constraint violation: url must be present");
22
+ }
23
+
24
+ const result2 = await Promise.resolve(fhirpath.evaluate(resource, "value.exists()", { resource }));
25
+ if (!result2.every(Boolean)) {
26
+ errors.push("Constraint violation: value must be present");
27
+ }
28
+ return { errors, warnings };
29
+ }