simcapture-sdk
SDK for SimCapture, the clinical simulation recording and assessment platform. It exposes its REST API as a typed TypeScript client.
Each entity has its own namespace (sc.Course, sc.Reservation, …) returning navigable handles, and its builders validate before touching the network.
Features
- One namespace per entity, with lookup by id or by name:
named()matches exactly and tells you when there is no match (NotFoundError) or more than one (AmbiguousMatchError, carrying the ids to disambiguate) - Navigable entities:
reservation.organization()fetches on demand, and.rawexposes the full API record, including what the SDK doesn’t model - Fluent builders:
save()validates locally and reports every missing field in a singleValidationError, instead of one round-trip per error - Value objects:
TimeWindowfor time windows (half-open overlap, explicit UTC),Location,Attachment all()walks the pages internally and concatenates; it stops only on an empty page, and throws rather than handing back half a list if the server never returns one- Authentication with token caching and transparent retry on
401; concurrent calls share a single in-flight/auth - Typed errors under
SimCaptureError, carrying the real upstream status instead of a blanket400 - Hexagonal architecture: the domain only knows ports (
HttpClient,TokenStore), so tests run without network and you can inject your own transport - Dual ESM/CJS build with generated
.d.tstypes and automated npm publishing through GitHub Actions
Tech
TypeScript, Bun, axios, GitHub Actions.
Install
npm install simcapture-sdk
Usage example
import { SimCapture, TimeWindow, Location, Reservation, SimCaptureError } from "simcapture-sdk";
const sc = new SimCapture({
apiUrl: process.env.SIMCAPTURE_API!,
inventoryUrl: process.env.SIMCAPTURE_INVENTORY_API!,
credentials: {
username: process.env.SIMCAPTURE_USER!,
password: process.env.SIMCAPTURE_PASSWORD!,
clientSubdomain: process.env.SIMCAPTURE_SUBDOMAIN!,
},
});
const org = await sc.Organization.named("School of Medicine");
const room = new Location({ name: "P3-H1" });
const date = new TimeWindow("2026-06-05").start().at("23:00").end().at("23:10");
const reservation = await new Reservation()
.titled("Test reservation")
.inOrganization(org) // handle, UUID or name
.at(room)
.when(date)
.save();
await reservation.organization(); // navigation: fetched on demand
reservation.date.durationMinutes();
try {
await sc.Reservation.get("bad-id");
} catch (error) {
if (error instanceof SimCaptureError) console.error(error.status, error.message);
}
Motivation
The two-way integration between the Interdisciplinary Center for Advanced Simulation platform and SimCapture needed the same client across several microservices. Extracting it into a versioned package removed the duplicated authentication logic and left the API responses typed in a single place.