@noego/proper 0.0.2 → 0.0.4

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/bin/cli.mjs CHANGED
@@ -1,8 +1,24 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
2
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
6
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
4
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
8
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
6
22
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
7
23
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
8
24
  }) : x)(function(x) {
@@ -559,6 +575,9 @@ var init_MigrationDialectParser = __esm({
559
575
  });
560
576
 
561
577
  // framework/SQLRunner.ts
578
+ function isPromiseLike(value) {
579
+ return !!value && typeof value.then === "function";
580
+ }
562
581
  var BaseSQLRunner, SQLRunner, SQLiteRunner;
563
582
  var init_SQLRunner = __esm({
564
583
  "framework/SQLRunner.ts"() {
@@ -615,14 +634,85 @@ var init_SQLRunner = __esm({
615
634
  super();
616
635
  this.connection = connection;
617
636
  }
637
+ prepareStatement(sql) {
638
+ return __async(this, null, function* () {
639
+ if (typeof this.connection.prepare !== "function") {
640
+ throw new Error("SQLite connection does not support prepare()");
641
+ }
642
+ const stmt = this.connection.prepare(sql);
643
+ return isPromiseLike(stmt) ? yield stmt : stmt;
644
+ });
645
+ }
646
+ finalizeStatement(stmt) {
647
+ return __async(this, null, function* () {
648
+ if (!stmt || typeof stmt.finalize !== "function") return;
649
+ const result = stmt.finalize();
650
+ if (isPromiseLike(result)) {
651
+ yield result;
652
+ }
653
+ });
654
+ }
655
+ statementAll(stmt, params) {
656
+ return __async(this, null, function* () {
657
+ if (typeof stmt.all !== "function") {
658
+ throw new Error("SQLite statement does not support all()");
659
+ }
660
+ if (stmt.all.length >= 2) {
661
+ return yield new Promise((resolve, reject) => {
662
+ const callback = (err, rows) => {
663
+ if (err) return reject(err);
664
+ resolve(rows || []);
665
+ };
666
+ try {
667
+ if (params.length > 0) {
668
+ stmt.all(params, callback);
669
+ } else {
670
+ stmt.all(callback);
671
+ }
672
+ } catch (error) {
673
+ reject(error);
674
+ }
675
+ });
676
+ }
677
+ const result = stmt.all(...params);
678
+ return isPromiseLike(result) ? yield result : result;
679
+ });
680
+ }
681
+ statementRun(stmt, params) {
682
+ return __async(this, null, function* () {
683
+ if (typeof stmt.run !== "function") {
684
+ throw new Error("SQLite statement does not support run()");
685
+ }
686
+ if (stmt.run.length >= 2) {
687
+ return yield new Promise((resolve, reject) => {
688
+ const callback = function(err) {
689
+ var _a;
690
+ if (err) return reject(err);
691
+ resolve({ changes: (_a = this == null ? void 0 : this.changes) != null ? _a : 0, lastID: this == null ? void 0 : this.lastID });
692
+ };
693
+ try {
694
+ if (params.length > 0) {
695
+ stmt.run(params, callback);
696
+ } else {
697
+ stmt.run(callback);
698
+ }
699
+ } catch (error) {
700
+ reject(error);
701
+ }
702
+ });
703
+ }
704
+ const result = stmt.run(...params);
705
+ return isPromiseLike(result) ? yield result : result;
706
+ });
707
+ }
618
708
  _query(_0) {
619
709
  return __async(this, arguments, function* (sql, params = []) {
620
- const stmt = yield this.connection.prepare(sql);
710
+ const stmt = yield this.prepareStatement(sql);
621
711
  try {
622
- const rows = yield stmt.all(...params);
712
+ const rows = yield this.statementAll(stmt, params);
623
713
  return rows;
624
714
  } finally {
625
- yield stmt.finalize();
715
+ yield this.finalizeStatement(stmt);
626
716
  }
627
717
  });
628
718
  }
@@ -636,12 +726,12 @@ ${sql}
636
726
  throw err;
637
727
  });
638
728
  }
639
- const stmt = yield this.connection.prepare(sql);
729
+ const stmt = yield this.prepareStatement(sql);
640
730
  try {
641
- const info = yield stmt.run(...params);
731
+ const info = yield this.statementRun(stmt, params);
642
732
  return info;
643
733
  } finally {
644
- yield stmt.finalize();
734
+ yield this.finalizeStatement(stmt);
645
735
  }
646
736
  });
647
737
  }
