npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@peerbits/soap-schema

v0.1.0

Published

Canonical JSON Schema and TypeScript validation library for structured SOAP clinical notes

Readme

soap-schema

CI License: Apache-2.0 npm Node

Canonical JSON Schema and TypeScript validation library for structured SOAP clinical notes

Category: AI Clinical Documentation — Clinical Documentation Components · License: Apache-2.0 · Status: alpha (v0.1.0, pre-1.0)

📖 New here? This README covers the essentials; for the complete narrative walkthrough — installation, every API in depth, error-handling patterns, the full schema field reference, versioning, and troubleshooting — see docs/USER_GUIDE.md.


1. What problem does this solve?

Every AI clinical documentation project reinvents the same structure: a SOAP note (Subjective, Objective, Assessment, Plan) as a data shape, with no canonical machine-readable schema to validate against. The result is ad hoc JSON shapes that drift between projects, AI-generated notes that silently produce malformed output with no validation layer to catch it before it reaches an EHR or a FHIR mapping step, and no shared vocabulary for what a "well-formed" structured note actually contains.

soap-schema defines that shape once — as a portable JSON Schema, not a library-specific type — validates against it reliably, and renders the result as a note a clinician can actually read, not just JSON a developer can parse.

2. Features

  • A canonical JSON Schema (draft 2020-12) for a structured SOAP note — usable from any language with a Draft 2020-12 validator, not just this TypeScript library
  • A typed validate() / assertValid() library (ajv-based) returning structured, per-field validation errors — not just true/false
  • TypeScript types generated directly from the schema, with a CI-enforced drift test so they can never silently fall out of sync
  • renderNote() — converts a valid note into a clean, human-readable markdown note with standard SOAP section headers
  • Five synthetic example fixtures (three valid, two deliberately invalid) used by both the test suite and the docs below
  • A documented FHIR mapping guide — guidance only, no code — describing how each section corresponds to FHIR Composition, Condition, and Observation

3. Installation

npm install @peerbits/soap-schema

Requires Node.js >= 18. No peer dependencies — ajv and ajv-formats are regular dependencies, installed automatically.

This package is ESM-only ("type": "module") — use import, not require(). If your project is CommonJS, use a dynamic import() instead (const { validate } = await import("@peerbits/soap-schema")) — see docs/USER_GUIDE.md §13 if you hit ERR_REQUIRE_ESM.

4. Quick Start

Every field below is invented and synthetic. This is a trimmed-down version of one of the repo's fixtures, kept short for readability — see fixtures/valid/primary-care-followup.json for the complete, untrimmed encounter (also rendered in full in §6 below).

Input — a plain JSON object (abbreviated from fixtures/valid/primary-care-followup.json):

{
  "metadata": {
    "encounterType": "primary-care-followup",
    "authorRole": "physician",
    "timestamp": "2026-02-10T14:30:00Z",
    "patientRef": "synthetic-patient-0001"
  },
  "subjective": {
    "chiefComplaint": "Follow-up for hypertension management",
    "historyOfPresentIllness": "Patient is a fictional 58-year-old presenting for a scheduled follow-up. Reports good adherence to lisinopril, home blood pressure readings averaging 132/84. Denies headache, chest pain, or shortness of breath.",
    "reviewOfSystems": [
      { "system": "cardiovascular", "status": "negative", "finding": "Denies chest pain, palpitations, or edema." }
    ]
  },
  "objective": {
    "vitals": {
      "bloodPressure": { "systolic": { "value": 130, "unit": "mmHg" }, "diastolic": { "value": 82, "unit": "mmHg" } },
      "heartRate": { "value": 72, "unit": "bpm" }
    },
    "examFindings": [
      { "bodySystem": "cardiovascular", "finding": "Regular rate and rhythm, no murmurs, rubs, or gallops." }
    ]
  },
  "assessment": [
    {
      "description": "Essential hypertension, well controlled on current regimen",
      "status": "chronic",
      "codedConcept": { "system": "http://hl7.org/fhir/sid/icd-10-cm", "code": "I10", "display": "Essential (primary) hypertension" }
    }
  ],
  "plan": [
    { "category": "medication", "detail": "Continue lisinopril 10mg once daily." },
    { "category": "followUp", "detail": "Return to clinic in 3 months for routine hypertension recheck." }
  ]
}
import { readFileSync } from "node:fs";
import { validate, renderNote, type SoapNote } from "@peerbits/soap-schema";

// Using readFileSync + JSON.parse here (rather than a JSON import) so this
// snippet runs unmodified under plain Node.js ESM, CommonJS, and bundlers
// alike — see docs/USER_GUIDE.md for bundler-specific JSON-import variants.
const note: unknown = JSON.parse(readFileSync("./primary-care-followup.json", "utf-8"));

