TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1/** Live fixture capture for magi: two series pages (Pokémon, Yu-Gi-Oh! incl. a SOLD item) + one sold item page. Usage: pnpm tsx connectors/api/magi/_capture.ts */2import { mkdirSync, writeFileSync } from 'node:fs';3import path from 'node:path';4import { fixtureDir, saveFixture } from '@rareindex/connectors/testing';5import meta from './meta.json' with { type: 'json' };6import { parseItemPage, parseSeriesPage, type MagiPayload, type MagiSeed } from './index.js';7import { HTML_HEADERS } from '../_g1-cards-eu-jp-lib/index.js';89const SITE = 'https://magi.camp';10const text = async (u: string) => (await fetch(u, { headers: HTML_HEADERS })).text();11const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));12mkdirSync(fixtureDir('magi'), { recursive: true });13const seeds = meta.config.seeds as MagiSeed[];1415let soldUrl: string | null = null;16for (const [series, name, keep] of [['31', 'pokemon-series-page1', 24], ['34', 'yugioh-series-page1', 24]] as const) {17 const seed = seeds.find((s) => s.series === series)!;18 const url = `${SITE}/series/${series}/items`;19 const doc = await text(url);20 if (series === '31') writeFileSync(path.join(fixtureDir('magi'), 'series-page.html'), doc);21 const { items, hasNext, heading } = parseSeriesPage(doc);22 // keep the first `keep` items but make sure a SOLD item is included when the page has one23 const sold = items.find((i) => i.sold);24 const kept = items.slice(0, keep);25 if (sold && !kept.includes(sold)) kept[kept.length - 1] = sold;26 if (sold) soldUrl = soldUrl ?? sold.url;27 const payload: MagiPayload = { type: 'list', seed, page: 1, items: kept };28 saveFixture('magi', name, {29 raw: { url, externalId: `series:${series}:p1`, kind: 'listing', engine: 'api', 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} (${heading}; ${items.length} items, hasNext=${hasNext}, sold on page: ${items.filter((i) => i.sold).length}; first ${kept.length} kept)`,32 });33 console.log(name, heading, items.length, hasNext, kept[0], sold?.title);34 await wait(4000);35}3637if (soldUrl) {38 const doc = await text(soldUrl);39 const item = parseItemPage(doc, soldUrl)!;40 const payload: MagiPayload = { type: 'item', seed: null, ...item };41 saveFixture('magi', 'sold-item-page', {42 raw: { url: soldUrl, externalId: item.id, kind: 'listing', engine: 'api', fetchedAt: new Date(), payload },43 expect: { minCount: 0, kinds: ['listing'] },44 note: `Live capture ${new Date().toISOString().slice(0, 10)} — ${soldUrl} JSON-LD Product (availability ${item.availability}); lookup path`,45 });46 console.log('sold item', item);47}48