UNPKG

kysely-ctl

Version:
1,571 lines (1,491 loc) 47.7 kB
#!/usr/bin/env node "use strict"; 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));