Ir al contenido

Tipos, validators y resolver

35 recursos R4 hand-crafted (no autogenerados). Distribución por dominio:

DominioRecursos
ClínicoPatient, Encounter, Condition, Observation, Procedure, AllergyIntolerance, MedicationRequest, Medication, DiagnosticReport, Specimen
DocumentosDocumentReference, Communication
PlanificaciónCarePlan, Task, ServiceRequest, PlanDefinition, ActivityDefinition
AdministrativoCoverage, Flag, RelatedPerson
FacturaciónInvoice, ChargeItem, PaymentNotice
AgendamientoAppointment, Schedule, Slot
OrganizacionalOrganization, Location, Device, Practitioner, PractitionerRole, HealthcareService
CuestionariosQuestionnaire, QuestionnaireResponse
InfraestructuraBundle

Record<ResourceType, Type> permite que el cliente infiera el tipo de retorno automáticamente sin pasar genéricos:

const patient = await fhir.read('Patient', id);
// ^ Patient (inferido)
const obs = await fhir.read('Observation', id);
// ^ Observation (inferido)

Esto reemplaza el patrón fhir.read<Patient>('Patient', id) que era verbose y propenso a errores.

Construye un Reference FHIR canonical:

import { ref } from 'emafhir';
const r = ref('Patient', 'abc123', 'Ana Pérez');
// → { reference: "Patient/abc123", display: "Ana Pérez" }

Construye un Bundle de tipo transaction o batch:

import { transaction } from 'emafhir';
const bundle = transaction()
.create(patient)
.create(observation)
.update(encounter)
.delete('Task', 'task-123')
.build();
await fhir.transaction(bundle);

Fluent API para construir SearchParameters:

import { search } from 'emafhir';
const params = search()
.where('name', 'García')
.whereToken('gender', 'http://hl7.org/fhir/administrative-gender', 'female')
.whereDate('birthdate', 'ge', '1990-01-01')
.include('Patient:general-practitioner')
.sort('-_lastUpdated')
.count(20)
.build();
const result = await fhir.searchResources('Patient', params);

Cuatro validadores de identificadores nacionales, todos puros (estructura + checksum, sin lookups externos a SII / RENAPO / SAT / RENIEC):

PaísFunciónVerifica
ChilevalidateRunRUN módulo 11. Acepta 12.345.678-K y 123456785
MéxicovalidateCurpCURP estructura (18 chars, H/M, 32 estados RENAPO)
MéxicovalidateRfcRFC estructura — persona física (13 chars), moral (12), permite Ñ y & (spec SAT)
PerúvalidateDniDNI estructura (8 dígitos numéricos)

Todas devuelven { valid: boolean, normalized: string }:

import { validateRun, validateCurp } from 'emafhir/validators';
validateRun('12.345.678-5'); // { valid: true, normalized: '12345678-5' }
validateRun('12.345.678-K'); // { valid: false, normalized: null }
validateCurp('garc850315hdfllr03'); // { valid: true, normalized: 'GARC850315HDFLLR03' }

Clase que resuelve el canonical URL de un perfil FHIR para un país y un recurso, leyendo runtime desde emashared.countries.

profile_url = `${ig_canonical_base}/StructureDefinition/${resourceType}`

Sin sufijo de versión. Aidbox FAR resuelve la versión activa al momento del write/read (ver ADR-0002).

const resolver = new ProfileResolver(supabaseClient);
// Resolver perfil para un país y recurso
const url = await resolver.getProfileUrl('CL', 'Patient');
// → 'https://emahealth.io/fhir/cl/StructureDefinition/Patient'
// Listar países activos
const countries = await resolver.getSupportedCountries();
// → ['CL', 'MX', 'PE', 'CO', 'BR', 'AR', 'CR']
// Forzar refresh del cache (Vault llama a esto on CRUD de país)
resolver.invalidate();

No

No

getProfileUrl

country, resourceType

¿Cache válido

< 5 min?

Retornar canonical URL

¿Hay query

en vuelo?

Esperar promesa compartida

thundering herd guard

Query Supabase

SELECT * FROM emashared.countries

Almacenar en cache

+ TTL 5 min

Resolver URL

Por qué importa el dedup: si 100 writes simultáneos llegan al worker y el cache está vacío, sólo una query a Supabase se ejecuta. Las demás esperan la misma promesa.

Función async idempotente que agrega/actualiza:

  • meta.profile — canonical resuelto via ProfileResolver, sin versión.
  • meta.tag = [{ system: 'urn:ema:tenant', code: tenant.id }]
  • meta.tag = [{ system: 'urn:ema:ig-version', code: tenant.igVersion }]
import { injectMeta } from 'emafhir';
const enriched = await injectMeta(resource, tenant, profileResolver);

Idempotente: llamarla dos veces sobre el mismo recurso no duplica tags ni profiles. Inmutable: el input no se mutea, devuelve un nuevo objeto. Preserva tags externos que no estén en los namespaces que controla.