webdedx

Loading WASM module...

Technical Reference

Developer resources

The project's design documents, specs, and logs live in the repository under docs/.

Running E2E tests locally

Playwright serves the built app with pnpm preview, so build first:

pnpm build
pnpm test:e2e

pnpm build runs node scripts/deploy.cjs via prebuild, so static/deploy.json is generated automatically — no extra manual step needed.

Dependency management & security policy

  • Prefer direct upgrades. If pnpm audit or Dependabot flags a vulnerability in a transitive dependency, upgrade the direct dependency that pulls it in. Do not add overrides as a first move.
  • Minimal overrides. Only use overrides as a last resort if no compatible upstream release exists.
  • Workspace-level configuration. Define overrides in the root pnpm-workspace.yaml under the overrides: key (not package.json's pnpm.overrides) for workspace-wide consistency.
  • Document overrides. Every override must have a YAML comment directly above it referencing the GHSA ID it mitigates and linking to the upstream issue.

Shareable URL contract

Every calculator and plot view is fully described by its query string, so a URL can be copied, bookmarked, and shared to reproduce the exact state. The query string follows a formal ABNF-style grammar, implemented as a Peggy parser that produces an abstract syntax tree. The current schema is v3 (urlv=3), which joins list values with ~ instead of , so messenger and email auto-linkifiers don't truncate a shared link at the first comma. Older urlv=2 links (comma-separated) still load and are rewritten to the ~ form; links using the retired v1 format are no longer parsed and instead show an "unsupported link" notice with a one-click "load defaults" action.

Grammar (v3, abridged)

query        = [pair *("&" [pair])]

pair         = extdata-pair / energies-pair / lookups-pair / series-pair
             / mat-elements-pair / entity-list-pair / scalar-pair / unknown-pair

; structured, list-bearing params  (list-sep = "~" / "," — see below)
energies-pair     = "energies=" energy-item *(list-sep energy-item)
energy-item       = number [":" energy-unit-token]
lookups-pair      = ("lookups=" / "ivalues=") lookup-item *(list-sep lookup-item)
series-pair       = "series=" series-item *(list-sep series-item)
series-item       = entity-id "." entity-id "." entity-id   ; program.particle.material
mat-elements-pair = "mat_elements=" mat-element *(list-sep mat-element)
mat-element       = digits ":" number                       ; Z:count
entity-list-pair  = ("programs=" / "particles=" / "materials=") entity-id *(list-sep entity-id)
extdata-pair      = "extdata=" label ":" url                ; split on the first ':'

; list-item separator: "~" is canonical (v3+, issue #672); legacy "," accepted on read
list-sep          = "~" / ","

; single-valued params (value kept verbatim, then percent-decoded)
scalar-pair  = scalar-key "=" value
scalar-key   = "urlv" / "mode" / "particle" / "material" / "program"
             / "eunit" / "uanchor" / "across" / "qshow" / "imode" / "iunit"
             / "istpbranch" / "stp_unit" / "xscale" / "yscale" / …

; forward-compatible catch-all (dropped by the resolver, never re-emitted)
unknown-pair = key ["=" value]

; lexical
entity-id    = digits / ("ext:" label ":" id)
number       = digits ["." digits] [("e"/"E") ["+"/"-"] digits]
digits       = 1*DIGIT

How a URL is processed

  1. Tokenize (parseQuery): split on raw &/=, percent-decode each component, and build the AST. Pure syntax — no defaults or validation.
  2. Resolve (resolveCalculatorState / resolvePlotState): duplicate keys resolve last-wins; defaults are applied; advanced-only params are ignored in basic mode; values are validated.
  3. Canonicalize: the URL is rewritten into a deterministic, ordered form so the same logical state always yields the same URL.

Error reporting

Each AST node carries the exact character span it came from, so when a link is malformed or a value is invalid the app can point at the precise spot. Problems are reported as diagnostics with a severity, a message, and a caret underline of the offending text, for example:

…energies=100~200:foo
                 ^^^
Expected an energy unit such as keV, MeV, or MeV/u.

Fatal errors (an unreadable link) block the calculation and offer "load defaults"; warnings (e.g. an out-of-range value) drop just the offending part and continue; unknown parameters are dropped silently.

Normative definition (full grammar, semantic rules, conformance vectors): shareable-urls-formal.md.

webdedx — Stopping power calculations

Built with Svelte 5 + WASM