Files
kongruity/backend/db/seed.js
2026-08-01 01:06:19 -04:00

81 lines
2.3 KiB
JavaScript

import 'dotenv/config';
import split2 from 'split2';
import { from as copyFrom } from 'pg-copy-streams';
import { createReadStream } from 'node:fs';
import { Transform } 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'];
const DEFAULTS = { x: 0, y: 0, color: 'yellow' };
const csvField = (value) => {
if (value === null || value === undefined) return '';
return `"${String(value).replaceAll('"', '""')}"`;
};
const toCsvRows = () => new Transform({
writableObjectMode: true,
transform(line, _encoding, callback) {
if (line.trim().length === 0) {
callback();
return;
}
let note;
try {
note = JSON.parse(line);
} 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 () => {
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 - inserted} already existed).`);
} catch (err) {
await client.query('ROLLBACK').catch(() => {});
console.error('Seed failed:', err.message);
process.exitCode = 1;
} finally {
client.release();
await close();
}
};
run();