@plyaz/db 0.3.1 → 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 +178 -24
- package/dist/cli/index.js.map +1 -1
- package/package.json +3 -2
- package/template/migrations/migration.sql.template +29 -0
- package/template/migrations/migration.ts.template +54 -0
- package/template/seeds/seed.csv.template +22 -0
- package/template/seeds/seed.sql.template +19 -0
- package/template/seeds/seed.ts.template +63 -0
package/dist/cli/index.js
CHANGED
|
@@ -8272,6 +8272,74 @@ var JSON_INDENT_SPACES = 2;
|
|
|
8272
8272
|
var VERSION_PAD_LENGTH = 3;
|
|
8273
8273
|
var DECIMAL_RADIX = 10;
|
|
8274
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");
|
|
8275
8343
|
async function getUserSchemas(adapter) {
|
|
8276
8344
|
const result = await adapter.query?.(`
|
|
8277
8345
|
SELECT schema_name FROM information_schema.schemata
|
|
@@ -8494,16 +8562,15 @@ migrateCommand.command("up").description("Run pending migrations").option("-t, -
|
|
|
8494
8562
|
});
|
|
8495
8563
|
migrateCommand.command("down").description("Rollback migrations").option("-s, --steps <number>", "Number of migrations to rollback", "1").action(async (options) => {
|
|
8496
8564
|
try {
|
|
8565
|
+
const steps = validateSteps(options.steps);
|
|
8497
8566
|
const { adapter, config } = await initDatabase(void 0, true);
|
|
8498
8567
|
const migrationManager = new MigrationManager({
|
|
8499
8568
|
adapter,
|
|
8500
8569
|
migrationsPath: config.migrationsPath ?? "./migrations",
|
|
8501
8570
|
tableName: config.migrationsTable ?? "schema_migrations"
|
|
8502
8571
|
});
|
|
8503
|
-
console.log(`🔄 Rolling back ${
|
|
8504
|
-
const result = await migrationManager.down(
|
|
8505
|
-
Number.parseInt(options.steps, 10)
|
|
8506
|
-
);
|
|
8572
|
+
console.log(`🔄 Rolling back ${steps} migration(s)...`);
|
|
8573
|
+
const result = await migrationManager.down(steps);
|
|
8507
8574
|
if (result.success) {
|
|
8508
8575
|
console.log(`✅ Rolled back ${result.value} migration(s)`);
|
|
8509
8576
|
process.exit(0);
|
|
@@ -9283,9 +9350,11 @@ async function runInteractiveMigration(config) {
|
|
|
9283
9350
|
console.error("❌ Migration name is required");
|
|
9284
9351
|
process.exit(1);
|
|
9285
9352
|
}
|
|
9353
|
+
validateName(name, "migration");
|
|
9286
9354
|
const snakeName = toSnakeCase(name);
|
|
9287
9355
|
const tableName = await promptInput("Table name", snakeName);
|
|
9288
9356
|
const schema = await promptInput("Database schema", "public");
|
|
9357
|
+
validateSchemaName(schema);
|
|
9289
9358
|
const typeChoice = await promptSelect("File type:", [
|
|
9290
9359
|
"SQL (.sql)",
|
|
9291
9360
|
"TypeScript (.ts)"
|
|
@@ -9309,10 +9378,20 @@ async function runInteractiveMigration(config) {
|
|
|
9309
9378
|
const migrationsPath = config.migrationsPath ?? "./migrations";
|
|
9310
9379
|
if (!fs4.existsSync(migrationsPath)) {
|
|
9311
9380
|
fs4.mkdirSync(migrationsPath, { recursive: true });
|
|
9381
|
+
console.log(`📁 Created directory: ${migrationsPath}`);
|
|
9312
9382
|
}
|
|
9313
9383
|
const version = getNextMigrationVersion(migrationsPath);
|
|
9314
9384
|
const filename = `${version}_${snakeName}.${type}`;
|
|
9315
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
|
+
}
|
|
9316
9395
|
const content = getMigrationTemplate({
|
|
9317
9396
|
name,
|
|
9318
9397
|
version,
|
|
@@ -9322,7 +9401,21 @@ async function runInteractiveMigration(config) {
|
|
|
9322
9401
|
type
|
|
9323
9402
|
});
|
|
9324
9403
|
fs4.writeFileSync(filepath, content, "utf-8");
|
|
9325
|
-
console.log(
|
|
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
|
+
);
|
|
9326
9419
|
}
|
|
9327
9420
|
__name(runInteractiveMigration, "runInteractiveMigration");
|
|
9328
9421
|
async function runInteractiveSeed(config) {
|
|
@@ -9335,9 +9428,11 @@ async function runInteractiveSeed(config) {
|
|
|
9335
9428
|
console.error("❌ Seed name is required");
|
|
9336
9429
|
process.exit(1);
|
|
9337
9430
|
}
|
|
9431
|
+
validateName(name, "seed");
|
|
9338
9432
|
const snakeName = toSnakeCase(name);
|
|
9339
9433
|
const tableName = await promptInput("Table name", snakeName);
|
|
9340
9434
|
const schema = await promptInput("Database schema", "public");
|
|
9435
|
+
validateSchemaName(schema);
|
|
9341
9436
|
const typeChoice = await promptSelect("File type:", [
|
|
9342
9437
|
"TypeScript (.ts)",
|
|
9343
9438
|
"SQL (.sql)",
|
|
@@ -9370,10 +9465,20 @@ async function runInteractiveSeed(config) {
|
|
|
9370
9465
|
const seedsPath = config.seedsPath ?? "./seeds";
|
|
9371
9466
|
if (!fs4.existsSync(seedsPath)) {
|
|
9372
9467
|
fs4.mkdirSync(seedsPath, { recursive: true });
|
|
9468
|
+
console.log(`📁 Created directory: ${seedsPath}`);
|
|
9373
9469
|
}
|
|
9374
9470
|
const order = getNextSeedOrder(seedsPath);
|
|
9375
9471
|
const filename = `${order}_${snakeName}.${type}`;
|
|
9376
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
|
+
}
|
|
9377
9482
|
let content;
|
|
9378
9483
|
if (type === "csv") {
|
|
9379
9484
|
content = getCsvSeedTemplate({ name, tableName, schema });
|
|
@@ -9388,7 +9493,19 @@ async function runInteractiveSeed(config) {
|
|
|
9388
9493
|
});
|
|
9389
9494
|
}
|
|
9390
9495
|
fs4.writeFileSync(filepath, content, "utf-8");
|
|
9391
|
-
console.log(
|
|
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`);
|
|
9392
9509
|
}
|
|
9393
9510
|
__name(runInteractiveSeed, "runInteractiveSeed");
|
|
9394
9511
|
function getNextMigrationVersion(migrationsPath) {
|
|
@@ -9513,14 +9630,17 @@ ${randomUUID()},Example 2,${(/* @__PURE__ */ new Date()).toISOString()}
|
|
|
9513
9630
|
}
|
|
9514
9631
|
__name(getCsvSeedTemplate, "getCsvSeedTemplate");
|
|
9515
9632
|
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
|
|
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(
|
|
9517
9634
|
"-T, --table <table>",
|
|
9518
9635
|
"Table name (default: derived from migration name)"
|
|
9519
9636
|
).option(
|
|
9520
9637
|
"-s, --schema <schema>",
|
|
9521
9638
|
"Database schema (default: public)",
|
|
9522
9639
|
"public"
|
|
9523
|
-
).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").
|
|
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(
|
|
9524
9644
|
async (name, options) => {
|
|
9525
9645
|
try {
|
|
9526
9646
|
const config = await loadConfig();
|
|
@@ -9528,8 +9648,11 @@ createCommand.command("migration").description("Create a new migration file (int
|
|
|
9528
9648
|
await runInteractiveMigration(config);
|
|
9529
9649
|
process.exit(0);
|
|
9530
9650
|
}
|
|
9651
|
+
validateName(name, "migration");
|
|
9652
|
+
validateSchemaName(options.schema);
|
|
9653
|
+
const fileType = validateMigrationType(options.type);
|
|
9531
9654
|
const migrationsPath = config.migrationsPath ?? "./migrations";
|
|
9532
|
-
if (!fs4.existsSync(migrationsPath)) {
|
|
9655
|
+
if (!options.dryRun && !fs4.existsSync(migrationsPath)) {
|
|
9533
9656
|
fs4.mkdirSync(migrationsPath, { recursive: true });
|
|
9534
9657
|
console.log(`📁 Created directory: ${migrationsPath}`);
|
|
9535
9658
|
}
|
|
@@ -9538,11 +9661,11 @@ createCommand.command("migration").description("Create a new migration file (int
|
|
|
9538
9661
|
const tableName = options.table ?? snakeName;
|
|
9539
9662
|
const schema = options.schema;
|
|
9540
9663
|
const author = options.author ? getGitUsername() : "";
|
|
9541
|
-
const
|
|
9542
|
-
const filename = `${version}_${snakeName}.${extension}`;
|
|
9664
|
+
const filename = `${version}_${snakeName}.${fileType}`;
|
|
9543
9665
|
const filepath = path4.join(migrationsPath, filename);
|
|
9544
|
-
if (fs4.existsSync(filepath)) {
|
|
9666
|
+
if (fs4.existsSync(filepath) && !options.force) {
|
|
9545
9667
|
console.error(`❌ File already exists: ${filepath}`);
|
|
9668
|
+
console.error(" Use --force to overwrite");
|
|
9546
9669
|
process.exit(1);
|
|
9547
9670
|
}
|
|
9548
9671
|
const content = getMigrationTemplate({
|
|
@@ -9551,12 +9674,27 @@ createCommand.command("migration").description("Create a new migration file (int
|
|
|
9551
9674
|
tableName,
|
|
9552
9675
|
schema,
|
|
9553
9676
|
author,
|
|
9554
|
-
type:
|
|
9677
|
+
type: fileType
|
|
9555
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
|
+
}
|
|
9556
9694
|
fs4.writeFileSync(filepath, content, "utf-8");
|
|
9557
9695
|
console.log(`✅ Created migration: ${filename}`);
|
|
9558
9696
|
console.log(` Path: ${filepath}`);
|
|
9559
|
-
console.log(` Type: ${
|
|
9697
|
+
console.log(` Type: ${fileType.toUpperCase()}`);
|
|
9560
9698
|
console.log(` Table: ${schema}.${tableName}`);
|
|
9561
9699
|
if (author) {
|
|
9562
9700
|
console.log(` Author: ${author}`);
|
|
@@ -9575,11 +9713,14 @@ createCommand.command("migration").description("Create a new migration file (int
|
|
|
9575
9713
|
}
|
|
9576
9714
|
}
|
|
9577
9715
|
);
|
|
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,
|
|
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(
|
|
9579
9717
|
"-s, --schema <schema>",
|
|
9580
9718
|
"Database schema (default: public)",
|
|
9581
9719
|
"public"
|
|
9582
|
-
).option("--no-author", "Skip adding git username as author").option("-i, --interactive", "Force interactive mode").
|
|
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(
|
|
9583
9724
|
async (name, options) => {
|
|
9584
9725
|
try {
|
|
9585
9726
|
const config = await loadConfig();
|
|
@@ -9587,8 +9728,11 @@ createCommand.command("seed").description("Create a new seed file (interactive i
|
|
|
9587
9728
|
await runInteractiveSeed(config);
|
|
9588
9729
|
process.exit(0);
|
|
9589
9730
|
}
|
|
9731
|
+
validateName(name, "seed");
|
|
9732
|
+
validateSchemaName(options.schema);
|
|
9733
|
+
const fileType = validateSeedType(options.type);
|
|
9590
9734
|
const seedsPath = config.seedsPath ?? "./seeds";
|
|
9591
|
-
if (!fs4.existsSync(seedsPath)) {
|
|
9735
|
+
if (!options.dryRun && !fs4.existsSync(seedsPath)) {
|
|
9592
9736
|
fs4.mkdirSync(seedsPath, { recursive: true });
|
|
9593
9737
|
console.log(`📁 Created directory: ${seedsPath}`);
|
|
9594
9738
|
}
|
|
@@ -9597,16 +9741,11 @@ createCommand.command("seed").description("Create a new seed file (interactive i
|
|
|
9597
9741
|
const tableName = options.table ?? snakeName;
|
|
9598
9742
|
const schema = options.schema;
|
|
9599
9743
|
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
9744
|
const filename = `${order}_${snakeName}.${fileType}`;
|
|
9607
9745
|
const filepath = path4.join(seedsPath, filename);
|
|
9608
|
-
if (fs4.existsSync(filepath)) {
|
|
9746
|
+
if (fs4.existsSync(filepath) && !options.force) {
|
|
9609
9747
|
console.error(`❌ File already exists: ${filepath}`);
|
|
9748
|
+
console.error(" Use --force to overwrite");
|
|
9610
9749
|
process.exit(1);
|
|
9611
9750
|
}
|
|
9612
9751
|
let content;
|
|
@@ -9622,6 +9761,21 @@ createCommand.command("seed").description("Create a new seed file (interactive i
|
|
|
9622
9761
|
type: fileType
|
|
9623
9762
|
});
|
|
9624
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
|
+
}
|
|
9625
9779
|
fs4.writeFileSync(filepath, content, "utf-8");
|
|
9626
9780
|
console.log(`✅ Created seed: ${filename}`);
|
|
9627
9781
|
console.log(` Path: ${filepath}`);
|