Skip to content

Project Structure

This page walks through the Rarebox codebase so you know where everything lives and why.

Top-Level Layout

rarebox/
├── api/                    # Vercel serverless functions (Python)
│   ├── health.py           # Health check endpoint
│   ├── search.py           # Meta deck scraping from Limitless TCG
│   ├── price.py            # Price proxy/lookup
│   └── sealed.py           # Sealed product price fetching
├── price-server/           # Standalone price server (development/testing)
├── public/                 # Static assets (favicon, manifest, robots.txt, sitemap)
├── src/
│   ├── assets/             # CSS, images, static resources
│   ├── components/         # Vue components
│   │   └── scanner/        # Camera scanner components
│   ├── data/               # Static data files
│   ├── router/             # Vue Router configuration
│   ├── services/           # API integration modules
│   │   └── tcg/            # Multi-TCG card cache, preload, search, providers
│   ├── stores/             # Pinia stores (state management)
│   ├── utils/              # Utility functions
│   ├── views/              # Page-level Vue components (routed)
│   ├── App.vue             # Root component
│   ├── db.js               # Dexie.js persistence layer
│   └── main.js             # App entry point
├── index.html              # SPA entry HTML
├── package.json
├── vercel.json             # Vercel deployment config
├── vite.config.js          # Vite build config
└── requirements.txt        # Python dependencies for serverless functions

Key Directories

src/stores/

The heart of the application. Pinia stores manage all state:

  • portfolio.js — The main store. Manages portfolios, items, snapshots, price alerts, settings. Handles persistence to IndexedDB via db.js. Contains the async init() method that hydrates state on load and the debounced persist() / immediate persistNow() methods for writing back. Supports all 6 TCGs with game-specific price refresh routing.
  • trade.js — Trade analyzer state. Side A vs Side B with item lists, fairness calculation. Stored in separate IDB key (trade_state) to prevent race conditions with portfolio persistence.
  • decks.js — Deck CRUD, card operations (addCardToDeck, updateCardQuantity, removeCardFromDeck), stats computation (getDeckStats cross-references against portfolio store), and meta deck import (importMetaDeck). Stored in localStorage (rarebox_decks), not IDB.
  • scanner.js — Camera scanner UI state. Minimal state holder for scanner open/close, current session, and process status.

src/db.js

The persistence layer. Wraps Dexie.js with these exports:

  • loadState() — reads the entire app state from IndexedDB
  • saveState(value) — writes the entire state blob to IndexedDB
  • loadTradeState() — reads trade state from separate IDB key
  • saveTradeState(value) — writes trade state to separate IDB key
  • isStale(item) — checks if an item's price is past its staleness threshold (24h for cards, 12h for sealed/graded)
  • hasNeverPriced(item) — returns true if an item has never had a successful price fetch
  • queryCache(table, key) — reads from a key-value cache table
  • updateCache(table, key, value, ttl) — writes to a cache table with TTL

Three IDB tables:

  • state — app state (app_state) and trade state (trade_state)
  • prices_cache — PriceCharting price cache with TTL
  • cards — preloaded card database indexed by [game+set+id]

src/services/

API integration modules. Each service encapsulates one external API:

  • pokemonApi.js — pokemontcg.io wrapper. searchCards(), getCard(), getSets(), getSetCards(), getMarketPrice(). Uses select= parameter to trim payloads. In-memory cache with 1h TTL.
  • priceCharting.js — PriceCharting paid API. Browser-direct fetch with CORS. searchPC() for sealed/graded product search. 1h TTL cache, max 200 entries. Requires API key.
  • priceFeedService.js — PriceCharting free tier. fetchGamePriceFeed(game) for bulk game pricing. resolvePrice(game, cardName) for individual card lookups. 6h IDB cache via prices_cache table. No API key required.
  • priceHistory.js — tcgdex price-history GitHub repo. Fetches historical TCGPlayer data (Nov 2022+). 404s cached as misses.
  • metaDecksApi.js — Serverless endpoint (/api/search) wrapper. Fetches live meta decks from Limitless TCG. 24h localStorage cache with fallback.
  • priceServer.js — Legacy price server client. Browser-direct PriceCharting JSON search. Used by scanPipeline and trade analyzer.

src/services/tcg/

Multi-TCG card system:

  • cardCache.js — Dexie IDB card cache. saveCardsToCache(), getCardsFromCache(), searchCacheIndex(). In-memory Map index for O(1) lookup. Used by cardPreloader and multiSearch.
  • cardPreloader.js — Two-phase background preload. Phase 1: fast set list fetch. Phase 2: full card data fetch. Per-TCG fetchers for all 6 games. Progress tracking for UI updates.
  • multiSearch.js — Fan-out search across all 6 TCG providers. Parallel queries, merge + deduplicate. Abort/timeout support (20s). Fast providers mode for scanner.
  • providers.js — Provider registry for browse. searchSets(game), searchCards(game, query), getCardDetail(game, id). Normalized Set/Card shapes across all games. Used by TcgSetsView and BrowseView.

