Files
krow_talent_app/scripts/seed-fixture.mjs
2026-08-28 11:02:02 +05:30

73 lines
3.6 KiB
JavaScript

/**
* Writes the backend's seed fixture from the frontend's seed module.
*
* node scripts/seed-fixture.mjs # check: exits non-zero if stale
* node scripts/seed-fixture.mjs --write # regenerate
*
* `src/api/seed.js` is the authored source: it has the helpers, the reasoning in
* comments, and the values a person edits. `seed/fixtures/seed.json` is what the
* Go seeder loads. They used to be maintained in parallel with nothing checking
* them, which is a duplication that only fails quietly — the demo and the API
* would answer the same question differently, and the first symptom would be a
* number on a page disagreeing with a number from an agent.
*
* `ShiftRecord` is deliberately not carried across. Its dates are anchored to
* the day the seeder runs rather than to this file's fixed calendar, so the Go
* side generates it (internal/seeder/shifts.go, a port of attendanceSeed.js).
* A frozen snapshot of it would read as permanently empty a fortnight later.
*/
import { createServer } from 'vite';
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
export const FIXTURE_PATH = join(process.cwd(), '..', 'krow-backend', 'seed', 'fixtures', 'seed.json');
/** The fixture the frontend seed implies. Pure — takes the loaded module. */
export const GENERATED_NOTE =
'Generated from krow-demo/src/api/seed.js — do not edit by hand. ' +
'Regenerate with: npm run seed:fixture';
export function fixtureFrom(seedModule) {
const { ShiftRecord, ...entities } = seedModule.seedData;
/* First key, so it is the first thing anyone opening the file reads. The Go
loader unmarshals into a struct of demoUser + entities and ignores the
rest, so this costs nothing on the reading side. */
return { _generated: GENERATED_NOTE, demoUser: seedModule.DEMO_USER, entities };
}
/** Serialised exactly as the committed file is: 2-space indent, no trailing newline. */
export const serialise = (fixture) => JSON.stringify(fixture, null, 2);
export async function buildFixture(server) {
return serialise(fixtureFrom(await server.ssrLoadModule('/src/api/seed.js')));
}
/* ── CLI ──────────────────────────────────────────────────────────────────── */
/* pathToFileURL, not string concatenation: this repo lives under a path with a
space in it, and import.meta.url percent-encodes it while `file://${path}`
does not — the guard silently never fires and the script does nothing. */
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
const server = await createServer({
root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', logLevel: 'error',
});
const built = await buildFixture(server);
await server.close();
if (process.argv.includes('--write')) {
writeFileSync(FIXTURE_PATH, built);
const { entities } = JSON.parse(built);
const n = Object.values(entities).reduce((s, v) => s + v.length, 0);
console.log(`seed.json written — ${Object.keys(entities).length} collections, ${n} records.`);
} else if (!existsSync(FIXTURE_PATH)) {
console.error('seed.json is missing. Run: node scripts/seed-fixture.mjs --write');
process.exit(1);
} else if (readFileSync(FIXTURE_PATH, 'utf8') !== built) {
console.error('seed.json is stale — it no longer matches src/api/seed.js.');
console.error('Run: node scripts/seed-fixture.mjs --write');
process.exit(1);
} else {
console.log('seed.json is in step with src/api/seed.js.');
}
}