@plyaz/db 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import * as fs3 from 'fs';
2
+ import * as fs4 from 'fs';
3
3
  import * as path4 from 'path';
4
4
  import { Command } from 'commander';
5
5
  import { DatabaseError } from '@plyaz/errors';
@@ -13,8 +13,10 @@ import { Pool } from 'pg';
13
13
  import { eq, sql, isNotNull, isNull, between, like, not, inArray, lte, lt, gte, gt, asc, desc } from 'drizzle-orm';
14
14
  import { PgColumn } from 'drizzle-orm/pg-core';
15
15
  import { createClient } from '@supabase/supabase-js';
16
- import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
17
- import { pathToFileURL } from 'url';
16
+ import { randomBytes, createCipheriv, createDecipheriv, randomUUID } from 'crypto';
17
+ import * as readline from 'readline';
18
+ import { pathToFileURL, fileURLToPath } from 'url';
19
+ import { execSync } from 'child_process';
18
20
 
19
21
  var __defProp = Object.defineProperty;
20
22
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -59,7 +61,7 @@ function suggestDownMigration(sqlContent, filename) {
59
61
  };
60
62
  }
61
63
  function addDownSection(filePath) {
62
- const content = fs3.readFileSync(filePath, "utf-8");
64
+ const content = fs4.readFileSync(filePath, "utf-8");
63
65
  if (content.includes("-- DOWN")) {
64
66
  return content;
65
67
  }
@@ -68,16 +70,16 @@ function addDownSection(filePath) {
68
70
  return content + downSection;
69
71
  }
70
72
  function processDirectory(migrationsPath, dryRun = true) {
71
- if (!fs3.existsSync(migrationsPath)) {
73
+ if (!fs4.existsSync(migrationsPath)) {
72
74
  console.error(`Migration directory not found: ${migrationsPath}`);
73
75
  return;
74
76
  }
75
- const files = fs3.readdirSync(migrationsPath).filter((f) => f.endsWith(".sql"));
77
+ const files = fs4.readdirSync(migrationsPath).filter((f) => f.endsWith(".sql"));
76
78
  console.log(`Found ${files.length} SQL migration files
77
79
  `);
78
80
  for (const file of files) {
79
81
  const filePath = path4.join(migrationsPath, file);
80
- const content = fs3.readFileSync(filePath, "utf-8");
82
+ const content = fs4.readFileSync(filePath, "utf-8");
81
83
  if (content.includes("-- DOWN")) {
82
84
  console.log(`✓ ${file} - Already has DOWN section`);
83
85
  continue;
@@ -91,7 +93,7 @@ function processDirectory(migrationsPath, dryRun = true) {
91
93
  const downSection = newContent.split("-- DOWN")[1];
92
94
  console.log(downSection);
93
95
  } else {
94
- fs3.writeFileSync(filePath, newContent, "utf-8");
96
+ fs4.writeFileSync(filePath, newContent, "utf-8");
95
97
  console.log(`✓ ${file} - Added DOWN section`);
96
98
  }
97
99
  }
@@ -258,12 +260,12 @@ var MigrationManager = class {
258
260
  * Discover migration files from migrations directory (including subdirectories)
259
261
  */
260
262
  async discoverMigrations() {
261
- if (!fs3.existsSync(this.migrationsPath)) {
263
+ if (!fs4.existsSync(this.migrationsPath)) {
262
264
  return [];
263
265
  }
264
266
  const migrations = [];
265
267
  const scanDirectory = /* @__PURE__ */ __name((dir) => {
266
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
268
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
267
269
  for (const entry of entries) {
268
270
  const fullPath = path4.join(dir, entry.name);
269
271
  if (entry.isDirectory()) {
@@ -426,7 +428,7 @@ var MigrationManager = class {
426
428
  * Load SQL migration from file
427
429
  */
428
430
  loadSqlMigration(migrationFile) {
429
- const sql2 = fs3.readFileSync(migrationFile.filePath, "utf-8");
431
+ const sql2 = fs4.readFileSync(migrationFile.filePath, "utf-8");
430
432
  const { upSQL, downSQL } = this.parseSqlSections(sql2);
431
433
  return {
432
434
  version: migrationFile.version,
@@ -770,10 +772,10 @@ var SeedManager = class {
770
772
  * Discover seed files from seeds directory
771
773
  */
772
774
  async discoverSeeds() {
773
- if (!fs3.existsSync(this.seedsPath)) {
775
+ if (!fs4.existsSync(this.seedsPath)) {
774
776
  return [];
775
777
  }
776
- const files = fs3.readdirSync(this.seedsPath);
778
+ const files = fs4.readdirSync(this.seedsPath);
777
779
  const seeds = [];
778
780
  for (const file of files) {
779
781
  const match = file.match(/^(\d+)_(.+)\.(ts|js|sql)$/);
@@ -903,7 +905,7 @@ var SeedManager = class {
903
905
  * Load SQL seed from file
904
906
  */
905
907
  loadSqlSeed(seedFile) {
906
- const sql2 = fs3.readFileSync(seedFile.filePath, "utf-8");
908
+ const sql2 = fs4.readFileSync(seedFile.filePath, "utf-8");
907
909
  return {
908
910
  name: seedFile.name,
909
911
  run: /* @__PURE__ */ __name(async () => {
@@ -8267,6 +8269,77 @@ async function createDatabaseService(config) {
8267
8269
  }
8268
8270
  __name(createDatabaseService, "createDatabaseService");
8269
8271
  var JSON_INDENT_SPACES = 2;
8272
+ var VERSION_PAD_LENGTH = 3;
8273
+ var DECIMAL_RADIX = 10;
8274
+ var SEPARATOR_WIDTH = 50;
8275
+ var VALID_MIGRATION_TYPES = ["sql", "ts"];
8276
+ var VALID_SEED_TYPES = ["ts", "sql", "csv"];
8277
+ var MIN_ROLLBACK_STEPS = 1;
8278
+ function validateName(name, type) {
8279
+ if (!name || name.trim() === "") {
8280
+ console.error(`❌ ${type} name cannot be empty`);
8281
+ process.exit(1);
8282
+ }
8283
+ if (!/[a-zA-Z]/.test(name)) {
8284
+ console.error(`❌ ${type} name must contain at least one letter`);
8285
+ process.exit(1);
8286
+ }
8287
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
8288
+ console.error(
8289
+ `❌ ${type} name can only contain letters, numbers, underscores, and hyphens`
8290
+ );
8291
+ process.exit(1);
8292
+ }
8293
+ }
8294
+ __name(validateName, "validateName");
8295
+ function validateMigrationType(type) {
8296
+ if (!VALID_MIGRATION_TYPES.includes(type)) {
8297
+ console.warn(
8298
+ `⚠ Invalid type "${type}". Valid options: ${VALID_MIGRATION_TYPES.join(", ")}. Using default: sql`
8299
+ );
8300
+ return "sql";
8301
+ }
8302
+ return type;
8303
+ }
8304
+ __name(validateMigrationType, "validateMigrationType");
8305
+ function validateSeedType(type) {
8306
+ if (!VALID_SEED_TYPES.includes(type)) {
8307
+ console.warn(
8308
+ `⚠ Invalid type "${type}". Valid options: ${VALID_SEED_TYPES.join(", ")}. Using default: ts`
8309
+ );
8310
+ return "ts";
8311
+ }
8312
+ return type;
8313
+ }
8314
+ __name(validateSeedType, "validateSeedType");
8315
+ function validateSteps(stepsStr) {
8316
+ const steps = Number.parseInt(stepsStr, DECIMAL_RADIX);
8317
+ if (Number.isNaN(steps)) {
8318
+ console.error(`❌ Steps must be a number, got: "${stepsStr}"`);
8319
+ process.exit(1);
8320
+ }
8321
+ if (steps < MIN_ROLLBACK_STEPS) {
8322
+ console.error(
8323
+ `❌ Steps must be at least ${MIN_ROLLBACK_STEPS}, got: ${steps}`
8324
+ );
8325
+ process.exit(1);
8326
+ }
8327
+ return steps;
8328
+ }
8329
+ __name(validateSteps, "validateSteps");
8330
+ function validateSchemaName(schema) {
8331
+ if (!schema || schema.trim() === "") {
8332
+ console.error("❌ Schema name cannot be empty");
8333
+ process.exit(1);
8334
+ }
8335
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schema)) {
8336
+ console.error(
8337
+ "❌ Invalid schema name. Must start with letter or underscore, contain only alphanumeric and underscores"
8338
+ );
8339
+ process.exit(1);
8340
+ }
8341
+ }
8342
+ __name(validateSchemaName, "validateSchemaName");
8270
8343
  async function getUserSchemas(adapter) {
8271
8344
  const result = await adapter.query?.(`
8272
8345
  SELECT schema_name FROM information_schema.schemata
@@ -8327,8 +8400,8 @@ function parseEnvLine(line) {
8327
8400
  }
8328
8401
  __name(parseEnvLine, "parseEnvLine");
8329
8402
  function loadEnvFile(envFilePath) {
8330
- if (!fs3.existsSync(envFilePath)) return;
8331
- const envContent = fs3.readFileSync(envFilePath, "utf-8");
8403
+ if (!fs4.existsSync(envFilePath)) return;
8404
+ const envContent = fs4.readFileSync(envFilePath, "utf-8");
8332
8405
  for (const line of envContent.split("\n")) {
8333
8406
  const parsed = parseEnvLine(line);
8334
8407
  if (parsed) {
@@ -8361,7 +8434,7 @@ __name(loadConfigFromPath, "loadConfigFromPath");
8361
8434
  async function findAndLoadConfig() {
8362
8435
  for (const configName of CONFIG_PATHS) {
8363
8436
  const configFilePath = path4.join(process.cwd(), configName);
8364
- if (fs3.existsSync(configFilePath)) {
8437
+ if (fs4.existsSync(configFilePath)) {
8365
8438
  return loadConfigFromPath(configFilePath);
8366
8439
  }
8367
8440
  }
@@ -8371,7 +8444,7 @@ __name(findAndLoadConfig, "findAndLoadConfig");
8371
8444
  function warnAboutTypeScriptConfigs() {
8372
8445
  for (const tsName of TS_CONFIG_PATHS) {
8373
8446
  const tsPath = path4.join(process.cwd(), tsName);
8374
- if (fs3.existsSync(tsPath)) {
8447
+ if (fs4.existsSync(tsPath)) {
8375
8448
  console.warn(
8376
8449
  `⚠️ Found ${path4.basename(tsPath)} but cannot import TypeScript files directly.`
8377
8450
  );
@@ -8406,7 +8479,7 @@ async function loadConfig(configPath) {
8406
8479
  const explicitPath = configPath ?? globalConfigPath;
8407
8480
  if (explicitPath) {
8408
8481
  const resolvedPath = path4.resolve(process.cwd(), explicitPath);
8409
- if (fs3.existsSync(resolvedPath)) {
8482
+ if (fs4.existsSync(resolvedPath)) {
8410
8483
  return loadConfigFromPath(resolvedPath);
8411
8484
  }
8412
8485
  console.error(`❌ Config file not found: ${resolvedPath}`);
@@ -8489,16 +8562,15 @@ migrateCommand.command("up").description("Run pending migrations").option("-t, -
8489
8562
  });
8490
8563
  migrateCommand.command("down").description("Rollback migrations").option("-s, --steps <number>", "Number of migrations to rollback", "1").action(async (options) => {
8491
8564
  try {
8565
+ const steps = validateSteps(options.steps);
8492
8566
  const { adapter, config } = await initDatabase(void 0, true);
8493
8567
  const migrationManager = new MigrationManager({
8494
8568
  adapter,
8495
8569
  migrationsPath: config.migrationsPath ?? "./migrations",
8496
8570
  tableName: config.migrationsTable ?? "schema_migrations"
8497
8571
  });
8498
- console.log(`🔄 Rolling back ${options.steps} migration(s)...`);
8499
- const result = await migrationManager.down(
8500
- Number.parseInt(options.steps, 10)
8501
- );
8572
+ console.log(`🔄 Rolling back ${steps} migration(s)...`);
8573
+ const result = await migrationManager.down(steps);
8502
8574
  if (result.success) {
8503
8575
  console.log(`✅ Rolled back ${result.value} migration(s)`);
8504
8576
  process.exit(0);
@@ -9226,6 +9298,504 @@ program.command("reset").description(
9226
9298
  process.exit(1);
9227
9299
  }
9228
9300
  });
9301
+ function createReadlineInterface() {
9302
+ return readline.createInterface({
9303
+ input: process.stdin,
9304
+ output: process.stdout
9305
+ });
9306
+ }
9307
+ __name(createReadlineInterface, "createReadlineInterface");
9308
+ async function promptInput(message, defaultValue) {
9309
+ const rl = createReadlineInterface();
9310
+ const prompt = defaultValue ? `${message} (${defaultValue}): ` : `${message}: `;
9311
+ return new Promise((resolve4) => {
9312
+ rl.question(prompt, (answer) => {
9313
+ rl.close();
9314
+ const trimmed = answer.trim();
9315
+ resolve4(trimmed !== "" ? trimmed : defaultValue ?? "");
9316
+ });
9317
+ });
9318
+ }
9319
+ __name(promptInput, "promptInput");
9320
+ async function promptSelect(message, options) {
9321
+ const rl = createReadlineInterface();
9322
+ console.log(message);
9323
+ options.forEach((opt, i) => console.log(` ${i + 1}) ${opt}`));
9324
+ return new Promise((resolve4) => {
9325
+ rl.question("Select (number): ", (answer) => {
9326
+ rl.close();
9327
+ const index = Number.parseInt(answer, DECIMAL_RADIX) - 1;
9328
+ resolve4(options[index] ?? options[0]);
9329
+ });
9330
+ });
9331
+ }
9332
+ __name(promptSelect, "promptSelect");
9333
+ async function promptConfirm(message) {
9334
+ const rl = createReadlineInterface();
9335
+ return new Promise((resolve4) => {
9336
+ rl.question(`${message} (y/N): `, (answer) => {
9337
+ rl.close();
9338
+ resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
9339
+ });
9340
+ });
9341
+ }
9342
+ __name(promptConfirm, "promptConfirm");
9343
+ async function runInteractiveMigration(config) {
9344
+ console.log();
9345
+ console.log("📦 Create Migration - Interactive Mode");
9346
+ console.log("─".repeat(SEPARATOR_WIDTH));
9347
+ console.log();
9348
+ const name = await promptInput("Migration name (e.g., add_users_table)");
9349
+ if (!name) {
9350
+ console.error("❌ Migration name is required");
9351
+ process.exit(1);
9352
+ }
9353
+ validateName(name, "migration");
9354
+ const snakeName = toSnakeCase(name);
9355
+ const tableName = await promptInput("Table name", snakeName);
9356
+ const schema = await promptInput("Database schema", "public");
9357
+ validateSchemaName(schema);
9358
+ const typeChoice = await promptSelect("File type:", [
9359
+ "SQL (.sql)",
9360
+ "TypeScript (.ts)"
9361
+ ]);
9362
+ const type = typeChoice.includes("TypeScript") ? "ts" : "sql";
9363
+ const gitUser = getGitUsername();
9364
+ const includeAuthor = gitUser ? await promptConfirm(`Include author (${gitUser})?`) : false;
9365
+ const author = includeAuthor ? gitUser : "";
9366
+ console.log();
9367
+ console.log("─".repeat(SEPARATOR_WIDTH));
9368
+ console.log(`Migration: ${name}`);
9369
+ console.log(`Table: ${schema}.${tableName}`);
9370
+ console.log(`Type: ${type.toUpperCase()}`);
9371
+ if (author) console.log(`Author: ${author}`);
9372
+ console.log("─".repeat(SEPARATOR_WIDTH));
9373
+ const confirm = await promptConfirm("Create migration?");
9374
+ if (!confirm) {
9375
+ console.log("Cancelled.");
9376
+ process.exit(0);
9377
+ }
9378
+ const migrationsPath = config.migrationsPath ?? "./migrations";
9379
+ if (!fs4.existsSync(migrationsPath)) {
9380
+ fs4.mkdirSync(migrationsPath, { recursive: true });
9381
+ console.log(`📁 Created directory: ${migrationsPath}`);
9382
+ }
9383
+ const version = getNextMigrationVersion(migrationsPath);
9384
+ const filename = `${version}_${snakeName}.${type}`;
9385
+ const filepath = path4.join(migrationsPath, filename);
9386
+ if (fs4.existsSync(filepath)) {
9387
+ const overwrite = await promptConfirm(
9388
+ `File exists. Overwrite ${filename}?`
9389
+ );
9390
+ if (!overwrite) {
9391
+ console.log("Cancelled.");
9392
+ process.exit(0);
9393
+ }
9394
+ }
9395
+ const content = getMigrationTemplate({
9396
+ name,
9397
+ version,
9398
+ tableName,
9399
+ schema,
9400
+ author,
9401
+ type
9402
+ });
9403
+ fs4.writeFileSync(filepath, content, "utf-8");
9404
+ console.log();
9405
+ console.log(`✅ Created migration: ${filename}`);
9406
+ console.log(` Path: ${filepath}`);
9407
+ console.log(` Type: ${type.toUpperCase()}`);
9408
+ console.log(` Table: ${schema}.${tableName}`);
9409
+ if (author) {
9410
+ console.log(` Author: ${author}`);
9411
+ }
9412
+ console.log("");
9413
+ console.log("📝 Next steps:");
9414
+ console.log(` 1. Edit the migration file to add your schema changes`);
9415
+ console.log(` 2. Run 'plyaz-db migrate up' to apply the migration`);
9416
+ console.log(
9417
+ ` 3. Test rollback with 'plyaz-db migrate down' before deploying`
9418
+ );
9419
+ }
9420
+ __name(runInteractiveMigration, "runInteractiveMigration");
9421
+ async function runInteractiveSeed(config) {
9422
+ console.log();
9423
+ console.log("🌱 Create Seed - Interactive Mode");
9424
+ console.log("─".repeat(SEPARATOR_WIDTH));
9425
+ console.log();
9426
+ const name = await promptInput("Seed name (e.g., admin_users)");
9427
+ if (!name) {
9428
+ console.error("❌ Seed name is required");
9429
+ process.exit(1);
9430
+ }
9431
+ validateName(name, "seed");
9432
+ const snakeName = toSnakeCase(name);
9433
+ const tableName = await promptInput("Table name", snakeName);
9434
+ const schema = await promptInput("Database schema", "public");
9435
+ validateSchemaName(schema);
9436
+ const typeChoice = await promptSelect("File type:", [
9437
+ "TypeScript (.ts)",
9438
+ "SQL (.sql)",
9439
+ "CSV (.csv)"
9440
+ ]);
9441
+ const typeMap = {
9442
+ "TypeScript (.ts)": "ts",
9443
+ "SQL (.sql)": "sql",
9444
+ "CSV (.csv)": "csv"
9445
+ };
9446
+ const type = typeMap[typeChoice] ?? "ts";
9447
+ let author = "";
9448
+ if (type !== "csv") {
9449
+ const gitUser = getGitUsername();
9450
+ const includeAuthor = gitUser ? await promptConfirm(`Include author (${gitUser})?`) : false;
9451
+ author = includeAuthor ? gitUser : "";
9452
+ }
9453
+ console.log();
9454
+ console.log("─".repeat(SEPARATOR_WIDTH));
9455
+ console.log(`Seed: ${name}`);
9456
+ console.log(`Table: ${schema}.${tableName}`);
9457
+ console.log(`Type: ${type.toUpperCase()}`);
9458
+ if (author) console.log(`Author: ${author}`);
9459
+ console.log("─".repeat(SEPARATOR_WIDTH));
9460
+ const confirm = await promptConfirm("Create seed?");
9461
+ if (!confirm) {
9462
+ console.log("Cancelled.");
9463
+ process.exit(0);
9464
+ }
9465
+ const seedsPath = config.seedsPath ?? "./seeds";
9466
+ if (!fs4.existsSync(seedsPath)) {
9467
+ fs4.mkdirSync(seedsPath, { recursive: true });
9468
+ console.log(`📁 Created directory: ${seedsPath}`);
9469
+ }
9470
+ const order = getNextSeedOrder(seedsPath);
9471
+ const filename = `${order}_${snakeName}.${type}`;
9472
+ const filepath = path4.join(seedsPath, filename);
9473
+ if (fs4.existsSync(filepath)) {
9474
+ const overwrite = await promptConfirm(
9475
+ `File exists. Overwrite ${filename}?`
9476
+ );
9477
+ if (!overwrite) {
9478
+ console.log("Cancelled.");
9479
+ process.exit(0);
9480
+ }
9481
+ }
9482
+ let content;
9483
+ if (type === "csv") {
9484
+ content = getCsvSeedTemplate({ name, tableName, schema });
9485
+ } else {
9486
+ content = getSeedTemplate({
9487
+ name,
9488
+ order,
9489
+ tableName,
9490
+ schema,
9491
+ author,
9492
+ type
9493
+ });
9494
+ }
9495
+ fs4.writeFileSync(filepath, content, "utf-8");
9496
+ console.log();
9497
+ console.log(`✅ Created seed: ${filename}`);
9498
+ console.log(` Path: ${filepath}`);
9499
+ console.log(` Type: ${type.toUpperCase()}`);
9500
+ console.log(` Table: ${schema}.${tableName}`);
9501
+ if (author && type !== "csv") {
9502
+ console.log(` Author: ${author}`);
9503
+ }
9504
+ console.log("");
9505
+ console.log("📝 Next steps:");
9506
+ console.log(` 1. Edit the seed file to add your data`);
9507
+ console.log(` 2. Run 'plyaz-db seed run' to execute seeds`);
9508
+ console.log(` 3. Use 'plyaz-db seed status' to check seed status`);
9509
+ }
9510
+ __name(runInteractiveSeed, "runInteractiveSeed");
9511
+ function getNextMigrationVersion(migrationsPath) {
9512
+ if (!fs4.existsSync(migrationsPath)) {
9513
+ return "001";
9514
+ }
9515
+ const files = fs4.readdirSync(migrationsPath);
9516
+ const versions = files.map((f) => {
9517
+ const match = f.match(/^(\d+)_/);
9518
+ return match ? Number.parseInt(match[1], DECIMAL_RADIX) : 0;
9519
+ }).filter((v) => v > 0);
9520
+ const maxVersion = versions.length > 0 ? Math.max(...versions) : 0;
9521
+ return String(maxVersion + 1).padStart(VERSION_PAD_LENGTH, "0");
9522
+ }
9523
+ __name(getNextMigrationVersion, "getNextMigrationVersion");
9524
+ function getNextSeedOrder(seedsPath) {
9525
+ if (!fs4.existsSync(seedsPath)) {
9526
+ return "001";
9527
+ }
9528
+ const files = fs4.readdirSync(seedsPath);
9529
+ const orders = files.map((f) => {
9530
+ const match = f.match(/^(\d+)_/);
9531
+ return match ? Number.parseInt(match[1], DECIMAL_RADIX) : 0;
9532
+ }).filter((v) => v > 0);
9533
+ const maxOrder = orders.length > 0 ? Math.max(...orders) : 0;
9534
+ return String(maxOrder + 1).padStart(VERSION_PAD_LENGTH, "0");
9535
+ }
9536
+ __name(getNextSeedOrder, "getNextSeedOrder");
9537
+ function toSnakeCase(name) {
9538
+ return name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_");
9539
+ }
9540
+ __name(toSnakeCase, "toSnakeCase");
9541
+ function toPascalCase(name) {
9542
+ return name.split(/[_\s-]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
9543
+ }
9544
+ __name(toPascalCase, "toPascalCase");
9545
+ function getTemplateDir() {
9546
+ const currentFile = fileURLToPath(import.meta.url);
9547
+ const currentDir = path4.dirname(currentFile);
9548
+ const packageRoot = path4.resolve(currentDir, "..", "..");
9549
+ return path4.join(packageRoot, "template");
9550
+ }
9551
+ __name(getTemplateDir, "getTemplateDir");
9552
+ function loadTemplate(templatePath, vars) {
9553
+ const fullPath = path4.join(getTemplateDir(), templatePath);
9554
+ if (!fs4.existsSync(fullPath)) {
9555
+ throw new Error(`Template not found: ${fullPath}`);
9556
+ }
9557
+ let content = fs4.readFileSync(fullPath, "utf-8");
9558
+ for (const [key, value] of Object.entries(vars)) {
9559
+ const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
9560
+ content = content.replace(regex, value ?? "");
9561
+ }
9562
+ return content;
9563
+ }
9564
+ __name(loadTemplate, "loadTemplate");
9565
+ function getGitUsername() {
9566
+ try {
9567
+ return execSync("git config user.name", { encoding: "utf-8" }).trim();
9568
+ } catch {
9569
+ return "";
9570
+ }
9571
+ }
9572
+ __name(getGitUsername, "getGitUsername");
9573
+ function getMigrationTemplate(options) {
9574
+ const { name, version, tableName, schema, author, type } = options;
9575
+ const description = toPascalCase(name);
9576
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9577
+ const vars = {
9578
+ VERSION: version,
9579
+ DESCRIPTION: description,
9580
+ TABLE_NAME: tableName,
9581
+ SCHEMA: schema,
9582
+ CREATED_DATE: date,
9583
+ AUTHOR: author
9584
+ };
9585
+ const templateFile = type === "ts" ? "migrations/migration.ts.template" : "migrations/migration.sql.template";
9586
+ return loadTemplate(templateFile, vars);
9587
+ }
9588
+ __name(getMigrationTemplate, "getMigrationTemplate");
9589
+ function getSeedTemplate(options) {
9590
+ const { name, order, tableName, schema, author, type } = options;
9591
+ const description = toPascalCase(name);
9592
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9593
+ const vars = {
9594
+ ORDER: order,
9595
+ DESCRIPTION: description,
9596
+ TABLE_NAME: tableName,
9597
+ SCHEMA: schema,
9598
+ CREATED_DATE: date,
9599
+ AUTHOR: author,
9600
+ UUID_1: randomUUID(),
9601
+ UUID_2: randomUUID(),
9602
+ UUID_3: randomUUID()
9603
+ };
9604
+ const templateFile = type === "ts" ? "seeds/seed.ts.template" : "seeds/seed.sql.template";
9605
+ return loadTemplate(templateFile, vars);
9606
+ }
9607
+ __name(getSeedTemplate, "getSeedTemplate");
9608
+ function getCsvSeedTemplate(options) {
9609
+ const { name, tableName, schema } = options;
9610
+ const description = toPascalCase(name);
9611
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9612
+ const vars = {
9613
+ DESCRIPTION: description,
9614
+ TABLE_NAME: tableName,
9615
+ SCHEMA: schema,
9616
+ CREATED_DATE: date,
9617
+ AUTHOR: "",
9618
+ UUID_1: randomUUID(),
9619
+ UUID_2: randomUUID(),
9620
+ UUID_3: randomUUID()
9621
+ };
9622
+ try {
9623
+ return loadTemplate("seeds/seed.csv.template", vars);
9624
+ } catch {
9625
+ return `id,name,created_at
9626
+ ${randomUUID()},Example 1,${(/* @__PURE__ */ new Date()).toISOString()}
9627
+ ${randomUUID()},Example 2,${(/* @__PURE__ */ new Date()).toISOString()}
9628
+ `;
9629
+ }
9630
+ }
9631
+ __name(getCsvSeedTemplate, "getCsvSeedTemplate");
9632
+ var createCommand = program.command("create").description("Create new migration or seed files");
9633
+ createCommand.command("migration").description("Create a new migration file (interactive if no name provided)").argument("[name]", "Migration name (e.g., add_users_table)").option("-t, --type <type>", "File type: {sql, ts} (default: sql)", "sql").option(
9634
+ "-T, --table <table>",
9635
+ "Table name (default: derived from migration name)"
9636
+ ).option(
9637
+ "-s, --schema <schema>",
9638
+ "Database schema (default: public)",
9639
+ "public"
9640
+ ).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").option(
9641
+ "-d, --dry-run",
9642
+ "Preview what would be created without writing files"
9643
+ ).option("-f, --force", "Overwrite existing file if it exists").action(
9644
+ async (name, options) => {
9645
+ try {
9646
+ const config = await loadConfig();
9647
+ if (!name || options.interactive) {
9648
+ await runInteractiveMigration(config);
9649
+ process.exit(0);
9650
+ }
9651
+ validateName(name, "migration");
9652
+ validateSchemaName(options.schema);
9653
+ const fileType = validateMigrationType(options.type);
9654
+ const migrationsPath = config.migrationsPath ?? "./migrations";
9655
+ if (!options.dryRun && !fs4.existsSync(migrationsPath)) {
9656
+ fs4.mkdirSync(migrationsPath, { recursive: true });
9657
+ console.log(`📁 Created directory: ${migrationsPath}`);
9658
+ }
9659
+ const version = getNextMigrationVersion(migrationsPath);
9660
+ const snakeName = toSnakeCase(name);
9661
+ const tableName = options.table ?? snakeName;
9662
+ const schema = options.schema;
9663
+ const author = options.author ? getGitUsername() : "";
9664
+ const filename = `${version}_${snakeName}.${fileType}`;
9665
+ const filepath = path4.join(migrationsPath, filename);
9666
+ if (fs4.existsSync(filepath) && !options.force) {
9667
+ console.error(`❌ File already exists: ${filepath}`);
9668
+ console.error(" Use --force to overwrite");
9669
+ process.exit(1);
9670
+ }
9671
+ const content = getMigrationTemplate({
9672
+ name,
9673
+ version,
9674
+ tableName,
9675
+ schema,
9676
+ author,
9677
+ type: fileType
9678
+ });
9679
+ if (options.dryRun) {
9680
+ console.log("🔍 Dry-run mode - no files will be created\n");
9681
+ console.log(`Would create: ${filename}`);
9682
+ console.log(` Path: ${filepath}`);
9683
+ console.log(` Type: ${fileType.toUpperCase()}`);
9684
+ console.log(` Table: ${schema}.${tableName}`);
9685
+ if (author) {
9686
+ console.log(` Author: ${author}`);
9687
+ }
9688
+ console.log("\n📄 File content preview:");
9689
+ console.log("─".repeat(SEPARATOR_WIDTH));
9690
+ console.log(content);
9691
+ console.log("─".repeat(SEPARATOR_WIDTH));
9692
+ process.exit(0);
9693
+ }
9694
+ fs4.writeFileSync(filepath, content, "utf-8");
9695
+ console.log(`✅ Created migration: ${filename}`);
9696
+ console.log(` Path: ${filepath}`);
9697
+ console.log(` Type: ${fileType.toUpperCase()}`);
9698
+ console.log(` Table: ${schema}.${tableName}`);
9699
+ if (author) {
9700
+ console.log(` Author: ${author}`);
9701
+ }
9702
+ console.log("");
9703
+ console.log("📝 Next steps:");
9704
+ console.log(` 1. Edit the migration file to add your schema changes`);
9705
+ console.log(` 2. Run 'plyaz-db migrate up' to apply the migration`);
9706
+ console.log(
9707
+ ` 3. Test rollback with 'plyaz-db migrate down' before deploying`
9708
+ );
9709
+ process.exit(0);
9710
+ } catch (error) {
9711
+ console.error("❌ Error:", error.message);
9712
+ process.exit(1);
9713
+ }
9714
+ }
9715
+ );
9716
+ createCommand.command("seed").description("Create a new seed file (interactive if no name provided)").argument("[name]", "Seed name (e.g., admin_users)").option("-t, --type <type>", "File type: {ts, sql, csv} (default: ts)", "ts").option("-T, --table <table>", "Table name (default: derived from seed name)").option(
9717
+ "-s, --schema <schema>",
9718
+ "Database schema (default: public)",
9719
+ "public"
9720
+ ).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").option(
9721
+ "-d, --dry-run",
9722
+ "Preview what would be created without writing files"
9723
+ ).option("-f, --force", "Overwrite existing file if it exists").action(
9724
+ async (name, options) => {
9725
+ try {
9726
+ const config = await loadConfig();
9727
+ if (!name || options.interactive) {
9728
+ await runInteractiveSeed(config);
9729
+ process.exit(0);
9730
+ }
9731
+ validateName(name, "seed");
9732
+ validateSchemaName(options.schema);
9733
+ const fileType = validateSeedType(options.type);
9734
+ const seedsPath = config.seedsPath ?? "./seeds";
9735
+ if (!options.dryRun && !fs4.existsSync(seedsPath)) {
9736
+ fs4.mkdirSync(seedsPath, { recursive: true });
9737
+ console.log(`📁 Created directory: ${seedsPath}`);
9738
+ }
9739
+ const order = getNextSeedOrder(seedsPath);
9740
+ const snakeName = toSnakeCase(name);
9741
+ const tableName = options.table ?? snakeName;
9742
+ const schema = options.schema;
9743
+ const author = options.author ? getGitUsername() : "";
9744
+ const filename = `${order}_${snakeName}.${fileType}`;
9745
+ const filepath = path4.join(seedsPath, filename);
9746
+ if (fs4.existsSync(filepath) && !options.force) {
9747
+ console.error(`❌ File already exists: ${filepath}`);
9748
+ console.error(" Use --force to overwrite");
9749
+ process.exit(1);
9750
+ }
9751
+ let content;
9752
+ if (fileType === "csv") {
9753
+ content = getCsvSeedTemplate({ name, tableName, schema });
9754
+ } else {
9755
+ content = getSeedTemplate({
9756
+ name,
9757
+ order,
9758
+ tableName,
9759
+ schema,
9760
+ author,
9761
+ type: fileType
9762
+ });
9763
+ }
9764
+ if (options.dryRun) {
9765
+ console.log("🔍 Dry-run mode - no files will be created\n");
9766
+ console.log(`Would create: ${filename}`);
9767
+ console.log(` Path: ${filepath}`);
9768
+ console.log(` Type: ${fileType.toUpperCase()}`);
9769
+ console.log(` Table: ${schema}.${tableName}`);
9770
+ if (author && fileType !== "csv") {
9771
+ console.log(` Author: ${author}`);
9772
+ }
9773
+ console.log("\n📄 File content preview:");
9774
+ console.log("─".repeat(SEPARATOR_WIDTH));
9775
+ console.log(content);
9776
+ console.log("─".repeat(SEPARATOR_WIDTH));
9777
+ process.exit(0);
9778
+ }
9779
+ fs4.writeFileSync(filepath, content, "utf-8");
9780
+ console.log(`✅ Created seed: ${filename}`);
9781
+ console.log(` Path: ${filepath}`);
9782
+ console.log(` Type: ${fileType.toUpperCase()}`);
9783
+ console.log(` Table: ${schema}.${tableName}`);
9784
+ if (author && fileType !== "csv") {
9785
+ console.log(` Author: ${author}`);
9786
+ }
9787
+ console.log("");
9788
+ console.log("📝 Next steps:");
9789
+ console.log(` 1. Edit the seed file to add your data`);
9790
+ console.log(` 2. Run 'plyaz-db seed run' to execute seeds`);
9791
+ console.log(` 3. Use 'plyaz-db seed status' to check seed status`);
9792
+ process.exit(0);
9793
+ } catch (error) {
9794
+ console.error("❌ Error:", error.message);
9795
+ process.exit(1);
9796
+ }
9797
+ }
9798
+ );
9229
9799
  program.parse();
9230
9800
  //# sourceMappingURL=index.js.map
9231
9801
  //# sourceMappingURL=index.js.map