Files
initiative/specs/029-on-demand-bestiary/quickstart.md

3.2 KiB

Quickstart: On-Demand Bestiary with Pre-Indexed Search

Feature: 029-on-demand-bestiary Date: 2026-03-10

What This Feature Does

Replaces the bundled full bestiary file (one source, ~1.3 MB of copyrighted content) with:

  1. A pre-shipped lightweight index (102 sources, 3,312 creatures, ~52 KB gzipped) for instant search and combatant creation
  2. On-demand fetching of full stat block data per source, cached in IndexedDB

Key Changes

Removed

  • data/bestiary/xmm.json — no longer shipped with the app

New Files

  • apps/web/src/adapters/bestiary-index-adapter.ts — loads and parses the shipped index, converts compact format to domain types
  • apps/web/src/adapters/bestiary-cache.ts — IndexedDB cache adapter for fetched source data
  • apps/web/src/components/source-fetch-prompt.tsx — dialog prompting user to fetch/upload source data
  • apps/web/src/components/source-manager.tsx — UI for viewing and clearing cached sources

Modified Files

  • apps/web/src/hooks/use-bestiary.ts — rewritten to search from index and look up creatures from cache
  • apps/web/src/hooks/use-encounter.tsaddFromBestiary accepts index entries (no fetch needed to add)
  • apps/web/src/components/bestiary-search.tsx — shows source display name in results
  • apps/web/src/components/stat-block-panel.tsx — triggers source fetch prompt when creature not cached
  • packages/domain/src/creature-types.ts — new BestiaryIndexEntry and BestiaryIndex types

Unchanged

  • apps/web/src/adapters/bestiary-adapter.ts — normalization pipeline processes fetched data exactly as before
  • apps/web/src/adapters/strip-tags.ts — tag stripping unchanged
  • apps/web/src/components/stat-block.tsx — stat block rendering unchanged
  • apps/web/src/persistence/encounter-storage.ts — encounter persistence unchanged

Architecture Overview

index.json (shipped, static)
    ↓ Vite JSON import
bestiary-index-adapter.ts
    ↓ BestiaryIndexEntry[]
use-bestiary.ts (search, add)
    ↓
bestiary-search.tsx → use-encounter.ts (addFromBestiary)
    ↓
stat-block-panel.tsx
    ↓ creatureId → source not cached?
source-fetch-prompt.tsx
    ↓ fetch URL or upload file
bestiary-adapter.ts (normalizeBestiary — unchanged)
    ↓ Creature[]
bestiary-cache.ts (IndexedDB)
    ↓
stat-block.tsx (renders full stat block)

Development Commands

pnpm check              # Must pass before every commit
pnpm test               # Run all tests
pnpm typecheck           # TypeScript type checking
pnpm --filter web dev   # Dev server at localhost:5173

Testing Strategy

  • Domain tests: Pure function tests for new BestiaryIndexEntry type and any utility functions
  • Adapter tests: Test index parsing (compact → readable format), test IndexedDB cache operations (mock IndexedDB via fake-indexeddb)
  • Component tests: Not in scope (existing pattern — components tested via manual verification)
  • Integration: Verify search returns multi-source results, verify add-from-index flow, verify fetch→cache→display flow

New Dependency

  • idb — Promise-based IndexedDB wrapper (~1.5 KB gzipped). Used only in bestiary-cache.ts.