const result = validate(note);
if (!result.valid) {
  throw new Error(`Invalid note: ${result.errors.map((e) => e.message).join("; ")}`);
}

console.log(renderNote(note as SoapNote));

OutputrenderNote()'s rendered note (this is the entire point of the library: the same JSON above, legible to a clinician without touching code):

# SOAP Note

*Encounter: primary-care-followup · Author: physician · 2026-02-10T14:30:00Z*

## Subjective

**Chief Complaint:** Follow-up for hypertension management

**History of Present Illness:**
Patient is a fictional 58-year-old presenting for a scheduled follow-up. Reports good adherence to lisinopril, home blood pressure readings averaging 132/84. Denies headache, chest pain, or shortness of breath.

**Review of Systems:**
- cardiovascular (negative) — Denies chest pain, palpitations, or edema.

## Objective

**Vitals:**
- Blood Pressure: 130/82 mmHg
- Heart Rate: 72 bpm

**Exam Findings:**
- cardiovascular: Regular rate and rhythm, no murmurs, rubs, or gallops.

## Assessment

- Essential hypertension, well controlled on current regimen (chronic) [Essential (primary) hypertension — http://hl7.org/fhir/sid/icd-10-cm I10]

## Plan

- **Medication:** Continue lisinopril 10mg once daily.
- **Follow-up:** Return to clinic in 3 months for routine hypertension recheck.

5. Architecture

schema/                       Canonical JSON Schema (draft 2020-12) — the source of truth
  soap-note.schema.json          Top-level note shape
  definitions/
    vitals.schema.json           Vital sign measurements, all optional
    coded-concept.schema.json    Generic {system, code, display} — not tied to any terminology
    note-metadata.schema.json    Encounter metadata; opaque patientRef, no direct identifiers

src/
  validate.ts                  validate(), assertValid() — ajv wrapper, structured errors
  types.ts                     Generated from schema/soap-note.schema.json (do not hand-edit)
  render.ts                    renderNote() — dependency-free markdown rendering
  index.ts                     Public API surface

fixtures/                      Synthetic example notes used by tests and docs
docs/FHIR_MAPPING_GUIDE.md     Section-by-section FHIR mapping guidance (docs only, no code)

Key decisions:

  • The JSON Schema file is the canonical artifact, not the TypeScript types. Any language with a Draft 2020-12 validator can use schema/*.json directly without touching this npm package.
  • Types are generated (npm run generate:types), never hand-maintained — a drift test in CI fails the build the moment the schema and the committed types disagree.
  • coded-concept.schema.json deliberately stays coding-system-agnostic (system + code + display) — it does not assume ICD-10, SNOMED, or any other terminology, and does not validate codes against a real code set.
  • No network calls, no OAuth, no sandbox dependency — this is a schema-and-validation library, structurally different from other repos in this initiative.

See docs/FHIR_MAPPING_GUIDE.md for how this schema's sections correspond to FHIR Composition, Condition, and Observation resources.

6. Example Usage

Three complete synthetic encounters live in fixtures/valid/ across distinct encounter types. §4 above showed a trimmed version of primary-care-followup.json for readability; all three fixtures' full, untrimmed renderNote() output — kept honest by the same snapshot test suite (tests/__snapshots__/render.test.ts.snap) — is reproduced in full below, not just linked.

primary-care-followup.json — routine chronic-disease follow-up, full fixture

# SOAP Note

*Encounter: primary-care-followup · Author: physician · 2026-02-10T14:30:00Z*

## Subjective

**Chief Complaint:** Follow-up for hypertension management

**History of Present Illness:**
Patient is a fictional 58-year-old presenting for a scheduled follow-up. Reports good adherence to lisinopril, home blood pressure readings averaging 132/84. Denies headache, chest pain, or shortness of breath. No new symptoms since last visit.

**Review of Systems:**
- cardiovascular (negative) — Denies chest pain, palpitations, or edema.
- respiratory (negative) — Denies dyspnea or cough.
- neurological (negative) — Denies headache or dizziness.

## Objective

**Vitals:**
- Blood Pressure: 130/82 mmHg
- Heart Rate: 72 bpm
- Temperature: 36.7 C
- Respiratory Rate: 16 breaths/min
- SpO2: 98 %
- Weight: 84 kg

**Exam Findings:**
- cardiovascular: Regular rate and rhythm, no murmurs, rubs, or gallops.
- respiratory: Clear to auscultation bilaterally, no wheezes or crackles.
- extremities: No peripheral edema.

## Assessment

- Essential hypertension, well controlled on current regimen (chronic) [Essential (primary) hypertension — http://hl7.org/fhir/sid/icd-10-cm I10]

## Plan

- **Medication:** Continue lisinopril 10mg once daily.
- **Patient Education:** Reinforced low-sodium diet and home blood pressure monitoring.
- **Follow-up:** Return to clinic in 3 months for routine hypertension recheck.

urgent-care-visit.json — acute injury visit with an imaging order

# SOAP Note

*Encounter: urgent-care · Author: nurse-practitioner · 2026-03-22T09:15:00Z*

## Subjective

**Chief Complaint:** Right ankle pain after fall

**History of Present Illness:**
Fictional 27-year-old presents after twisting their right ankle while playing recreational basketball approximately 2 hours prior. Reports immediate swelling and difficulty bearing weight. Denies numbness or tingling. No prior ankle injuries on this side.

**Review of Systems:**
- musculoskeletal (positive) — Right ankle pain and swelling.
- neurological (negative) — Denies numbness or tingling in the foot.

## Objective

**Vitals:**
- Heart Rate: 84 bpm
- Temperature: 36.9 C
- Respiratory Rate: 18 breaths/min
- SpO2: 99 %

**Exam Findings:**
- musculoskeletal: Moderate swelling and ecchymosis over the right lateral malleolus, tender to palpation. No obvious deformity.
- musculoskeletal: Negative Ottawa ankle rules; able to bear weight for four steps with discomfort.
- skin: No open wounds or lacerations.

## Assessment

- Right lateral ankle sprain, grade II (active) [Sprain of unspecified ligament of right ankle, initial encounter — http://hl7.org/fhir/sid/icd-10-cm S93.401A]

## Plan

- **Order:** Right ankle X-ray series to rule out fracture, given borderline Ottawa criteria.
- **Medication:** Ibuprofen 400mg every 6 hours as needed for pain, with food.
- **Patient Education:** Discussed RICE protocol (rest, ice, compression, elevation) and use of an ankle brace.
- **Follow-up:** Follow up with primary care or orthopedics in 1 week if symptoms have not improved.

telehealth-checkin.json — minimal encounter with no vitals captured

Demonstrates that every objective.vitals field, and vitals itself, is optional — this note validates and renders cleanly with none of them present.

# SOAP Note

*Encounter: telehealth-checkin · Author: physician-assistant · 2026-01-05T16:45:00Z*

## Subjective

**Chief Complaint:** Medication check-in for seasonal allergies

**History of Present Illness:**
Fictional 34-year-old video-visit check-in regarding seasonal allergic rhinitis started last month. Reports significant improvement in nasal congestion and sneezing since starting cetirizine. No new symptoms. No side effects noted.

**Review of Systems:**
- ENT (positive) — Mild residual nasal congestion, improved from baseline.
- respiratory (negative) — Denies wheeze or shortness of breath.

## Objective

**Exam Findings:**
- general: Patient appears well on video, no acute distress observed.

## Assessment

- Seasonal allergic rhinitis, improving on current therapy (active) [Other seasonal allergic rhinitis — http://hl7.org/fhir/sid/icd-10-cm J30.2]

## Plan

- **Medication:** Continue cetirizine 10mg once daily.
- **Patient Education:** Advised on pollen-avoidance measures and saline nasal rinse as an adjunct.
- **Follow-up:** Telehealth follow-up in 4 weeks, or sooner if symptoms worsen.

Runnable versions of all of the above live in docs/examples/ — including error-handling with assertValid() and SoapNoteValidationError. See docs/USER_GUIDE.md for the full narrative walkthrough.

Two deliberately invalid fixtures in fixtures/invalid/ exercise the validator's error reporting — see tests/validate.test.ts for the exact expected error path/keyword each one asserts on.

import { assertValid, SoapNoteValidationError } from "@peerbits/soap-schema";

try {
  assertValid(candidateNote); // throws if invalid
  // candidateNote is now typed as SoapNote
} catch (err) {
  if (err instanceof SoapNoteValidationError) {
    for (const e of err.errors) {
      console.error(`${e.path}: ${e.message} (${e.keyword})`);
    }
  }
}

7. Roadmap

  • [ ] A full FHIR Composition resource generator (turning a valid SOAP note into an actual FHIR resource) — candidate future repo once this schema and fhir-client both exist, per docs/FHIR_MAPPING_GUIDE.md
  • [ ] A future medical-terminology repo for ICD-10/CPT/SNOMED code validation and lookup against coded-concept references — explicitly out of scope here
  • [ ] Clinical review sign-off on docs/FHIR_MAPPING_GUIDE.md §5 open items

8. Contributing

See CONTRIBUTING.md. Issues tagged good first issue are a good place to start.

9. License

Apache License 2.0 — see LICENSE.

10. About PeerbitsSolution

soap-schema is part of the PeerbitsSolution HealthTech Open Source initiative — reusable engineering components extracted from our healthcare technology work, published so other teams don't have to solve the same problems from scratch. This repository contains generalized, reusable logic only; it is not tied to any specific client engagement or commercial product.