SDK API Reference
The @sbtools/sdk package provides shared types, interfaces, and utilities for supabase-tools plugins.
Installation
npm install @sbtools/sdkCore Types
- SbtPlugin — Plugin contract with
name,version,commands, and optional hooks - PluginContext — Runtime context passed to every plugin hook:
projectRoot,toolsDir,sbtDataDir,artifactsDir— absolute directory pathsapiUrl— Supabase API URLpaths— shared config paths resolved to absolute:migrations,snapshot,docsOutput,functionspluginConfig— plugin-specific config fromplugins[].configsiblingPlugins— other loaded plugins for cross-plugin collaboration
- ResolvedPaths — Type for
ctx.paths - SbtPluginCommand — Command definition with
name,description,run - ArtifactCapabilities — Declare which artifact IDs a plugin
producesandconsumes(used by tooling to validate artifact availability)
UI Utilities
import { ui } from "@sbtools/sdk";
ui.info("Message");
ui.success("Done");
ui.warn("Warning");
ui.error("Error");
ui.step("Step");
ui.detail("Detail");
ui.heading("Heading");
ui.table([["a", "b"], ["1", "2"]], 2);Error Classes
ConfigError— Config validation failuresDatabaseError— DB connection/query failuresSnapshotError— Snapshot generation failuresPluginError— Plugin load/hook failuresSbtError— Generic CLI errors
Use handleError(err) for consistent error output.
Filesystem Utilities
ensureDir(path)— Create directory recursivelyreadText(path)— Read file as UTF-8writeFileInDir(dir, filename, content)— Write file in directorysafeName(str)— Replace non-word chars with underscore (for identifiers; preserves dots and hyphens)safeFileName(baseName, maxLength?)— Truncate long filenames with hashsanitizeSlug(str)— Hyphenated slug (e.g. plugin names, directory names)sanitizeIdentifier(str)— Alphanumeric + underscore (e.g. Mermaid node IDs)
Container Utilities
For Docker Compose project naming (used by core and plugins that interact with containers):
sanitizeContainerPrefix(projectName)— Sanitize raw project name into valid Docker prefixderiveContainerPrefix(projectRoot)— Readsupabase-tools.config.jsonforproject.name, fallback to basename, return sanitized prefix
import { deriveContainerPrefix, sanitizeContainerPrefix } from "@sbtools/sdk";
const prefix = deriveContainerPrefix(ctx.projectRoot);
const container = `${prefix}-supabase-db`;
// Or when you already have the name:
const prefix2 = sanitizeContainerPrefix(config.project.name);Compose Utilities
Extract values from Docker Compose YAML files:
extractComposeKey(composePath, patterns)— First matching regex captureextractSupabaseKeys(composePath)— Returns{ anonKey, serviceKey }in one read (seeSupabaseKeystype)
import { extractComposeKey, extractSupabaseKeys } from "@sbtools/sdk";
const { anonKey, serviceKey } = extractSupabaseKeys(path.join(ctx.toolsDir, "docker-compose.db.yml"));
const jwtSecret = extractComposeKey(composePath, [/JWT_SECRET:\s*([^\s]+)/]);CLI Utilities
hasFlag(args, ...names)— Check for CLI flags (e.g.--help,-h)getArg(args, name)— Get CLI argument value (e.g.--port 3000→"3000")openFile(path)— Open file in default editorwithHelp(helpText, fn)— Wrap a command handler to provide--help/-hsupportloadPackageVersion(importMetaUrl)— Load version from nearest package.json (useimport.meta.url)
withHelp Example
import { withHelp } from "@sbtools/sdk";
const HELP = `
my-command — does something useful
Usage:
sbt my-command [--flag] [--arg VALUE]
Options:
--flag Enable feature
--arg VALUE Set value
-h, --help Show this help
`;
const myCommand = withHelp(HELP, async (args: string[], ctx: PluginContext) => {
// Command implementation
});loadPackageVersion Example
import { loadPackageVersion } from "@sbtools/sdk";
const plugin: SbtPlugin = {
name: "@sbtools/plugin-example",
version: loadPackageVersion(import.meta.url), // Reads ../package.json
commands: [/* ... */],
};Plugin Config Helpers
Typed accessors for plugin config values — replaces unsafe (ctx.pluginConfig.foo as string) ?? default casts:
getConfigString(ctx, key, fallback)— Get a string config valuegetConfigNumber(ctx, key, fallback)— Get a number config value (parses strings too)getConfigStringArray(ctx, key, fallback)— Get a string array config valueresolveConfigPath(ctx, key, fallbackRelPath)— Get a path config value; relative paths are resolved againstctx.projectRoot
import { getConfigString, getConfigNumber, getConfigStringArray, resolveConfigPath } from "@sbtools/sdk";
const output = getConfigString(ctx, "outputDir", "docs/my-output");
const port = getConfigNumber(ctx, "port", 3333);
const scanDirs = getConfigStringArray(ctx, "scanPaths", ["src/"]);
const typesOut = resolveConfigPath(ctx, "typesOutput", "src/types/supabase.ts");Artifact Helpers
Read and write typed artifact envelopes stored in .sbt/artifacts/:
writeArtifact(dir, id, version, payload)— Write artifact JSON filereadArtifact(dir, id, version)— Read artifact; throws if missing or version mismatchreadArtifactOrNull(dir, id, version)— Read artifact; returnsnullif missingcreateArtifactWriter(opts)— Factory for writing multiple artifacts from the same plugin
import { writeArtifact, readArtifactOrNull } from "@sbtools/sdk";
// Write
await writeArtifact(ctx.artifactsDir, "my.artifact", "1.0.0", { items: [] });
// Read (returns null when missing instead of throwing)
const result = readArtifactOrNull(ctx.artifactsDir, "migration.analysis", "1.0.0");
if (result) { /* result.payload is the data */ }DB Utilities
Optional; plugins that need DB access must have pg installed. SDK exposes thin wrappers:
resolveDbUrl()— FromDATABASE_URL,SUPABASE_DB_URL,POSTGRES_URL, or default local URLcreatePgClient()— Createpg.Client(throws ifpgnot installed)testConnection(client)— Returnstrueif connect succeedsdisconnectClient(client)— Safe disconnect
import { createPgClient, testConnection, disconnectClient } from "@sbtools/sdk";
const client = createPgClient();
try {
if (await testConnection(client)) { /* ... */ }
} finally {
await disconnectClient(client);
}Migration Scanner
scanMigrationFiles(dir)— ReturnsMigrationFileInfo[](.sql files, sorted)parseTimestampPrefix(filename)— ExtractYYYYMMDDHHMMSSfrom migration filename
import { scanMigrationFiles, parseTimestampPrefix } from "@sbtools/sdk";
const files = scanMigrationFiles(ctx.paths.migrations);
const ts = parseTimestampPrefix("20240101120000_foo.sql"); // "20240101120000"SQL Analyzer
analyzeMigrationSql(sql)— Regex-based DDL classifier. ReturnsMigrationSqlAnalysiswithoperations,touchedObjectKeys,riskFlags,confidence.
Used by migration-audit and migration-studio. No Node.js deps; can run in browser.
Dashboard View
Use getDashboardView() to contribute sections to the sbt dashboard UI. Returns JSON-serializable config — no JS strings.
Interfaces
DashboardSectionDef— section config (id, title, description, dataKey, layout, stats, card, table, link)DashboardCardDef— card layout (titleField, subtitleField, searchFields, badges, details)DashboardTableDef— table layout (columns with header, field, format)DashboardStatDef— stat row (label, field, tone)DashboardBadgeDef— badge (field, toneMap)DashboardDetailDef— detail row (label, field, format)DashboardView—{ sections: DashboardSectionDef[] }
Example
import type { DashboardSectionDef, DashboardView } from "@sbtools/sdk";
export function getMyPluginDashboardView(): DashboardView {
return {
sections: [
{
id: "my-items",
title: "My Items",
description: "Items from the database.",
dataKey: "my_items",
layout: "cards",
stats: [
{ label: "Total", field: "summary.total", tone: "good" },
],
card: {
titleField: "name",
subtitleField: "type",
searchFields: ["name", "description"],
badges: [{ field: "status", toneMap: { active: "good", inactive: "warn" } }],
details: [
{ label: "Description", field: "description" },
{ label: "Created", field: "created_at", format: "date" },
],
},
},
],
};
}Formats: text, code, date, bytes, ms, number. Tones: default, good, warn, bad, accent.
Schema Filters (Parameterized Queries)
SchemaFilter — { clause: string; params: string[] } — used by snapshot extractors to inject schema filters into SQL queries using parameterized placeholders ($1, $2, ...) instead of string interpolation.
import type { SchemaFilter } from "@sbtools/sdk";
const filter: SchemaFilter = {
clause: "AND n.nspname IN ($1, $2)",
params: ["public", "extensions"],
};
const result = await client.query(
`SELECT * FROM pg_namespace n WHERE true ${filter.clause}`,
filter.params
);Core utility: getSchemaFilter(schemas, column) in @sbtools/core generates these filters from user config.
Building Plugins
See plugin-scaffold to scaffold a new plugin, or inspect existing plugins in the repository.