JSPM

  • Created
  • Published
  • Downloads 46
  • Score
    100M100P100Q81009F
  • License MIT

Shared utilities for FHIR DSL packages

Package Exports

  • @fhir-dsl/utils

Readme

@fhir-dsl/utils

Shared toolbox for the fhir-dsl monorepo: the cross-package error contract (FhirDslError + Result<T, E> + helpers), a leveled logger, naming helpers, and the search-param → TS literal mapper.

Every other @fhir-dsl/* package re-exports its own error subclasses from here, so error shapes stay identical across the monorepo. You can install @fhir-dsl/utils directly when you want to extend the contract from your own code.

Install

npm install @fhir-dsl/utils

Errors

Every error in @fhir-dsl/* extends FhirDslError, which carries:

  • kind — a string-literal discriminator (e.g. "core.request", "fhirpath.evaluation").
  • context — a structured payload, typed per subclass.
  • cause — the native ES2022 cause chain, walked by toJSON() and formatErrorChain().
  • toJSON() — returns a transport-safe SerializedFhirDslError.
import { isFhirDslError, formatErrorChain } from "@fhir-dsl/utils";
import { FhirRequestError } from "@fhir-dsl/core";

try {
  await fhir.read("Patient", "123");
} catch (err) {
  if (isFhirDslError(err)) {
    console.error(err.kind, err.context);
    console.error(formatErrorChain(err));
    // FhirRequestError: 503 Service Unavailable ← TypeError: fetch failed
  }
  if (err instanceof FhirRequestError) {
    console.error("status:", err.context.status);
  }
}

To define your own subclass, extend FhirDslError with a unique kind and a typed context payload:

import { FhirDslError } from "@fhir-dsl/utils";

interface IngestErrorContext {
  bundleId: string;
  failureIndex: number;
}

class IngestError extends FhirDslError<"my.ingest", IngestErrorContext> {
  readonly kind = "my.ingest" as const;
}

throw new IngestError(
  "bundle ingest failed at entry 7",
  { bundleId: "b-1", failureIndex: 7 },
  { cause: originalError },
);

Result toolkit

Result<T, E> is a discriminated union with a literal ok boolean — narrow without an extra import. tryAsync / trySync lift throwing functions into a Result, with an optional mapError hook for boundary coercion.

import { tryAsync, mapOk, match } from "@fhir-dsl/utils";

const r = await tryAsync(() => fhir.read("Patient", "123"));
if (r.ok) console.log(r.value);
else console.error("status:", r.error.context.status);

const idOnly = mapOk(r, (p) => p.id);

const summary = match(r, {
  ok: (p) => `read ${p.id}`,
  err: (e) => `failed: ${e.message}`,
});

mapErr / mapOk / match round out an Effect-style typed channel — see the error-handling guide for the full surface and the table of built-in subclasses.

Logger

import { Logger, logger } from "@fhir-dsl/utils";

logger.info("generating types"); // default singleton, level "info"

const quiet = new Logger("error");
quiet.debug("dropped"); // suppressed
quiet.error("only errors escape", { attempt: 3 });

The default singleton is at level info and has no public level setter — construct your own new Logger("debug") when you need verbose output (e.g. generator diagnostics).

Internal helpers

Re-exported for the generator and CLI; stable but rarely useful outside the monorepo.

import {
  toPascalCase,        // "observation-category" → "ObservationCategory"
  toCamelCase,         // "observation_category" → "observationCategory"
  toKebabCase,         // "ObservationCategory"   → "observation-category"
  fhirTypeToFileName,  // "Observation"           → "observation.ts"
  fhirPathToPropertyName, // "Patient.name.given" → "name.given"
  capitalizeFirst,     // "patient" → "Patient"
  searchParamTypeToTs, // "string"  → '{ type: "string"; value: string }'
} from "@fhir-dsl/utils";

searchParamTypeToTs returns the exact TypeScript literal the generator emits into search-params/*.ts; the inputs are FHIR search-param types ("string", "token", "date", "reference", "quantity", "number", "uri", "composite", "special").

License

MIT