Reference source · 0.1.0-dev.11
resource-profile.ts
src/resource-profile.ts · Download original file
import { requireThat } from './core.ts';
import { concept, validateFields, validateExtensions } from './fhir-validation.ts';
export const RESOURCE_TYPES = ['Condition','AllergyIntolerance','MedicationStatement','Encounter','Procedure','Immunization'] as const;
export type ResourceType = typeof RESOURCE_TYPES[number];
export const RESOURCE_SCOPES = {
Condition: { read: 'patient/Condition.rs', create: 'patient/Condition.c', update: 'patient/Condition.u', delete: 'patient/Condition.d' },
AllergyIntolerance: { read: 'patient/AllergyIntolerance.rs', create: 'patient/AllergyIntolerance.c', update: 'patient/AllergyIntolerance.u', delete: 'patient/AllergyIntolerance.d' },
MedicationStatement: { read: 'patient/MedicationStatement.rs', create: 'patient/MedicationStatement.c', update: 'patient/MedicationStatement.u', delete: 'patient/MedicationStatement.d' },
Encounter: { read: 'patient/Encounter.rs', create: 'patient/Encounter.c', update: 'patient/Encounter.u', delete: 'patient/Encounter.d' },
Procedure: { read: 'patient/Procedure.rs', create: 'patient/Procedure.c', update: 'patient/Procedure.u', delete: 'patient/Procedure.d' },
Immunization: { read: 'patient/Immunization.rs', create: 'patient/Immunization.c', update: 'patient/Immunization.u', delete: 'patient/Immunization.d' },
} as const;
export function resourceType(value: unknown): asserts value is ResourceType {
requireThat(typeof value === 'string' && (RESOURCE_TYPES as readonly string[]).includes(value), 'unsupported_resource_type');
}
export function resourceId(value: unknown): asserts value is string {
requireThat(typeof value === 'string' && /^[A-Za-z0-9.-]{1,64}$/.test(value), 'invalid_resource_id');
}
export function validateClinical(value: Record<string, any>, patient: string): void {
requireThat(value && typeof value === 'object' && !Array.isArray(value), 'invalid_resource'); resourceType(value.resourceType);
const field = ['AllergyIntolerance','Immunization'].includes(value.resourceType) ? 'patient' : 'subject';
requireThat(value[field]?.reference === `Patient/${patient}`, 'wrong_resource_patient');
if (value.id !== undefined) resourceId(value.id);
switch (value.resourceType) {
case 'Condition': case 'AllergyIntolerance': concept(value.code); break;
case 'MedicationStatement':
requireThat(['active','completed','entered-in-error','intended','stopped','on-hold','unknown','not-taken'].includes(value.status) && value.medicationReference === undefined, 'invalid_resource_status'); concept(value.medicationCodeableConcept); break;
case 'Encounter':
requireThat(['planned','arrived','triaged','in-progress','onleave','finished','cancelled','entered-in-error','unknown'].includes(value.status), 'invalid_resource_status');
requireThat(typeof value.class?.system === 'string' && typeof value.class?.code === 'string', 'invalid_resource_code'); break;
case 'Procedure':
requireThat(['preparation','in-progress','not-done','on-hold','stopped','completed','entered-in-error','unknown'].includes(value.status), 'invalid_resource_status'); concept(value.code); break;
case 'Immunization':
requireThat(['completed','entered-in-error','not-done'].includes(value.status), 'invalid_resource_status'); concept(value.vaccineCode);
requireThat((typeof value.occurrenceDateTime === 'string') !== (typeof value.occurrenceString === 'string'), 'invalid_resource_occurrence'); break;
}
validateFields(value);
const walk = (object: any, depth = 0): void => {
requireThat(depth < 40, 'resource_too_deep');
if (!object || typeof object !== 'object') return;
if (!Array.isArray(object)) validateExtensions(object);
for (const [key, nested] of Object.entries(object)) {
requireThat(!['contained','modifierExtension','photo','derivedFrom'].includes(key) && !key.toLowerCase().endsWith('attachment'), 'unsupported_resource_content');
if (key === 'reference' && typeof nested === 'string' && (nested.startsWith('Patient/') || nested.includes('/Patient/'))) requireThat(nested === `Patient/${patient}`, 'wrong_resource_patient');
walk(nested, depth + 1);
}
};
walk(value);
}