SPB Git

spb/rareindex

Public
6.7 KB · 167 lines typescript
1import { fileURLToPath } from 'node:url';2import path from 'node:path';3import { readFileSync, existsSync } from 'node:fs';4import { sql } from 'drizzle-orm';5import { CATEGORIES, GRADERS, INDICES, INDEX_BASE_DATE, INDEX_BASE_VALUE } from '@rareindex/taxonomy';6import { getDb, closeDb } from './client.js';7import { categories, graders, indices, sources, connectors } from './schema/index.js';89/** Upsert taxonomy nodes + graders from data/taxonomy (idempotent). */10export async function seedTaxonomy(): Promise<void> {11  const db = getDb();12  for (const c of CATEGORIES) {13    await db14      .insert(categories)15      .values({16        slug: c.slug,17        parentSlug: c.parent ?? null,18        familySlug: c.familySlug,19        name: c.name,20        shortName: c.short ?? null,21        description: c.description ?? null,22        level: c.level,23        phase: c.phase,24        conditionScale: c.conditionScale ?? 'general',25        graders: c.graders ?? [],26        indexTicker: c.index ?? null,27        sortOrder: c.sortOrder,28        complianceFlags: c.compliance ?? [],29        icon: c.icon ?? null,30      })31      .onConflictDoUpdate({32        target: categories.slug,33        set: {34          parentSlug: c.parent ?? null,35          familySlug: c.familySlug,36          name: c.name,37          shortName: c.short ?? null,38          description: c.description ?? null,39          level: c.level,40          phase: c.phase,41          conditionScale: c.conditionScale ?? 'general',42          graders: c.graders ?? [],43          indexTicker: c.index ?? null,44          sortOrder: c.sortOrder,45          complianceFlags: c.compliance ?? [],46          icon: c.icon ?? null,47          updatedAt: new Date(),48        },49      });50  }51  for (const g of GRADERS) {52    await db53      .insert(graders)54      .values({ slug: g.slug, name: g.name, categorySlugs: g.categories, scale: g.scale, populationUrl: g.populationUrl ?? null, verifyUrl: g.verifyUrl ?? null })55      .onConflictDoUpdate({ target: graders.slug, set: { name: g.name, categorySlugs: g.categories, scale: g.scale, populationUrl: g.populationUrl ?? null, verifyUrl: g.verifyUrl ?? null } });56  }57}5859/** Upsert index definitions (§3). */60export async function seedIndices(): Promise<void> {61  const db = getDb();62  for (const i of INDICES) {63    const familySlugs = i.families;64    const categorySlugs = CATEGORIES.filter((c) => familySlugs.includes(c.familySlug)).map((c) => c.slug);65    await db66      .insert(indices)67      .values({68        id: `idx_${i.ticker.toLowerCase().replace(/[^a-z0-9]+/g, '_')}`,69        ticker: i.ticker,70        name: i.name,71        description: i.description ?? null,72        categorySlugs,73        familySlugs,74        parentTicker: i.flagship ? null : 'RARE',75        baseDate: INDEX_BASE_DATE,76        baseValue: INDEX_BASE_VALUE,77        minConstituents: i.minConstituents,78        isFlagship: Boolean(i.flagship),79        color: i.color,80      })81      .onConflictDoUpdate({ target: indices.ticker, set: { name: i.name, description: i.description ?? null, categorySlugs, familySlugs, minConstituents: i.minConstituents, color: i.color } });82  }83}8485/** Mirror connectors/registry.json into sources + connectors tables (state columns are preserved). */86export async function seedRegistry(): Promise<void> {87  const here = path.dirname(fileURLToPath(import.meta.url));88  const registryPath = path.resolve(here, '../../../connectors/registry.json');89  if (!existsSync(registryPath)) return;90  const reg = JSON.parse(readFileSync(registryPath, 'utf8')) as { connectors: Array<Record<string, any>> };91  const db = getDb();92  for (const c of reg.connectors) {93    await db94      .insert(sources)95      .values({ id: c.sourceId, name: c.sourceName, sourceType: c.sourceType, url: c.sourceUrl, countries: c.regions ?? [], languages: c.languages ?? ['en'], currencies: c.currency ?? ['USD'], trustScore: c.trustScore ?? 0.6, attributionRequired: c.attributionRequired ?? true, termsUrl: c.termsUrl ?? null })96      .onConflictDoUpdate({ target: sources.id, set: { name: c.sourceName, sourceType: c.sourceType, url: c.sourceUrl, trustScore: c.trustScore ?? 0.6, termsUrl: c.termsUrl ?? null, updatedAt: new Date() } });97    await db98      .insert(connectors)99      .values({100        id: c.id,101        sourceId: c.sourceId,102        displayName: c.displayName,103        enginePriority: c.enginePriority,104        categories: c.categories,105        regions: c.regions ?? [],106        languages: c.languages ?? ['en'],107        currency: c.currency ?? ['USD'],108        supportsListings: c.supportsListings ?? false,109        supportsSold: c.supportsSold ?? false,110        supportsAuctions: c.supportsAuctions ?? false,111        supportsImages: c.supportsImages ?? true,112        supportsCatalog: c.supportsCatalog ?? false,113        supportsPopulation: c.supportsPopulation ?? false,114        refreshFrequencyMinutes: c.refreshFrequencyMinutes ?? 1440,115        priority: c.priority ?? 'medium',116        status: c.enabled === false ? 'disabled' : 'active',117        schemaVersion: c.schemaVersion ?? '1.0',118        config: c.config ?? {},119      })120      .onConflictDoUpdate({121        target: connectors.id,122        set: {123          sourceId: c.sourceId,124          displayName: c.displayName,125          enginePriority: c.enginePriority,126          categories: c.categories,127          regions: c.regions ?? [],128          languages: c.languages ?? ['en'],129          currency: c.currency ?? ['USD'],130          supportsListings: c.supportsListings ?? false,131          supportsSold: c.supportsSold ?? false,132          supportsAuctions: c.supportsAuctions ?? false,133          supportsImages: c.supportsImages ?? true,134          supportsCatalog: c.supportsCatalog ?? false,135          supportsPopulation: c.supportsPopulation ?? false,136          refreshFrequencyMinutes: c.refreshFrequencyMinutes ?? 1440,137          priority: c.priority ?? 'medium',138          schemaVersion: c.schemaVersion ?? '1.0',139          config: c.config ?? {},140          updatedAt: new Date(),141          // status is intentionally NOT overwritten (admin pause survives reseeds) except when disabled in registry142          ...(c.enabled === false ? { status: 'disabled' } : {}),143        },144      });145  }146}147148export async function seedAll(): Promise<void> {149  await seedTaxonomy();150  await seedIndices();151  await seedRegistry();152  const db = getDb();153  const [{ n }] = (await db.execute(sql`select count(*)::int as n from categories`)) as unknown as [{ n: number }];154  console.log(`[seed] categories=${n} indices=${INDICES.length}`);155}156157const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);158if (isMain) {159  seedAll()160    .then(() => closeDb())161    .catch(async (e) => {162      console.error(e);163      await closeDb();164      process.exit(1);165    });166}167
Files · 78cf9f7f3b34154bdac4f0af831574da262502f2
Menu

Theme