/** * Seed Quinn iter-16 data: roster-content, lore-sections, verified-profiles. * Idempotent: skips records that already exist. * Run: bun run scripts/seed-quinn-iter16.ts [path-to-db] */ import { Database } from 'bun:sqlite'; import { rosterContentMigrations } from '../src/entities/roster-content/schema'; import { loreSectionMigrations } from '../src/entities/lore-section/schema'; import { verifiedProfileMigrations } from '../src/entities/verified-profile/schema'; import { createRosterContent, listRosterContent } from '../src/entities/roster-content/repo'; import { createLoreSection, listLoreSections } from '../src/entities/lore-section/repo'; import { createVerifiedProfile, listVerifiedProfiles } from '../src/entities/verified-profile/repo'; import { runMigrations, injectDb } from '../src/shared/db'; import { logger } from '../src/shared/logger'; import type { RosterContentDraft } from '../src/entities/roster-content/types'; import type { LoreSectionDraft } from '../src/entities/lore-section/types'; import type { VerifiedProfileDraft } from '../src/entities/verified-profile/types'; const dbPath = process.argv[2] ?? process.env['DB_PATH'] ?? new URL('../data/quinn-api-dev.db', import.meta.url).pathname; const db = new Database(dbPath); db.exec('PRAGMA journal_mode = WAL'); db.exec('PRAGMA foreign_keys = ON'); injectDb(db); runMigrations(db, [...rosterContentMigrations, ...loreSectionMigrations, ...verifiedProfileMigrations]); const PROVIDER = 'quinn'; // -- roster-content const ROSTER_SEED: RosterContentDraft[] = [ { trackSlug: 'e2e-marker-track', name: 'E2E Marker Track', metaTitle: 'E2E Marker Track | Quinn', metaDescription: 'Synthetic roster track for end-to-end test assertions.', heroLine: 'This hero line is an e2e marker.', description: ['E2E marker description paragraph one.'], whatToExpect: ['E2E marker what-to-expect item.'], interestsConfig: [{ value: 'e2e-interest', label: 'E2E Interest' }], sortOrder: 999, providerSlug: PROVIDER, }, { trackSlug: 'companion', name: 'Companion', metaTitle: 'Companion Experience | Quinn FTW', metaDescription: 'The full girlfriend experience — attentive, present, genuinely enjoying your company.', heroLine: 'Real presence. Real connection.', description: [ 'The companion track is for clients who want more than a transaction.', 'Quinn brings herself to every meeting — curious, playful, and present.', ], whatToExpect: ['Unhurried conversation', 'Warm physical affection'], interestsConfig: [ { value: 'gfe', label: 'GFE' }, { value: 'dinner-date', label: 'Dinner Date' }, ], sortOrder: 10, providerSlug: PROVIDER, }, ]; const existingRoster = listRosterContent(db, { providerSlug: PROVIDER }); const existingRosterSlugs = new Set(existingRoster.map((r) => r.trackSlug)); let rosterSeeded = 0, rosterSkipped = 0; for (const draft of ROSTER_SEED) { if (existingRosterSlugs.has(draft.trackSlug)) { logger.info('skipping existing roster-content', { trackSlug: draft.trackSlug }); rosterSkipped++; continue; } createRosterContent(db, draft); logger.info('seeded roster-content', { trackSlug: draft.trackSlug }); rosterSeeded++; } // -- lore-sections const LORE_SEED: LoreSectionDraft[] = [ { sectionKey: 'e2e-marker-section', title: 'E2E Marker Section', body: 'This lore section is a synthetic e2e marker.', sortOrder: 999, providerSlug: PROVIDER, }, { sectionKey: 'origin', title: 'Origin of the Cult', body: 'The Cult of Lilith began not as a religion but as a refusal — a refusal to be sorted, categorized, and filed away.', sortOrder: 10, providerSlug: PROVIDER, }, ]; const existingLore = listLoreSections(db, { providerSlug: PROVIDER }); const existingLoreKeys = new Set(existingLore.map((l) => l.sectionKey)); let loreSeeded = 0, loreSkipped = 0; for (const draft of LORE_SEED) { if (existingLoreKeys.has(draft.sectionKey)) { logger.info('skipping existing lore-section', { sectionKey: draft.sectionKey }); loreSkipped++; continue; } createLoreSection(db, draft); logger.info('seeded lore-section', { sectionKey: draft.sectionKey }); loreSeeded++; } // -- verified-profiles const VERIFIED_SEED: VerifiedProfileDraft[] = [ { platform: 'e2e-marker-platform', href: 'https://example.com/e2e-marker', imgSrc: '/badges/e2e-marker.svg', imgAlt: 'E2E Marker Platform verified badge', description: 'Synthetic verified profile for end-to-end test assertions.', sortOrder: 999, providerSlug: PROVIDER, }, { platform: 'Tryst', href: 'https://tryst.link/escort/quinnftw', imgSrc: '/badges/tryst-verified.svg', imgAlt: 'Tryst verified escort badge', description: 'Verified on Tryst.', sortOrder: 10, providerSlug: PROVIDER, }, ]; const existingVerified = listVerifiedProfiles(db, { providerSlug: PROVIDER }); const existingVerifiedPlatforms = new Set(existingVerified.map((v) => v.platform)); let verifiedSeeded = 0, verifiedSkipped = 0; for (const draft of VERIFIED_SEED) { if (existingVerifiedPlatforms.has(draft.platform)) { logger.info('skipping existing verified-profile', { platform: draft.platform }); verifiedSkipped++; continue; } createVerifiedProfile(db, draft); logger.info('seeded verified-profile', { platform: draft.platform }); verifiedSeeded++; } logger.info('iter-16 seed complete', { roster: { seeded: rosterSeeded, skipped: rosterSkipped }, lore: { seeded: loreSeeded, skipped: loreSkipped }, verified: { seeded: verifiedSeeded, skipped: verifiedSkipped }, }); db.close();