marmar-context / src / shared / emergency / contract.ts
contract.ts
Raw
import { z } from 'zod';

// This file and its fixtures are copied byte-for-byte to the backend by the
// sync script. Keep policy independent of React, storage, transport and Node.
export const MAX_REGISTRY_CHARS = 250_000;
const date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/).refine((value) => !Number.isNaN(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value);
const id = z.string().min(1).max(100);
const status = z.enum(['source_checked', 'needs_verification', 'withdrawn']);
const contactSchema = z.object({
  id, dialNumber: z.string().regex(/^\d{2,15}$/), service: z.enum(['medical', 'general']),
  channel: z.literal('voice'), domesticUseOnly: z.literal(true),
  phoneAccess: z.enum(['not_specified', 'mobile_only', 'fixed_and_mobile']),
  coverage: z.object({ type: z.enum(['country', 'region']), regionCodes: z.array(z.string().max(20)).max(100), label: z.string().min(1).max(100) }).strict(),
  evidenceStatus: status, sourceIds: z.array(id).min(1).max(20),
}).strict();
const countrySchema = z.object({
  countryCode: z.string().regex(/^[A-Z]{2}$/), name: z.string().min(1).max(100),
  selectionStatus: z.enum(['source_checked', 'region_required', 'needs_verification', 'withdrawn']),
  defaultMedicalContactId: id.nullable(), contacts: z.array(contactSchema).max(30), notes: z.array(z.string().max(2000)).max(20),
}).strict();
const registrySchema = z.object({
  schemaVersion: z.literal(1), datasetVersion: z.string().min(1).max(100), revision: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
  researchedAt: date,
  release: z.object({ status: z.literal('approved'), approvedBy: z.string().min(1).max(200), approvedAt: date }).strict(),
  scope: z.string().max(2000), unknownCountryContactId: z.null(),
  verification: z.object({ method: z.literal('public_web_sources'), liveCallsMade: z.boolean(), meaning: z.string().max(2000) }).strict(),
  countries: z.array(countrySchema).min(1).max(300),
  sources: z.record(id, z.object({ publisher: z.string().min(1).max(300), url: z.string().url().max(3000).refine((s) => s.startsWith('https://')),
    kind: z.enum(['local_authority', 'emergency_provider', 'supranational_authority', 'government_travel_advice', 'historical_research']),
    checkedAt: date, evidence: z.string().min(1).max(3000) }).strict()),
}).strict();
export type EmergencyRegistry = z.infer<typeof registrySchema>;
export type EmergencyContact = z.infer<typeof contactSchema>;
export const envelopeSchema = z.object({ keyId: id, payload: z.string().min(1).max(MAX_REGISTRY_CHARS), signature: z.string().regex(/^[a-f0-9]{128}$/) }).strict();
export type SignedRegistry = z.infer<typeof envelopeSchema>;

export function parseRegistry(input: unknown): EmergencyRegistry {
  const r = registrySchema.parse(input);
  if (r.release.approvedAt < r.researchedAt || Object.values(r.sources).some((source) => source.checkedAt > r.researchedAt)) throw new Error('Invalid emergency review dates');
  const countries = new Set<string>(), contacts = new Set<string>();
  for (const c of r.countries) {
    if (countries.has(c.countryCode)) throw new Error('Duplicate emergency country');
    countries.add(c.countryCode);
    for (const n of c.contacts) {
      if (contacts.has(n.id) || !n.id.startsWith(c.countryCode.toLowerCase() + '-')) throw new Error('Invalid emergency contact ID');
      contacts.add(n.id);
      if (n.sourceIds.some((s) => !r.sources[s])) throw new Error('Missing emergency source');
      if (n.coverage.type === 'region' ? !n.coverage.regionCodes.length || n.coverage.regionCodes.some((v) => !v.startsWith(c.countryCode + '-')) : n.coverage.regionCodes.length !== 0) throw new Error('Invalid emergency region');
    }
    if (c.selectionStatus === 'source_checked') {
      const n = c.contacts.find((n) => n.id === c.defaultMedicalContactId);
      if (!n || n.evidenceStatus !== 'source_checked' || n.coverage.type !== 'country' || n.phoneAccess === 'mobile_only') throw new Error('Unsafe emergency default');
    } else if (c.defaultMedicalContactId !== null) throw new Error('Unresolved emergency default');
  }
  return r;
}

export function readSignedRegistry(input: unknown, verify: (envelope: SignedRegistry) => boolean): { envelope: SignedRegistry; registry: EmergencyRegistry } {
  const envelope = envelopeSchema.parse(input);
  if (!verify(envelope)) throw new Error('Invalid emergency signature');
  return { envelope, registry: parseRegistry(JSON.parse(envelope.payload)) };
}

export function assertSuccessor(next: EmergencyRegistry, current: EmergencyRegistry) {
  if (next.revision <= current.revision) throw new Error('Emergency revision did not increase');
  for (const previous of current.countries) {
    const country = next.countries.find((c) => c.countryCode === previous.countryCode);
    if (!country) throw new Error('Emergency update omitted a country');
    if (previous.selectionStatus === 'withdrawn' && country.selectionStatus !== 'withdrawn') throw new Error('Emergency country withdrawal removed');
    for (const old of previous.contacts) {
      const contact = country.contacts.find((n) => n.id === old.id);
      if (!contact) throw new Error('Emergency update omitted a contact; retain a withdrawal');
      if (old.evidenceStatus === 'withdrawn' && contact.evidenceStatus !== 'withdrawn') throw new Error('Emergency withdrawal removed');
    }
  }
}

export interface EmergencyLocation { countryCode?: string | null; regionCode?: string | null; callerWithPatient?: boolean }
export function resolveEmergency(registry: EmergencyRegistry | null, location: EmergencyLocation): EmergencyContact | null {
  if (!registry || location.callerWithPatient === false) return null;
  const country = registry.countries.find((c) => c.countryCode === location.countryCode?.trim().toUpperCase());
  if (!country || !['source_checked', 'region_required'].includes(country.selectionStatus)) return null;
  const regional = country.contacts.find((n) => n.evidenceStatus === 'source_checked' && n.coverage.type === 'region' && n.coverage.regionCodes.includes(location.regionCode?.toUpperCase() ?? ''));
  const contact = regional ?? country.contacts.find((n) => n.id === country.defaultMedicalContactId);
  return contact?.evidenceStatus === 'source_checked' ? contact : null;
}

export const GENERIC_EMERGENCY_ACTION = 'Contact local emergency services now, or go to the nearest hospital emergency unit.';
export interface EmergencyAdvice { number: string | null; extra: string | null; countryCode?: string | null; contactId?: string | null; locationLabel?: string | null; registryRevision?: number | null; action?: string }
export function emergencyAction(advice: EmergencyAdvice): string {
  return advice.action ?? (advice.number ? `Call ${advice.number} now${advice.extra ? `, ${advice.extra}` : ''}.` : GENERIC_EMERGENCY_ACTION);
}
export function registryAdvice(registry: EmergencyRegistry | null, countryCode?: string | null): EmergencyAdvice {
  const code = countryCode?.trim().toUpperCase() ?? null;
  const contact = resolveEmergency(registry, { countryCode: code });
  const name = contact?.coverage.label;
  return { number: contact?.dialNumber ?? null, extra: null, countryCode: code,
    contactId: contact?.id ?? null, locationLabel: name ?? null, registryRevision: registry?.revision ?? null,
    // A saved home country is only a suggestion. Do not direct a traveller or
    // caregiver abroad to a domestic short code without saying where it applies.
    action: contact ? `If the emergency is in ${name}, call ${contact.dialNumber} from there now. Otherwise, contact local emergency services or the nearest hospital emergency unit.` : GENERIC_EMERGENCY_ACTION };
}