117 lines
4.7 KiB
JavaScript
117 lines
4.7 KiB
JavaScript
import { Command } from "commander";
|
|
import createDBSchema from "../lib/schema/create-db-schema";
|
|
import grabDirNames from "../data/grab-dir-names";
|
|
import path from "path";
|
|
import dbSchemaToTypeDef from "../lib/sqlite/schema-to-typedef";
|
|
import appendDefaultFieldsToDbSchema from "../utils/append-default-fields-to-db-schema";
|
|
import chalk from "chalk";
|
|
import { writeLiveSchema } from "../functions/live-schema";
|
|
import grabDBDir from "../utils/grab-db-dir";
|
|
import snapshotDb from "../utils/snapshot-db";
|
|
import replaceDbFile from "../utils/replace-db-file";
|
|
import { confirm } from "@inquirer/prompts";
|
|
export default function () {
|
|
return new Command("schema")
|
|
.description("Build DB From Schema")
|
|
.option("-v, --vector", "Recreate Vector Tables. This will drop and rebuild all vector tables")
|
|
.option("-t, --typedef", "Generate typescript type definitions")
|
|
.option("--dry-run", "Print the schema changes that would be applied, without applying them")
|
|
.option("-y, --yes", "Apply destructive changes (dropped tables / columns) without confirmation")
|
|
.action(async (opts) => {
|
|
console.log(`Starting process ...`);
|
|
const config = global.CONFIG;
|
|
const dbSchema = global.DB_SCHEMA;
|
|
const DbClient = global.SQLITE_CLIENT;
|
|
const { ROOT_DIR, BUN_SQLITE_TEMP_DB_FILE_PATH } = grabDirNames();
|
|
const { db_file_path } = grabDBDir({ config });
|
|
const isVector = Boolean(opts.vector || opts.v);
|
|
const isTypeDef = Boolean(opts.typedef || opts.t);
|
|
const finaldbSchema = appendDefaultFieldsToDbSchema({
|
|
dbSchema,
|
|
});
|
|
const params = {
|
|
db: DbClient,
|
|
db_schema: finaldbSchema,
|
|
recreate_vector_table: isVector,
|
|
};
|
|
/**
|
|
* Plan: run the sync in a transaction that is rolled back
|
|
*/
|
|
const plan = [];
|
|
try {
|
|
createDBSchema({ ...params, dry_run: true, changes: plan });
|
|
}
|
|
catch (error) {
|
|
console.error(chalk.red(`Schema sync ERROR => ${error.message}`));
|
|
process.exit(1);
|
|
}
|
|
const destructive = plan.filter((change) => change.destructive);
|
|
if (opts.dryRun) {
|
|
if (!plan.length) {
|
|
console.log(chalk.green(`Schema is up to date.`));
|
|
}
|
|
else {
|
|
console.log(chalk.bold(`Planned schema changes:\n`));
|
|
for (const change of plan) {
|
|
console.log(change.destructive
|
|
? chalk.red(` ${change.description}`)
|
|
: ` ${change.description}`);
|
|
}
|
|
if (destructive.length) {
|
|
console.log(chalk.red(`\n${destructive.length} destructive change(s) (in red) would discard data.`));
|
|
}
|
|
}
|
|
process.exit(0);
|
|
}
|
|
if (destructive.length && !opts.yes) {
|
|
console.log(chalk.yellow(`\nThese changes will permanently discard data:\n`));
|
|
for (const change of destructive) {
|
|
console.log(chalk.red(` ${change.description}`));
|
|
}
|
|
console.log();
|
|
if (!process.stdin.isTTY) {
|
|
console.error(chalk.red(`Refusing to apply destructive changes non-interactively. Re-run with --yes to apply them.`));
|
|
process.exit(1);
|
|
}
|
|
const proceed = await confirm({
|
|
message: `Apply ${destructive.length} destructive change(s)?`,
|
|
default: false,
|
|
});
|
|
if (!proceed) {
|
|
console.log(chalk.dim(`Schema sync cancelled.`));
|
|
process.exit(1);
|
|
}
|
|
}
|
|
snapshotDb({ db: DbClient, dst: BUN_SQLITE_TEMP_DB_FILE_PATH });
|
|
try {
|
|
createDBSchema({
|
|
...params,
|
|
allow_destructive: opts.yes
|
|
? true
|
|
: new Set(destructive.map((c) => c.description)),
|
|
});
|
|
DbClient.close();
|
|
if (isTypeDef && config.typedef_file_path) {
|
|
const out_file = path.resolve(ROOT_DIR, config.typedef_file_path);
|
|
dbSchemaToTypeDef({
|
|
dbSchema: finaldbSchema,
|
|
dst_file: out_file,
|
|
config,
|
|
});
|
|
}
|
|
writeLiveSchema({ schema: finaldbSchema });
|
|
console.log(`${chalk.bold(chalk.green(`DB Schema setup success!`))}`);
|
|
process.exit();
|
|
}
|
|
catch (error) {
|
|
console.log(error);
|
|
DbClient.close();
|
|
replaceDbFile({
|
|
src: BUN_SQLITE_TEMP_DB_FILE_PATH,
|
|
db_file_path,
|
|
});
|
|
process.exit(1);
|
|
}
|
|
});
|
|
}
|