Aller au contenu
Krizaka TS
Krizaka TS

@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

bashTerminal
npm install @krizaka/db

Aussi 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, or ROLLBACK when the work throws.
  • Module schemas (defineModule): each module's tables live in a Postgres schema named after it. Its versions are frozen and checksummed.
  • emitMigration writes a module version into your own migration sequence, followed by a marker in public.krizaka_module_versions.
  • adoptMigration adopts tables you already have: markers only, no DDL.
  • assertModules, at boot, refuses a database that lacks a version the code needs.
  • migrate is a runner for products without one: an advisory lock, a history table, one transaction per step.
  • @krizaka/db/testing: createTestDatabase, assertEmbedded (your drift test) and assertUpgrade.
pnpm add @krizaka/db

A 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 emitMigration writes today (whitespace and drizzle's --> statement-breakpoint aside), 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): embed adoptMigration(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 default

Tests 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 stable code (DB_MISSING_MODULES, DB_FROZEN_VERSION_CHANGED, DB_MODULE_ORDER, DB_UNSAFE_IDENTIFIER…) and the items in details.

Exports

Les 25 noms que le paquet exporte, lus dans ses types.

NomNatureCe que c’est
adoptMigrationfonctionThe SQL that adopts tables a product already has, up to version: the markers alone, no DDL.
assertModulesfonctionThe boot check: refuses to serve on a database that lacks a module version the code needs, or whose recorded version differs.
checksumOffonctionThe checksum of one version for one product's declaration: what the frozen-version guard compares.
ClocktypeThe clock, injected: domain code never calls new Date().
compareVersionsfonctionCompares what the code expects with what was found, by « module vN ».
ComposedModuletypeA module, as the product composes it: the schema and the product's declaration.
ConnecttypeOpens a connection (from the product's pool).
ConnectiontypeOne connection to the database.
DB_CODEconstanteStable codes of the errors this package throws.
DbCodetype
DbErrorclasseA refusal of this package: a stable code, and the items it concerns (audit v2, a table name).
defineModulefonctionDeclares a module's schema.
emitMigrationfonctionThe 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…
identfonctionA SQL identifier from a product's declaration: lower-case letters, digits and underscores, at most 63 characters (the PostgreSQL limit).
MARKER_TABLEconstanteWhere the module versions a database has are recorded: one row per (module, version), written by the embedded SQL.
migratefonctionThe runner for products without one: applies the migrations not applied yet, in order, one transaction each, under an advisory lock.
MigrateOptionstype
ModuleContexttypeWhat the product declares for a module: the real names of the tables it references, and optionally another schema name.
ModuleMigrationtypeOne frozen version of a module's schema.
ModuleSchematypeA module's schema: its name, the product tables it references (by role: owner, scope…), the modules it needs first.
ProductMigrationtypeOne step of a product's own migration sequence: its own tables, or a module version from emitMigration.
QuerytypeRuns one SQL statement with its parameters and returns the rows: a pool, or the client of an open transaction.
queryOffonctionA Query over one connection: the rows of each statement.
SchemaContexttypeWhat a module's migration receives: its Postgres schema, and the product tables it declared, by role.
transactionfonctionRuns work in one transaction: BEGIN, the work with its Query, then COMMIT, or ROLLBACK when the work throws.

@krizaka/db/testing

NomNatureCe que c’est
assertEmbeddedfonctionThe product's drift test.
assertUpgradefonctionApplies the previous release's sequence, writes rows, applies this release's, then checks: an upgrade keeps the data.
createTestDatabasefonctionCreates 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.
TestDatabasetypeA database made for one test run; drop removes it.
TestDatabaseOptionstype
UpgradeCasetype

Paquet

Version sur npm0.1.0 (npm)
LicenceApache-2.0
Node.js>=22
Dépendancesaucune
Code sourcedb

Journal des versions

0.1.0

Minor changes

  • First release: the database foundation of the Krizaka modules (ADR-001). Shared Query/Connection/Clock types, 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).

Sur cette page