kysely-ctl
Version:
Command-line tool for Kysely
1,571 lines (1,491 loc) • 47.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/.pnpm/tsup@8.5.0_jiti@2.4.2_postcss@8.4.39_tsx@4.19.2_typescript@5.8.3_yaml@2.4.5/node_modules/tsup/assets/cjs_shims.js
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
// src/bin.mts
var import_std_env8 = require("std-env");
// src/cli.mts
var import_citty4 = require("citty");
// src/commands/root.mts
var import_citty3 = require("citty");
var import_consola22 = require("consola");
// src/arguments/cwd.mts
var CWDArg = {
cwd: {
description: "The current working directory to use for relative paths.",
type: "string"
}
};
// src/arguments/debug.mts
var DebugArg = {
debug: {
default: false,
description: "Show debug information.",
type: "boolean"
}
};
// src/arguments/environment.mts
var EnvironmentArg = {
environment: {
alias: "e",
description: "Apply environment-specific overrides to the configuration. See https://github.com/unjs/c12#environment-specific-configuration for more information.",
type: "string",
valueHint: "production | development | test | ..."
}
};
// src/arguments/jiti.mts
var ExperimentalResolveTSConfigPathsArg = {
"experimental-resolve-tsconfig-paths": {
default: false,
description: "Attempts to resolve path aliases using the tsconfig.json file.",
type: "boolean"
}
};
var NoFilesystemCachingArg = {
"no-filesystem-caching": {
description: "Will not write cache files to disk. See https://github.com/unjs/jiti#fscache for more information.",
type: "boolean"
}
};
var JitiArgs = {
...ExperimentalResolveTSConfigPathsArg,
...NoFilesystemCachingArg
};
// src/arguments/no-outdated-check.mts
var NoOutdatedCheckArg = {
"no-outdated-check": {
description: "Will not check for latest kysely/kysely-ctl versions and notice newer versions exist.",
type: "boolean"
}
};
// src/arguments/common.mts
var CommonArgs = {
...CWDArg,
...DebugArg,
...EnvironmentArg,
...JitiArgs,
...NoOutdatedCheckArg
};
// src/config/get-cwd.mts
var import_pathe = require("pathe");
var import_std_env = require("std-env");
var ACTUAL_CWD = (
// biome-ignore lint/suspicious/noExplicitAny: it's fine
import_std_env.process.cwd?.() || (import_std_env.isDeno ? globalThis.Deno.cwd() : "")
);
var cwd;
function getCWD(args13) {
return cwd ||= args13?.cwd ? (0, import_pathe.resolve)(ACTUAL_CWD, args13.cwd) : ACTUAL_CWD;
}
// src/utils/is-in-subcommand.mts
function isInSubcommand(context) {
const {
cmd: { subCommands },
rawArgs
} = context;
if (!subCommands) {
return false;
}
const potentialSubCommand = rawArgs.find((arg) => !arg.startsWith("-"));
return potentialSubCommand !== void 0 && potentialSubCommand in subCommands;
}
// src/utils/version.mts
var import_consola = require("consola");
var import_ofetch = require("ofetch");
var import_std_env3 = require("std-env");
// src/utils/package-manager.mts
var import_nypm = require("nypm");
var import_std_env2 = require("std-env");
async function getPackageManager(args13) {
const packageManager = await (0, import_nypm.detectPackageManager)(getCWD(args13), {
ignoreArgv: true,
includeParentDirs: true
});
if (packageManager) {
return { ...packageManager, inProject: true };
}
const name = import_std_env2.isDeno ? "deno" : import_std_env2.isBun ? "bun" : "npm";
return { name, command: name, inProject: false };
}
// src/utils/pkg-json.mts
var import_pkg_types = require("pkg-types");
var PACKAGE_JSONS = {};
async function getConsumerPackageJSON(args13) {
return await getPackageJSON({ startingFrom: getCWD(args13) });
}
async function getCTLPackageJSON() {
return await getPackageJSON({ id: "kysely-ctl" });
}
async function getPackageJSON(options) {
const { id, startingFrom = __dirname } = options;
return PACKAGE_JSONS[`${String(id)}_${startingFrom}`] ||= await (0, import_pkg_types.readPackageJSON)(id, { from: startingFrom });
}
// src/utils/version.mts
async function getKyselyInstalledVersion(args13) {
try {
const pkgJSON = await getConsumerPackageJSON(args13);
return getVersionFromPackageJSON("kysely", pkgJSON);
} catch {
return null;
}
}
async function getCTLInstalledVersion() {
try {
const pkgJSON = await getCTLPackageJSON();
return getVersionFromPackageJSON("kysely-ctl", pkgJSON);
} catch {
return null;
}
}
function getVersionFromPackageJSON(name, pkgJSON) {
if (pkgJSON.name === name) {
return pkgJSON.version || null;
}
const rawVersion = pkgJSON.dependencies?.[name] || pkgJSON.devDependencies?.[name];
return rawVersion?.replace(/^[\^~]?(.+)$/, "$1") || null;
}
async function printInstalledVersions(args13) {
const [cliVersion, kyselyVersion] = await Promise.all([
getCTLInstalledVersion(),
getKyselyInstalledVersion(args13)
]);
import_consola.consola.log(
`kysely ${kyselyVersion ? `v${kyselyVersion}` : "[not installed]"}`
);
import_consola.consola.log(`kysely-ctl v${cliVersion}`);
}
async function getKyselyLatestVersion() {
return await getPackageLatestVersion("kysely");
}
async function getCTLLatestVersion() {
return await getPackageLatestVersion("kysely-ctl");
}
async function getPackageLatestVersion(packageName) {
const response = await (0, import_ofetch.ofetch)(
`https://registry.npmjs.org/${packageName}`
);
return response["dist-tags"].latest;
}
async function printUpgradeNotice(args13) {
if (args13["outdated-check"] === false || import_std_env3.isCI) {
return;
}
const [
kyselyInstalledVersion,
kyselyLatestVersion,
ctlInstalledVersion,
ctlLatestVersion
] = await Promise.all([
getKyselyInstalledVersion(args13),
getKyselyLatestVersion(),
getCTLInstalledVersion(),
getCTLLatestVersion()
]);
const notices = [];
if (kyselyInstalledVersion && kyselyInstalledVersion !== kyselyLatestVersion) {
notices.push(["Kysely", "kysely", kyselyLatestVersion]);
}
if (ctlInstalledVersion !== ctlLatestVersion) {
notices.push(["KyselyCTL", "kysely-ctl", ctlLatestVersion]);
}
if (!notices.length) {
return;
}
const packageManager = await getPackageManager(args13);
const installCommand = {
[packageManager.name]: "install",
bun: "add",
pnpm: "add",
yarn: "add"
}[packageManager.name];
import_consola.consola.box(
notices.map(
([prettyName, name, latestVersion]) => `A new version of ${prettyName} is available: v${latestVersion}
Run \`${packageManager.command} ${installCommand} ${packageManager.name === "deno" ? "npm:" : ""}${name}@latest\` to upgrade.`
).join("\n\n")
);
}
// src/commands/init.mts
var import_promises = require("fs/promises");
var import_consola3 = require("consola");
var import_pathe3 = require("pathe");
// src/arguments/extension.mts
var TS_EXTENSIONS = ["ts", "mts", "cts"];
var JS_EXTENSIONS = ["js", "mjs", "cjs"];
var ALL_EXTENSIONS = [...TS_EXTENSIONS, ...JS_EXTENSIONS];
var ExtensionArg = {
extension: {
alias: "x",
default: "ts",
description: "The file extension to use.",
type: "string",
valueHint: ALL_EXTENSIONS.map((extension) => `"${extension}"`).join(" | ")
}
};
function assertExtension(thing, config, context) {
const allowJS = config?.[context || "migrations"].allowJS ?? true;
if (!allowJS && JS_EXTENSIONS.includes(thing)) {
throw new Error(
`Invalid file extension "${thing}"! Expected ${TS_EXTENSIONS.map(
(extension) => `"${extension}"`
).join(" | ")}. To use JS extensions, set "${context}.allowJS" to true.`
);
}
const extensions = allowJS ? ALL_EXTENSIONS : TS_EXTENSIONS;
if (!extensions.includes(thing)) {
throw new Error(
`Invalid file extension "${thing}"! Expected ${ExtensionArg.extension.valueHint}`
);
}
}
// src/config/get-config.mts
var import_c12 = require("c12");
var import_consola2 = require("consola");
// src/utils/jiti.mts
var import_pathe2 = require("pathe");
var import_std_env4 = require("std-env");
// src/utils/tsconfig.mts
var import_pkg_types2 = require("pkg-types");
var tsconfig;
async function getTSConfig() {
return tsconfig ||= await (0, import_pkg_types2.readTSConfig)(void 0, {
from: getCWD()
});
}
// src/utils/jiti.mts
async function getJiti(args13) {
const jitiOptions = await getJitiOptions(args13);
const { createJiti } = await import("jiti");
return createJiti(importMetaUrl, jitiOptions);
}
async function getJitiOptions(args13) {
return {
alias: args13.experimentalResolveTSConfigPaths ? await getJitiAlias() : void 0,
debug: Boolean(args13.debug),
fsCache: Boolean(args13.filesystemCaching),
jsx: true,
tryNative: import_std_env4.runtime !== "node"
};
}
async function getJitiAlias() {
try {
const tsconfig2 = await getTSConfig();
const { baseUrl, paths } = tsconfig2.compilerOptions || {};
if (!paths) {
return {};
}
const cwd2 = getCWD();
const jitiAlias = {};
for (const [alias, [path]] of Object.entries(paths)) {
if (!path) {
continue;
}
jitiAlias[removeWildcards(alias)] = removeWildcards(
(0, import_pathe2.join)(cwd2, baseUrl || ".", path)
);
}
return jitiAlias;
} catch {
return {};
}
}
function removeWildcards(path) {
return path.replace(/(\/?\*\*?)+$/, "");
}
// src/config/get-file-prefix.mts
function getMillisPrefix() {
return `${Date.now()}_`;
}
// src/config/get-config.mts
async function getConfig(args13) {
const cwd2 = getCWD(args13);
const jiti = await getJiti({
debug: args13.debug,
filesystemCaching: args13["filesystem-caching"],
experimentalResolveTSConfigPaths: args13["experimental-resolve-tsconfig-paths"]
});
const loadedConfig = await (0, import_c12.loadConfig)({
cwd: cwd2,
dotenv: true,
envName: args13.environment,
jiti,
globalRc: false,
name: "kysely",
packageJson: false,
rcFile: false
});
import_consola2.consola.debug(loadedConfig);
const { config, ...configMetadata } = loadedConfig;
return {
...config,
args: args13,
configMetadata,
cwd: cwd2,
migrations: {
allowJS: false,
getMigrationPrefix: getMillisPrefix,
migrationFolder: "migrations",
...config.migrations,
disableTransactions: args13.transaction === false || config.migrations?.disableTransactions
},
seeds: {
allowJS: false,
getSeedPrefix: getMillisPrefix,
seedFolder: "seeds",
...config.seeds
}
};
}
function configFileExists(config) {
const { configFile } = config.configMetadata;
return configFile !== void 0 && configFile !== "kysely.config";
}
async function getConfigOrFail(args13) {
const config = await getConfig(args13);
if (!configFileExists(config)) {
throw new Error("No config file found");
}
return config;
}
// src/utils/get-template-extension.mts
async function getTemplateExtension(extension) {
if (extension === "js") {
const pkgJSON = await getConsumerPackageJSON();
return pkgJSON.type === "module" ? "mjs" : "cjs";
}
return extension === "cjs" || extension === "mjs" ? extension : "ts";
}
// src/commands/init.mts
var args = {
...CWDArg,
...DebugArg,
...ExtensionArg,
...NoOutdatedCheckArg
};
var InitCommand = {
init: {
meta: {
name: "init",
description: "Create a sample `kysely.config` file"
},
args,
async run(context) {
const { args: args13 } = context;
const { extension } = args13;
import_consola3.consola.debug(context, []);
const config = await getConfig(args13);
if (configFileExists(config)) {
return import_consola3.consola.warn(
`Init skipped: config file already exists at ${config.configMetadata.configFile}`
);
}
assertExtension(extension);
const configFolderPath = (0, import_pathe3.join)(config.cwd, ".config");
import_consola3.consola.debug("Config folder path:", configFolderPath);
const wasConfigFolderCreated = Boolean(
await (0, import_promises.mkdir)(configFolderPath, { recursive: true })
);
if (wasConfigFolderCreated) {
import_consola3.consola.debug("Config folder created");
}
const filePath = (0, import_pathe3.join)(configFolderPath, `kysely.config.${extension}`);
import_consola3.consola.debug("File path:", filePath);
const templateExtension = await getTemplateExtension(extension);
const templatePath = (0, import_pathe3.join)(
__dirname,
`templates/config-template.${templateExtension}`
);
import_consola3.consola.debug("Template path:", templatePath);
await (0, import_promises.copyFile)(templatePath, filePath);
import_consola3.consola.success(`Config file created at ${filePath}`);
}
}
};
// src/commands/migrate/down.mts
var import_consola8 = require("consola");
// src/arguments/migrate.mts
var NoTransactionArg = {
"no-transaction": {
description: "Don't use a transaction when running the migrations. This will not work for now if you've provided your own Migrator factory.",
type: "boolean"
}
};
var MigrateArgs = {
...NoTransactionArg
};
// src/arguments/migration-name.mts
var createMigrationNameArg = (required = false) => ({
migration_name: {
description: "Migration name to run/undo.",
required,
type: "positional"
}
});
// src/kysely/is-wrong-direction.mts
async function isWrongDirection(migrationName, expectedDirection, migrator) {
if (!migrationName) {
return false;
}
const migrations2 = await migrator.getMigrations();
return migrations2.find(
(migration) => migration.name === migrationName && (expectedDirection === "up" && migration.executedAt !== void 0 || expectedDirection === "down" && !migration.executedAt)
) !== void 0;
}
// src/kysely/process-migration-result-set.mts
var import_consola5 = require("consola");
var import_utils = require("consola/utils");
// src/utils/assert-defined.mts
function assertDefined(thing) {
if (thing === void 0) {
throw new Error("Expected value to be defined");
}
}
// src/utils/error.mts
var import_consola4 = require("consola");
var import_std_env5 = require("std-env");
function exitWithError(error) {
if (error instanceof AggregateError) {
for (const subError of error.errors) {
import_consola4.consola.error(subError);
}
} else {
import_consola4.consola.error(error);
}
import_std_env5.process.exit?.(1);
throw error;
}
// src/kysely/get-migrations.mts
var migrations;
async function getMigrations(migrator) {
return migrations ||= await migrator.getMigrations();
}
// src/kysely/process-migration-result-set.mts
async function processMigrationResultSet(resultSet, direction, migrator) {
import_consola5.consola.debug(resultSet);
let { error, results } = resultSet;
if (error) {
const failedMigration = results?.find((result) => result.status === "Error");
import_consola5.consola.fail(
`Migration failed with \`${error}\`${failedMigration ? ` @ "${failedMigration.migrationName}"` : ""}`
);
exitWithError(error);
}
if (!results?.length) {
return import_consola5.consola.info(
`Migration skipped: no ${direction === "up" ? "new" : "completed"} migrations found`
);
}
import_consola5.consola.success("Migration complete");
import_consola5.consola.info(
`${direction === "up" ? "Ran" : "Undone"} ${results.length} migration${results.length > 1 ? "s" : ""}:`
);
if (direction === "down") {
results = [...results].reverse();
}
const migrations2 = await getMigrations(migrator);
const firstResult = results[0];
assertDefined(firstResult);
const { migrationName: firstResultMigrationName } = firstResult;
const untouchedMigrationsBefore = migrations2.slice(
0,
migrations2.findIndex(
(migration) => migration.name === firstResultMigrationName
)
);
const lastResult = results.at(-1);
assertDefined(lastResult);
const { migrationName: lastResultMigrationName } = lastResult;
const untouchedMigrationsAfter = migrations2.slice(
migrations2.findIndex(
(migration) => migration.name === lastResultMigrationName
) + 1
);
for (const migration of untouchedMigrationsBefore) {
import_consola5.consola.log(`[\u2713] ${migration.name}`);
}
for (const result of results) {
import_consola5.consola.log(
`[${(0, import_utils.colorize)("green", result.direction === "Up" ? "\u2713" : "\u237B")}] ${result.migrationName}`
);
}
for (const migration of untouchedMigrationsAfter) {
import_consola5.consola.log(`[ ] ${migration.name}`);
}
}
// src/kysely/get-migrator.mts
var import_kysely = require("kysely");
var import_pathe5 = require("pathe");
// src/utils/hydrate.mts
async function hydrate(factory, parameters, defaultValue) {
if (factory == null) {
return defaultValue?.() ?? factory;
}
if (typeof factory !== "function") {
return factory;
}
return await factory(...parameters);
}
// src/kysely/ts-file-migration-provider.mts
var import_consola6 = require("consola");
var import_pathe4 = require("pathe");
var import_utils2 = require("pathe/utils");
// src/utils/get-file-type.mts
function getFileType(path) {
let extension = "";
const lastIndex = path.length - 1;
let i = 0;
for (; i < 3; i++) {
const char = path.charAt(lastIndex - i);
if (char === ".") {
break;
}
extension = `${char}${extension}`;
}
if (extension.length < 2 || path.charAt(lastIndex - i) !== "." || path.charAt(lastIndex - (i + 1)) === "d" && path.charAt(lastIndex - (i + 2)) === ".") {
return "IRRELEVANT";
}
if (["ts", "mts", "cts"].includes(extension)) {
return "TS";
}
if (["js", "mjs", "cjs"].includes(extension)) {
return "JS";
}
return "IRRELEVANT";
}
// src/utils/import-ts-file.mts
var import_std_env6 = require("std-env");
async function importTSFile(path, args13) {
if (import_std_env6.runtime !== "node") {
return await import(path);
}
const jiti = await getJiti(args13);
return await jiti.import(path);
}
// src/utils/is-object.mts
function isObject(thing) {
return typeof thing === "object" && thing !== null && !Array.isArray(thing);
}
// src/utils/safe-readdir.mts
var import_promises2 = require("fs/promises");
async function safeReaddir(path) {
try {
return await (0, import_promises2.readdir)(path);
} catch {
await (0, import_promises2.mkdir)(path);
return await (0, import_promises2.readdir)(path);
}
}
// src/kysely/ts-file-migration-provider.mts
var TSFileMigrationProvider = class {
#props;
constructor(props) {
this.#props = props;
}
async getMigrations() {
const files = await safeReaddir(this.#props.migrationFolder);
const migrations2 = {};
for (const fileName of files) {
const fileType = getFileType(fileName);
const isTS = fileType === "TS";
if (!isTS) {
if (!this.#props.allowJS) {
import_consola6.consola.warn(`Ignoring \`${fileName}\` - not a TS file.`);
continue;
}
if (fileType !== "JS") {
import_consola6.consola.warn(`Ignoring \`${fileName}\` - not a TS/JS file.`);
continue;
}
}
const filePath = (0, import_pathe4.join)(this.#props.migrationFolder, fileName);
const migrationModule = await (isTS ? importTSFile(filePath, this.#props) : import(filePath));
const migrationKey = (0, import_utils2.filename)(fileName);
if (!migrationKey) {
continue;
}
const migration = isMigration(migrationModule?.default) ? migrationModule.default : isMigration(migrationModule) ? migrationModule : null;
if (!migration) {
import_consola6.consola.warn(`Ignoring \`${fileName}\` - not a migration.`);
continue;
}
migrations2[migrationKey] = migration;
}
return migrations2;
}
};
function isMigration(thing) {
return isObject(thing) && typeof thing.up === "function";
}
// src/kysely/get-migrator.mts
async function getMigrator(config) {
const { args: args13, kysely, migrations: migrations2 } = config;
const { allowJS, migrationFolder, migrator, ...migratorOptions } = migrations2;
if (migrator) {
return await hydrate(migrator, [kysely]);
}
const provider = await hydrate(
migrations2.provider,
[],
() => new TSFileMigrationProvider({
allowJS,
debug: args13.debug,
experimentalResolveTSConfigPaths: args13["experimental-resolve-tsconfig-paths"],
filesystemCaching: args13["filesystem-caching"],
migrationFolder: (0, import_pathe5.join)(config.cwd, migrationFolder)
})
);
return new import_kysely.Migrator({ ...migratorOptions, db: kysely, provider });
}
// src/kysely/get-kysely.mts
var import_consola7 = require("consola");
var import_kysely3 = require("kysely");
// src/kysely/get-dialect.mts
var import_kysely2 = require("kysely");
async function getDialect(config) {
const { dialect } = config;
if (!dialect) {
throw new Error("No dialect provided");
}
if (typeof dialect !== "string") {
return await hydrate(dialect, []);
}
const dialectConfig = await hydrate(config.dialectConfig, []);
if (dialect === "pg") {
return new import_kysely2.PostgresDialect(dialectConfig);
}
if (dialect === "mysql2") {
return new import_kysely2.MysqlDialect(dialectConfig);
}
if (dialect === "tedious") {
return new (await import("kysely")).MssqlDialect(dialectConfig);
}
if (dialect === "better-sqlite3") {
return new import_kysely2.SqliteDialect(dialectConfig);
}
if (dialect === "postgres") {
return new (await import("kysely-postgres-js")).PostgresJSDialect(
dialectConfig
);
}
throw new Error(`Unknown dialect: ${dialect}`);
}
// src/kysely/get-kysely.mts
async function getKysely(config, debug = false) {
const { kysely } = config;
if (kysely) {
return await hydrate(kysely, []);
}
const [dialect, plugins] = await Promise.all([
getDialect(config),
hydrate(config.plugins, [])
]);
return new import_kysely3.Kysely({
dialect,
log: debug ? (event) => {
if (event.level === "error") {
return import_consola7.consola.error(event.error);
}
return import_consola7.consola.log(
`executed \`${event.query.sql}\` in ${event.queryDurationMillis}ms`
);
} : [],
plugins
});
}
// src/kysely/using-kysely.mts
async function usingKysely(config, callback) {
const kysely = await getKysely(config);
try {
return await callback(kysely);
} finally {
if (config.destroyOnExit !== false) {
await kysely.destroy();
}
}
}
// src/kysely/using-migrator.mts
async function usingMigrator(args13, callback) {
const config = await getConfigOrFail(args13);
return await usingKysely(config, async (kysely) => {
const migrator = await getMigrator({ ...config, kysely });
return await callback(migrator);
});
}
// src/utils/create-subcommand.mts
function createSubcommand(name, def) {
return {
[name]: {
...def,
meta: {
...def.meta,
name
}
}
// biome-ignore lint/suspicious/noExplicitAny: this is perfectly fine
};
}
// src/commands/migrate/down.mts
var args2 = {
...CommonArgs,
...MigrateArgs,
...createMigrationNameArg()
};
var BaseDownCommand = {
meta: {
name: "down",
description: "Undo the last/specified migration that was run"
},
args: args2,
async run(context) {
const { args: args13 } = context;
const { migration_name } = context.args;
import_consola8.consola.debug(context, []);
await usingMigrator(args13, async (migrator) => {
if (await isWrongDirection(migration_name, "down", migrator)) {
return import_consola8.consola.info(
`Migration skipped: "${migration_name}" has not been run yet`
);
}
import_consola8.consola.start("Starting migration down");
const resultSet = migration_name ? await migrator.migrateTo(migration_name) : await migrator.migrateDown();
await processMigrationResultSet(resultSet, "down", migrator);
});
}
};
var DownCommand = createSubcommand("down", BaseDownCommand);
var LegacyDownCommand = createSubcommand(
"migrate:down",
BaseDownCommand
);
// src/commands/migrate/latest.mts
var import_consola9 = require("consola");
var args3 = {
...CommonArgs,
...MigrateArgs
};
var BaseLatestCommand = {
meta: {
name: "latest",
description: "Update the database schema to the latest version"
},
args: args3,
async run(context) {
import_consola9.consola.debug(context, []);
await usingMigrator(context.args, async (migrator) => {
import_consola9.consola.start("Starting migration to latest");
const resultSet = await migrator.migrateToLatest();
await processMigrationResultSet(resultSet, "up", migrator);
});
}
};
var LatestCommand = createSubcommand("latest", BaseLatestCommand);
var LegacyLatestCommand = createSubcommand(
"migrate:latest",
BaseLatestCommand
);
// src/commands/migrate/list.mts
var import_consola10 = require("consola");
var args4 = {
...CommonArgs,
"fail-on-pending": {
type: "boolean",
default: false,
required: false
}
};
var BaseListCommand = {
meta: {
name: "list",
description: "List both completed and pending migrations"
},
args: args4,
async run(context) {
import_consola10.consola.debug(context, []);
const migrations2 = await usingMigrator(context.args, getMigrations);
import_consola10.consola.debug(migrations2);
if (!migrations2.length) {
return import_consola10.consola.info("No migrations found.");
}
import_consola10.consola.info(
`Found ${migrations2.length} migration${migrations2.length > 1 ? "s" : ""}:`
);
for (const migration of migrations2) {
import_consola10.consola.log(`[${migration.executedAt ? "`\u2713`" : " "}] ${migration.name}`);
}
if (!context.args["fail-on-pending"]) {
return;
}
const hasPending = migrations2.some(
(migration) => migration.executedAt == null
);
if (hasPending) {
exitWithError("Failed due to pending migrations.");
}
}
};
var ListCommand = createSubcommand("list", BaseListCommand);
var LegacyListCommand = createSubcommand(
"migrate:list",
BaseListCommand
);
// src/commands/migrate/make.mts
var import_promises3 = require("fs/promises");
var import_consola11 = require("consola");
var import_pathe6 = require("pathe");
var args5 = {
...CommonArgs,
...createMigrationNameArg(true),
...ExtensionArg
};
var BaseMakeCommand = {
meta: {
description: "Create a new migration file"
},
args: args5,
async run(context) {
const { args: args13 } = context;
const { extension } = args13;
import_consola11.consola.debug(context, []);
const config = await getConfigOrFail(args13);
assertExtension(extension, config, "migrations");
const migrationsFolderPath = (0, import_pathe6.join)(
config.cwd,
config.migrations.migrationFolder
);
import_consola11.consola.debug("Migrations folder path:", migrationsFolderPath);
const wasMigrationsFolderCreated = Boolean(
await (0, import_promises3.mkdir)(migrationsFolderPath, { recursive: true })
);
if (wasMigrationsFolderCreated) {
import_consola11.consola.debug("Migrations folder created");
}
const filename3 = `${await config.migrations.getMigrationPrefix()}${args13.migration_name}.${extension}`;
import_consola11.consola.debug("Filename:", filename3);
const filePath = (0, import_pathe6.join)(migrationsFolderPath, filename3);
import_consola11.consola.debug("File path:", filePath);
const templateExtension = await getTemplateExtension(extension);
const templatePath = (0, import_pathe6.join)(
__dirname,
`templates/migration-template.${templateExtension}`
);
import_consola11.consola.debug("Template path:", templatePath);
await (0, import_promises3.copyFile)(templatePath, filePath);
import_consola11.consola.success(`Created migration file at ${filePath}`);
}
};
var MakeCommand = createSubcommand("make", BaseMakeCommand);
var LegacyMakeCommand = createSubcommand(
"migrate:make",
BaseMakeCommand
);
// src/commands/migrate/rollback.mts
var import_consola12 = require("consola");
var import_kysely4 = require("kysely");
var args6 = {
all: {
description: "Rollback all completed migrations",
required: true,
// remove this if and when Migrator supports migration batches.
type: "boolean"
},
...CommonArgs,
...MigrateArgs
};
var BaseRollbackCommand = {
meta: {
name: "rollback",
description: "Rollback all the completed migrations"
},
args: args6,
async run(context) {
import_consola12.consola.debug(context, []);
await usingMigrator(context.args, async (migrator) => {
import_consola12.consola.start("Starting migration rollback");
const resultSet = await migrator.migrateTo(import_kysely4.NO_MIGRATIONS);
await processMigrationResultSet(resultSet, "down", migrator);
});
}
};
var RollbackCommand = createSubcommand("rollback", BaseRollbackCommand);
var LegacyRollbackCommand = createSubcommand(
"migrate:rollback",
BaseRollbackCommand
);
// src/commands/migrate/root.mts
var import_citty = require("citty");
var import_consola14 = require("consola");
// src/commands/migrate/up.mts
var import_consola13 = require("consola");
var args7 = {
...CommonArgs,
...MigrateArgs,
...createMigrationNameArg()
};
var BaseUpCommand = {
meta: {
name: "up",
description: "Run the next migration that has not yet been run"
},
args: args7,
async run(context) {
const { args: args13 } = context;
const { migration_name } = args13;
import_consola13.consola.debug(context, []);
await usingMigrator(args13, async (migrator) => {
if (await isWrongDirection(migration_name, "up", migrator)) {
return import_consola13.consola.info(
`Migration skipped: migration "${migration_name}" has already been run`
);
}
import_consola13.consola.start("Starting migration up");
const resultSet = migration_name ? await migrator.migrateTo(migration_name) : await migrator.migrateUp();
await processMigrationResultSet(resultSet, "up", migrator);
});
}
};
var UpCommand = createSubcommand("up", BaseUpCommand);
var LegacyUpCommand = createSubcommand("migrate:up", BaseUpCommand);
// src/commands/migrate/root.mts
var MigrateCommand = {
migrate: {
meta: {
name: "migrate",
description: "Migrate the database schema"
},
args: CommonArgs,
subCommands: {
...DownCommand,
...LatestCommand,
...ListCommand,
...MakeCommand,
...RollbackCommand,
...UpCommand
},
async run(context) {
if (!isInSubcommand(context)) {
import_consola14.consola.debug(context, []);
await (0, import_citty.showUsage)(context.cmd, RootCommand);
}
}
}
};
// src/commands/seed/make.mts
var import_promises4 = require("fs/promises");
var import_consola15 = require("consola");
var import_pathe7 = require("pathe");
var args8 = {
...CommonArgs,
...ExtensionArg,
seed_name: {
description: "Seed file name to create",
required: true,
type: "positional"
}
};
var BaseMakeCommand2 = {
meta: {
description: "Create a new seed file"
},
args: args8,
async run(context) {
const { args: args13 } = context;
const { extension } = args13;
import_consola15.consola.debug(context, []);
const config = await getConfigOrFail(args13);
assertExtension(extension, config, "seeds");
const seedsFolderPath = (0, import_pathe7.join)(config.cwd, config.seeds.seedFolder);
import_consola15.consola.debug("Seeds folder path:", seedsFolderPath);
const wasSeedsFolderCreated = Boolean(
await (0, import_promises4.mkdir)(seedsFolderPath, { recursive: true })
);
if (wasSeedsFolderCreated) {
import_consola15.consola.debug("Seeds folder created");
}
const filename3 = `${await config.seeds.getSeedPrefix()}${args13.seed_name}.${extension}`;
import_consola15.consola.debug("Filename:", filename3);
const filePath = (0, import_pathe7.join)(seedsFolderPath, filename3);
import_consola15.consola.debug("File path:", filePath);
const templateExtension = await getTemplateExtension(extension);
const templatePath = (0, import_pathe7.join)(
__dirname,
`templates/seed-template.${templateExtension}`
);
import_consola15.consola.debug("Template path:", templatePath);
await (0, import_promises4.copyFile)(templatePath, filePath);
import_consola15.consola.success(`Created seed file at ${filePath}`);
}
};
var MakeCommand2 = createSubcommand("make", BaseMakeCommand2);
var LegacyMakeCommand2 = createSubcommand("seed:make", BaseMakeCommand2);
// src/commands/seed/root.mts
var import_citty2 = require("citty");
var import_consola19 = require("consola");
// src/commands/seed/list.mts
var import_consola17 = require("consola");
// src/seeds/get-seeder.mts
var import_pathe9 = require("pathe");
// src/seeds/file-seed-provider.mts
var import_consola16 = require("consola");
var import_pathe8 = require("pathe");
var import_utils3 = require("pathe/utils");
// src/utils/as-array.mts
function asArray(thing) {
return Array.isArray(thing) ? thing : [thing];
}
// src/seeds/file-seed-provider.mts
var FileSeedProvider = class {
#props;
constructor(props) {
this.#props = props;
}
async getSeeds(seedNames) {
const seedNamesMap = {};
if (seedNames) {
for (const seedName of asArray(seedNames)) {
seedNamesMap[seedName] = true;
}
}
const fileNames = await safeReaddir(this.#props.seedFolder);
const seeds = {};
for (const fileName of fileNames) {
const fileType = getFileType(fileName);
const isTS = fileType === "TS";
if (!isTS) {
if (!this.#props.allowJS) {
import_consola16.consola.warn(`Ignoring \`${fileName}\` - not a TS file.`);
continue;
}
if (fileType !== "JS") {
import_consola16.consola.warn(`Ignoring \`${fileName}\` - not a TS/JS file.`);
continue;
}
}
const seedKey = (0, import_utils3.filename)(fileName);
if (!seedKey || seedNames && !seedNamesMap[seedKey]) {
continue;
}
const filePath = (0, import_pathe8.join)(this.#props.seedFolder, fileName);
const seedModule = await (isTS ? importTSFile(filePath, this.#props) : import(filePath));
const seed = isSeed(seedModule?.default) ? seedModule.default : isSeed(seedModule) ? seedModule : null;
if (!seed) {
import_consola16.consola.warn(`Ignoring \`${fileName}\` - not a seed.`);
continue;
}
seeds[seedKey] = seed;
}
return seeds;
}
};
function isSeed(thing) {
return isObject(thing) && typeof thing.seed === "function";
}
// src/seeds/seeder.mts
var Seeder = class {
#props;
constructor(props) {
this.#props = props;
}
async getSeeds(seedNames) {
const seeds = await this.#props.provider.getSeeds(seedNames);
return Object.entries(seeds).map(([name, seed]) => ({
name,
seed
}));
}
async run(seedNames) {
const seeds = await this.getSeeds(seedNames);
const resultSet = {
error: void 0,
results: seeds.map(
(seed) => ({
seedName: seed.name,
status: "NotExecuted"
})
)
};
for (let i = 0, len = seeds.length; i < len && !resultSet.error; ++i) {
const result = resultSet.results[i];
assertDefined(result);
const seedInfo = seeds[i];
assertDefined(seedInfo);
try {
await seedInfo.seed.seed(this.#props.db);
result.status = "Success";
} catch (err) {
result.status = "Error";
resultSet.error = err;
}
}
return resultSet;
}
};
// src/seeds/get-seeder.mts
async function getSeeder(config) {
const { args: args13, kysely, seeds } = config;
const { allowJS, seedFolder, seeder, ...seederOptions } = seeds;
if (seeder) {
return await hydrate(seeder, [kysely]);
}
const provider = await hydrate(
seeds.provider,
[],
() => new FileSeedProvider({
allowJS,
debug: args13.debug,
filesystemCaching: args13["filesystem-caching"],
experimentalResolveTSConfigPaths: args13["experimental-resolve-tsconfig-paths"],
seedFolder: (0, import_pathe9.join)(config.cwd, seedFolder)
})
);
return new Seeder({ ...seederOptions, db: kysely, provider });
}
// src/seeds/using-seeder.mts
async function usingSeeder(args13, callback) {
const config = await getConfigOrFail(args13);
return await usingKysely(config, async (kysely) => {
const seeder = await getSeeder({ ...config, kysely });
return await callback(seeder);
});
}
// src/commands/seed/list.mts
var args9 = {
...CommonArgs
};
var ListCommand2 = {
list: {
meta: {
name: "list",
description: "List seeds"
},
args: args9,
async run(context) {
import_consola17.consola.debug(context, []);
const seeds = await usingSeeder(
context.args,
(seeder) => seeder.getSeeds()
);
import_consola17.consola.debug(seeds);
if (!seeds.length) {
return import_consola17.consola.info("No seeds found.");
}
import_consola17.consola.info(`Found ${seeds.length} seed${seeds.length > 1 ? "s" : ""}:`);
for (const seed of seeds) {
import_consola17.consola.log(seed.name);
}
}
}
};
// src/commands/seed/run.mts
var import_consola18 = require("consola");
var import_utils4 = require("consola/utils");
var args10 = {
...CommonArgs,
specific: {
description: "Run seed file/s with given name/s",
type: "string"
}
};
var BaseRunCommand = {
meta: {
description: "Run seed files",
name: "run"
},
args: args10,
async run(context) {
const { args: args13 } = context;
const { specific } = args13;
import_consola18.consola.debug(context, []);
import_consola18.consola.start("Starting seed run");
const resultSet = await usingSeeder(args13, (seeder) => seeder.run(specific));
import_consola18.consola.debug(resultSet);
const { error, results } = resultSet;
if (!results.length) {
return import_consola18.consola.info("No seeds found.");
}
if (!error) {
import_consola18.consola.success("Seed successful");
}
const actuallyRan = error ? results.filter((result) => result.status !== "NotExecuted") : results;
import_consola18.consola.info(
`Ran ${actuallyRan.length} seed${actuallyRan.length > 1 ? "s" : ""}:`
);
for (const result of results) {
import_consola18.consola.log(
`[${{
Error: (0, import_utils4.colorize)("red", "\u2717"),
NotExecuted: " ",
Success: (0, import_utils4.colorize)("green", "\u2713")
}[result.status]}] ${result.seedName}${error && result.status === "Error" ? ` - ${error}` : ""}`
);
}
if (error) {
exitWithError(error);
}
}
};
var RunCommand = createSubcommand("run", BaseRunCommand);
var LegacyRunCommand = createSubcommand("seed:run", BaseRunCommand);
// src/commands/seed/root.mts
var SeedCommand = {
seed: {
meta: {
name: "seed",
description: "Populate your database with test or seed data independent of your migration files"
},
args: CommonArgs,
subCommands: {
...ListCommand2,
...MakeCommand2,
...RunCommand
},
async run(context) {
if (!isInSubcommand(context)) {
import_consola19.consola.debug(context, []);
await (0, import_citty2.showUsage)(context.cmd, RootCommand);
}
}
}
};
// src/commands/sql.mts
var import_promises5 = require("readline/promises");
var import_consola21 = require("consola");
var import_utils5 = require("consola/utils");
var import_std_env7 = require("std-env");
// src/kysely/execute-query.mts
var import_kysely5 = require("kysely");
async function executeQuery(query, config) {
return await config.kysely.executeQuery(
import_kysely5.CompiledQuery.raw(query.sql, query.parameters)
);
}
// src/kysely/infer-dialect-name.mts
var CAPTURE_COMMON_CLASS_SUFFIXES = /adapter|dialect/i;
function inferDialectName(kysely) {
return kysely.getExecutor().adapter.constructor.name.replaceAll(CAPTURE_COMMON_CLASS_SUFFIXES, "").toLowerCase();
}
// src/utils/print-csv.mts
var import_consola20 = require("consola");
function printCSV(rows) {
const [row0] = rows;
if (!row0) {
return;
}
import_consola20.consola.log(`"${Object.keys(row0).join('","')}"`);
for (const row of rows) {
const transformedValues = [];
for (const value of Object.values(row)) {
transformedValues.push(typeof value === "string" ? `"${value}"` : value);
}
import_consola20.consola.log(transformedValues.join(","));
}
}
// src/commands/sql.mts
var args11 = {
...CommonArgs,
format: {
alias: "f",
default: "csv",
description: "The format to output the result in.",
required: false,
type: "string",
valueHint: "csv | json"
},
query: {
description: "The SQL query to execute. When not provided, and not in CI, will open an interactive SQL shell.",
required: import_std_env7.isCI,
type: "positional"
}
};
var SqlCommand = {
sql: {
meta: {
name: "sql",
description: "Execute SQL queries"
},
args: args11,
subCommands: {},
async run(context) {
const { args: args13 } = context;
const { format, query } = args13;
import_consola21.consola.debug(context, []);
assertQuery(query);
assertFormat(format);
const config = await getConfigOrFail(args13);
await usingKysely(config, async (kysely) => {
const hydratedConfig = { ...config, kysely };
if (query) {
return await executeQueryAndPrint(args13, hydratedConfig);
}
await startInteractiveExecution(args13, hydratedConfig);
});
}
}
};
function assertQuery(thing) {
if (!import_std_env7.isCI && typeof thing !== "string" || typeof thing === "string" && thing.length > 0) {
return;
}
throw new Error("Query must be a non-empty string!");
}
var FORMATS = ["csv", "json"];
function assertFormat(thing) {
if (thing == null || FORMATS.includes(thing)) {
return;
}
throw new Error(
`Invalid format "${thing}"! Expected ${FORMATS.map(
(format) => `"${format}"`
).join(" | ")}`
);
}
async function executeQueryAndPrint(argz, config) {
const result = await executeQuery({ sql: argz.query }, config);
if (argz.format === "json") {
return import_consola21.consola.log(JSON.stringify(result, null, 2));
}
const { insertId, numAffectedRows, numChangedRows, rows } = result;
const [row0] = rows;
if (!row0 && (insertId != null || numAffectedRows != null || numChangedRows != null)) {
const summary = {
"Affected Rows": numAffectedRows,
"Changed Rows": numChangedRows,
"Insert ID": insertId,
rowCount: rows.length
};
return printCSV([summary]);
}
return printCSV(rows);
}
async function startInteractiveExecution(argz, config) {
while (true) {
let query = await import_consola21.consola.prompt(getPrompt(argz, config), {
cancel: "null",
placeholder: "select 1",
required: true,
type: "text"
});
if (query == null) {
return;
}
query = query.trim();
if (isSafeword(query)) {
return;
}
if (!query.endsWith(";")) {
const readline = (0, import_promises5.createInterface)({
// biome-ignore lint/style/noNonNullAssertion: yolo
input: import_std_env7.process.stdin,
output: import_std_env7.process.stdout
});
do {
const moreQuery = await readline.question("");
query += ` ${moreQuery.trim()}`;
} while (!query.endsWith(";"));
readline.close();
}
try {
await executeQueryAndPrint({ ...argz, query }, config);
} catch (error) {
import_consola21.consola.error(error instanceof Error ? error.message : error);
}
}
}
var SAFEWORDS = ["exit", "quit", "bye", ":q"];
function isSafeword(thing) {
return SAFEWORDS.includes(thing);
}
function getPrompt(argz, config) {
const { environment } = argz;
const { dialect } = config;
return [
typeof dialect === "string" ? dialect : inferDialectName(config.kysely),
environment ? (0, import_utils5.colorize)("gray", `(${environment})`) : null,
(0, import_utils5.colorize)("cyan", "\u276F")
].filter(Boolean).join(" ");
}
// src/commands/root.mts
var args12 = {
...CommonArgs,
version: {
alias: "v",
default: false,
description: "Show version number",
type: "boolean"
}
};
var RootCommand = {
meta: {
name: "kysely",
description: "A command-line tool for Kysely"
},
args: args12,
subCommands: {
...InitCommand,
...LegacyDownCommand,
...LegacyLatestCommand,
...LegacyListCommand,
...LegacyMakeCommand,
...LegacyRollbackCommand,
...LegacyRunCommand,
...LegacyMakeCommand2,
...LegacyUpCommand,
...MigrateCommand,
...SeedCommand,
...SqlCommand
},
setup(context) {
const { args: args13 } = context;
if (args13.debug) {
import_consola22.consola.level = import_consola22.LogLevels.debug;
}
import_consola22.consola.options.formatOptions.date = false;
getCWD(args13);
},
async run(context) {
const { args: args13 } = context;
if (!isInSubcommand(context)) {
import_consola22.consola.debug(context, []);
if (args13.version) {
return await printInstalledVersions(args13);
}
await (0, import_citty3.showUsage)(context.cmd);
}
await printUpgradeNotice(args13);
import_consola22.consola.debug(`finished running from "${__filename}"`);
}
};
// src/cli.mts
function buildCLI() {
const runCLI = (0, import_citty4.createMain)(RootCommand);
return {
parse: async (argv) => {
await runCLI({ rawArgs: argv });
}
};
}
// src/bin.mts
var cli = buildCLI();
cli.parse(import_std_env8.process.argv.slice(2));