Book Tracker is a responsive web app for tracking books, reading progress, reading sessions, and reading statistics. It supports offline work and optional public profiles.
New to the codebase? Start with the architecture and site map.
Any change to the architecture, routes, access model, data flows, or external integrations must update the relevant map source and regenerate its image artifacts in the same change. Keep every public map sanitized according to the architecture guide.
These are the sanitized public views. The architecture guide contains the editable Mermaid and JavaScript sources, PNG copies, and rendering commands.
- Adds, edits, and removes books from a personal library.
- Tracks page progress, finished books, and reading sessions.
- Starts and stops reading timers, including an optional time-tracking integration.
- Shows yearly statistics, reading streaks, a daily activity heatmap, and per-book reading speed.
- Resolves authors through one shared catalog and lets authorized operators curate author identities, aliases, classifications, and merges.
- Suggests existing shared works and editions while keeping each reader's own title, page count, progress, and reading history independent.
- Shows opted-in reading summaries for a shared work without exposing raw session records.
- Finds books whose ISBN data needs repair.
- Publishes an optional reading profile with a separate search-discovery setting.
- Provides a restricted, read-only operational overview.
The route catalog and access matrix live in the architecture guide.
The add and edit dialog can look up an ISBN through three catalog sources. The requests are independent, so one unavailable source does not discard useful responses from the others.
Metadata precedence is field-specific:
| Field | Preferred source | Fallbacks |
|---|---|---|
| Cover | Metered catalog | Open catalog, then national catalog |
| Fiction classification | Metered catalog | National catalog, then open catalog |
| Publisher and publication date | Open catalog | Metered catalog, then national catalog |
| Subjects | Open catalog | Metered catalog, then national catalog |
The lookup also fills empty title, author, and page-count inputs. It does not silently replace values the user already entered.
Stored metadata is advisory display data. Firestore Rules allowlist the book fields, validate their types and sizes, and restrict writes to the owner. Application authorization must never depend on catalog metadata being correct.
Historical enrichment scripts are gap-fill migrations. They use a deliberate source order and must follow the review, snapshot, rehearsal, and audit process in MIGRATIONS.md. The Goodreads script is a manual historical fallback only. It is not part of the live app or a scheduled workflow.
Books without a valid ISBN appear on /isbns and can be repaired through the
normal edit dialog.
The shared catalog connects personal books without replacing them. A Work is the common intellectual work and references shared author entities. An Edition is a publication of that Work and can carry its own identifier, title, cover, page-count hint, publisher, date, language, format, and translators. Work-level title, authors, cover, subjects, and fiction classification provide fallback metadata.
Selecting a suggestion fills only empty personal fields. The personal book
remains authoritative for tracking and may use a different page count or title,
or remain unlinked. Verified accounts create what the catalog is missing: when
a search finds nothing, the app creates the Work and Edition, and resolves or
creates the shared authors it needs. Bibliographic records are public whoever
added them; operators curate them afterwards through the restricted catalog
page. Reader comparisons on /books/[workId] include only records whose owners
enabled book sharing through a public profile.
The collections, statuses, consent boundary, and admin operations are documented in docs/catalog.md. The migration and rollout boundary is in MIGRATIONS.md.
- Node.js 22.18 or newer.
.nvmrcpins the repository version. - npm.
- Firebase CLI only for emulator and deployment work. Commands in this repository pin the CLI version.
git clone <repository-url>
cd book-tracker
npm ci
npm --prefix functions ciThe repository already contains its Firebase configuration. Do not run
firebase init in this checkout. It can replace tracked rules, indexes, and
deployment settings.
Use the emulators for application development. This exercises Authentication, Firestore, and Functions without sending application data or metered requests to deployed services.
Start the emulators:
npm --prefix functions run serveIn another terminal, route the browser client to them:
VITE_EMULATOR=1 npm run devOpen http://localhost:5173.
Plain npm run dev does not enable the emulators. It uses the Firebase
configuration bundled with the application and should be used only by someone
who understands and is authorized to access that environment. App Check is
enforced there, so the dev client must present a registered debug token: put
it in a gitignored .env.local as VITE_APPCHECK_DEBUG_TOKEN (register the
value under App Check → Apps → Manage debug tokens, or via the
firebaseappcheck REST API). Without it the SDK prints a fresh token to the
browser console and every request is refused until that token is registered.
Never commit a debug token — it bypasses attestation for whoever holds it.
npm run build
npm run previewThe static build goes to public/. The build also creates the matching HTML
shell used for public profile rendering. Treat both outputs as one release
artifact.
Run the default test suite:
npm testRun the release validation suite before a deployment:
npm run validatevalidate runs type and framework checks, unit tests, rules and emulator
tests, PWA tests, Functions tests, a production build, artifact checks, bundle
budgets, and dependency audits. The root audit excludes development-only
packages; the Functions audit checks its complete package tree.
Maintained source, tests, configuration, and repository tooling are
TypeScript-only. public/service-worker.js is the sole JavaScript exception:
it is generated from src/service-worker.ts during the build. Unit tests
enforce both this boundary and the absence of unchecked TypeScript escape
hatches.
Root package commands:
| Command | Purpose |
|---|---|
npm run dev |
Start the Vite development server |
npm run build |
Production build and renderer-shell synchronization |
npm run preview |
Preview the production build locally |
npm test |
Default checks and automated test suite |
npm run validate |
Release validation, build, artifact and bundle checks, and audits |
npm run check |
Svelte and TypeScript checks for the app, Node tools, and service worker |
npm run check:app |
SvelteKit synchronization and Svelte checks |
npm run check:node |
Type-check repository Node tools |
npm run check:service-worker |
Type-check the service worker |
npm run check:watch |
Watch-mode Svelte checks |
npm run test:unit |
Application and migration unit tests |
npm run test:rules |
Firestore Rules and integration tests against local emulators |
npm run test:catalog-emulator |
Restricted catalog preview/apply workflow against local emulators |
npm run test:functions |
Functions lint, production build, strict test type-check, and tests |
npm run test:pwa |
Service-worker and PWA behavior tests |
npm run test:artifacts |
Generated build and renderer artifact checks |
npm run test:bundle |
JavaScript and CSS bundle budgets |
npm run test:e2e |
Browser tests against local emulators |
npm run test:e2e:browser |
Run Playwright against an already running test environment |
npm run pages:assemble |
Assemble the Cloudflare Pages upload directory (.pages-dist/: the build plus the worker) |
npm run pages:deploy |
Assemble and deploy the site to Cloudflare Pages with the pinned wrangler |
npm run pages:purge |
Purge the Cloudflare edge cache so a release is visible before the TTL expires |
node docs/architecture/verify.ts |
Route coverage, image freshness, and map sanitization |
Functions package commands:
| Command | Purpose |
|---|---|
npm --prefix functions test |
Lint and type-check production/test backend code, then run backend tests |
npm --prefix functions run lint |
Lint backend TypeScript |
npm --prefix functions run clean |
Remove compiled backend output |
npm --prefix functions run build |
Compile backend TypeScript (copies shared/ into functions/src/shared first) |
npm --prefix functions run sync-shared |
Copy the app-and-functions shared modules into the functions source tree |
npm --prefix functions run check:test |
Strictly type-check backend tests |
npm --prefix functions run stage:emulator-secrets |
Stage checked-in local-only emulator credentials |
npm --prefix functions run serve |
Build and start the local emulator suite |
npm --prefix functions run shell |
Alias for the emulator workflow |
npm --prefix functions start |
Alias for the emulator workflow |
npm --prefix functions run deploy |
Deploy backend services with the pinned CLI |
npm --prefix functions run logs |
Read backend logs with the pinned CLI |
npm test intentionally omits the browser end-to-end suite, artifact checks,
and bundle budgets. npm run validate adds artifact and bundle checks. Run
npm run test:e2e separately when a change affects a complete browser flow.
Deployment requires authorized operator access and the private operational runbooks. This public README documents the safe release boundary, not project identifiers, service identities, secret names, quotas, incident commands, or recovery credentials.
For a routine release:
- Start from a clean branch and install locked dependencies with
npm ciin both package roots. - Run
npm testand any relevant browser end-to-end tests. - Run
npm run buildandnode docs/architecture/verify.ts. - Review the generated web and profile-renderer artifacts together.
- Commit the source and generated artifacts.
- Run
npm run validatefrom that clean commit. The artifact checks compare the generated files withHEAD, so this step belongs after the artifact commit and before deployment. Confirm the working tree remains clean. - Deploy the site to Cloudflare Pages, then the backend, with the pinned CLIs, and follow the private verification and rollback runbooks.
npm run pages:deploy
npm exec --yes --package firebase-tools@15.24.0 -- firebase deployThe site and the public profile renderer are coupled: the renderer serves a
shell synchronized from the site build, so release both together, never one
by itself. firebase deploy also republishes the retired Firebase Hosting
site, which only redirects to the canonical domain. A routine release must not
rerun database migrations, legacy configuration exports, or completed rollout
steps.
For any data-shape change, use MIGRATIONS.md and the migration script's own header. Production migration timing and emergency procedures stay in private operator documentation.
book-tracker/
├── src/
│ ├── lib/ UI, stores, Firebase access, types, and utilities
│ └── routes/ SvelteKit pages
├── static/ Static assets served from the site root (keep them small)
├── docs/screenshots/ README screenshots, not deployed
├── cloudflare/ Pages worker (routing, renderer proxy, edge cache, header policy, /__/auth/* hop to the Hosting action page) and its assembly script
├── hosting-retired/ Redirect-only content for the retired Firebase Hosting site
├── functions/
│ ├── src/ Backend services and event handlers
│ └── test/ Functions tests
├── tests/ App, rules, migration, PWA, and artifact tests
├── docs/architecture/ Sanitized map sources and rendered images
├── migrate-*.ts One-time and maintenance migration tools
├── db-audit.ts Read-only data consistency audit
├── db-snapshot.ts Snapshot tool used before migrations
├── MIGRATIONS.md Public-safe migration procedure and status ledger
├── firestore.rules Application data permissions
├── firestore-secrets.rules Restricted credential-store permissions
└── firebase.json Emulator and deployment configuration
- Svelte 5 and SvelteKit 2
- Vite 7 and TypeScript 5
- Bootstrap 5
- Firebase Authentication, Firestore, and Functions
- Cloudflare Pages for the site, with a worker that proxies public profile pages to the renderer
- Node.js 22 for backend services and repository tools
- Pages use SvelteKit file-based routing under
src/routes. - New components use Svelte 5 runes.
- Browser writes preserve offline behavior through Firestore's local cache.
- Navigation guards improve the user experience, but Rules and backend authorization are the security boundaries.
- Generated artifacts must match their source and committed revision.
- Changes to routes, architecture, access, data flows, or integrations must update and regenerate the maps.
If the Node version is wrong:
nvm useIf dependencies are inconsistent, reinstall from the lockfiles:
npm ci
npm --prefix functions ciIf an emulator command fails because a port is already in use, stop the other emulator process and rerun the command. Do not switch to deployed services as a shortcut.
Version 2 is the current application line. Earlier framework-upgrade notes are historical and no longer part of setup or deployment. Database rollout status is recorded in MIGRATIONS.md.
MIT