npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bydey/tusk

v0.5.0

Published

SQL-first PostgreSQL migrations for Node.js and Bun

Readme

Tusk

CI

Tusk is a SQL-first PostgreSQL migration tool for Node.js and Bun.

It is explicit and low abstraction:

  • write plain SQL, not a migration DSL
  • define up and down steps directly

It handles execution for you:

  • transactional migrations with advisory locking
  • one migration contract across pg, postgres.js, and the CLI

Tusk favors control and embeddability over schema builders and generated migrations.

Requirements

  • Node.js 18+ or Bun 1+
  • PostgreSQL 13+

Recommended for new projects:

  • Node.js 24
  • PostgreSQL 18

Install

With pg:

npm install @bydey/tusk pg
# or
bun add @bydey/tusk pg

With postgres.js:

npm install @bydey/tusk postgres
# or
bun add @bydey/tusk postgres

Migration Model

Tusk uses timestamped SQL files with explicit directionality.

Tusk reads SQL files from a migrations directory:

migrations/
  1728123456789_create_users.up.sql
  1728123456789_create_users.down.sql
  • *.up.sql is applied when you run up
  • *.down.sql is applied when you run down
  • executed migrations are tracked in the _migrations table
  • migrations are protected by a Postgres advisory lock so two runners do not apply them at the same time

Tusk does not provide a migration DSL or schema abstraction layer.

Example:

-- migrations/1728123456789_create_users.up.sql
CREATE TABLE users (
  id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  email TEXT NOT NULL UNIQUE
);
-- migrations/1728123456789_create_users.down.sql
DROP TABLE IF EXISTS users;

Quick Start

Set your database connection with either DATABASE_URL or individual variables:

DATABASE_URL=postgresql://user:password@localhost:5432/app

# or
DB_HOST=localhost
DB_PORT=5432
DB_NAME=app
DB_USER=postgres
DB_PASSWORD=secret

MIGRATIONS_PATH=./migrations
LOG_LEVEL=info

Set MIGRATIONS_PATH to use a different migration directory.

Create your first migration:

npx tusk create create_users
# or
bunx tusk create create_users

Run pending migrations:

npx tusk up

Check status:

npx tusk status
npx tusk status --exit-code
npx tusk status --json
npx tusk status --quiet

Validate migration files before applying them:

npx tusk validate
npx tusk validate --json
npx tusk validate --db --json

Check whether the project and database are ready for Tusk:

npx tusk doctor
npx tusk doctor --json

Preview exactly what would run:

npx tusk up --dry-run
npx tusk up --dry-run --json

Roll back migrations. down defaults to one rollback so an omitted argument cannot accidentally undo the full migration history:

npx tusk down
npx tusk down --dry-run
npx tusk down 3
npx tusk down --all

Starting From an Existing Database

If your schema already exists, Tusk can derive a baseline migration from the live database:

npx tusk init

This creates 0000000000000_initial.up.sql and 0000000000000_initial.down.sql so future schema changes can be managed through normal migrations.

The generated initial migration is also recorded in _migrations as already applied. That makes takeover explicit: tusk up will skip the baseline, new migrations can run normally, and tusk down 1 can roll back the baseline if it is the latest applied migration.

Programmatic Use

With pg:

import { Pool } from "pg";
import { createPgAdapter, runUp } from "@bydey/tusk";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = createPgAdapter(pool);

await runUp(adapter, "./migrations");

With postgres.js:

import postgres from "postgres";
import { createPostgresJsAdapter, runUp } from "@bydey/tusk";

const sql = postgres(process.env.DATABASE_URL!);
const adapter = createPostgresJsAdapter(sql);

await runUp(adapter, "./migrations");

Generate an initial migration programmatically:

import { Pool } from "pg";
import { createInitialMigration, createPgAdapter } from "@bydey/tusk";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = createPgAdapter(pool);

await createInitialMigration(adapter, "./migrations");

Elysia

import { Elysia } from "elysia";
import { migrate } from "@bydey/tusk";

new Elysia()
  .use(
    migrate({
      connectionString: process.env.DATABASE_URL,
      migrationsPath: "./migrations",
    })
  )
  .listen(3000);

By default the plugin runs up on startup.

CLI Commands

tusk create <name>
tusk init
tusk up
tusk down [count]
tusk down --all
tusk status
tusk validate
tusk doctor
tusk version

tusk down rolls back one migration by default. This is intentionally narrow: the safest rollback is the latest migration only, and broader rollback should be explicit. Use tusk down <count> to roll back several migrations, or tusk down --all to roll back every applied migration.

tusk up --dry-run, tusk down --dry-run, tusk down <count> --dry-run, and tusk down --all --dry-run print the ordered migration SQL without applying it.

tusk status --exit-code exits with status 1 when migrations are pending and 0 when the schema is clean.

tusk status --quiet suppresses the detailed sections and prints only the summary line, which is useful for scripts and CI logs.

tusk status --json prints machine-readable status data with ok, command, executed, pending, and summary fields. It can be combined with --exit-code, but not with --quiet.

tusk validate checks migration filenames, pairs, executable SQL, duplicate timestamps, and transaction-control statements. Add --db to check executed migration checksums against the configured database without modifying migration state.

tusk doctor runs a read-only health check over the migration directory, database configuration, connection, PostgreSQL compatibility, migration metadata, checksum drift, status readability, and advisory lock support. It exits 0 when there are no failing checks and 1 when action is needed. Use tusk doctor --json for automation.

--json is supported by create, init, up, down, status, validate, and doctor for machine-readable automation output.

Agent and MCP Use

For AI agents and automation, prefer the safe loop in Agent workflow: doctor --json, validate --json, validate --db --json, up --dry-run --json, then apply only after the plan is reviewed.

Tusk also includes a stdio MCP server:

tusk-mcp

It exposes tools for validation, status, dry-run planning, and migration file creation.

Support Policy

Tusk keeps a wide support floor for teams working on older projects while still treating the current stack as the primary development lane.

  • Supported floor: Node.js 18+, Bun 1+, PostgreSQL 13+
  • Recommended stack: Node.js 24, PostgreSQL 18
  • Required PR CI checks:
    • Verify (Node 24, PostgreSQL 18) runs the full build and test suite
    • Minimum Support (Node 18, PostgreSQL 13) runs the packaged smoke test against the oldest supported runtime/database pair
  • Scheduled compatibility coverage:
    • the Compatibility Matrix workflow exercises packaged smoke tests across multiple supported Node.js and PostgreSQL versions

If a supported floor version stops passing CI, it is a regression and should be treated as a bug.

More

License

MIT