Skip to content
bun:sqlite/ prisma adapter

Development

bun install
bun run typecheck
bun run lint
bun run format:check   # `bun run format` to apply
bun test

Layout

PathContents
src/The adapter.
test/unit/Pure-function and adapter-level tests.
test/differential/Parity comparison against the official adapter.
test/e2e/A generated PrismaClient against a real schema.
test/cli/prisma migrate / db push / db pull under Bun.
docs/Guides and the generated API reference.
site/This documentation site.

Within src/, conversion.ts and errors.ts hold most of the correctness risk and depend on nothing but their inputs — no Database, no filesystem — so the bulk of the test coverage lives against them directly.

The four test layers

Each catches things the others structurally cannot, which is the reason for having all four rather than the largest one.

Unit

bun test test/unit

Argument mapping across every scalar type, row mapping, column-type inference, error translation, statement-cache eviction, and mutex ordering. Also covers the adapter surface directly: transactions, savepoints, connection ownership, PRAGMAs.

Differential

bun test test/differential
bun run fixtures   # regenerate the reference fixture (requires Node)

Runs a shared corpus through this adapter and through @prisma/adapter-better-sqlite3, comparing full result sets.

The two cannot run in one process, because Bun refuses to load better-sqlite3. So test/differential/corpus.ts is consumed twice: by a Node script that writes fixtures/reference.json, and by the Bun test that compares against it. Both sides share the same serializer, which encodes bigint and byte values as tagged forms — JSON.stringify throws on the former and Node’s Buffer serialises differently from Bun’s Uint8Array, so the comparison has to be byte-level rather than representational.

The fixture is committed, and CI regenerates it and fails on any diff. Without that check it would quietly become a snapshot of this adapter’s own behaviour the moment the reference changes.

To record an intentional divergence, add divergence: { reason, expect } to the corpus case. The suite then asserts the stated result and that the reference still disagrees, so the exemption cannot outlive its justification.

End-to-end

cd test/e2e && bunx prisma generate --config prisma.config.ts
bun test test/e2e

A real schema and generated client: CRUD, defaults, filtering, aggregates, nested writes, cascades, batch and interactive transactions, and raw queries. The unit and differential layers both sit below Prisma’s query compiler, so this is the only one that proves the adapter works through it.

It earns its place. It caught a bug neither other layer could: with usePhantomQuery: false, Prisma issues the closing COMMIT itself through executeRaw and only then calls commit(). An implementation that also issued one failed with cannot commit - no transaction is active and desynchronised Prisma’s transaction state machine.

CLI

bun test test/cli

Runs migrate dev, db push, and db pull in temporary workspaces and checks the resulting database is readable and writable through the adapter. Since the CLI does not route through the adapter (see Migrations), these assert what actually matters to a user rather than a call chain that does not exist.

Documentation

bun run docs:api    # regenerate docs/api from the source JSDoc
cd site && bun run dev
cd site && bun run build && bun run check:links   # verify every internal link resolves

The site emits `sitemap-index.xml`, `robots.txt`, and `llms.txt`, and every page
carries JSON-LD (`SoftwareSourceCode` on the home page, `TechArticle` for guides,
`APIReference` for the reference, plus a `BreadcrumbList`). `llms.txt` is the
answer-engine summary — keep it accurate when the guides change, since it is what
an LLM reads instead of crawling the site.

Note that `robots.txt` is inert on GitHub Pages project sites: crawlers read it
from the domain root, which this repository does not own. It is committed so the
rules travel if the docs ever move to their own domain.

The API reference under docs/api/ is generated from the doc comments in src/, so it is not edited by hand. Guides under docs/guides/ are written directly. The site reads both.

Linting

ESLint with typescript-eslint (type-checked) and eslint-plugin-unicorn. Violations are fixed rather than suppressed. Two exemptions remain, both justified where they appear:

  • unicorn/prefer-number-coercion on two parseFloat calls. Number() disagrees with parseFloat on trailing garbage and whitespace-only input, and the official adapter uses parseFloat, so switching would be a silent behavioural divergence.
  • unicorn/no-null, project-wide. bun:sqlite declares Statement.declaredTypes as Array<string | null> and Prisma’s ResultValue includes null, so undefined would not typecheck.

Every class member carries an explicit public/private/protected modifier, enforced by @typescript-eslint/explicit-member-accessibility.

Formatting is Prettier’s, checked in CI by format:check. eslint-config-prettier is applied last in the ESLint config so the two cannot disagree. Generated artifacts and Markdown prose are excluded — see .prettierignore for why.

Both staleness gates (the differential fixture and the API reference) call scripts/assert-generated-current.sh, so the reasoning behind using git status --porcelain rather than git diff lives in one place.

site/ is excluded from this config. It is a separate package with its own dependencies and Astro-generated types, so linting it from the root would mean installing them and running astro sync first. It is typechecked by astro check, which the Docs workflow runs on every deploy.