Seeders
Load fixture data into a database, per environment, recording what has been loaded so a second run does not load it twice.
Seeds and migrations answer different questions. A migration changes the schema and runs everywhere. A seed puts rows in, and which rows depend on where you are: a local admin account belongs in dev and nowhere else. So a seed lives under the environment it is for, and a run names the environment it wants.
A seed may declare that it must run after another, and the order is resolved from those declarations rather than from the file numbers alone, because a fixture that references another's rows has to follow it whatever they happen to be numbered.
A seed may also declare itself rerunnable, for a baseline that is written to be idempotent and should be reasserted on every run. Anything else is applied once and recorded.
Like migrations, a run takes a lock in the database, so two processes seeding together do not both insert the same fixture.
b.seeders.create(opts) #
{
dir: string, // directory holding the per-env seed folders; required
db: object, // database handle; defaults to b.db
audit: boolean|object, // audit sink
auditApplied: boolean, // default true — audit each applied seed
auditFailures: boolean, // default true — audit each failure
lockStaleAfterMs: number, // default 0 — never take over a held lock
clock: function, // () → epoch ms; a test seam
}
Open the seeder over a directory and answer the handle to work it. The handle carries run(args), status(args), list(args) and dir, and each of the first three takes { env }.
A seed file lives at and exports description, an optional envs array narrowing where it applies, an optional rerunnable flag, and optional dependsOn names. A file whose name does not match is not a seed and is skipped.
run applies what has not been applied, in dependency order, and records each one. A rerunnable seed is applied every time, which is for a baseline written to be idempotent. status answers what is applied and what is pending without changing anything.
A run takes a lock in the database first, so two processes seeding together do not both insert the same fixture. lockStaleAfterMs is when a lock left behind by a dead process may be taken over; it defaults to 0, which means a held lock is never taken over and a crash during a seed needs the lock cleared.
var b = require("@blamejs/core");
var seed = b.seeders.create({ dir: "./seeders" });
await seed.status({ env: "dev" }); // → what is applied and what is pending
await seed.run({ env: "dev" });