background-loader.ts

PURPOSE

Loads every BackgroundDef at module-load time from two sources, validates each entry, and exposes them as a sorted readonly array plus an id-keyed map. Bad JSON throws synchronously at import — authoring mistakes surface the moment the dev tool boots, never at bake time.

OWNS

  • loaded: BackgroundDef[] — internal mutable accumulator, frozen after sort via the readonly export.
  • ALL_BACKGROUNDS: readonly BackgroundDef[] — id-sorted (localeCompare) catalog of every validated def.
  • BACKGROUND_MAP: ReadonlyMap<string, BackgroundDef> — id → def lookup built from ALL_BACKGROUNDS.
  • getBackground(id) — id lookup that throws with the full available-id list on miss.

READS FROM

  • ./background-schemavalidateBackgroundDef, BackgroundDef type.
  • ../../data/backgrounds/*.background.json — Vite eager import.meta.glob, default export, source 1.
  • ../../data/backgrounds/v2-catalog.json — static import, array of BackgroundDef, source 2.

PUSHES TO

  • Nothing at runtime. Pure module-level data. Consumers import ALL_BACKGROUNDS, BACKGROUND_MAP, or getBackground.

DOES NOT

  • Does not load lazily — every def is parsed/validated at first import.
  • Does not deduplicate ids across the two sources; a collision would just leave the later-inserted entry visible via BACKGROUND_MAP (Map overwrite on construction) while both occupy slots in ALL_BACKGROUNDS.
  • Does not log; it throws.
  • Does not bake, transform, or normalize defs — validateBackgroundDef is the only gate.
  • Does not handle async — no fetch, no IO beyond the bundler’s static imports.

Signals

  • Source 1 error: BackgroundDef invalid (<path>): <reason> — thrown when an individual *.background.json fails validation.
  • Source 2 error: v2-catalog[<i>] (<id-or-?>) invalid: <reason> — thrown for any failing catalog entry, with array index and best-effort id.
  • Lookup miss: background '<id>' not found. Available: <comma-list> — thrown by getBackground with the sorted available-id list.

Entry points

  • import { ALL_BACKGROUNDS } from '.../background-loader' — full sorted list.
  • import { BACKGROUND_MAP } from '.../background-loader' — direct map access.
  • import { getBackground } from '.../background-loader' — single-id fetch with crash-on-miss.

Pattern notes

  • Crash-on-bad-data, no silent fallbacks — matches the project rule: internal config validation throws, defensive logic only at user/IO/API boundaries.
  • Two-source design lets v2 ship ~100 backgrounds via a single v2-catalog.json array without minting 100 separate files, while keeping the old per-file path open for legacy entries.
  • import.meta.glob(..., { eager: true, import: 'default' }) is the Vite idiom for bundling a folder of JSON at build time; the loader assumes a Vite environment.
  • Sort is stable via localeCompare on id; downstream code can rely on ALL_BACKGROUNDS order for deterministic iteration in tools.
  • Cast pattern mod as unknown as BackgroundDef is used post-validation; the schema function is the type-narrowing authority, not TS inference.

EXTRACT-CANDIDATE

  • Two-source-with-validate-and-sort loader pattern is a duplicate of the same shape used elsewhere in the engine (e.g. enemy/weapon/affix loaders). A generic buildStaticCatalog<T>({ glob, catalog, validate, idOf }) helper could collapse the duplication if it appears in 3+ loaders. Defer until the third instance is confirmed.
  • Catalog-vs-files dedup policy is currently implicit. If id collisions become a real risk, add an explicit Set<string> check during insert and throw duplicate background id '<id>' from <source>.