@noego/proper 0.0.3 → 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)`);
@@ -1169,7 +1498,7 @@ var require_cli = __commonJS({
1169
1498
  throw new CLIError("Invalid format. Use --format table or --format json");
1170
1499
  }
1171
1500
  try {
1172
- const [results] = yield runner.sqlrunner.query(sql_query);
1501
+ const [results] = yield runner.query(sql_query);
1173
1502
  if (Array.isArray(results) && results.length > 0) {
1174
1503
  if (format === "json") {
1175
1504
  console.log(JSON.stringify(results, null, 2));
@@ -1189,6 +1518,40 @@ Returned ${results.length} row(s)`);
1189
1518
  }
1190
1519
  yield runner.close();
1191
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
+ }
1192
1555
  default:
1193
1556
  throw CLIError.unknownCommand(command);
1194
1557
  }
@@ -1220,12 +1583,15 @@ Commands:
1220
1583
  Options:
1221
1584
  -c, --config Specify the config file (default: proper.json)
1222
1585
  --increment <n> Limit the number of migrations to apply or roll back
1586
+ --all Roll back all completed migrations (down command only)
1223
1587
  --format <fmt> Output format for query command: 'table' (default) or 'json'
1224
1588
 
1225
1589
  Examples:
1226
1590
  proper up Apply all pending migrations
1227
1591
  proper up --increment 1 Apply only the next pending migration
1228
- proper down Roll back the last applied 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
1229
1595
  proper create my_migration Create a new migration named "my_migration"
1230
1596
  proper init Create a new config file
1231
1597
  proper status Show the status of all migrations