Convert card inventory CSVs (currently ManaBox) into TCGPlayer bulk CSV format using a Scryfall-ID-first matching pipeline on Cloudflare D1.
The Worker now uses a single primary path:
- Parse source CSV using an adapter.
- Normalize rows to a source-agnostic shape.
- Resolve
Scryfall IDthroughscryfall_bridge. - Resolve TCG product in
products. - Resolve SKU in
skusby:product_idprinting_id(normal / foil / etched behavior)condition_idlanguage_id
Legacy set/collector/name fallback matching has been removed from the core processing path.
- Browser upload + paste modes
- Scryfall-ID-first matching for higher accuracy
- Condition/language/finish-aware SKU selection
- Failure CSV with explicit reasons (missing/invalid/unmapped Scryfall ID, SKU miss)
- Adapter-based CSV ingestion for future source formats
worker/- Cloudflare Worker appsrc/main.ts- HTTP entrypoint and route wiringsrc/routes/convert.ts- CSV conversion orchestrationsrc/matcher/scryfall.ts- bridge/product resolution helperssrc/matcher/sku.ts- SKU batch query helperssrc/ui/html.ts- HTML renderingsrc/manabox.ts- ManaBox adaptersrc/custom-source.ts- scaffold for next CSV sourcesrc/sources.ts- adapter registry + auto-detection
sync_r2_to_d1.py- R2 Parquet -> SQLite -> SQL dumps -> D1 syncscryfall_bridge.py- buildsscryfall_bridgefrom Scryfall bulk data
groupsproductsskusscryfall_bridge(scryfall_id,product_id,etched_product_id)
cd worker
npm install
npx wrangler whoamicd worker
npx tsc -p tsconfig.json --noEmitcd worker
npx wrangler devIf Wrangler asks for preview bindings, configure preview resources in worker/wrangler.toml.
python sync_r2_to_d1.pyUseful flags:
--skip-download- reuse existingtcg_data.db--skip-import- only build local SQL dumps--skip-scryfall-bridge- skip bridge table population--scryfall-jsonl PATH- use an existing gzip-compressed Scryfall JSONL file--refresh-scryfall-jsonl- force a fresh Scryfall bulk-data download--products-chunk-size N- tune D1 import chunking
python scryfall_bridge.py /path/to/tcg_data.dbDownloads Scryfall's gzip-compressed default_cards JSONL (if missing), streams it
without loading the full dataset into memory, and populates scryfall_bridge.
Source handling is adapter-driven via CsvSourceAdapter.
Current adapters:
manabox(active)custom-source(scaffold, intentionally non-matching until configured)
To onboard a new source quickly, edit only:
worker/src/custom-source.ts
Steps:
- Set
id - Replace
requiredHeaders - Map fields in:
normalizeRow()toFailureContext()
No core matcher changes are required.
Minimum columns used:
Scryfall IDFoilConditionLanguage(defaults toenif empty)QuantityCollector number(for output metadata)Purchase price(optional)
full test.csv- one card across multiple finishes/conditionsfin .csv- broader real-world sample
MIT