How it works
From a list of shops to a search engine, every day, on free GitHub Actions and GitHub Pages.
Everything runs on free GitHub Actions and GitHub Pages: no server, no database, no paid service.
data/shops/*.json ──▶ Refresh (daily) ──▶ "data" release ──▶ Deploy ──▶ GitHub Pages
discover OSM shops shops-state.json search index dist/
check every shop products.ndjson.gz Astro site
crawl Shopify / Woo
- Shops are curated in
data/shops/<host>.json, one file per shop (schema). - Discover (
npm run discover, monthly): finds record shops with a website on OpenStreetMap, checks them, and keeps those whose catalog we can read. - Check (
npm run check): for every shop, is it up, does robots.txt allow us, which platform is it, does it still sell records? Dead, moved, duplicated, non-music and blocked shops are excluded automatically. Results:generated/shops-state.json, shown on/status/. - Crawl (
npm run crawl): reads public catalogs (Shopifyproducts.json, WooCommerce Store API), keeps in-stock records, one request at a time per shop. Output:generated/products.ndjson.gz. - Quality checks (start of
npm run index, code inscripts/quality/, settings indata/config.jsonquality): shop test listings ((testproduct)), untitled listings and non-records (the product filter ofproducts.exclude) are left out of everything below. Wrong prices are removed but the record stays searchable without a price, so it can’t be the cheapest offer, a price drop or a low price, and the ledger forgets it: a price under €0.50 or over €3,000 (converted withdata/rates.json), and every price of a shop whose median is under €2 or over €500 (a wrong currency, e.g. dollars labelled yen). Report:generated/quality-report.json(counts per reason, suspect shops, samples), counts on/status/. - Offer ledger (part of
npm run index, code inscripts/ledger/): one row per offer (shop + product path) with first/last seen, last in stock, price, lowest price in 90 days and statusin | out | gone. Each UTC day (LEDGER_DATE=YYYY-MM-DDoverrides it) is diffed against the day before into events:new,restock,soldout,price-down,gone. A shop’s first two crawls emit nonew. Deploy keepsgenerated/ledger.ndjson.gzandledger-prev.ndjson.gz(the day before, for a second build on the same day) in thedatarelease. Outputs:public/w/(watchlist data:now/<abc>.json,d/<day>.jsonfor 14 days,index.json) andgenerated/new-arrivals.jsonfor/new/pages and/feed/*.xmlAtom feeds. - Build (
npm run build): writes a static, sharded search index topublic/search/and builds the Astro site (search, shops by country, shop pages, artist pages, sitemap) intodist/.
Search runs in the browser: <vf-search> downloads only the index shards for the words you type.