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.
- package/README.md +7 -7
- package/out/generator/index.js +10 -15
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionAppointment.ts +94 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionAppointmentClass.ts +194 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckIn.ts +29 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInClass.ts +194 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInNoCode.ts +29 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionCheckInNoCodeClass.ts +194 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDataFlow.ts +51 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDataFlowClass.ts +194 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDevice.ts +67 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/AdmissionDeviceClass.ts +199 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/RandomSupport.ts +26 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/ValueSet-AdmissionFlowIds.ts +38 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/ValueSet-AdmissionReasonCodeGroup.ts +42 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/generated/valuesets/index.ts +18 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/.index.db +0 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/.index.json +234 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/CodeSystem-admission-flow-ids.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/CodeSystem-admission-reason-codes.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/ImplementationGuide-pink.admission.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionCheckIn.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionCheckInNoCode.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionUrl.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/SearchParameter-SearchParameter-Appointment-AdmissionUuid.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionAppointment.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionCheckIn.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionCheckInNoCode.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDataFlow.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDevice.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionDocumentMetadata.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormData.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormUrl.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionFormUuid.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionIntegrationPoints.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionPatient.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionPatientData.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-AdmissionRoutingRule.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-LocationTomlConfiguration.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-Pink-Location-With-Toml.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/StructureDefinition-PinkLocation.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/ValueSet-AdmissionFlowIdsValueSet.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/ValueSet-AdmissionReasonCodeGroup.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/AllergyIntolerance-Example-PenicillinAllergy.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Appointment-Example-AdmissionAppointment.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Condition-Example-DiabetesCondition.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Device-Example-Surgery-Room.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/DocumentReference-Example-PreOperativeEvaluation.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Location-Example-Location-With-Toml.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/Patient-Example-Patient.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/example/QuestionnaireResponse-Example-AdmissionFormResponse.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/spec.internals +313 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/validation-oo.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/other/validation-summary.json +1 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/package.json +23 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionAppointment.sch +22 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionCheckIn.sch +18 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionCheckInNoCode.sch +18 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionDevice.sch +12 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionFormUrl.sch +18 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionFormUuid.sch +18 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-AdmissionPatient.sch +12 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-Pink-Location-With-Toml.sch +26 -0
- package/out/generator/temp/1761673242558-4u3in1qziv2/package/xml/StructureDefinition-PinkLocation.sch +12 -0
- 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
|
|
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`
|
|
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.
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
```ts
|
|
98
|
+
/// <reference path="./fhir-r4.d.ts" />
|
|
99
|
+
```
|
|
100
100
|
|
|
101
|
-
|
|
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
|
|
package/out/generator/index.js
CHANGED
|
@@ -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
|
|
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
|
+
}
|