SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
3.1 KB · 43 lines typescript
Raw Blame History
1/**2 * Live fixture capture for cardrush: one MTG list page (plain HTTP, set-code brackets), one One Piece3 * page (rarity brackets, sealed boxes) and — if a Scrapfly capture of the Pokémon store exists at4 * /tmp/cr_pokemon_list2.html (taken once during research, 25 credits) — the Pokémon page.5 * Usage: pnpm tsx connectors/api/cardrush/_capture.ts6 */7import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';8import path from 'node:path';9import { fixtureDir, saveFixture } from '@rareindex/connectors/testing';10import meta from './meta.json' with { type: 'json' };11import { parseListPage, type CardrushPayload, type CardrushStore } from './index.js';12import { HTML_HEADERS } from '../_g1-cards-eu-jp-lib/index.js';1314const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));15mkdirSync(fixtureDir('cardrush'), { recursive: true });16const stores = meta.config.stores as unknown as CardrushStore[];1718async function capture(host: string, listId: string, name: string, doc: string | null, engine: 'api' | 'scrapfly', keep: number, note: string) {19  const store = stores.find((s) => s.host === host)!;20  const url = `https://${host}/product-list/${listId}`;21  const page = doc ?? (await (await fetch(url, { headers: HTML_HEADERS })).text());22  if (name === 'mtg-list-page1') writeFileSync(path.join(fixtureDir('cardrush'), 'list-page.html'), page);23  const { title, items, hasNext } = parseListPage(page, host);24  const soldOut = items.find((i) => i.soldOut);25  const kept = items.slice(0, keep);26  if (soldOut && !kept.includes(soldOut)) kept[kept.length - 1] = soldOut;27  const payload: CardrushPayload = { store: { host, categorySlug: store.categorySlug, franchise: store.franchise ?? null, brand: store.brand ?? null, bracket: store.bracket ?? 'rarity' }, listId, listName: title ?? store.lists[listId]!, page: 1, items: kept };28  saveFixture('cardrush', name, {29    raw: { url, externalId: `${host}:${listId}:p1`, kind: 'listing', engine, fetchedAt: new Date(), payload },30    expect: { minCount: 5, kinds: ['listing'], requiredFields: ['price', 'attributes.name'] },31    note: `Live capture ${new Date().toISOString().slice(0, 10)} — ${url} via ${engine} (${note}; ${items.length} items, hasNext=${hasNext}, first ${kept.length} kept incl. a sold-out row when present)`,32  });33  console.log(name, title, items.length, hasNext, kept[0]);34}3536await capture('www.cardrush-mtg.jp', '100', 'mtg-list-page1', null, 'api', 16, 'Magic 4th Edition list: [COND]JP/EN name《language》【SET】');37await wait(4000);38await capture('www.cardrush-op.jp', '4', 'one-piece-sealed-boxes', null, 'api', 8, 'One Piece sealed boxes (【未開封BOX】 → completeness sealed)');39await wait(4000);40await capture('www.cardrush-op.jp', '17', 'one-piece-red-singles', null, 'api', 12, 'One Piece red singles with 【rarity】{number}');41const pk = '/tmp/cr_pokemon_list2.html';42if (existsSync(pk)) await capture('www.cardrush-pokemon.jp', '2', 'pokemon-scrapfly-page1', readFileSync(pk, 'utf8'), 'scrapfly', 12, 'Pokémon store behind Cloudflare, page captured through Scrapfly ASP (country jp, no JS); PSA-graded titles');43