TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { ShopifyStoreConnector, type ConnectorMeta, type RawRecordLike } from '@rareindex/connectors';2import type { NormalizedRecord } from '@rareindex/shared';34/**5 * Video Game Trader (videogametrader.com) — Shopify storefront read through the shared Shopify adapter6 * (collections → taxonomy mapping and title rules live in meta.json). This subclass only enriches the7 * condition block from the store's own vocabulary: titles end with " - <Platform> - Used (Complete)",8 * " - Used (Loose)", " - New" and products carry "Condition:…" tags.9 */10export function completenessFromTitle(title: string, tags: string[] = []): { completeness: string | null; conditionRaw: string | null } {11 const cond = tags.find((t) => /^Condition:/i.test(t))?.replace(/^Condition:/i, '').trim() ?? title.match(/-\s*((?:Used|New|NEW|Preowned|Pre-owned|Brand New|Sealed)[^-]*)$/)?.[1]?.trim() ?? null;12 const c = (cond ?? title).toLowerCase();13 let completeness: string | null = null;14 if (/\b(sealed|brand new|\bnew\b)/.test(c) && !/used/.test(c)) completeness = 'sealed';15 else if (/complete|cib|\(cib\)/.test(c)) completeness = 'cib';16 else if (/loose|cart(?:ridge)? only|disc only|game only/.test(c)) completeness = 'loose';17 else if (/box only|no game|manual only/.test(c)) completeness = 'box_only';18 return { completeness, conditionRaw: cond };19}2021export class VideoGameTraderConnector extends ShopifyStoreConnector {22 override async normalize(raw: RawRecordLike): Promise<NormalizedRecord[]> {23 const out = await super.normalize(raw);24 for (const r of out) {25 if (r.kind !== 'listing') continue;26 const tags = Array.isArray(r.attributes.metadata.tags) ? (r.attributes.metadata.tags as string[]) : [];27 const c = completenessFromTitle(r.rawTitle, tags);28 r.condition = { condition: r.condition.condition, conditionRaw: c.conditionRaw ?? r.condition.conditionRaw, completeness: c.completeness ?? r.condition.completeness };29 // platform tag → attributes.set when the title pattern did not catch it30 const platform = tags.find((t) => /^Platform:/i.test(t))?.replace(/^Platform:/i, '').trim();31 if (platform && !r.attributes.set) r.attributes.set = platform;32 if (platform) r.attributes.metadata.platform = platform;33 // strip the store's condition suffix from the asset name34 r.attributes.name = r.attributes.name.replace(/\s*-\s*(?:Used|New|NEW|Preowned|Pre-owned|Brand New|Sealed)\b.*$/i, '').replace(/\s*\*\s*NEW\s*$/i, '').replace(/\s*[|-]\s*[^|-]{2,40}$/, (m) => (platform && m.toLowerCase().includes(platform.toLowerCase().slice(0, 4)) ? '' : m)).trim() || r.attributes.name;35 }36 return out;37 }38}3940export default (meta: ConnectorMeta) => new VideoGameTraderConnector(meta);41