@krizaka/db
La fondation base de données : types partagés, transactions, schémas de modules intégrés aux migrations du produit, base de test.
Installer
npm install @krizaka/dbAussi publié en sous-chemins : @krizaka/db/testing.
Le README ci-dessous est celui du paquet sur npm, en anglais.
The database foundation of every Krizaka module (ADR-001). It has no database
client: you pass your own connection (pg's Client or PoolClient fits as it is).
- The shared types:
Query,Connection,Connect,Clock. transaction(connect, work):BEGIN … COMMIT, orROLLBACKwhen the work throws.- Module schemas (
defineModule): each module's tables live in a Postgres schema named after it. Its versions are frozen and checksummed. emitMigrationwrites a module version into your own migration sequence, followed by a marker inpublic.krizaka_module_versions.adoptMigrationadopts tables you already have: markers only, no DDL.assertModules, at boot, refuses a database that lacks a version the code needs.migrateis a runner for products without one: an advisory lock, a history table, one transaction per step.@krizaka/db/testing:createTestDatabase,assertEmbedded(your drift test) andassertUpgrade.
pnpm add @krizaka/dbA module declares its schema
import { defineModule } from "@krizaka/db";
export const auditSchema = defineModule({
module: "audit", // also its Postgres schema, unless the product renames it
tables: ["owner"], // the product tables it references, by role, and only those
migrations: [
{ version: 1, name: "entries", sql: (c) => `CREATE TABLE ${c.schema}.entry (id bigserial PRIMARY KEY, actor_id uuid REFERENCES ${c.tables.owner}(id) ON DELETE SET NULL);` },
], // 1, 2, 3… in order; a released version never changes
});A product embeds it in its own sequence
import { emitMigration } from "@krizaka/db";
import { auditSchema } from "@krizaka/audit";
const context = { tables: { owner: "accounts" } };
// drizzle-kit: `drizzle-kit generate --custom --name=audit_1`, then write this text into the generated file.
// Your own list: append { version: "0031_audit_1", sql: emitMigration(auditSchema, 1, context) }.
process.stdout.write(emitMigration(auditSchema, 1, context));Commit what it prints. Never generate it at runtime: the database changes only through your reviewed sequence.
-
Upgrading a module is a package bump. Your drift test then names the version to add. It compares the SQL text itself with what
emitMigrationwrites today (whitespace and drizzle's--> statement-breakpointaside), not only the marker, so an edited or deleted statement fails too. It also checks that a module comes after the modules it requires, within a file as across files:import { assertEmbedded } from "@krizaka/db/testing"; test("every module version is embedded", () => assertEmbedded(readMigrationFiles(), [{ schema: auditSchema, context }])); -
At boot, after your migrations:
await assertModules(query, [{ schema: auditSchema, context }]). -
Tables that already exist (copied from a package before
@krizaka/db): embedadoptMigration(auditSchema, 1, context)once. It writes the markers, and nothing is rebuilt.
One use case, one transaction
import { transaction } from "@krizaka/db";
await transaction(() => pool.connect(), async (tx) => {
await coupons.redeem(tx, input);
await audit.record(tx, entry); // both commit, or neither
});The runner, for products without one
import { migrate } from "@krizaka/db";
await migrate(() => pool.connect(), migrations, { lockKey: 73191 }); // history table `schema_migrations (version text)`, by defaultTests on a real PostgreSQL
import { createTestDatabase } from "@krizaka/db/testing";
const db = await createTestDatabase({
connect: async (database) => { const c = new pg.Client({ connectionString: urlOf(database) }); await c.connect(); return { query: (s, p) => c.query(s, p), release: () => c.end() }; },
migrate: (connect) => migrate(connect, migrations, { lockKey: 1 }),
prefix: "ak",
});
// … tests on db.name …
await db.drop();- No container and no network beyond the server you point at (CI's service).
- Errors are
DbError, with a stablecode(DB_MISSING_MODULES,DB_FROZEN_VERSION_CHANGED,DB_MODULE_ORDER,DB_UNSAFE_IDENTIFIER…) and the items indetails.
Exports
Les 25 noms que le paquet exporte, lus dans ses types.
| Nom | Nature | Ce que c’est |
|---|---|---|
adoptMigration | fonction | The SQL that adopts tables a product already has, up to version: the markers alone, no DDL. |
assertModules | fonction | The boot check: refuses to serve on a database that lacks a module version the code needs, or whose recorded version differs. |
checksumOf | fonction | The checksum of one version for one product's declaration: what the frozen-version guard compares. |
Clock | type | The clock, injected: domain code never calls new Date(). |
compareVersions | fonction | Compares what the code expects with what was found, by « module vN ». |
ComposedModule | type | A module, as the product composes it: the schema and the product's declaration. |
Connect | type | Opens a connection (from the product's pool). |
Connection | type | One connection to the database. |
DB_CODE | constante | Stable codes of the errors this package throws. |
DbCode | type | |
DbError | classe | A refusal of this package: a stable code, and the items it concerns (audit v2, a table name). |
defineModule | fonction | Declares a module's schema. |
emitMigration | fonction | The SQL a product commits into its own migration sequence for one version of a module (ADR-001 §3): the marker table if missing, the module's schema, the ver… |
ident | fonction | A SQL identifier from a product's declaration: lower-case letters, digits and underscores, at most 63 characters (the PostgreSQL limit). |
MARKER_TABLE | constante | Where the module versions a database has are recorded: one row per (module, version), written by the embedded SQL. |
migrate | fonction | The runner for products without one: applies the migrations not applied yet, in order, one transaction each, under an advisory lock. |
MigrateOptions | type | |
ModuleContext | type | What the product declares for a module: the real names of the tables it references, and optionally another schema name. |
ModuleMigration | type | One frozen version of a module's schema. |
ModuleSchema | type | A module's schema: its name, the product tables it references (by role: owner, scope…), the modules it needs first. |
ProductMigration | type | One step of a product's own migration sequence: its own tables, or a module version from emitMigration. |
Query | type | Runs one SQL statement with its parameters and returns the rows: a pool, or the client of an open transaction. |
queryOf | fonction | A Query over one connection: the rows of each statement. |
SchemaContext | type | What a module's migration receives: its Postgres schema, and the product tables it declared, by role. |
transaction | fonction | Runs work in one transaction: BEGIN, the work with its Query, then COMMIT, or ROLLBACK when the work throws. |
@krizaka/db/testing
| Nom | Nature | Ce que c’est |
|---|---|---|
assertEmbedded | fonction | The product's drift test. |
assertUpgrade | fonction | Applies the previous release's sequence, writes rows, applies this release's, then checks: an upgrade keeps the data. |
createTestDatabase | fonction | Creates a fresh database on the server the tests point at (CI's service, or a local one), migrates it, and hands back a way to drop it. |
TestDatabase | type | A database made for one test run; drop removes it. |
TestDatabaseOptions | type | |
UpgradeCase | type |
Paquet
Journal des versions
0.1.0
Minor changes
- First release: the database foundation of the Krizaka modules (ADR-001). Shared
Query/Connection/Clocktypes,transaction, frozen and checksummed module schemas (defineModule) embedded in the product's own migration sequence (emitMigration,adoptMigration) with a boot check (assertModules), a runner for products without one (migrate), and@krizaka/db/testing(createTestDatabase,assertEmbedded,assertUpgrade).