src/utils/

  • alerts.js — Price alert CRUD, browser notification integration, checkAlerts() against a price map. localStorage persistence (rarebox_alerts).
  • backup.js — JSON export/import of all Rarebox data. exportBackup() dumps portfolios, settings, snapshots, and price caches. importBackup() atomically replaces all data and reloads. Uses pako for gzip compression.
  • collectrImport.js — Collectr CSV/XLSX import. Parses card names, sets, quantities, conditions, grading info. Auto-detects game type. Japanese card detection. Variance normalization.
  • excel.js — XLSX export of portfolio data (summary + items sheets). Uses SheetJS.
  • ocrService.ts — Tesseract.js OCR worker. Lazy-loaded singleton. Card name extraction from OCR text. Card number extraction via regex patterns.
  • scanPipeline.ts — OCR → multiSearch pipeline. Candidate scoring (name similarity + price availability). Fast providers only for speed. 25s total timeout.

src/data/

  • metaDecks.js — Static meta deck data with hardcoded card IDs and images for instant resolution. Fallback when live API is unavailable.

src/components/

Vue single-file components:

  • AddItemModal.vue — Add cards, sealed products, or graded slabs. Three-step flow: type → search → configure.
  • BulkImportModal.vue — Paste PTCGL/PTCGO deck lists for bulk card import.
  • CardDatabaseLoader.vue — First-run TCG selection modal. Shows card counts per game, triggers background preload.
  • CardLoadIndicator.vue — Floating pill showing preload progress. Expandable per-game status, speed/ETA.
  • InstallPrompt.vue — PWA install prompt with platform detection.
  • LocalSyncModal.vue — Device-to-device sync via QR code or clipboard. Gzip-compressed base64 transfer.
  • PortfolioChart.vue — Portfolio value-over-time chart (ApexCharts). LOCF algorithm.
  • PriceChart.vue — Individual card price history chart (ApexCharts). Variant selector.
  • PullToRefresh.vue — Touch pull-to-refresh with directional lock.
  • TourModal.vue — Feature tour video modal.
  • scanner/CameraViewfinder.vue — Camera capture via getUserMedia. Rear camera preferred. File upload fallback.

src/views/

Page-level components mapped to routes:

ViewRouteDescription
DashboardView.vue/Combined portfolio overview, total value, gain/loss, new user landing
SearchView.vue/searchMulti-TCG card search across all 6 games
SetsView.vue/setsBrowse Pokémon sets (EN/JP), click to browse cards
TcgSetsView.vue/tcg-setsGeneric TCG sets browse for MTG/Yu-Gi-Oh!/Lorcana/One Piece/Riftbound
BrowseView.vue/browseTCG selector landing page — tiles linking to sets views
PortfolioView.vue/portfolio/:idSingle portfolio detail with chart, item table, group-by/sort
DeckListView.vue/decksGrid of all decks with stats, game filter
MetaDecksView.vue/decks/metaLive meta decks from Limitless TCG, multi-game
DeckBuilderView.vue/decks/:idDeck editor with card search and ownership tracking
TradeLanding.vue/tradeTrade analyzer intro/landing page
TradeAnalyzerView.vue/trade/analyzerSide A vs Side B comparison with grading and fairness
SettingsView.vue/settingsExport, backup, alerts, reset, Collectr import, API key
TermsView.vue/termsTerms & Conditions + Privacy Policy

api/

Python serverless functions deployed as Vercel Functions:

FileEndpointPurpose
health.py/api/healthHealth check
search.py/api/searchMeta deck scraping from Limitless TCG
price.py/api/pricePrice proxy/lookup
sealed.py/api/sealedSealed product price fetching from PriceCharting

All use httpx for async HTTP requests and beautifulsoup4 for HTML parsing. Max duration is 30 seconds (configured in vercel.json).

Configuration Files

vercel.json

Defines the deployment:

  • Build command: npm run build
  • Output: dist/
  • Rewrites: API routes pass through to serverless functions; everything else falls through to index.html (SPA routing)
  • Python runtime: @vercel/python@4.5.0

vite.config.js

Standard Vite config with the Vue plugin. Nothing unusual.

requirements.txt

Python dependencies for the serverless functions:

  • httpx>=0.27.0 — async HTTP client
  • beautifulsoup4>=4.12.0 — HTML parser

Released under the MIT License.