import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { console.log("Seeding AI visibility + competitors..."); const brands = await prisma.brand.findMany(); if (brands.length === 0) { console.log("No brands. Run main seed first."); return; } for (const brand of brands) { // Clear existing await prisma.aiVisibilitySnapshot.deleteMany({ where: { brandId: brand.id } }); await prisma.competitorSnapshot.deleteMany({ where: { brandId: brand.id } }); await prisma.competitor.deleteMany({ where: { brandId: brand.id } }); // ── AI Visibility: 30 days of snapshots ── const platforms = ["chatgpt", "claude", "perplexity", "gemini", "google_aio"]; const topics = [ "best project management tools", "SEO audit checklist", "technical SEO guide", "AI visibility optimization", "website speed optimization", ]; const pages = ["/features", "/blog/seo-guide", "/technical-audit", "/pricing", "/about"]; for (let d = 30; d >= 0; d--) { const date = new Date(); date.setDate(date.getDate() - d); date.setHours(12, 0, 0, 0); for (let p = 0; p < platforms.length; p++) { const baseMentions = [5, 4, 3, 2, 1][p]; const growth = 1 + ((30 - d) / 30) * 0.2; const noise = () => 0.7 + Math.random() * 0.6; for (let t = 0; t < 2; t++) { const topicIdx = (p + t) % topics.length; const mentions = Math.round(baseMentions * growth * noise()); const citations = Math.round(mentions * (0.3 + Math.random() * 0.4)); await prisma.aiVisibilitySnapshot.create({ data: { brandId: brand.id, date, platform: platforms[p], topic: topics[topicIdx], mentions, citations, visibilityScore: Math.min(100, Math.round(mentions * 8 + citations * 12)), sourceUrl: pages[topicIdx], change: d < 28 ? Math.round((Math.random() - 0.3) * 3) : 0, }, }); } } } console.log(` AI visibility: 30 days × 5 platforms × 2 topics for ${brand.name}`); // ── Competitors ── const comps = [ { name: "RankBoost.io", domain: "rankboost.io", type: "national", strongestPage: "/features/seo-audit", whyWinning: "Published 4 new long-form guides targeting core keywords. Strong internal linking structure.", watchlist: true }, { name: "ContentKing", domain: "contentking.com", type: "national", strongestPage: "/technical-seo-guide", whyWinning: "12,000-word technical SEO guide displacing competitors in AI citations.", watchlist: true }, { name: "LocalSEO Pro", domain: "localseepro.com", type: "local", strongestPage: "/local-seo-services", whyWinning: "Dominates local pack for city-specific queries. Strong GBP optimization.", watchlist: false }, { name: "AgencyFirst", domain: "agencyfirst.io", type: "emerging", strongestPage: "/blog/ai-seo", whyWinning: "New player publishing AI-focused SEO content rapidly.", watchlist: false }, ]; for (const c of comps) { const competitor = await prisma.competitor.create({ data: { brandId: brand.id, name: c.name, domain: c.domain, type: c.type, strongestPage: c.strongestPage, whyWinning: c.whyWinning, watchlist: c.watchlist, }, }); // Add weekly snapshots for 12 weeks for (let w = 12; w >= 0; w--) { const date = new Date(); date.setDate(date.getDate() - w * 7); date.setHours(12, 0, 0, 0); const threat = c.watchlist ? (w < 4 ? "high" : "medium") : "low"; const baseAi = c.name === "RankBoost.io" ? 8 : c.name === "ContentKing" ? 6 : 3; await prisma.competitorSnapshot.create({ data: { competitorId: competitor.id, brandId: brand.id, date, movement: w < 3 ? `#${5 - w} → #${3 - w > 0 ? 3 - w : 1}` : "Stable", rankChange: w < 4 ? Math.round(Math.random() * 3) : 0, aiMentions: Math.round(baseAi * (1 + (12 - w) / 12 * 0.3) + (Math.random() - 0.5) * 2), aiMentionChange: w < 6 ? Math.round((Math.random() - 0.3) * 3) : 0, trafficEstimate: Math.round((5000 + Math.random() * 10000) * (1 + (12 - w) / 12 * 0.15)), topKeyword: c.name === "RankBoost.io" ? "project management SEO tool" : c.name === "ContentKing" ? "technical SEO audit" : "local SEO services", threat, }, }); } } console.log(` Competitors: ${comps.length} with 13 weekly snapshots each for ${brand.name}`); } console.log("AI visibility + competitors seeding complete!"); } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());