lilith-platform.live/codebase/@features/api/scripts/seed-quinn-iter16.ts
2026-04-19 02:27:25 -07:00

163 lines
5.6 KiB
TypeScript

/**
* 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();