import 'dotenv/config'; import split2 from 'split2'; import { from as copyFrom } from 'pg-copy-streams'; import { createReadStream } from 'node:fs'; import { Transform, type TransformCallback } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { fileURLToPath } from 'node:url'; import { getPool, close } from './index.js'; const FIXTURE = fileURLToPath(new URL('./fixtures/notes.jsonl', import.meta.url)); const COLUMNS = ['id', 'text', 'x', 'y', 'author', 'color'] as const; type Column = typeof COLUMNS[number]; const DEFAULTS: Partial> = { x: 0, y: 0, color: 'yellow' }; const csvField = (value: unknown): string => { if (value === null || value === undefined) return ''; return `"${String(value).replaceAll('"', '""')}"`; }; const toCsvRows = (): Transform => new Transform({ writableObjectMode: true, transform(line: string, _encoding: BufferEncoding, callback: TransformCallback) { if (line.trim().length === 0) { callback(); return; } let note: Record; try { note = JSON.parse(line) as Record; } catch { callback(new Error(`Fixture contains a malformed JSON line: ${line.slice(0, 80)}`)); return; } const row = COLUMNS.map((col) => csvField(note[col] ?? DEFAULTS[col])); callback(null, `${row.join(',')}\n`); }, }); const run = async (): Promise => { const client = await getPool().connect(); try { await client.query('BEGIN'); // COPY has no ON CONFLICT, so land the fixture in a temp table first and // let a single INSERT ... SELECT apply the existing idempotency. await client.query( 'CREATE TEMP TABLE notes_import (LIKE notes INCLUDING DEFAULTS) ON COMMIT DROP' ); const copy = client.query( copyFrom(`COPY notes_import (${COLUMNS.join(', ')}) FROM STDIN WITH (FORMAT csv)`) ); await pipeline(createReadStream(FIXTURE), split2(), toCsvRows(), copy); const { rowCount: staged } = await client.query('SELECT 1 FROM notes_import'); const { rowCount: inserted } = await client.query( `INSERT INTO notes (${COLUMNS.join(', ')}) SELECT ${COLUMNS.join(', ')} FROM notes_import ON CONFLICT (id) DO NOTHING` ); await client.query('COMMIT'); console.log(`Seed complete — ${inserted} notes inserted (${(staged ?? 0) - (inserted ?? 0)} already existed).`); } catch (err) { await client.query('ROLLBACK').catch(() => {}); console.error('Seed failed:', err instanceof Error ? err.message : err); process.exitCode = 1; } finally { client.release(); await close(); } }; run();