@plyaz/db 0.2.0 → 0.3.1

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()) {
@@ -347,7 +349,11 @@ var MigrationManager = class {
347
349
  const isEmptyOrComment = trimmedLine === "" || trimmedLine.startsWith("--");
348
350
  current += line + "\n";
349
351
  if (isEmptyOrComment) continue;
350
- const dollarState = this.processDollarDelimiters(line, inDollarBlock, dollarTag);
352
+ const dollarState = this.processDollarDelimiters(
353
+ line,
354
+ inDollarBlock,
355
+ dollarTag
356
+ );
351
357
  inDollarBlock = dollarState.inDollarBlock;
352
358
  dollarTag = dollarState.dollarTag;
353
359
  const isEndOfStatement = !inDollarBlock && trimmedLine.endsWith(";");
@@ -398,7 +404,9 @@ var MigrationManager = class {
398
404
  await adapter.query(statement);
399
405
  const isInterval = (i + 1) % PROGRESS_LOG_INTERVAL === 0;
400
406
  const isLast = i === total - 1;
401
- const isSignificant = Boolean(description.match(/^(CREATE TABLE|CREATE FUNCTION|CREATE TRIGGER)/i));
407
+ const isSignificant = Boolean(
408
+ description.match(/^(CREATE TABLE|CREATE FUNCTION|CREATE TRIGGER)/i)
409
+ );
402
410
  if (isInterval || isLast || isSignificant) {
403
411
  console.log(` ✓ [${i + 1}/${total}] ${description}`);
404
412
  }
@@ -420,7 +428,7 @@ var MigrationManager = class {
420
428
  * Load SQL migration from file
421
429
  */
422
430
  loadSqlMigration(migrationFile) {
423
- const sql2 = fs3.readFileSync(migrationFile.filePath, "utf-8");
431
+ const sql2 = fs4.readFileSync(migrationFile.filePath, "utf-8");
424
432
  const { upSQL, downSQL } = this.parseSqlSections(sql2);
425
433
  return {
426
434
  version: migrationFile.version,
@@ -764,10 +772,10 @@ var SeedManager = class {
764
772
  * Discover seed files from seeds directory
765
773
  */
766
774
  async discoverSeeds() {
767
- if (!fs3.existsSync(this.seedsPath)) {
775
+ if (!fs4.existsSync(this.seedsPath)) {
768
776
  return [];
769
777
  }
770
- const files = fs3.readdirSync(this.seedsPath);
778
+ const files = fs4.readdirSync(this.seedsPath);
771
779
  const seeds = [];
772
780
  for (const file of files) {
773
781
  const match = file.match(/^(\d+)_(.+)\.(ts|js|sql)$/);
@@ -821,7 +829,11 @@ var SeedManager = class {
821
829
  const isEmptyOrComment = trimmedLine === "" || trimmedLine.startsWith("--");
822
830
  current += line + "\n";
823
831
  if (isEmptyOrComment) continue;
824
- const dollarState = this.processDollarDelimiters(line, inDollarBlock, dollarTag);
832
+ const dollarState = this.processDollarDelimiters(
833
+ line,
834
+ inDollarBlock,
835
+ dollarTag
836
+ );
825
837
  inDollarBlock = dollarState.inDollarBlock;
826
838
  dollarTag = dollarState.dollarTag;
827
839
  if (!inDollarBlock && trimmedLine.endsWith(";") && current.trim()) {
@@ -893,7 +905,7 @@ var SeedManager = class {
893
905
  * Load SQL seed from file
894
906
  */
895
907
  loadSqlSeed(seedFile) {
896
- const sql2 = fs3.readFileSync(seedFile.filePath, "utf-8");
908
+ const sql2 = fs4.readFileSync(seedFile.filePath, "utf-8");
897
909
  return {
898
910
  name: seedFile.name,
899
911
  run: /* @__PURE__ */ __name(async () => {
@@ -8257,6 +8269,9 @@ async function createDatabaseService(config) {
8257
8269
  }
8258
8270
  __name(createDatabaseService, "createDatabaseService");
8259
8271
  var JSON_INDENT_SPACES = 2;
8272
+ var VERSION_PAD_LENGTH = 3;
8273
+ var DECIMAL_RADIX = 10;
8274
+ var SEPARATOR_WIDTH = 50;
8260
8275
  async function getUserSchemas(adapter) {
8261
8276
  const result = await adapter.query?.(`
8262
8277
  SELECT schema_name FROM information_schema.schemata
@@ -8317,8 +8332,8 @@ function parseEnvLine(line) {
8317
8332
  }
8318
8333
  __name(parseEnvLine, "parseEnvLine");
8319
8334
  function loadEnvFile(envFilePath) {
8320
- if (!fs3.existsSync(envFilePath)) return;
8321
- const envContent = fs3.readFileSync(envFilePath, "utf-8");
8335
+ if (!fs4.existsSync(envFilePath)) return;
8336
+ const envContent = fs4.readFileSync(envFilePath, "utf-8");
8322
8337
  for (const line of envContent.split("\n")) {
8323
8338
  const parsed = parseEnvLine(line);
8324
8339
  if (parsed) {
@@ -8351,7 +8366,7 @@ __name(loadConfigFromPath, "loadConfigFromPath");
8351
8366
  async function findAndLoadConfig() {
8352
8367
  for (const configName of CONFIG_PATHS) {
8353
8368
  const configFilePath = path4.join(process.cwd(), configName);
8354
- if (fs3.existsSync(configFilePath)) {
8369
+ if (fs4.existsSync(configFilePath)) {
8355
8370
  return loadConfigFromPath(configFilePath);
8356
8371
  }
8357
8372
  }
@@ -8361,7 +8376,7 @@ __name(findAndLoadConfig, "findAndLoadConfig");
8361
8376
  function warnAboutTypeScriptConfigs() {
8362
8377
  for (const tsName of TS_CONFIG_PATHS) {
8363
8378
  const tsPath = path4.join(process.cwd(), tsName);
8364
- if (fs3.existsSync(tsPath)) {
8379
+ if (fs4.existsSync(tsPath)) {
8365
8380
  console.warn(
8366
8381
  `⚠️ Found ${path4.basename(tsPath)} but cannot import TypeScript files directly.`
8367
8382
  );
@@ -8396,7 +8411,7 @@ async function loadConfig(configPath) {
8396
8411
  const explicitPath = configPath ?? globalConfigPath;
8397
8412
  if (explicitPath) {
8398
8413
  const resolvedPath = path4.resolve(process.cwd(), explicitPath);
8399
- if (fs3.existsSync(resolvedPath)) {
8414
+ if (fs4.existsSync(resolvedPath)) {
8400
8415
  return loadConfigFromPath(resolvedPath);
8401
8416
  }
8402
8417
  console.error(`❌ Config file not found: ${resolvedPath}`);
@@ -8408,8 +8423,28 @@ async function loadConfig(configPath) {
8408
8423
  return getEnvFallbackConfig();
8409
8424
  }
8410
8425
  __name(loadConfig, "loadConfig");
8411
- async function initDatabase(configPath) {
8426
+ var MIGRATION_POOL_SETTINGS = {
8427
+ max: 3,
8428
+ // Fewer connections, more stable
8429
+ idleTimeoutMillis: 0,
8430
+ // Never timeout idle connections during migrations
8431
+ connectionTimeoutMillis: 3e4,
8432
+ // 30s to establish connection
8433
+ keepAlive: true,
8434
+ keepAliveInitialDelayMillis: 5e3,
8435
+ // Start keepalive after 5s
8436
+ allowExitOnIdle: false
8437
+ // Don't exit while migrations running
8438
+ };
8439
+ async function initDatabase(configPath, forMigration = false) {
8412
8440
  const config = await loadConfig(configPath);
8441
+ if (forMigration && config.config) {
8442
+ config.pool = {
8443
+ ...MIGRATION_POOL_SETTINGS,
8444
+ ...config.pool
8445
+ // User config can still override
8446
+ };
8447
+ }
8413
8448
  const db = await createDatabaseService(config);
8414
8449
  let adapter = db.adapter;
8415
8450
  while (adapter.baseAdapter) {
@@ -8437,7 +8472,7 @@ program.name("plyaz-db").description("Database management CLI for @plyaz/db").ve
8437
8472
  var migrateCommand = program.command("migrate").description("Database migration commands");
8438
8473
  migrateCommand.command("up").description("Run pending migrations").option("-t, --target <version>", "Target migration version").action(async (options) => {
8439
8474
  try {
8440
- const { adapter, config } = await initDatabase();
8475
+ const { adapter, config } = await initDatabase(void 0, true);
8441
8476
  const migrationManager = new MigrationManager({
8442
8477
  adapter,
8443
8478
  migrationsPath: config.migrationsPath ?? "./migrations",
@@ -8459,7 +8494,7 @@ migrateCommand.command("up").description("Run pending migrations").option("-t, -
8459
8494
  });
8460
8495
  migrateCommand.command("down").description("Rollback migrations").option("-s, --steps <number>", "Number of migrations to rollback", "1").action(async (options) => {
8461
8496
  try {
8462
- const { adapter, config } = await initDatabase();
8497
+ const { adapter, config } = await initDatabase(void 0, true);
8463
8498
  const migrationManager = new MigrationManager({
8464
8499
  adapter,
8465
8500
  migrationsPath: config.migrationsPath ?? "./migrations",
@@ -8522,7 +8557,7 @@ migrateCommand.command("reset").description("Rollback all migrations").option("-
8522
8557
  process.exit(1);
8523
8558
  }
8524
8559
  try {
8525
- const { adapter, config } = await initDatabase();
8560
+ const { adapter, config } = await initDatabase(void 0, true);
8526
8561
  const migrationManager = new MigrationManager({
8527
8562
  adapter,
8528
8563
  migrationsPath: config.migrationsPath ?? "./migrations",
@@ -8565,7 +8600,7 @@ migrateCommand.command("generate-down").description("Generate DOWN sections for
8565
8600
  var seedCommand = program.command("seed").description("Database seeding commands");
8566
8601
  seedCommand.command("run").description("Run seeds").option("-n, --name <name>", "Specific seed to run").option("--skip-existing", "Skip seeds that have already been run").action(async (options) => {
8567
8602
  try {
8568
- const { adapter, config } = await initDatabase();
8603
+ const { adapter, config } = await initDatabase(void 0, true);
8569
8604
  const seedManager = new SeedManager({
8570
8605
  adapter,
8571
8606
  seedsPath: config.seedsPath ?? "./seeds",
@@ -8625,7 +8660,7 @@ seedCommand.command("reset").description("Reset seeds (run cleanup functions)").
8625
8660
  process.exit(1);
8626
8661
  }
8627
8662
  try {
8628
- const { adapter, config } = await initDatabase();
8663
+ const { adapter, config } = await initDatabase(void 0, true);
8629
8664
  const seedManager = new SeedManager({
8630
8665
  adapter,
8631
8666
  seedsPath: config.seedsPath ?? "./seeds",
@@ -9196,6 +9231,417 @@ program.command("reset").description(
9196
9231
  process.exit(1);
9197
9232
  }
9198
9233
  });
9234
+ function createReadlineInterface() {
9235
+ return readline.createInterface({
9236
+ input: process.stdin,
9237
+ output: process.stdout
9238
+ });
9239
+ }
9240
+ __name(createReadlineInterface, "createReadlineInterface");
9241
+ async function promptInput(message, defaultValue) {
9242
+ const rl = createReadlineInterface();
9243
+ const prompt = defaultValue ? `${message} (${defaultValue}): ` : `${message}: `;
9244
+ return new Promise((resolve4) => {
9245
+ rl.question(prompt, (answer) => {
9246
+ rl.close();
9247
+ const trimmed = answer.trim();
9248
+ resolve4(trimmed !== "" ? trimmed : defaultValue ?? "");
9249
+ });
9250
+ });
9251
+ }
9252
+ __name(promptInput, "promptInput");
9253
+ async function promptSelect(message, options) {
9254
+ const rl = createReadlineInterface();
9255
+ console.log(message);
9256
+ options.forEach((opt, i) => console.log(` ${i + 1}) ${opt}`));
9257
+ return new Promise((resolve4) => {
9258
+ rl.question("Select (number): ", (answer) => {
9259
+ rl.close();
9260
+ const index = Number.parseInt(answer, DECIMAL_RADIX) - 1;
9261
+ resolve4(options[index] ?? options[0]);
9262
+ });
9263
+ });
9264
+ }
9265
+ __name(promptSelect, "promptSelect");
9266
+ async function promptConfirm(message) {
9267
+ const rl = createReadlineInterface();
9268
+ return new Promise((resolve4) => {
9269
+ rl.question(`${message} (y/N): `, (answer) => {
9270
+ rl.close();
9271
+ resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
9272
+ });
9273
+ });
9274
+ }
9275
+ __name(promptConfirm, "promptConfirm");
9276
+ async function runInteractiveMigration(config) {
9277
+ console.log();
9278
+ console.log("📦 Create Migration - Interactive Mode");
9279
+ console.log("─".repeat(SEPARATOR_WIDTH));
9280
+ console.log();
9281
+ const name = await promptInput("Migration name (e.g., add_users_table)");
9282
+ if (!name) {
9283
+ console.error("❌ Migration name is required");
9284
+ process.exit(1);
9285
+ }
9286
+ const snakeName = toSnakeCase(name);
9287
+ const tableName = await promptInput("Table name", snakeName);
9288
+ const schema = await promptInput("Database schema", "public");
9289
+ const typeChoice = await promptSelect("File type:", [
9290
+ "SQL (.sql)",
9291
+ "TypeScript (.ts)"
9292
+ ]);
9293
+ const type = typeChoice.includes("TypeScript") ? "ts" : "sql";
9294
+ const gitUser = getGitUsername();
9295
+ const includeAuthor = gitUser ? await promptConfirm(`Include author (${gitUser})?`) : false;
9296
+ const author = includeAuthor ? gitUser : "";
9297
+ console.log();
9298
+ console.log("─".repeat(SEPARATOR_WIDTH));
9299
+ console.log(`Migration: ${name}`);
9300
+ console.log(`Table: ${schema}.${tableName}`);
9301
+ console.log(`Type: ${type.toUpperCase()}`);
9302
+ if (author) console.log(`Author: ${author}`);
9303
+ console.log("─".repeat(SEPARATOR_WIDTH));
9304
+ const confirm = await promptConfirm("Create migration?");
9305
+ if (!confirm) {
9306
+ console.log("Cancelled.");
9307
+ process.exit(0);
9308
+ }
9309
+ const migrationsPath = config.migrationsPath ?? "./migrations";
9310
+ if (!fs4.existsSync(migrationsPath)) {
9311
+ fs4.mkdirSync(migrationsPath, { recursive: true });
9312
+ }
9313
+ const version = getNextMigrationVersion(migrationsPath);
9314
+ const filename = `${version}_${snakeName}.${type}`;
9315
+ const filepath = path4.join(migrationsPath, filename);
9316
+ const content = getMigrationTemplate({
9317
+ name,
9318
+ version,
9319
+ tableName,
9320
+ schema,
9321
+ author,
9322
+ type
9323
+ });
9324
+ fs4.writeFileSync(filepath, content, "utf-8");
9325
+ console.log(`✅ Created: ${filepath}`);
9326
+ }
9327
+ __name(runInteractiveMigration, "runInteractiveMigration");
9328
+ async function runInteractiveSeed(config) {
9329
+ console.log();
9330
+ console.log("🌱 Create Seed - Interactive Mode");
9331
+ console.log("─".repeat(SEPARATOR_WIDTH));
9332
+ console.log();
9333
+ const name = await promptInput("Seed name (e.g., admin_users)");
9334
+ if (!name) {
9335
+ console.error("❌ Seed name is required");
9336
+ process.exit(1);
9337
+ }
9338
+ const snakeName = toSnakeCase(name);
9339
+ const tableName = await promptInput("Table name", snakeName);
9340
+ const schema = await promptInput("Database schema", "public");
9341
+ const typeChoice = await promptSelect("File type:", [
9342
+ "TypeScript (.ts)",
9343
+ "SQL (.sql)",
9344
+ "CSV (.csv)"
9345
+ ]);
9346
+ const typeMap = {
9347
+ "TypeScript (.ts)": "ts",
9348
+ "SQL (.sql)": "sql",
9349
+ "CSV (.csv)": "csv"
9350
+ };
9351
+ const type = typeMap[typeChoice] ?? "ts";
9352
+ let author = "";
9353
+ if (type !== "csv") {
9354
+ const gitUser = getGitUsername();
9355
+ const includeAuthor = gitUser ? await promptConfirm(`Include author (${gitUser})?`) : false;
9356
+ author = includeAuthor ? gitUser : "";
9357
+ }
9358
+ console.log();
9359
+ console.log("─".repeat(SEPARATOR_WIDTH));
9360
+ console.log(`Seed: ${name}`);
9361
+ console.log(`Table: ${schema}.${tableName}`);
9362
+ console.log(`Type: ${type.toUpperCase()}`);
9363
+ if (author) console.log(`Author: ${author}`);
9364
+ console.log("─".repeat(SEPARATOR_WIDTH));
9365
+ const confirm = await promptConfirm("Create seed?");
9366
+ if (!confirm) {
9367
+ console.log("Cancelled.");
9368
+ process.exit(0);
9369
+ }
9370
+ const seedsPath = config.seedsPath ?? "./seeds";
9371
+ if (!fs4.existsSync(seedsPath)) {
9372
+ fs4.mkdirSync(seedsPath, { recursive: true });
9373
+ }
9374
+ const order = getNextSeedOrder(seedsPath);
9375
+ const filename = `${order}_${snakeName}.${type}`;
9376
+ const filepath = path4.join(seedsPath, filename);
9377
+ let content;
9378
+ if (type === "csv") {
9379
+ content = getCsvSeedTemplate({ name, tableName, schema });
9380
+ } else {
9381
+ content = getSeedTemplate({
9382
+ name,
9383
+ order,
9384
+ tableName,
9385
+ schema,
9386
+ author,
9387
+ type
9388
+ });
9389
+ }
9390
+ fs4.writeFileSync(filepath, content, "utf-8");
9391
+ console.log(`✅ Created: ${filepath}`);
9392
+ }
9393
+ __name(runInteractiveSeed, "runInteractiveSeed");
9394
+ function getNextMigrationVersion(migrationsPath) {
9395
+ if (!fs4.existsSync(migrationsPath)) {
9396
+ return "001";
9397
+ }
9398
+ const files = fs4.readdirSync(migrationsPath);
9399
+ const versions = files.map((f) => {
9400
+ const match = f.match(/^(\d+)_/);
9401
+ return match ? Number.parseInt(match[1], DECIMAL_RADIX) : 0;
9402
+ }).filter((v) => v > 0);
9403
+ const maxVersion = versions.length > 0 ? Math.max(...versions) : 0;
9404
+ return String(maxVersion + 1).padStart(VERSION_PAD_LENGTH, "0");
9405
+ }
9406
+ __name(getNextMigrationVersion, "getNextMigrationVersion");
9407
+ function getNextSeedOrder(seedsPath) {
9408
+ if (!fs4.existsSync(seedsPath)) {
9409
+ return "001";
9410
+ }
9411
+ const files = fs4.readdirSync(seedsPath);
9412
+ const orders = files.map((f) => {
9413
+ const match = f.match(/^(\d+)_/);
9414
+ return match ? Number.parseInt(match[1], DECIMAL_RADIX) : 0;
9415
+ }).filter((v) => v > 0);
9416
+ const maxOrder = orders.length > 0 ? Math.max(...orders) : 0;
9417
+ return String(maxOrder + 1).padStart(VERSION_PAD_LENGTH, "0");
9418
+ }
9419
+ __name(getNextSeedOrder, "getNextSeedOrder");
9420
+ function toSnakeCase(name) {
9421
+ return name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_");
9422
+ }
9423
+ __name(toSnakeCase, "toSnakeCase");
9424
+ function toPascalCase(name) {
9425
+ return name.split(/[_\s-]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
9426
+ }
9427
+ __name(toPascalCase, "toPascalCase");
9428
+ function getTemplateDir() {
9429
+ const currentFile = fileURLToPath(import.meta.url);
9430
+ const currentDir = path4.dirname(currentFile);
9431
+ const packageRoot = path4.resolve(currentDir, "..", "..");
9432
+ return path4.join(packageRoot, "template");
9433
+ }
9434
+ __name(getTemplateDir, "getTemplateDir");
9435
+ function loadTemplate(templatePath, vars) {
9436
+ const fullPath = path4.join(getTemplateDir(), templatePath);
9437
+ if (!fs4.existsSync(fullPath)) {
9438
+ throw new Error(`Template not found: ${fullPath}`);
9439
+ }
9440
+ let content = fs4.readFileSync(fullPath, "utf-8");
9441
+ for (const [key, value] of Object.entries(vars)) {
9442
+ const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
9443
+ content = content.replace(regex, value ?? "");
9444
+ }
9445
+ return content;
9446
+ }
9447
+ __name(loadTemplate, "loadTemplate");
9448
+ function getGitUsername() {
9449
+ try {
9450
+ return execSync("git config user.name", { encoding: "utf-8" }).trim();
9451
+ } catch {
9452
+ return "";
9453
+ }
9454
+ }
9455
+ __name(getGitUsername, "getGitUsername");
9456
+ function getMigrationTemplate(options) {
9457
+ const { name, version, tableName, schema, author, type } = options;
9458
+ const description = toPascalCase(name);
9459
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9460
+ const vars = {
9461
+ VERSION: version,
9462
+ DESCRIPTION: description,
9463
+ TABLE_NAME: tableName,
9464
+ SCHEMA: schema,
9465
+ CREATED_DATE: date,
9466
+ AUTHOR: author
9467
+ };
9468
+ const templateFile = type === "ts" ? "migrations/migration.ts.template" : "migrations/migration.sql.template";
9469
+ return loadTemplate(templateFile, vars);
9470
+ }
9471
+ __name(getMigrationTemplate, "getMigrationTemplate");
9472
+ function getSeedTemplate(options) {
9473
+ const { name, order, tableName, schema, author, type } = options;
9474
+ const description = toPascalCase(name);
9475
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9476
+ const vars = {
9477
+ ORDER: order,
9478
+ DESCRIPTION: description,
9479
+ TABLE_NAME: tableName,
9480
+ SCHEMA: schema,
9481
+ CREATED_DATE: date,
9482
+ AUTHOR: author,
9483
+ UUID_1: randomUUID(),
9484
+ UUID_2: randomUUID(),
9485
+ UUID_3: randomUUID()
9486
+ };
9487
+ const templateFile = type === "ts" ? "seeds/seed.ts.template" : "seeds/seed.sql.template";
9488
+ return loadTemplate(templateFile, vars);
9489
+ }
9490
+ __name(getSeedTemplate, "getSeedTemplate");
9491
+ function getCsvSeedTemplate(options) {
9492
+ const { name, tableName, schema } = options;
9493
+ const description = toPascalCase(name);
9494
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9495
+ const vars = {
9496
+ DESCRIPTION: description,
9497
+ TABLE_NAME: tableName,
9498
+ SCHEMA: schema,
9499
+ CREATED_DATE: date,
9500
+ AUTHOR: "",
9501
+ UUID_1: randomUUID(),
9502
+ UUID_2: randomUUID(),
9503
+ UUID_3: randomUUID()
9504
+ };
9505
+ try {
9506
+ return loadTemplate("seeds/seed.csv.template", vars);
9507
+ } catch {
9508
+ return `id,name,created_at
9509
+ ${randomUUID()},Example 1,${(/* @__PURE__ */ new Date()).toISOString()}
9510
+ ${randomUUID()},Example 2,${(/* @__PURE__ */ new Date()).toISOString()}
9511
+ `;
9512
+ }
9513
+ }
9514
+ __name(getCsvSeedTemplate, "getCsvSeedTemplate");
9515
+ var createCommand = program.command("create").description("Create new migration or seed files");
9516
+ 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 or ts (default: sql)", "sql").option(
9517
+ "-T, --table <table>",
9518
+ "Table name (default: derived from migration name)"
9519
+ ).option(
9520
+ "-s, --schema <schema>",
9521
+ "Database schema (default: public)",
9522
+ "public"
9523
+ ).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").action(
9524
+ async (name, options) => {
9525
+ try {
9526
+ const config = await loadConfig();
9527
+ if (!name || options.interactive) {
9528
+ await runInteractiveMigration(config);
9529
+ process.exit(0);
9530
+ }
9531
+ const migrationsPath = config.migrationsPath ?? "./migrations";
9532
+ if (!fs4.existsSync(migrationsPath)) {
9533
+ fs4.mkdirSync(migrationsPath, { recursive: true });
9534
+ console.log(`📁 Created directory: ${migrationsPath}`);
9535
+ }
9536
+ const version = getNextMigrationVersion(migrationsPath);
9537
+ const snakeName = toSnakeCase(name);
9538
+ const tableName = options.table ?? snakeName;
9539
+ const schema = options.schema;
9540
+ const author = options.author ? getGitUsername() : "";
9541
+ const extension = options.type === "ts" ? "ts" : "sql";
9542
+ const filename = `${version}_${snakeName}.${extension}`;
9543
+ const filepath = path4.join(migrationsPath, filename);
9544
+ if (fs4.existsSync(filepath)) {
9545
+ console.error(`❌ File already exists: ${filepath}`);
9546
+ process.exit(1);
9547
+ }
9548
+ const content = getMigrationTemplate({
9549
+ name,
9550
+ version,
9551
+ tableName,
9552
+ schema,
9553
+ author,
9554
+ type: options.type === "ts" ? "ts" : "sql"
9555
+ });
9556
+ fs4.writeFileSync(filepath, content, "utf-8");
9557
+ console.log(`✅ Created migration: ${filename}`);
9558
+ console.log(` Path: ${filepath}`);
9559
+ console.log(` Type: ${options.type.toUpperCase()}`);
9560
+ console.log(` Table: ${schema}.${tableName}`);
9561
+ if (author) {
9562
+ console.log(` Author: ${author}`);
9563
+ }
9564
+ console.log("");
9565
+ console.log("📝 Next steps:");
9566
+ console.log(` 1. Edit the migration file to add your schema changes`);
9567
+ console.log(` 2. Run 'plyaz-db migrate up' to apply the migration`);
9568
+ console.log(
9569
+ ` 3. Test rollback with 'plyaz-db migrate down' before deploying`
9570
+ );
9571
+ process.exit(0);
9572
+ } catch (error) {
9573
+ console.error("❌ Error:", error.message);
9574
+ process.exit(1);
9575
+ }
9576
+ }
9577
+ );
9578
+ 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, or csv (default: ts)", "ts").option("-T, --table <table>", "Table name (default: derived from seed name)").option(
9579
+ "-s, --schema <schema>",
9580
+ "Database schema (default: public)",
9581
+ "public"
9582
+ ).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").action(
9583
+ async (name, options) => {
9584
+ try {
9585
+ const config = await loadConfig();
9586
+ if (!name || options.interactive) {
9587
+ await runInteractiveSeed(config);
9588
+ process.exit(0);
9589
+ }
9590
+ const seedsPath = config.seedsPath ?? "./seeds";
9591
+ if (!fs4.existsSync(seedsPath)) {
9592
+ fs4.mkdirSync(seedsPath, { recursive: true });
9593
+ console.log(`📁 Created directory: ${seedsPath}`);
9594
+ }
9595
+ const order = getNextSeedOrder(seedsPath);
9596
+ const snakeName = toSnakeCase(name);
9597
+ const tableName = options.table ?? snakeName;
9598
+ const schema = options.schema;
9599
+ const author = options.author ? getGitUsername() : "";
9600
+ const typeMap = {
9601
+ ts: "ts",
9602
+ sql: "sql",
9603
+ csv: "csv"
9604
+ };
9605
+ const fileType = typeMap[options.type] ?? "ts";
9606
+ const filename = `${order}_${snakeName}.${fileType}`;
9607
+ const filepath = path4.join(seedsPath, filename);
9608
+ if (fs4.existsSync(filepath)) {
9609
+ console.error(`❌ File already exists: ${filepath}`);
9610
+ process.exit(1);
9611
+ }
9612
+ let content;
9613
+ if (fileType === "csv") {
9614
+ content = getCsvSeedTemplate({ name, tableName, schema });
9615
+ } else {
9616
+ content = getSeedTemplate({
9617
+ name,
9618
+ order,
9619
+ tableName,
9620
+ schema,
9621
+ author,
9622
+ type: fileType
9623
+ });
9624
+ }
9625
+ fs4.writeFileSync(filepath, content, "utf-8");
9626
+ console.log(`✅ Created seed: ${filename}`);
9627
+ console.log(` Path: ${filepath}`);
9628
+ console.log(` Type: ${fileType.toUpperCase()}`);
9629
+ console.log(` Table: ${schema}.${tableName}`);
9630
+ if (author && fileType !== "csv") {
9631
+ console.log(` Author: ${author}`);
9632
+ }
9633
+ console.log("");
9634
+ console.log("📝 Next steps:");
9635
+ console.log(` 1. Edit the seed file to add your data`);
9636
+ console.log(` 2. Run 'plyaz-db seed run' to execute seeds`);
9637
+ console.log(` 3. Use 'plyaz-db seed status' to check seed status`);
9638
+ process.exit(0);
9639
+ } catch (error) {
9640
+ console.error("❌ Error:", error.message);
9641
+ process.exit(1);
9642
+ }
9643
+ }
9644
+ );
9199
9645
  program.parse();
9200
9646
  //# sourceMappingURL=index.js.map
9201
9647
  //# sourceMappingURL=index.js.map