@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/index.mjs CHANGED
@@ -40,9 +40,6 @@ var __async = (__this, __arguments, generator) => {
40
40
 
41
41
  // framework/MigrationRunner.ts
42
42
  import fs3 from "fs";
43
- import mysql from "mysql2/promise";
44
- import * as sqlite from "sqlite";
45
- import * as sqlite3 from "sqlite3";
46
43
 
47
44
  // framework/MigrationDirectoryReader.ts
48
45
  import fs from "fs";
@@ -334,9 +331,10 @@ var MigrationDirectoryReader = class {
334
331
  /**
335
332
  * Resolves the appropriate file for a migration based on dialect.
336
333
  * Priority: dialect-specific file > generic file
334
+ * File extensions: `.mysql.up.sql`, `.sqlite.up.sql`, `.pg.up.sql`.
337
335
  */
338
336
  resolveFile(baseName, direction) {
339
- const dialectExt = this.dialect === "sql" ? "mysql" : "sqlite";
337
+ const dialectExt = this.dialect === "sql" ? "mysql" : this.dialect;
340
338
  const dialectFile = path.join(this.directory, `${baseName}.${dialectExt}.${direction}.sql`);
341
339
  if (fs.existsSync(dialectFile)) return dialectFile;
342
340
  const genericFile = path.join(this.directory, `${baseName}.${direction}.sql`);
@@ -347,14 +345,14 @@ var MigrationDirectoryReader = class {
347
345
  * Checks if a file path is dialect-specific (contains .mysql. or .sqlite. in the name)
348
346
  */
349
347
  isDialectSpecific(filePath) {
350
- return /\.(mysql|sqlite)\.(up|down)\.sql$/i.test(filePath);
348
+ return /\.(mysql|sqlite|pg)\.(up|down)\.sql$/i.test(filePath);
351
349
  }
352
350
  loadMigrations(table, connection) {
353
351
  fs.existsSync(this.directory) || fs.mkdirSync(this.directory);
354
352
  const dir_content = fs.readdirSync(this.directory, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name);
355
353
  const uniqueKeys = /* @__PURE__ */ new Set();
356
354
  dir_content.forEach((file) => {
357
- const key = file.replace(/(?:\.(mysql|sqlite))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
355
+ const key = file.replace(/(?:\.(mysql|sqlite|pg))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
358
356
  uniqueKeys.add(key);
359
357
  });
360
358
  const migration_sorter = {};
@@ -426,6 +424,12 @@ var MigrationSetup = class {
426
424
  up TEXT,
427
425
  down TEXT,
428
426
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
427
+ )` : this.config.database === "pg" ? `CREATE TABLE IF NOT EXISTS ${tableName} (
428
+ id SERIAL PRIMARY KEY,
429
+ migration_key TEXT,
430
+ up TEXT,
431
+ down TEXT,
432
+ created_at TIMESTAMPTZ DEFAULT now()
429
433
  )` : `CREATE TABLE IF NOT EXISTS ${tableName} (
430
434
  id INT AUTO_INCREMENT PRIMARY KEY,
431
435
  migration_key VARCHAR(255),
@@ -485,31 +489,35 @@ function MySqlDialectParser(sql) {
485
489
  }
486
490
  return result;
487
491
  }
488
- function SqliteDialectParser(sql) {
489
- const lines = sql.split("\n");
490
- let result = "";
491
- let isInSqliteBlock = true;
492
- const startSqliteRegex = /^\s*--\s*\[\s*sqlite\s*\]\s*$/i;
492
+ function markerDialectParser(names) {
493
+ const startRegex = new RegExp(`^\\s*--\\s*\\[\\s*(${names.join("|")})\\s*\\]\\s*$`, "i");
493
494
  const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
494
- for (const line of lines) {
495
- const trimmed = line.trim();
496
- if (startSqliteRegex.test(trimmed)) {
497
- isInSqliteBlock = true;
498
- result += line + "\n";
499
- continue;
500
- } else if (anyDialectRegex.test(trimmed) && !startSqliteRegex.test(trimmed)) {
501
- isInSqliteBlock = false;
502
- continue;
503
- }
504
- if (!isInSqliteBlock && line.toLowerCase().includes("create index")) {
505
- isInSqliteBlock = true;
506
- }
507
- if (isInSqliteBlock) {
508
- result += line + "\n";
495
+ return function(sql) {
496
+ const lines = sql.split("\n");
497
+ let result = "";
498
+ let capturing = true;
499
+ for (const line of lines) {
500
+ const trimmed = line.trim();
501
+ if (startRegex.test(trimmed)) {
502
+ capturing = true;
503
+ result += line + "\n";
504
+ continue;
505
+ } else if (anyDialectRegex.test(trimmed)) {
506
+ capturing = false;
507
+ continue;
508
+ }
509
+ if (!capturing && line.toLowerCase().includes("create index")) {
510
+ capturing = true;
511
+ }
512
+ if (capturing) {
513
+ result += line + "\n";
514
+ }
509
515
  }
510
- }
511
- return result;
516
+ return result;
517
+ };
512
518
  }
519
+ var SqliteDialectParser = markerDialectParser(["sqlite"]);
520
+ var PgDialectParser = markerDialectParser(["pg", "postgres", "postgresql"]);
513
521
 
514
522
  // framework/SQLRunner.ts
515
523
  var BaseSQLRunner = class {
@@ -725,12 +733,122 @@ ${sql}
725
733
  });
726
734
  }
727
735
  };
736
+ var PgRunner = class _PgRunner extends BaseSQLRunner {
737
+ constructor(connection) {
738
+ super();
739
+ this.connection = connection;
740
+ }
741
+ /** `?` -> `$1`, `$2`, ... outside of string literals / comments. */
742
+ static toPositional(sql) {
743
+ let out = "";
744
+ let n = 0;
745
+ let inSingle = false;
746
+ let inDouble = false;
747
+ let inLineComment = false;
748
+ let inBlockComment = false;
749
+ for (let i = 0; i < sql.length; i++) {
750
+ const ch = sql[i];
751
+ const next = sql[i + 1];
752
+ if (inLineComment) {
753
+ out += ch;
754
+ if (ch === "\n") inLineComment = false;
755
+ continue;
756
+ }
757
+ if (inBlockComment) {
758
+ out += ch;
759
+ if (ch === "*" && next === "/") {
760
+ out += next;
761
+ i++;
762
+ inBlockComment = false;
763
+ }
764
+ continue;
765
+ }
766
+ if (inSingle) {
767
+ out += ch;
768
+ if (ch === "'") inSingle = false;
769
+ continue;
770
+ }
771
+ if (inDouble) {
772
+ out += ch;
773
+ if (ch === '"') inDouble = false;
774
+ continue;
775
+ }
776
+ if (ch === "-" && next === "-") {
777
+ out += ch;
778
+ inLineComment = true;
779
+ continue;
780
+ }
781
+ if (ch === "/" && next === "*") {
782
+ out += ch + next;
783
+ i++;
784
+ inBlockComment = true;
785
+ continue;
786
+ }
787
+ if (ch === "'") {
788
+ out += ch;
789
+ inSingle = true;
790
+ continue;
791
+ }
792
+ if (ch === '"') {
793
+ out += ch;
794
+ inDouble = true;
795
+ continue;
796
+ }
797
+ if (ch === "?") {
798
+ out += `$${++n}`;
799
+ continue;
800
+ }
801
+ out += ch;
802
+ }
803
+ return out;
804
+ }
805
+ run(sql, params) {
806
+ return __async(this, null, function* () {
807
+ if (params.length > 0) {
808
+ return yield this.connection.query(_PgRunner.toPositional(sql), params);
809
+ }
810
+ return yield this.connection.query(sql);
811
+ });
812
+ }
813
+ _query(_0) {
814
+ return __async(this, arguments, function* (sql, params = []) {
815
+ const result = yield this.run(sql, params);
816
+ return [result.rows, result];
817
+ });
818
+ }
819
+ _execute(_0) {
820
+ return __async(this, arguments, function* (sql, params = []) {
821
+ var _a;
822
+ const result = yield this.run(sql, params);
823
+ return [{ changes: (_a = result.rowCount) != null ? _a : 0, lastID: void 0 }, result];
824
+ });
825
+ }
826
+ _end() {
827
+ return __async(this, null, function* () {
828
+ if (this.connection && typeof this.connection.end === "function") {
829
+ yield this.connection.end();
830
+ }
831
+ });
832
+ }
833
+ };
728
834
 
729
835
  // framework/MigrationRunner.ts
730
836
  function toError(error) {
731
837
  if (error instanceof Error) return error;
732
838
  return new Error(String(error));
733
839
  }
840
+ function makeRunner(database, conn) {
841
+ switch (database) {
842
+ case "sql":
843
+ return new SQLRunner(conn);
844
+ case "sqlite":
845
+ return new SQLiteRunner(conn);
846
+ case "pg":
847
+ return new PgRunner(conn);
848
+ default:
849
+ throw ConfigurationError.unknownDatabaseType(database);
850
+ }
851
+ }
734
852
  function loadMigrationConfig(configFile) {
735
853
  const reader = new FileMigrationConfigReader(configFile);
736
854
  return reader.loadFile();
@@ -751,6 +869,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
751
869
  }
752
870
  static createConnection(config) {
753
871
  return __async(this, null, function* () {
872
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
754
873
  let conn = null;
755
874
  switch (config.database) {
756
875
  case "sql":
@@ -761,7 +880,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
761
880
  password: process.env.SQL_PASSWORD
762
881
  }, config.sql);
763
882
  try {
764
- conn = yield mysql.createConnection(settings);
883
+ const mysql = yield import("mysql2/promise");
884
+ conn = yield ((_b = (_a = mysql.default) == null ? void 0 : _a.createConnection) != null ? _b : mysql.createConnection)(settings);
765
885
  return conn;
766
886
  } catch (error) {
767
887
  throw DatabaseConnectionError.connectionFailed("sql", toError(error).message);
@@ -771,14 +891,32 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
771
891
  throw ConfigurationError.missingDatabaseConfiguration("sqlite");
772
892
  }
773
893
  try {
774
- conn = yield sqlite.open({
894
+ const sqlite = yield import("sqlite");
895
+ const sqlite3 = yield import("sqlite3");
896
+ conn = yield ((_d = (_c = sqlite.default) == null ? void 0 : _c.open) != null ? _d : sqlite.open)({
775
897
  filename: config.sqlite.database,
776
- driver: sqlite3.Database
898
+ driver: (_f = (_e = sqlite3.default) == null ? void 0 : _e.Database) != null ? _f : sqlite3.Database
777
899
  });
778
900
  return conn;
779
901
  } catch (error) {
780
902
  throw DatabaseConnectionError.connectionFailed("sqlite", toError(error).message);
781
903
  }
904
+ case "pg":
905
+ if (!config.pg && !process.env.DATABASE_URL) {
906
+ throw ConfigurationError.missingDatabaseConfiguration("pg");
907
+ }
908
+ try {
909
+ const pgcfg = (_g = config.pg) != null ? _g : {};
910
+ const connectionString = (_h = pgcfg.connectionString) != null ? _h : process.env.DATABASE_URL;
911
+ const settings2 = connectionString ? { connectionString, ssl: pgcfg.ssl } : __spreadProps(__spreadValues({}, pgcfg), { password: (_i = pgcfg.password) != null ? _i : process.env.PG_PASSWORD });
912
+ const pg = yield import("pg");
913
+ const Client = (_k = (_j = pg.default) == null ? void 0 : _j.Client) != null ? _k : pg.Client;
914
+ conn = new Client(settings2);
915
+ yield conn.connect();
916
+ return conn;
917
+ } catch (error) {
918
+ throw DatabaseConnectionError.connectionFailed("pg", toError(error).message);
919
+ }
782
920
  default:
783
921
  throw ConfigurationError.unknownDatabaseType(config.database);
784
922
  }
@@ -799,16 +937,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
799
937
  sqlrunner = conn;
800
938
  driverConnection = null;
801
939
  } else {
802
- switch (config.database) {
803
- case "sql":
804
- sqlrunner = new SQLRunner(conn);
805
- break;
806
- case "sqlite":
807
- sqlrunner = new SQLiteRunner(conn);
808
- break;
809
- default:
810
- throw ConfigurationError.unknownDatabaseType(config.database);
811
- }
940
+ sqlrunner = makeRunner(config.database, conn);
812
941
  }
813
942
  const setup = new MigrationSetup(sqlrunner, config);
814
943
  const read_strategy = this.getReadStategy(config);
@@ -820,17 +949,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
820
949
  createEmpty(config) {
821
950
  return __async(this, null, function* () {
822
951
  const conn = null;
823
- let sqlrunner;
824
- switch (config.database) {
825
- case "sql":
826
- sqlrunner = new SQLRunner(conn);
827
- break;
828
- case "sqlite":
829
- sqlrunner = new SQLiteRunner(conn);
830
- break;
831
- default:
832
- throw ConfigurationError.unknownDatabaseType(config.database);
833
- }
952
+ const sqlrunner = makeRunner(config.database, conn);
834
953
  const setup = new MigrationSetup(sqlrunner, config);
835
954
  const read_strategy = this.getReadStategy(config);
836
955
  const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner, config.database);
@@ -843,6 +962,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
843
962
  return MySqlDialectParser;
844
963
  case "sqlite":
845
964
  return SqliteDialectParser;
965
+ case "pg":
966
+ return PgDialectParser;
846
967
  default:
847
968
  throw ConfigurationError.unknownDatabaseType(config.database);
848
969
  }
@@ -1029,6 +1150,8 @@ var FileMigrationConfigReader = class {
1029
1150
  config.database = "sql";
1030
1151
  } else if (config.sqlite) {
1031
1152
  config.database = "sqlite";
1153
+ } else if (config.pg) {
1154
+ config.database = "pg";
1032
1155
  } else {
1033
1156
  throw ConfigurationError.missingRequiredProperty("database");
1034
1157
  }
@@ -1343,6 +1466,9 @@ function createSeedFactory(options) {
1343
1466
  export {
1344
1467
  MySQLMigrationRunner as MigrationRunner,
1345
1468
  MigrationRunnerFactory,
1469
+ PgRunner,
1470
+ SQLRunner,
1471
+ SQLiteRunner,
1346
1472
  createSeedFactory,
1347
1473
  loadMigrationConfig,
1348
1474
  runSeedsWithRunner