@noego/proper 0.0.9 → 0.1.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/bin/cli.js CHANGED
@@ -124,9 +124,6 @@ function migration_filter(_0) {
124
124
 
125
125
  // framework/MigrationRunner.ts
126
126
  var import_fs3 = __toESM(require("fs"));
127
- var import_promise = __toESM(require("mysql2/promise"));
128
- var sqlite = __toESM(require("sqlite"));
129
- var sqlite3 = __toESM(require("sqlite3"));
130
127
 
131
128
  // framework/MigrationDirectoryReader.ts
132
129
  var import_fs = __toESM(require("fs"));
@@ -418,9 +415,10 @@ var MigrationDirectoryReader = class {
418
415
  /**
419
416
  * Resolves the appropriate file for a migration based on dialect.
420
417
  * Priority: dialect-specific file > generic file
418
+ * File extensions: `.mysql.up.sql`, `.sqlite.up.sql`, `.pg.up.sql`.
421
419
  */
422
420
  resolveFile(baseName, direction) {
423
- const dialectExt = this.dialect === "sql" ? "mysql" : "sqlite";
421
+ const dialectExt = this.dialect === "sql" ? "mysql" : this.dialect;
424
422
  const dialectFile = import_path.default.join(this.directory, `${baseName}.${dialectExt}.${direction}.sql`);
425
423
  if (import_fs.default.existsSync(dialectFile)) return dialectFile;
426
424
  const genericFile = import_path.default.join(this.directory, `${baseName}.${direction}.sql`);
@@ -431,14 +429,14 @@ var MigrationDirectoryReader = class {
431
429
  * Checks if a file path is dialect-specific (contains .mysql. or .sqlite. in the name)
432
430
  */
433
431
  isDialectSpecific(filePath) {
434
- return /\.(mysql|sqlite)\.(up|down)\.sql$/i.test(filePath);
432
+ return /\.(mysql|sqlite|pg)\.(up|down)\.sql$/i.test(filePath);
435
433
  }
436
434
  loadMigrations(table, connection) {
437
435
  import_fs.default.existsSync(this.directory) || import_fs.default.mkdirSync(this.directory);
438
436
  const dir_content = import_fs.default.readdirSync(this.directory, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name);
439
437
  const uniqueKeys = /* @__PURE__ */ new Set();
440
438
  dir_content.forEach((file) => {
441
- const key = file.replace(/(?:\.(mysql|sqlite))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
439
+ const key = file.replace(/(?:\.(mysql|sqlite|pg))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
442
440
  uniqueKeys.add(key);
443
441
  });
444
442
  const migration_sorter = {};
@@ -510,6 +508,12 @@ var MigrationSetup = class {
510
508
  up TEXT,
511
509
  down TEXT,
512
510
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
511
+ )` : this.config.database === "pg" ? `CREATE TABLE IF NOT EXISTS ${tableName} (
512
+ id SERIAL PRIMARY KEY,
513
+ migration_key TEXT,
514
+ up TEXT,
515
+ down TEXT,
516
+ created_at TIMESTAMPTZ DEFAULT now()
513
517
  )` : `CREATE TABLE IF NOT EXISTS ${tableName} (
514
518
  id INT AUTO_INCREMENT PRIMARY KEY,
515
519
  migration_key VARCHAR(255),
@@ -554,31 +558,35 @@ function MySqlDialectParser(sql) {
554
558
  }
555
559
  return result;
556
560
  }
557
- function SqliteDialectParser(sql) {
558
- const lines = sql.split("\n");
559
- let result = "";
560
- let isInSqliteBlock = true;
561
- const startSqliteRegex = /^\s*--\s*\[\s*sqlite\s*\]\s*$/i;
561
+ function markerDialectParser(names) {
562
+ const startRegex = new RegExp(`^\\s*--\\s*\\[\\s*(${names.join("|")})\\s*\\]\\s*$`, "i");
562
563
  const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
563
- for (const line of lines) {
564
- const trimmed = line.trim();
565
- if (startSqliteRegex.test(trimmed)) {
566
- isInSqliteBlock = true;
567
- result += line + "\n";
568
- continue;
569
- } else if (anyDialectRegex.test(trimmed) && !startSqliteRegex.test(trimmed)) {
570
- isInSqliteBlock = false;
571
- continue;
572
- }
573
- if (!isInSqliteBlock && line.toLowerCase().includes("create index")) {
574
- isInSqliteBlock = true;
575
- }
576
- if (isInSqliteBlock) {
577
- result += line + "\n";
564
+ return function(sql) {
565
+ const lines = sql.split("\n");
566
+ let result = "";
567
+ let capturing = true;
568
+ for (const line of lines) {
569
+ const trimmed = line.trim();
570
+ if (startRegex.test(trimmed)) {
571
+ capturing = true;
572
+ result += line + "\n";
573
+ continue;
574
+ } else if (anyDialectRegex.test(trimmed)) {
575
+ capturing = false;
576
+ continue;
577
+ }
578
+ if (!capturing && line.toLowerCase().includes("create index")) {
579
+ capturing = true;
580
+ }
581
+ if (capturing) {
582
+ result += line + "\n";
583
+ }
578
584
  }
579
- }
580
- return result;
585
+ return result;
586
+ };
581
587
  }
588
+ var SqliteDialectParser = markerDialectParser(["sqlite"]);
589
+ var PgDialectParser = markerDialectParser(["pg", "postgres", "postgresql"]);
582
590
 
583
591
  // framework/SQLRunner.ts
584
592
  var BaseSQLRunner = class {
@@ -794,12 +802,122 @@ ${sql}
794
802
  });
795
803
  }
796
804
  };
805
+ var PgRunner = class _PgRunner extends BaseSQLRunner {
806
+ constructor(connection) {
807
+ super();
808
+ this.connection = connection;
809
+ }
810
+ /** `?` -> `$1`, `$2`, ... outside of string literals / comments. */
811
+ static toPositional(sql) {
812
+ let out = "";
813
+ let n = 0;
814
+ let inSingle = false;
815
+ let inDouble = false;
816
+ let inLineComment = false;
817
+ let inBlockComment = false;
818
+ for (let i = 0; i < sql.length; i++) {
819
+ const ch = sql[i];
820
+ const next = sql[i + 1];
821
+ if (inLineComment) {
822
+ out += ch;
823
+ if (ch === "\n") inLineComment = false;
824
+ continue;
825
+ }
826
+ if (inBlockComment) {
827
+ out += ch;
828
+ if (ch === "*" && next === "/") {
829
+ out += next;
830
+ i++;
831
+ inBlockComment = false;
832
+ }
833
+ continue;
834
+ }
835
+ if (inSingle) {
836
+ out += ch;
837
+ if (ch === "'") inSingle = false;
838
+ continue;
839
+ }
840
+ if (inDouble) {
841
+ out += ch;
842
+ if (ch === '"') inDouble = false;
843
+ continue;
844
+ }
845
+ if (ch === "-" && next === "-") {
846
+ out += ch;
847
+ inLineComment = true;
848
+ continue;
849
+ }
850
+ if (ch === "/" && next === "*") {
851
+ out += ch + next;
852
+ i++;
853
+ inBlockComment = true;
854
+ continue;
855
+ }
856
+ if (ch === "'") {
857
+ out += ch;
858
+ inSingle = true;
859
+ continue;
860
+ }
861
+ if (ch === '"') {
862
+ out += ch;
863
+ inDouble = true;
864
+ continue;
865
+ }
866
+ if (ch === "?") {
867
+ out += `$${++n}`;
868
+ continue;
869
+ }
870
+ out += ch;
871
+ }
872
+ return out;
873
+ }
874
+ run(sql, params) {
875
+ return __async(this, null, function* () {
876
+ if (params.length > 0) {
877
+ return yield this.connection.query(_PgRunner.toPositional(sql), params);
878
+ }
879
+ return yield this.connection.query(sql);
880
+ });
881
+ }
882
+ _query(_0) {
883
+ return __async(this, arguments, function* (sql, params = []) {
884
+ const result = yield this.run(sql, params);
885
+ return [result.rows, result];
886
+ });
887
+ }
888
+ _execute(_0) {
889
+ return __async(this, arguments, function* (sql, params = []) {
890
+ var _a;
891
+ const result = yield this.run(sql, params);
892
+ return [{ changes: (_a = result.rowCount) != null ? _a : 0, lastID: void 0 }, result];
893
+ });
894
+ }
895
+ _end() {
896
+ return __async(this, null, function* () {
897
+ if (this.connection && typeof this.connection.end === "function") {
898
+ yield this.connection.end();
899
+ }
900
+ });
901
+ }
902
+ };
797
903
 
798
904
  // framework/MigrationRunner.ts
799
905
  function toError(error) {
800
906
  if (error instanceof Error) return error;
801
907
  return new Error(String(error));
802
908
  }
909
+ function makeRunner(database, conn) {
910
+ switch (database) {
911
+ case "sql":
912
+ return new SQLRunner(conn);
913
+ case "sqlite":
914
+ return new SQLiteRunner(conn);
915
+ case "pg":
916
+ return new PgRunner(conn);
917
+ default:
918
+ throw ConfigurationError.unknownDatabaseType(database);
919
+ }
920
+ }
803
921
  var MigrationRunnerFactory = class _MigrationRunnerFactory {
804
922
  static isSQLRunner(conn) {
805
923
  return !!conn && typeof conn.query === "function" && typeof conn.execute === "function" && typeof conn.end === "function";
@@ -816,6 +934,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
816
934
  }
817
935
  static createConnection(config) {
818
936
  return __async(this, null, function* () {
937
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
819
938
  let conn = null;
820
939
  switch (config.database) {
821
940
  case "sql":
@@ -826,7 +945,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
826
945
  password: process.env.SQL_PASSWORD
827
946
  }, config.sql);
828
947
  try {
829
- conn = yield import_promise.default.createConnection(settings);
948
+ const mysql = yield import("mysql2/promise");
949
+ conn = yield ((_b = (_a = mysql.default) == null ? void 0 : _a.createConnection) != null ? _b : mysql.createConnection)(settings);
830
950
  return conn;
831
951
  } catch (error) {
832
952
  throw DatabaseConnectionError.connectionFailed("sql", toError(error).message);
@@ -836,14 +956,32 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
836
956
  throw ConfigurationError.missingDatabaseConfiguration("sqlite");
837
957
  }
838
958
  try {
839
- conn = yield sqlite.open({
959
+ const sqlite = yield import("sqlite");
960
+ const sqlite3 = yield import("sqlite3");
961
+ conn = yield ((_d = (_c = sqlite.default) == null ? void 0 : _c.open) != null ? _d : sqlite.open)({
840
962
  filename: config.sqlite.database,
841
- driver: sqlite3.Database
963
+ driver: (_f = (_e = sqlite3.default) == null ? void 0 : _e.Database) != null ? _f : sqlite3.Database
842
964
  });
843
965
  return conn;
844
966
  } catch (error) {
845
967
  throw DatabaseConnectionError.connectionFailed("sqlite", toError(error).message);
846
968
  }
969
+ case "pg":
970
+ if (!config.pg && !process.env.DATABASE_URL) {
971
+ throw ConfigurationError.missingDatabaseConfiguration("pg");
972
+ }
973
+ try {
974
+ const pgcfg = (_g = config.pg) != null ? _g : {};
975
+ const connectionString = (_h = pgcfg.connectionString) != null ? _h : process.env.DATABASE_URL;
976
+ const settings2 = connectionString ? { connectionString, ssl: pgcfg.ssl } : __spreadProps(__spreadValues({}, pgcfg), { password: (_i = pgcfg.password) != null ? _i : process.env.PG_PASSWORD });
977
+ const pg = yield import("pg");
978
+ const Client = (_k = (_j = pg.default) == null ? void 0 : _j.Client) != null ? _k : pg.Client;
979
+ conn = new Client(settings2);
980
+ yield conn.connect();
981
+ return conn;
982
+ } catch (error) {
983
+ throw DatabaseConnectionError.connectionFailed("pg", toError(error).message);
984
+ }
847
985
  default:
848
986
  throw ConfigurationError.unknownDatabaseType(config.database);
849
987
  }
@@ -864,16 +1002,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
864
1002
  sqlrunner = conn;
865
1003
  driverConnection = null;
866
1004
  } else {
867
- switch (config.database) {
868
- case "sql":
869
- sqlrunner = new SQLRunner(conn);
870
- break;
871
- case "sqlite":
872
- sqlrunner = new SQLiteRunner(conn);
873
- break;
874
- default:
875
- throw ConfigurationError.unknownDatabaseType(config.database);
876
- }
1005
+ sqlrunner = makeRunner(config.database, conn);
877
1006
  }
878
1007
  const setup = new MigrationSetup(sqlrunner, config);
879
1008
  const read_strategy = this.getReadStategy(config);
@@ -885,17 +1014,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
885
1014
  createEmpty(config) {
886
1015
  return __async(this, null, function* () {
887
1016
  const conn = null;
888
- let sqlrunner;
889
- switch (config.database) {
890
- case "sql":
891
- sqlrunner = new SQLRunner(conn);
892
- break;
893
- case "sqlite":
894
- sqlrunner = new SQLiteRunner(conn);
895
- break;
896
- default:
897
- throw ConfigurationError.unknownDatabaseType(config.database);
898
- }
1017
+ const sqlrunner = makeRunner(config.database, conn);
899
1018
  const setup = new MigrationSetup(sqlrunner, config);
900
1019
  const read_strategy = this.getReadStategy(config);
901
1020
  const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner, config.database);
@@ -908,6 +1027,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
908
1027
  return MySqlDialectParser;
909
1028
  case "sqlite":
910
1029
  return SqliteDialectParser;
1030
+ case "pg":
1031
+ return PgDialectParser;
911
1032
  default:
912
1033
  throw ConfigurationError.unknownDatabaseType(config.database);
913
1034
  }
@@ -1094,6 +1215,8 @@ var FileMigrationConfigReader = class {
1094
1215
  config.database = "sql";
1095
1216
  } else if (config.sqlite) {
1096
1217
  config.database = "sqlite";
1218
+ } else if (config.pg) {
1219
+ config.database = "pg";
1097
1220
  } else {
1098
1221
  throw ConfigurationError.missingRequiredProperty("database");
1099
1222
  }
@@ -1376,6 +1499,10 @@ function runSeedsWithRunner(runner, migrationConfig, direction, options) {
1376
1499
 
1377
1500
  // cli.ts
1378
1501
  var args = MigrationCLIFactory.setup(process.argv);
1502
+ if (args.flags.help) {
1503
+ printUsage();
1504
+ process.exit(0);
1505
+ }
1379
1506
  if (!args.commands || args.commands.length === 0) {
1380
1507
  console.error("Error: No command specified");
1381
1508
  printUsage();