fhir-resource-diff 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Veronez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # fhir-resource-diff
2
+
3
+ Structural diff, validation, and normalization for FHIR R4 / R4B / R5 JSON resources.
4
+
5
+ [![CI](https://github.com/dnlbox/fhir-resource-diff/actions/workflows/ci.yml/badge.svg)](https://github.com/dnlbox/fhir-resource-diff/actions/workflows/ci.yml)
6
+ [![CodeQL](https://github.com/dnlbox/fhir-resource-diff/actions/workflows/codeql.yml/badge.svg)](https://github.com/dnlbox/fhir-resource-diff/actions/workflows/codeql.yml)
7
+ [![npm](https://img.shields.io/npm/v/fhir-resource-diff)](https://www.npmjs.com/package/fhir-resource-diff)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
9
+
10
+ ## What is FHIR?
11
+
12
+ FHIR (Fast Healthcare Interoperability Resources) is the modern standard for exchanging healthcare data, published by HL7. This tool works with FHIR JSON resources — the building blocks of healthcare APIs. → [Learn more at hl7.org/fhir](https://hl7.org/fhir/)
13
+
14
+ ## Why this exists
15
+
16
+ FHIR resources evolve across API versions, profiles, and integration points. `fhir-resource-diff` is a fast, local tool for TypeScript and Node.js environments — built for CI pipelines, developer workflows, and AI agents that need programmatic access to FHIR payloads without a server-side validation dependency.
17
+
18
+ ## Features
19
+
20
+ - Multi-version support: R4, R4B, R5 — auto-detected or explicit via `--fhir-version`
21
+ - Structural diff with path-level change tracking
22
+ - Structural validation with severity levels (error, warning, info)
23
+ - Resource type lookup with HL7 documentation links (`info` command)
24
+ - Resource discovery via `list-resources`
25
+ - Stdin/pipe support — compose with `curl`, `jq`, and other unix tools
26
+ - Machine-consumable JSON output with summary counts, doc URLs, and metadata envelope
27
+ - `--quiet` mode for headless CI (exit code only, no stdout)
28
+ - `--exit-on-diff` for CI gate checks
29
+ - Named presets for common ignore patterns (`metadata`, `clinical`, `strict`)
30
+ - Full TypeScript library API — import `diff()`, `validate()`, `parseJson()` directly
31
+
32
+ ## The FHIR TypeScript ecosystem
33
+
34
+ The JavaScript/TypeScript FHIR community has built excellent tools across the stack
35
+ — type systems, API clients, platform SDKs, auth libraries, and IG authoring tools.
36
+ Each project solves a different slice of the problem, and `fhir-resource-diff` is
37
+ designed to complement them, not compete.
38
+
39
+ ### Where each tool shines
40
+
41
+ | Focus area | Tool | What it does best |
42
+ |---|---|---|
43
+ | Type definitions | [`@types/fhir`](https://www.npmjs.com/package/@types/fhir), [`@medplum/fhirtypes`](https://www.npmjs.com/package/@medplum/fhirtypes) | TypeScript interfaces for FHIR resources — essential for type-safe application code |
44
+ | Platform SDK | [`@medplum/core`](https://www.npmjs.com/package/@medplum/core) | Full-featured FHIR client with profile validation, FHIRPath, and the Medplum platform |
45
+ | XML/JSON serialization | [`fhir`](https://www.npmjs.com/package/fhir) (Lantana) | FHIR XML ↔ JSON conversion and JSON Schema validation — one of the earliest FHIR JS tools |
46
+ | Auth & API client | [`fhirclient`](https://www.npmjs.com/package/fhirclient) | SMART on FHIR auth flows and API calls, maintained by SMART Health IT at Boston Children's |
47
+ | Conformance validation | [HL7 FHIR Validator](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator) | The reference implementation for full profile, terminology, and invariant validation |
48
+ | **Diff, fast validation, CI/CD** | **`fhir-resource-diff`** | **Structural diffing, format validation, and automation-first output** |
49
+
50
+ ### What this tool adds to the ecosystem
51
+
52
+ `fhir-resource-diff` focuses on three areas where we saw a gap in the existing
53
+ tooling — not because other tools should have built these, but because they fall
54
+ outside the scope of what type libraries, API clients, and platform SDKs are designed
55
+ to solve:
56
+
57
+ **FHIR-aware structural diff.** Compare two resources path by path and get a
58
+ classified list of additions, removals, and changes — with dot-notation paths,
59
+ array index tracking, and ignore presets for metadata noise.
60
+
61
+ **AI agent and automation friendly.** Every command supports `--format json` for
62
+ structured output, `--envelope` for metadata wrapping (tool version, FHIR version,
63
+ timestamps, HL7 doc URLs), and stdin pipes for in-memory payloads. An agent can
64
+ validate and diff FHIR payloads without writing temp files, parse the output in one
65
+ pass, and follow the documentation links — no second tool call needed.
66
+
67
+ **CI/CD native.** `--exit-on-diff` fails the step when resources diverge.
68
+ `--quiet` suppresses stdout for exit-code-only gates. Exit codes are
69
+ severity-aware — warnings and info findings never produce non-zero exits.
70
+ JSON envelope output includes summary counts for automated triage.
71
+
72
+ ### Using them together
73
+
74
+ These tools work well in combination:
75
+
76
+ - **[`@types/fhir`](https://www.npmjs.com/package/@types/fhir)** or **[`@medplum/fhirtypes`](https://www.npmjs.com/package/@medplum/fhirtypes)** for your application's TypeScript types, `fhir-resource-diff` for runtime validation and diffing
77
+ - **[`fhirclient`](https://www.npmjs.com/package/fhirclient)** for SMART auth and API transport, then pipe responses into `fhir-resource-diff` for validation and comparison
78
+ - **[HL7 FHIR Validator](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator)** for full profile conformance checks in staging, `fhir-resource-diff` for fast local validation and CI gates in the development loop
79
+
80
+ ## Supported FHIR versions
81
+
82
+ | Version | Status | Spec URL |
83
+ |---------|--------|----------|
84
+ | R4 (4.0.1) | Default, fully supported | https://hl7.org/fhir/R4/ |
85
+ | R4B (4.3.0) | Supported | https://hl7.org/fhir/R4B/ |
86
+ | R5 (5.0.0) | Supported | https://hl7.org/fhir/R5/ |
87
+
88
+ ## Install
89
+
90
+ ```bash
91
+ npm install -g fhir-resource-diff
92
+ # or
93
+ pnpm add -g fhir-resource-diff
94
+ ```
95
+
96
+ For project-local installation: `pnpm add -D fhir-resource-diff`
97
+
98
+ ## Quick start
99
+
100
+ Compare two resources:
101
+
102
+ ```bash
103
+ fhir-resource-diff compare examples/patient-a.json examples/patient-b.json
104
+ ```
105
+
106
+ Validate a resource:
107
+
108
+ ```bash
109
+ fhir-resource-diff validate examples/patient-a.json
110
+ # → valid
111
+ ```
112
+
113
+ Validate with a specific FHIR version:
114
+
115
+ ```bash
116
+ fhir-resource-diff validate examples/r5/patient-a.json --fhir-version R5
117
+ ```
118
+
119
+ Output diff as JSON:
120
+
121
+ ```bash
122
+ fhir-resource-diff compare a.json b.json --format json
123
+ ```
124
+
125
+ ## CLI reference
126
+
127
+ ### compare
128
+
129
+ ```
130
+ fhir-resource-diff compare <file-a> <file-b> [options]
131
+
132
+ Arguments:
133
+ file-a, file-b File paths or - to read from stdin
134
+
135
+ Options:
136
+ --format <fmt> text | json | markdown (default: text)
137
+ --fhir-version <ver> R4 | R4B | R5 (default: auto-detect or R4)
138
+ --ignore <paths> comma-separated paths to ignore (e.g. meta.lastUpdated,id)
139
+ --preset <name> metadata | clinical | strict
140
+ --normalize <name> canonical | none
141
+ --exit-on-diff exit 1 if differences found (for CI)
142
+ --quiet suppress stdout output
143
+ --envelope wrap JSON output in metadata envelope (requires --format json)
144
+ ```
145
+
146
+ ### validate
147
+
148
+ ```
149
+ fhir-resource-diff validate <file> [options]
150
+
151
+ Arguments:
152
+ file File path or - to read from stdin
153
+
154
+ Options:
155
+ --format <fmt> text | json (default: text)
156
+ --fhir-version <ver> R4 | R4B | R5 (default: auto-detect or R4)
157
+ --quiet suppress stdout output
158
+ --envelope wrap JSON output in metadata envelope (requires --format json)
159
+ ```
160
+
161
+ ### normalize
162
+
163
+ ```
164
+ fhir-resource-diff normalize <file> [options]
165
+
166
+ Arguments:
167
+ file File path or - to read from stdin
168
+
169
+ Options:
170
+ --preset <name> canonical | none (default: canonical)
171
+ --fhir-version <ver> R4 | R4B | R5 (default: auto-detect or R4)
172
+ --output <path> write to file instead of stdout
173
+ --quiet suppress stdout output
174
+ ```
175
+
176
+ ### info
177
+
178
+ ```
179
+ fhir-resource-diff info <resourceType> [options]
180
+
181
+ Lookup a FHIR resource type and get its HL7 documentation links.
182
+
183
+ Options:
184
+ --fhir-version <ver> Show docs link for a specific version only
185
+ --format <fmt> text | json (default: text)
186
+ ```
187
+
188
+ Example:
189
+
190
+ ```bash
191
+ fhir-resource-diff info Patient
192
+ # Patient (base)
193
+ # FHIR versions: R4, R4B, R5
194
+ # Documentation:
195
+ # R4: https://hl7.org/fhir/R4/patient.html
196
+ # R4B: https://hl7.org/fhir/R4B/patient.html
197
+ # R5: https://hl7.org/fhir/R5/patient.html
198
+ ```
199
+
200
+ ### list-resources
201
+
202
+ ```
203
+ fhir-resource-diff list-resources [options]
204
+
205
+ List known FHIR resource types.
206
+
207
+ Options:
208
+ --fhir-version <ver> Filter to types available in a specific version
209
+ --category <cat> foundation | base | clinical | financial | specialized | conformance
210
+ --format <fmt> text | json (default: text)
211
+ ```
212
+
213
+ Example:
214
+
215
+ ```bash
216
+ fhir-resource-diff list-resources --category clinical
217
+ fhir-resource-diff list-resources --fhir-version R5 --format json
218
+ ```
219
+
220
+ ## Use in CI
221
+
222
+ Use `fhir-resource-diff` as a CI gate to validate and diff FHIR payloads automatically.
223
+
224
+ **GitHub Actions example:**
225
+
226
+ ```yaml
227
+ - name: Validate FHIR resource
228
+ run: fhir-resource-diff validate payload.json --format json --fhir-version R4
229
+
230
+ - name: Diff against expected baseline
231
+ run: |
232
+ fhir-resource-diff compare expected.json actual.json \
233
+ --format json --exit-on-diff --preset metadata --quiet
234
+ ```
235
+
236
+ Key points:
237
+
238
+ - `--exit-on-diff` exits 1 when differences are found — fails the CI step
239
+ - `--quiet` suppresses stdout — useful when you only need the exit code
240
+ - `--format json` produces machine-parseable output for downstream tooling
241
+ - `--format json --envelope` wraps results in a metadata envelope with summary counts, tool version, FHIR version, and HL7 documentation URL
242
+ - Exit codes: 0 = success, 1 = differences found / validation errors, 2 = input error
243
+
244
+ ## Use with AI agents and test harnesses
245
+
246
+ `fhir-resource-diff` is designed for automated tooling. Agents and test harnesses can pipe FHIR payloads directly without writing temp files.
247
+
248
+ **CLI — agent validates a payload from memory (no temp file):**
249
+
250
+ ```bash
251
+ echo "$FHIR_PAYLOAD" | fhir-resource-diff validate - --format json --fhir-version R4
252
+ ```
253
+
254
+ **CLI — agent diffs actual vs expected and gets structured output:**
255
+
256
+ ```bash
257
+ echo "$ACTUAL_PAYLOAD" | fhir-resource-diff compare - expected.json \
258
+ --format json --envelope --preset metadata
259
+ ```
260
+
261
+ The `--envelope` JSON output is designed for automated consumers:
262
+
263
+ ```json
264
+ {
265
+ "tool": "fhir-resource-diff",
266
+ "version": "0.2.0",
267
+ "command": "compare",
268
+ "fhirVersion": "R4",
269
+ "timestamp": "2026-03-10T21:00:00.000Z",
270
+ "result": {
271
+ "resourceType": "Patient",
272
+ "identical": false,
273
+ "summary": { "added": 5, "removed": 0, "changed": 3, "typeChanged": 0, "total": 8 },
274
+ "entries": [...],
275
+ "documentation": "https://hl7.org/fhir/R4/patient.html"
276
+ }
277
+ }
278
+ ```
279
+
280
+ An agent can parse this once and know: what changed, how many changes, what FHIR version, and where to find the HL7 docs — without a second tool call.
281
+
282
+ **TypeScript library — agent harness imports directly:**
283
+
284
+ ```typescript
285
+ import { parseJson, validate, diff } from "fhir-resource-diff";
286
+
287
+ const parsed = parseJson(responseBody);
288
+ if (!parsed.success) {
289
+ throw new Error(`Invalid FHIR JSON: ${parsed.error}`);
290
+ }
291
+
292
+ const validation = validate(parsed.resource, "R4");
293
+ if (validation.valid === false) {
294
+ const errors = validation.errors.filter(e => e.severity === "error");
295
+ // errors[].docUrl points to the relevant HL7 page
296
+ }
297
+
298
+ const result = diff(parsed.resource, expectedFixture, {
299
+ ignorePaths: ["meta.lastUpdated", "id"],
300
+ });
301
+ if (!result.identical) {
302
+ // result.entries has structured diff for programmatic inspection
303
+ }
304
+ ```
305
+
306
+ ## Library usage
307
+
308
+ ```typescript
309
+ import { parseJson, validate, diff, formatText } from "fhir-resource-diff";
310
+
311
+ const left = parseJson(rawJsonStringA);
312
+ const right = parseJson(rawJsonStringB);
313
+
314
+ if (left.success && right.success) {
315
+ const result = diff(left.resource, right.resource, {
316
+ ignorePaths: ["meta.lastUpdated", "id"],
317
+ });
318
+ console.log(formatText(result));
319
+ }
320
+ ```
321
+
322
+ `parseJson`, `validate`, `diff`, and the formatters are browser-safe and can be used in a React/Vite app or any bundler.
323
+
324
+ ## Architecture overview
325
+
326
+ ```
327
+ ┌─────────────────────────────────────────────────┐
328
+ │ CLI adapter (src/cli/) │
329
+ │ Node.js only — file I/O, flags, exit codes │
330
+ ├─────────────────────────────────────────────────┤
331
+ │ Formatters (src/formatters/) │
332
+ │ Browser-safe — text, JSON, markdown renderers │
333
+ ├─────────────────────────────────────────────────┤
334
+ │ Core library (src/core/) │
335
+ │ Browser-safe — parse, validate, diff, version │
336
+ ├─────────────────────────────────────────────────┤
337
+ │ Presets (src/presets/) │
338
+ │ Browser-safe — ignore fields, normalization │
339
+ └─────────────────────────────────────────────────┘
340
+ ```
341
+
342
+ The core library has no Node.js dependencies and can run in the browser. The CLI adapter is a thin layer on top that handles file I/O and process exit codes.
343
+
344
+ ## Development
345
+
346
+ ### Prerequisites
347
+
348
+ - Node.js ≥ 20
349
+ - pnpm ≥ 9
350
+
351
+ ### Setup
352
+
353
+ ```bash
354
+ git clone https://github.com/<owner>/fhir-resource-diff.git
355
+ cd fhir-resource-diff
356
+ pnpm install
357
+ ```
358
+
359
+ ### Run the CLI locally (no build needed)
360
+
361
+ ```bash
362
+ pnpm cli -- compare examples/patient-a.json examples/patient-b.json
363
+ pnpm cli -- validate examples/patient-a.json
364
+ pnpm cli -- normalize examples/observation-a.json
365
+ ```
366
+
367
+ Note: the `--` separator after `pnpm cli` is required so pnpm passes flags to the script rather than consuming them.
368
+
369
+ ### Common scripts
370
+
371
+ | Script | Purpose |
372
+ |--------|---------|
373
+ | `pnpm cli -- <args>` | Run CLI from source via tsx |
374
+ | `pnpm test` | Run tests |
375
+ | `pnpm test:watch` | Run tests in watch mode |
376
+ | `pnpm typecheck` | TypeScript type checking |
377
+ | `pnpm lint` | ESLint |
378
+ | `pnpm build` | Production build (tsup) |
379
+ | `pnpm dev` | Watch mode build (tsup --watch) |
380
+
381
+ ## Roadmap
382
+
383
+ - **Phase 1** (complete): core diff engine, validation, CLI, text/JSON/markdown output, presets
384
+ - **Phase 2** (complete): multi-version FHIR support (R4/R4B/R5), resource registry, `info` and `list-resources` commands, stdin/pipe support, CI affordances (`--quiet`, `--envelope`, `--exit-on-diff`)
385
+ - **Phase 3** (in progress): format validation rules (id, date, reference), structural validation rules (required fields, status values, CodeableConcept shape), profile awareness (meta.profile detection, IG registry)
386
+ - **Phase 4** (planned): enriched `info` command (maturity levels, use cases, key fields, version notes), Snyk security scanning, showcase with real HL7 R4 data
387
+
388
+ ## Related resources
389
+
390
+ - [FHIR specification](https://hl7.org/fhir/)
391
+ - [FHIR resource type listing](https://hl7.org/fhir/resourcelist.html)
392
+ - [FHIR R4](https://hl7.org/fhir/R4/) / [R4B](https://hl7.org/fhir/R4B/) / [R5](https://hl7.org/fhir/R5/)
393
+ - [HL7 FHIR tooling ecosystem](https://confluence.hl7.org/display/FHIR/FHIR+Tooling)
394
+
395
+ ## License
396
+
397
+ MIT.