Skip to content

RFC: dcmjs 1.0 core architecture — TypeScript, standalone composable packages, generated from the DICOM standard #517

Description

@pieper

This RFC builds on #423 and sets out the core architecture for dcmjs 1.0. Please comment on the principles and the open questions below.

Decisions so far

  • A hard break from the 0.x API is acceptable. dcmjs has never left 0.x.
  • 0.x stays maintained on a maintenance branch for OHIF/Cornerstone during the transition. Radical Imaging maintainers have offered to look after it.
  • TypeScript.
  • Standalone repositories that combine freely, not a monorepo. Each package should be reusable on its own and combinable in more than one way.
  • The core architecture comes first. Domain packages (volume reconstruction, pixel codecs, de-identification, SEG/SR, validation, indexing) follow once the core interfaces are stable.
  • AI-assisted implementation is welcome, within small, human-reviewed PRs (see "Process" below).

Principles

  1. One shared data model. Packages exchange a typed DICOM JSON Model (PS3.18 Annex F) and a single event-stream interface. No package imports another package's internals. For example, a volume-reconstruction package should work the same on datasets parsed from Part 10 files and on metadata fetched with dicomweb-client.
  2. Minimal dependencies. Most packages should depend only on the generated dictionary/types package, not on the parser.
  3. Generated from the standard. Dictionaries, UIDs, VR rules and later IOD tables come from the DICOM DocBook source, pinned to an edition. Generated code is reproducible and never edited by hand. See Set up dcmjs-template repo and a dcmjs-dictionary package generated from the DICOM standard DocBook #515.
  4. Real-world behavior is written down and tested. Special cases and pitfalls are documented in the language-neutral dicom-companion (Create dcmjs-org/dicom-companion: a language-neutral guide to DICOM special cases and gotchas #514) and backed by test data.
  5. One parser engine. Today there are two readers (DicomMessage._read and AsyncDicomReader); 1.0 has one event-based parser with synchronous and asynchronous drivers, and one writer.

Proposed scope of dcmjs core 1.0

  • Part 10 read and write for all VRs and all non-compressed transfer syntaxes, including big endian and deflate (both read and write)
  • Streaming: bounded memory, cancellation, untilTag/stop conditions that report byte offsets, errors propagated instead of hanging, streaming writes (header followed by bulk or pixel data taken from a Blob or stream)
  • Byte-preserving writes as an option, alongside normalized writes
  • Per-call validation modes (strict / warn / ignore) instead of global behavior
  • SpecificCharacterSet, including multiple values and ISO 2022 code extensions, in sequences as well as at the top level
  • Private tags preserved, including inside sequence items, with explicit options (this matters for de-identification)
  • A typed dataset API. The proxies go away, replaced with consistent value accessors (see the Release version 1.0 with API updates #423 proposal: array, first, typed getters, VM-aware singletons)
  • A logging API scoped to dcmjs
  • ESM with a package exports map; runs in browsers, Node and Deno

Not in core (separate packages later): volume reconstruction and clinical interpretation (dcmjs-normalizers), pixel frame and codec handling, PS3.15 de-identification, SEG/SR/PM, IOD validation, DICOMDIR, FHIR, and the Cornerstone/vtk.js adapters (already maintained in @cornerstonejs/adapters).

Existing work to reconcile

Source What to take or decide
#423 Types, removing proxies, value accessors, frame-level accessors, private tags in items, ESM-only, logging
#502 Semantics for cancellation, bounded read-ahead and stop conditions
#481 / #477 Deflate in the async path; the shape of pixel buffers in the output
#496 / #492 Rules for UN-encoded sequences (PS3.5 §6.2.2), including when promotion to SQ is safe
#500 AsyncDicomWriter
#455 / #480 / #483 VR and encoding refactor
#512 The backpatching writer, deflate-on-write, the ISO 2022 decoder, many targeted bug fixes, regression suites built from issues, and the event-stream design (to be discussed here, not merged as-is)
#163 / #165 Type declarations

Testing and validation

  • A pinned test corpus: dcmjs-org/data plus public sets such as pydicom-data, GDCM's malformed files, NEMA WG-04 and dclunie's charset samples, each recorded with hash, license and provenance
  • Automatic comparison against reference outputs (e.g. DCMTK dcm2json, pydicom)
  • Parity tests across synchronous, asynchronous and streaming reads, plus round-trip tests
  • Fast PR CI; the full matrix runs nightly

Process

Open questions

  1. npm naming for the new packages Decided: new packages are published as @dcmjs-org/<name> on both npm and JSR, matching the GitHub organization and repository paths (e.g. dcmjs-org/dcmjs-normalizers@dcmjs-org/normalizers). The core package stays unscoped as dcmjs.
  2. In-memory model: DICOM JSON Model with typed accessors as the primary form, with "naturalized" (keyword-keyed) objects as a view? Or the reverse?
  3. Event-stream vocabulary: which events, how sequences and items are delimited, how bulk data is referenced (inline, BulkDataURI, lazy handles), how backpressure works.
  4. Bulk data representation in the data model.
  5. Runtime targets and minimum versions.
  6. Timeline and naming for the 1.0 beta, and how long 0.x maintenance continues.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions