Files
kongruity/backend/db/seed.ts

83 lines
2.6 KiB
TypeScript

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<Record<Column, unknown>> = { 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<string, unknown>;
try {
note = JSON.parse(line) as Record<string, unknown>;
} 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<void> => {
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();