@@ -657,13 +747,13 @@ ${sql}
657
747
  ).filter((s) => s.trim() !== "");
658
748
  const infos = yield statements.reduce((prev, statement) => __async(this, null, function* () {
659
749
  const infos2 = yield prev;
660
- const stmt = yield this.connection.prepare(`${statement};`);
750
+ const stmt = yield this.prepareStatement(`${statement};`);
661
751
  try {
662
- const info = yield stmt.run(...params);
752
+ const info = yield this.statementRun(stmt, params);
663
753
  infos2.push(info);
664
754
  return infos2;
665
755
  } finally {
666
- yield stmt.finalize();
756
+ yield this.finalizeStatement(stmt);
667
757
  }
668
758
  }), Promise.resolve([null])).then((infos2) => {
669
759
  return infos2.filter((info) => info !== null);
@@ -712,6 +802,9 @@ var init_MigrationRunner = __esm({
712
802
  init_SQLRunner();
713
803
  init_errors();
714
804
  MigrationRunnerFactory = class _MigrationRunnerFactory {
805
+ static isSQLRunner(conn) {
806
+ return !!conn && typeof conn.query === "function" && typeof conn.execute === "function" && typeof conn.end === "function";
807
+ }
715
808
  static create(configFile, conn) {
716
809
  return __async(this, null, function* () {
717
810
  const configReader = new FileMigrationConfigReader(configFile);
@@ -767,21 +860,27 @@ var init_MigrationRunner = __esm({
767
860
  create(config, conn) {
768
861
  return __async(this, null, function* () {
769
862
  let sqlrunner;
770
- switch (config.database) {
771
- case "sql":
772
- sqlrunner = new SQLRunner(conn);
773
- break;
774
- case "sqlite":
775
- sqlrunner = new SQLiteRunner(conn);
776
- break;
777
- default:
778
- throw ConfigurationError.unknownDatabaseType(config.database);
863
+ let driverConnection = conn;
864
+ if (_MigrationRunnerFactory.isSQLRunner(conn)) {
865
+ sqlrunner = conn;
866
+ driverConnection = null;
867
+ } else {
868
+ switch (config.database) {
869
+ case "sql":
870
+ sqlrunner = new SQLRunner(conn);
871
+ break;
872
+ case "sqlite":
873
+ sqlrunner = new SQLiteRunner(conn);
874
+ break;
875
+ default:
876
+ throw ConfigurationError.unknownDatabaseType(config.database);
877
+ }
779
878
  }
780
879
  const setup = new MigrationSetup(sqlrunner, config);
781
880
  const read_strategy = this.getReadStategy(config);
782
881
  const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
783
882
  yield setup.setup();
784
- return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, conn);
883
+ return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, driverConnection);
785
884
  });
786
885
  }
787
886
  createEmpty(config) {
@@ -945,6 +1044,11 @@ var init_MigrationRunner = __esm({
945
1044
  }
946
1045
  });
947
1046
  }
1047
+ query(sql, params) {
1048
+ return __async(this, null, function* () {
1049
+ return yield this.sqlrunner.query(sql, params);
1050
+ });
1051
+ }
948
1052
  init(config_file) {
949
1053
  return __async(this, null, function* () {
950
1054
  try {
@@ -1043,6 +1147,229 @@ var init_MigrationRunner = __esm({
1043
1147
  }
1044
1148
  });
1045
1149
 
1150
+ // framework/SeedRunner.ts
1151
+ import fs4 from "fs";
1152
+ import path2 from "path";
1153
+ import Ajv from "ajv";
1154
+ import addFormats from "ajv-formats";
1155
+ function resolveAlias(name, aliasMap) {
1156
+ var _a;
1157
+ if (!aliasMap) return name;
1158
+ return (_a = aliasMap[name]) != null ? _a : name;
1159
+ }
1160
+ function walkForFile(rootDir, fileName) {
1161
+ if (!fs4.existsSync(rootDir)) return null;
1162
+ const entries = fs4.readdirSync(rootDir, { withFileTypes: true });
1163
+ for (const entry of entries) {
1164
+ const full = path2.join(rootDir, entry.name);
1165
+ if (entry.isDirectory()) {
1166
+ const found = walkForFile(full, fileName);
1167
+ if (found) return found;
1168
+ } else if (entry.isFile() && entry.name === fileName) {
1169
+ return full;
1170
+ }
1171
+ }
1172
+ return null;
1173
+ }
1174
+ function resolveMigrationsDir(migrationConfig, options) {
1175
+ var _a;
1176
+ const fromOptions = options.migrationsDir;
1177
+ const fromConfig = (_a = migrationConfig.seeds) == null ? void 0 : _a.migrationsDir;
1178
+ const dir = fromOptions != null ? fromOptions : fromConfig;
1179
+ if (!dir) {
1180
+ throw new Error("Seed migrationsDir not configured. Set seeds.migrationsDir in proper.json or pass it explicitly.");
1181
+ }
1182
+ return dir;
1183
+ }
1184
+ function mergeSeedConfig(migrationConfig, options) {
1185
+ var _a, _b, _c, _d;
1186
+ const names = options.names && options.names.length ? options.names : ((_a = migrationConfig.seeds) == null ? void 0 : _a.list) && migrationConfig.seeds.list.length ? migrationConfig.seeds.list : [];
1187
+ if (!names.length) {
1188
+ throw new Error("No seed names provided and no seeds.list defined in proper config");
1189
+ }
1190
+ const migrationsDir = resolveMigrationsDir(migrationConfig, options);
1191
+ const dataDir = (_c = options.dataDir) != null ? _c : (_b = migrationConfig.seeds) == null ? void 0 : _b.dataDir;
1192
+ const validate = options.validate !== void 0 ? options.validate : true;
1193
+ const transactional = (_d = options.transactional) != null ? _d : "none";
1194
+ const finalOptions = __spreadProps(__spreadValues({}, options), {
1195
+ migrationsDir,
1196
+ dataDir,
1197
+ validate,
1198
+ transactional
1199
+ });
1200
+ return { names, finalOptions };
1201
+ }
1202
+ function resolveSqlPair(name, migrationsDir) {
1203
+ const up = walkForFile(migrationsDir, `${name}.up.sql`);
1204
+ const down = walkForFile(migrationsDir, `${name}.down.sql`);
1205
+ if (up && down) {
1206
+ return { upPath: up, downPath: down };
1207
+ }
1208
+ return null;
1209
+ }
1210
+ function resolveModule(name, migrationsDir) {
1211
+ const ts = walkForFile(migrationsDir, `${name}.ts`);
1212
+ if (ts) return { kind: "ts", modulePath: ts };
1213
+ const js = walkForFile(migrationsDir, `${name}.js`);
1214
+ if (js) return { kind: "js", modulePath: js };
1215
+ return null;
1216
+ }
1217
+ function resolveSeed(name, migrationConfig, options) {
1218
+ return __async(this, null, function* () {
1219
+ var _a, _b;
1220
+ const migrationsDir = resolveMigrationsDir(migrationConfig, options);
1221
+ const alias = resolveAlias(name, options.aliasMap);
1222
+ const sqlPair = resolveSqlPair(name, migrationsDir);
1223
+ if (sqlPair) {
1224
+ return {
1225
+ kind: "sql",
1226
+ name,
1227
+ alias,
1228
+ upPath: sqlPair.upPath,
1229
+ downPath: sqlPair.downPath,
1230
+ dataPath: null,
1231
+ schemaPath: null
1232
+ };
1233
+ }
1234
+ const module = resolveModule(name, migrationsDir);
1235
+ if (!module) {
1236
+ throw new Error(`Seed implementation not found for "${name}" under ${migrationsDir}`);
1237
+ }
1238
+ const dataDir = (_b = options.dataDir) != null ? _b : (_a = migrationConfig.seeds) == null ? void 0 : _a.dataDir;
1239
+ let dataPath = null;
1240
+ let schemaPath = null;
1241
+ if (dataDir) {
1242
+ dataPath = walkForFile(dataDir, `${alias}.json`);
1243
+ schemaPath = walkForFile(dataDir, `${alias}.schema.json`);
1244
+ }
1245
+ return {
1246
+ kind: module.kind,
1247
+ name,
1248
+ alias,
1249
+ upPath: module.modulePath,
1250
+ downPath: module.modulePath,
1251
+ dataPath,
1252
+ schemaPath
1253
+ };
1254
+ });
1255
+ }
1256
+ function loadJson(filePath) {
1257
+ return __async(this, null, function* () {
1258
+ const content = yield fs4.promises.readFile(filePath, "utf8");
1259
+ return JSON.parse(content);
1260
+ });
1261
+ }
1262
+ function createValidator() {
1263
+ const ajv = new Ajv({ allErrors: true, strict: false });
1264
+ addFormats(ajv);
1265
+ return ajv;
1266
+ }
1267
+ function validateData(schemaPath, data, validate, log) {
1268
+ return __async(this, null, function* () {
1269
+ if (!validate || !schemaPath) return;
1270
+ const content = yield fs4.promises.readFile(schemaPath, "utf8");
1271
+ const schema = JSON.parse(content);
1272
+ const ajv = createValidator();
1273
+ const validateFn = ajv.compile(schema);
1274
+ const ok = validateFn(data);
1275
+ if (!ok) {
1276
+ log == null ? void 0 : log(`Validation failed for seed data (${schemaPath})`);
1277
+ throw new Error(`Seed data validation failed: ${ajv.errorsText(validateFn.errors || [])}`);
1278
+ }
1279
+ });
1280
+ }
1281
+ function runSqlSeed(runner, resolved, direction) {
1282
+ return __async(this, null, function* () {
1283
+ const sqlPath = direction === "up" ? resolved.upPath : resolved.downPath;
1284
+ const sql = yield fs4.promises.readFile(sqlPath, "utf8");
1285
+ yield runner.query(sql);
1286
+ });
1287
+ }
1288
+ function runModuleSeed(runner, resolved, migrationConfig, options, direction) {
1289
+ return __async(this, null, function* () {
1290
+ const { log } = options;
1291
+ const module = yield import(path2.resolve(resolved.upPath));
1292
+ const handler = module[direction];
1293
+ if (typeof handler !== "function") {
1294
+ throw new Error(`Seed module "${resolved.name}" does not export ${direction}()`);
1295
+ }
1296
+ let data = null;
1297
+ if (options.preloadedData && Object.prototype.hasOwnProperty.call(options.preloadedData, resolved.name)) {
1298
+ data = options.preloadedData[resolved.name];
1299
+ } else if (resolved.dataPath) {
1300
+ data = yield loadJson(resolved.dataPath);
1301
+ }
1302
+ yield validateData(resolved.schemaPath, data, options.validate, log);
1303
+ const ctx = {
1304
+ data,
1305
+ log,
1306
+ dialect: migrationConfig.database || "sql"
1307
+ };
1308
+ yield handler(runner, ctx);
1309
+ });
1310
+ }
1311
+ function runSingleSeed(runner, migrationConfig, name, options, direction) {
1312
+ return __async(this, null, function* () {
1313
+ const resolved = yield resolveSeed(name, migrationConfig, options);
1314
+ if (resolved.kind === "sql") {
1315
+ yield runSqlSeed(runner, resolved, direction);
1316
+ } else {
1317
+ yield runModuleSeed(runner, resolved, migrationConfig, options, direction);
1318
+ }
1319
+ });
1320
+ }
1321
+ function withTransactionalMode(runner, migrationConfig, names, options, direction) {
1322
+ return __async(this, null, function* () {
1323
+ const mode = options.transactional;
1324
+ if (mode === "runner") {
1325
+ yield runner.query("BEGIN");
1326
+ try {
1327
+ for (const name of names) {
1328
+ yield runSingleSeed(runner, migrationConfig, name, options, direction);
1329
+ }
1330
+ yield runner.query("COMMIT");
1331
+ } catch (err) {
1332
+ try {
1333
+ yield runner.query("ROLLBACK");
1334
+ } catch (e) {
1335
+ }
1336
+ throw err;
1337
+ }
1338
+ return;
1339
+ }
1340
+ if (mode === "seed") {
1341
+ for (const name of names) {
1342
+ yield runner.query("BEGIN");
1343
+ try {
1344
+ yield runSingleSeed(runner, migrationConfig, name, options, direction);
1345
+ yield runner.query("COMMIT");
1346
+ } catch (err) {
1347
+ try {
1348
+ yield runner.query("ROLLBACK");
1349
+ } catch (e) {
1350
+ }
1351
+ throw err;
1352
+ }
1353
+ }
1354
+ return;
1355
+ }
1356
+ for (const name of names) {
1357
+ yield runSingleSeed(runner, migrationConfig, name, options, direction);
1358
+ }
1359
+ });
1360
+ }
1361
+ function runSeedsWithRunner(runner, migrationConfig, direction, options) {
1362
+ return __async(this, null, function* () {
1363
+ const { names, finalOptions } = mergeSeedConfig(migrationConfig, options);
1364
+ yield withTransactionalMode(runner, migrationConfig, names, finalOptions, direction);
1365
+ });
1366
+ }
1367
+ var init_SeedRunner = __esm({
1368
+ "framework/SeedRunner.ts"() {
1369
+ init_MigrationRunner();
1370
+ }
1371
+ });
1372
+
1046
1373
  // cli.ts
1047
1374
  import "source-map-support/register";
1048
1375
  var require_cli = __commonJS({
@@ -1051,6 +1378,7 @@ var require_cli = __commonJS({
1051
1378
  init_MigrationFilter();
1052
1379
  init_MigrationRunner();
1053
1380
  init_errors();
1381
+ init_SeedRunner();
1054
1382
  var args = MigrationCLIFactory.setup(process.argv);
1055
1383
  if (!args.commands || args.commands.length === 0) {
1056
1384
  console.error("Error: No command specified");
@@ -1096,8 +1424,9 @@ var require_cli = __commonJS({
1096
1424
  const migrations_rollback = yield runner.getMigrations();
1097
1425
  let downgraded = yield migration_filter(migrations_rollback, true);
1098
1426
  downgraded = downgraded.reverse();
1099
- if (args.flags.increment) {
1100
- const increment = parseInt(args.flags.increment);
1427
+ if (args.flags.all) {
1428
+ } else {
1429
+ const increment = parseInt(args.flags.increment || "1");
1101
1430
  downgraded = downgraded.slice(0, increment);
1102
1431
  }
1103
1432
  console.log(`Rolling back ${downgraded.length} migration(s)`);
@@ -1145,6 +1474,84 @@ var require_cli = __commonJS({
1145
1474
  printTable(yield Promise.all(table));
1146
1475
  runner.close();
1147
1476
  break;
1477
+ case "query":
1478
+ const sql_query = args.flags.query || commands[1];
1479
+ const format = (args.flags.format || "table").toLowerCase();
1480
+ if (!sql_query) {
1481
+ throw new CLIError('SQL query is required. Usage: proper query "SELECT * FROM table"');
1482
+ }
1483
+ const trimmed_query = sql_query.trim().toUpperCase();
1484
+ const is_select = trimmed_query.startsWith("SELECT");
1485
+ const is_cte = trimmed_query.startsWith("WITH");
1486
+ if (!is_select && !is_cte) {
1487
+ throw new CLIError("Only SELECT queries and CTEs (WITH clauses) are allowed");
1488
+ }
1489
+ const dangerous_keywords = ["INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER", "TRUNCATE", "GRANT", "REVOKE"];
1490
+ const has_dangerous_keyword = dangerous_keywords.some((keyword) => {
1491
+ const regex = new RegExp(`\\b${keyword}\\b`, "i");
1492
+ return regex.test(sql_query);
1493
+ });
1494
+ if (has_dangerous_keyword) {
1495
+ throw new CLIError("Query contains disallowed keywords. Only SELECT and CTE queries are permitted");
1496
+ }
1497
+ if (!["table", "json"].includes(format)) {
1498
+ throw new CLIError("Invalid format. Use --format table or --format json");
1499
+ }
1500
+ try {
1501
+ const [results] = yield runner.query(sql_query);
1502
+ if (Array.isArray(results) && results.length > 0) {
1503
+ if (format === "json") {
1504
+ console.log(JSON.stringify(results, null, 2));
1505
+ } else {
1506
+ const { printTable: printTable2 } = __require("console-table-printer");
1507
+ printTable2(results);
1508
+ }
1509
+ console.log(`
1510
+ Returned ${results.length} row(s)`);
1511
+ } else if (Array.isArray(results) && results.length === 0) {
1512
+ console.log("Query returned 0 rows");
1513
+ } else {
1514
+ console.log("Query executed successfully");
1515
+ }
1516
+ } catch (error) {
1517
+ throw new CLIError(`Query execution failed: ${error.message}`);
1518
+ }
1519
+ yield runner.close();
1520
+ break;
1521
+ case "seed": {
1522
+ const subCommands = commands.slice(1);
1523
+ const actionFlag = (args.flags.action || "").toString().toLowerCase();
1524
+ const firstSub = (subCommands[0] || "").toString().toLowerCase();
1525
+ const action = actionFlag === "down" || firstSub === "down" ? "down" : "up";
1526
+ const positionalNames = actionFlag ? subCommands : firstSub === "up" || firstSub === "down" ? subCommands.slice(1) : subCommands;
1527
+ const aliasFlag = args.flags.alias;
1528
+ const aliasMap = {};
1529
+ if (aliasFlag) {
1530
+ const aliases = Array.isArray(aliasFlag) ? aliasFlag : [aliasFlag];
1531
+ for (const entry of aliases) {
1532
+ const eq = entry.indexOf("=");
1533
+ if (eq > 0) {
1534
+ const key = entry.slice(0, eq);
1535
+ const value = entry.slice(eq + 1);
1536
+ if (key) aliasMap[key] = value;
1537
+ }
1538
+ }
1539
+ }
1540
+ const seedOptions = {
1541
+ migrationsDir: args.flags.migrationsDir || args.flags["migrations-dir"],
1542
+ dataDir: args.flags.dataDir || args.flags["data-dir"],
1543
+ validate: args.flags.validate === void 0 ? true : String(args.flags.validate).toLowerCase() !== "false",
1544
+ transactional: args.flags.transactional,
1545
+ aliasMap: Object.keys(aliasMap).length ? aliasMap : void 0,
1546
+ names: positionalNames.length ? positionalNames : void 0,
1547
+ log: (msg) => console.log(msg)
1548
+ };
1549
+ const reader = new FileMigrationConfigReader(config_file);
1550
+ const migrationConfig = reader.loadFile();
1551
+ yield runSeedsWithRunner(runner, migrationConfig, action, seedOptions);
1552
+ yield runner.close();
1553
+ break;
1554
+ }
1148
1555
  default:
1149
1556
  throw CLIError.unknownCommand(command);
1150
1557
  }
@@ -1160,7 +1567,7 @@ var require_cli = __commonJS({
1160
1567
  console.log(`
1161
1568
  SQL Proper - Database migration tool
1162
1569
 
1163
- Usage:
1570
+ Usage:
1164
1571
  proper <command> [options]
1165
1572
 
1166
1573
  Commands:
@@ -1170,20 +1577,27 @@ Commands:
1170
1577
  create Create a new migration
1171
1578
  init Initialize a new config file
1172
1579
  status Show migration status
1580
+ query Execute a SQL query and display results
1173
1581
  help Show this help message
1174
1582
 
1175
1583
  Options:
1176
1584
  -c, --config Specify the config file (default: proper.json)
1177
1585
  --increment <n> Limit the number of migrations to apply or roll back
1586
+ --all Roll back all completed migrations (down command only)
1587
+ --format <fmt> Output format for query command: 'table' (default) or 'json'
1178
1588
 
1179
1589
  Examples:
1180
- proper up Apply all pending migrations
1181
- proper up --increment 1 Apply only the next pending migration
1182
- proper down Roll back the last applied migration
1183
- proper create my_migration Create a new migration named "my_migration"
1184
- proper init Create a new config file
1185
- proper status Show the status of all migrations
1186
- proper -c custom.json up Use a custom config file
1590
+ proper up Apply all pending migrations
1591
+ proper up --increment 1 Apply only the next pending migration
1592
+ proper down Roll back the last applied migration (default: 1)
1593
+ proper down --increment 3 Roll back the last 3 applied migrations
1594
+ proper down --all Roll back all completed migrations
1595
+ proper create my_migration Create a new migration named "my_migration"
1596
+ proper init Create a new config file
1597
+ proper status Show the status of all migrations
1598
+ proper query "select * from users" Execute a SQL query
1599
+ proper query "select * from users" --format json Execute a query and output as JSON
1600
+ proper -c custom.json up Use a custom config file
1187
1601
  `);
1188
1602
  }
1189
1603
  }