@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.d.mts +1 -0
- package/bin/cli.d.ts +1 -0
- package/bin/cli.js +436 -29
- package/bin/cli.js.map +1 -1
- package/bin/cli.mjs +443 -29
- package/bin/cli.mjs.map +1 -1
- package/bin/index.d.mts +169 -0
- package/bin/index.d.ts +169 -0
- package/bin/index.js +386 -21
- package/bin/index.js.map +1 -1
- package/bin/index.mjs +384 -20
- package/bin/index.mjs.map +1 -1
- package/lib/runner.d.mts +51 -0
- package/lib/runner.d.ts +51 -0
- package/lib/runner.js +257 -0
- package/lib/runner.js.map +1 -0
- package/lib/runner.mjs +230 -0
- package/lib/runner.mjs.map +1 -0
- package/package.json +12 -1
- package/readme.md +295 -39
package/bin/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/bin/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/bin/cli.js
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defProps = Object.defineProperties;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
7
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
8
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
9
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
10
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
11
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __spreadValues = (a, b) => {
|
|
14
|
+
for (var prop in b || (b = {}))
|
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
if (__getOwnPropSymbols)
|
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
+
if (__propIsEnum.call(b, prop))
|
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
10
25
|
var __objRest = (source, exclude) => {
|
|
11
26
|
var target = {};
|
|
12
27
|
for (var prop in source)
|
|
@@ -561,6 +576,9 @@ var BaseSQLRunner = class {
|
|
|
561
576
|
});
|
|
562
577
|
}
|
|
563
578
|
};
|
|
579
|
+
function isPromiseLike(value) {
|
|
580
|
+
return !!value && typeof value.then === "function";
|
|
581
|
+
}
|
|
564
582
|
var SQLRunner = class extends BaseSQLRunner {
|
|
565
583
|
constructor(connection) {
|
|
566
584
|
super();
|
|
@@ -590,14 +608,85 @@ var SQLiteRunner = class extends BaseSQLRunner {
|
|
|
590
608
|
super();
|
|
591
609
|
this.connection = connection;
|
|
592
610
|
}
|
|
611
|
+
prepareStatement(sql) {
|
|
612
|
+
return __async(this, null, function* () {
|
|
613
|
+
if (typeof this.connection.prepare !== "function") {
|
|
614
|
+
throw new Error("SQLite connection does not support prepare()");
|
|
615
|
+
}
|
|
616
|
+
const stmt = this.connection.prepare(sql);
|
|
617
|
+
return isPromiseLike(stmt) ? yield stmt : stmt;
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
finalizeStatement(stmt) {
|
|
621
|
+
return __async(this, null, function* () {
|
|
622
|
+
if (!stmt || typeof stmt.finalize !== "function") return;
|
|
623
|
+
const result = stmt.finalize();
|
|
624
|
+
if (isPromiseLike(result)) {
|
|
625
|
+
yield result;
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
statementAll(stmt, params) {
|
|
630
|
+
return __async(this, null, function* () {
|
|
631
|
+
if (typeof stmt.all !== "function") {
|
|
632
|
+
throw new Error("SQLite statement does not support all()");
|
|
633
|
+
}
|
|
634
|
+
if (stmt.all.length >= 2) {
|
|
635
|
+
return yield new Promise((resolve, reject) => {
|
|
636
|
+
const callback = (err, rows) => {
|
|
637
|
+
if (err) return reject(err);
|
|
638
|
+
resolve(rows || []);
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
if (params.length > 0) {
|
|
642
|
+
stmt.all(params, callback);
|
|
643
|
+
} else {
|
|
644
|
+
stmt.all(callback);
|
|
645
|
+
}
|
|
646
|
+
} catch (error) {
|
|
647
|
+
reject(error);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
const result = stmt.all(...params);
|
|
652
|
+
return isPromiseLike(result) ? yield result : result;
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
statementRun(stmt, params) {
|
|
656
|
+
return __async(this, null, function* () {
|
|
657
|
+
if (typeof stmt.run !== "function") {
|
|
658
|
+
throw new Error("SQLite statement does not support run()");
|
|
659
|
+
}
|
|
660
|
+
if (stmt.run.length >= 2) {
|
|
661
|
+
return yield new Promise((resolve, reject) => {
|
|
662
|
+
const callback = function(err) {
|
|
663
|
+
var _a;
|
|
664
|
+
if (err) return reject(err);
|
|
665
|
+
resolve({ changes: (_a = this == null ? void 0 : this.changes) != null ? _a : 0, lastID: this == null ? void 0 : this.lastID });
|
|
666
|
+
};
|
|
667
|
+
try {
|
|
668
|
+
if (params.length > 0) {
|
|
669
|
+
stmt.run(params, callback);
|
|
670
|
+
} else {
|
|
671
|
+
stmt.run(callback);
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
reject(error);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
const result = stmt.run(...params);
|
|
679
|
+
return isPromiseLike(result) ? yield result : result;
|
|
680
|
+
});
|
|
681
|
+
}
|
|
593
682
|
_query(_0) {
|
|
594
683
|
return __async(this, arguments, function* (sql, params = []) {
|
|
595
|
-
const stmt = yield this.
|
|
684
|
+
const stmt = yield this.prepareStatement(sql);
|
|
596
685
|
try {
|
|
597
|
-
const rows = yield
|
|
686
|
+
const rows = yield this.statementAll(stmt, params);
|
|
598
687
|
return rows;
|
|
599
688
|
} finally {
|
|
600
|
-
yield
|
|
689
|
+
yield this.finalizeStatement(stmt);
|
|
601
690
|
}
|
|
602
691
|
});
|
|
603
692
|
}
|
|
@@ -611,12 +700,12 @@ ${sql}
|
|
|
611
700
|
throw err;
|
|
612
701
|
});
|
|
613
702
|
}
|
|
614
|
-
const stmt = yield this.
|
|
703
|
+
const stmt = yield this.prepareStatement(sql);
|
|
615
704
|
try {
|
|
616
|
-
const info = yield
|
|
705
|
+
const info = yield this.statementRun(stmt, params);
|
|
617
706
|
return info;
|
|
618
707
|
} finally {
|
|
619
|
-
yield
|
|
708
|
+
yield this.finalizeStatement(stmt);
|
|
620
709
|
}
|
|
621
710
|
});
|
|
622
711
|
}
|
|
@@ -632,13 +721,13 @@ ${sql}
|
|
|
632
721
|
).filter((s) => s.trim() !== "");
|
|
633
722
|
const infos = yield statements.reduce((prev, statement) => __async(this, null, function* () {
|
|
634
723
|
const infos2 = yield prev;
|
|
635
|
-
const stmt = yield this.
|
|
724
|
+
const stmt = yield this.prepareStatement(`${statement};`);
|
|
636
725
|
try {
|
|
637
|
-
const info = yield
|
|
726
|
+
const info = yield this.statementRun(stmt, params);
|
|
638
727
|
infos2.push(info);
|
|
639
728
|
return infos2;
|
|
640
729
|
} finally {
|
|
641
|
-
yield
|
|
730
|
+
yield this.finalizeStatement(stmt);
|
|
642
731
|
}
|
|
643
732
|
}), Promise.resolve([null])).then((infos2) => {
|
|
644
733
|
return infos2.filter((info) => info !== null);
|
|
@@ -672,6 +761,9 @@ function toError(error) {
|
|
|
672
761
|
return new Error(String(error));
|
|
673
762
|
}
|
|
674
763
|
var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
764
|
+
static isSQLRunner(conn) {
|
|
765
|
+
return !!conn && typeof conn.query === "function" && typeof conn.execute === "function" && typeof conn.end === "function";
|
|
766
|
+
}
|
|
675
767
|
static create(configFile, conn) {
|
|
676
768
|
return __async(this, null, function* () {
|
|
677
769
|
const configReader = new FileMigrationConfigReader(configFile);
|
|
@@ -727,21 +819,27 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
727
819
|
create(config, conn) {
|
|
728
820
|
return __async(this, null, function* () {
|
|
729
821
|
let sqlrunner;
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
822
|
+
let driverConnection = conn;
|
|
823
|
+
if (_MigrationRunnerFactory.isSQLRunner(conn)) {
|
|
824
|
+
sqlrunner = conn;
|
|
825
|
+
driverConnection = null;
|
|
826
|
+
} else {
|
|
827
|
+
switch (config.database) {
|
|
828
|
+
case "sql":
|
|
829
|
+
sqlrunner = new SQLRunner(conn);
|
|
830
|
+
break;
|
|
831
|
+
case "sqlite":
|
|
832
|
+
sqlrunner = new SQLiteRunner(conn);
|
|
833
|
+
break;
|
|
834
|
+
default:
|
|
835
|
+
throw ConfigurationError.unknownDatabaseType(config.database);
|
|
836
|
+
}
|
|
739
837
|
}
|
|
740
838
|
const setup = new MigrationSetup(sqlrunner, config);
|
|
741
839
|
const read_strategy = this.getReadStategy(config);
|
|
742
840
|
const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
|
|
743
841
|
yield setup.setup();
|
|
744
|
-
return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner,
|
|
842
|
+
return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, driverConnection);
|
|
745
843
|
});
|
|
746
844
|
}
|
|
747
845
|
createEmpty(config) {
|
|
@@ -905,6 +1003,11 @@ var MySQLMigrationRunner = class {
|
|
|
905
1003
|
}
|
|
906
1004
|
});
|
|
907
1005
|
}
|
|
1006
|
+
query(sql, params) {
|
|
1007
|
+
return __async(this, null, function* () {
|
|
1008
|
+
return yield this.sqlrunner.query(sql, params);
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
908
1011
|
init(config_file2) {
|
|
909
1012
|
return __async(this, null, function* () {
|
|
910
1013
|
try {
|
|
@@ -1001,6 +1104,224 @@ var MigrationCreator = class {
|
|
|
1001
1104
|
}
|
|
1002
1105
|
};
|
|
1003
1106
|
|
|
1107
|
+
// framework/SeedRunner.ts
|
|
1108
|
+
var import_fs4 = __toESM(require("fs"));
|
|
1109
|
+
var import_path2 = __toESM(require("path"));
|
|
1110
|
+
var import_ajv = __toESM(require("ajv"));
|
|
1111
|
+
var import_ajv_formats = __toESM(require("ajv-formats"));
|
|
1112
|
+
function resolveAlias(name, aliasMap) {
|
|
1113
|
+
var _a;
|
|
1114
|
+
if (!aliasMap) return name;
|
|
1115
|
+
return (_a = aliasMap[name]) != null ? _a : name;
|
|
1116
|
+
}
|
|
1117
|
+
function walkForFile(rootDir, fileName) {
|
|
1118
|
+
if (!import_fs4.default.existsSync(rootDir)) return null;
|
|
1119
|
+
const entries = import_fs4.default.readdirSync(rootDir, { withFileTypes: true });
|
|
1120
|
+
for (const entry of entries) {
|
|
1121
|
+
const full = import_path2.default.join(rootDir, entry.name);
|
|
1122
|
+
if (entry.isDirectory()) {
|
|
1123
|
+
const found = walkForFile(full, fileName);
|
|
1124
|
+
if (found) return found;
|
|
1125
|
+
} else if (entry.isFile() && entry.name === fileName) {
|
|
1126
|
+
return full;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
function resolveMigrationsDir(migrationConfig, options) {
|
|
1132
|
+
var _a;
|
|
1133
|
+
const fromOptions = options.migrationsDir;
|
|
1134
|
+
const fromConfig = (_a = migrationConfig.seeds) == null ? void 0 : _a.migrationsDir;
|
|
1135
|
+
const dir = fromOptions != null ? fromOptions : fromConfig;
|
|
1136
|
+
if (!dir) {
|
|
1137
|
+
throw new Error("Seed migrationsDir not configured. Set seeds.migrationsDir in proper.json or pass it explicitly.");
|
|
1138
|
+
}
|
|
1139
|
+
return dir;
|
|
1140
|
+
}
|
|
1141
|
+
function mergeSeedConfig(migrationConfig, options) {
|
|
1142
|
+
var _a, _b, _c, _d;
|
|
1143
|
+
const names = options.names && options.names.length ? options.names : ((_a = migrationConfig.seeds) == null ? void 0 : _a.list) && migrationConfig.seeds.list.length ? migrationConfig.seeds.list : [];
|
|
1144
|
+
if (!names.length) {
|
|
1145
|
+
throw new Error("No seed names provided and no seeds.list defined in proper config");
|
|
1146
|
+
}
|
|
1147
|
+
const migrationsDir = resolveMigrationsDir(migrationConfig, options);
|
|
1148
|
+
const dataDir = (_c = options.dataDir) != null ? _c : (_b = migrationConfig.seeds) == null ? void 0 : _b.dataDir;
|
|
1149
|
+
const validate = options.validate !== void 0 ? options.validate : true;
|
|
1150
|
+
const transactional = (_d = options.transactional) != null ? _d : "none";
|
|
1151
|
+
const finalOptions = __spreadProps(__spreadValues({}, options), {
|
|
1152
|
+
migrationsDir,
|
|
1153
|
+
dataDir,
|
|
1154
|
+
validate,
|
|
1155
|
+
transactional
|
|
1156
|
+
});
|
|
1157
|
+
return { names, finalOptions };
|
|
1158
|
+
}
|
|
1159
|
+
function resolveSqlPair(name, migrationsDir) {
|
|
1160
|
+
const up = walkForFile(migrationsDir, `${name}.up.sql`);
|
|
1161
|
+
const down = walkForFile(migrationsDir, `${name}.down.sql`);
|
|
1162
|
+
if (up && down) {
|
|
1163
|
+
return { upPath: up, downPath: down };
|
|
1164
|
+
}
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
function resolveModule(name, migrationsDir) {
|
|
1168
|
+
const ts = walkForFile(migrationsDir, `${name}.ts`);
|
|
1169
|
+
if (ts) return { kind: "ts", modulePath: ts };
|
|
1170
|
+
const js = walkForFile(migrationsDir, `${name}.js`);
|
|
1171
|
+
if (js) return { kind: "js", modulePath: js };
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
function resolveSeed(name, migrationConfig, options) {
|
|
1175
|
+
return __async(this, null, function* () {
|
|
1176
|
+
var _a, _b;
|
|
1177
|
+
const migrationsDir = resolveMigrationsDir(migrationConfig, options);
|
|
1178
|
+
const alias = resolveAlias(name, options.aliasMap);
|
|
1179
|
+
const sqlPair = resolveSqlPair(name, migrationsDir);
|
|
1180
|
+
if (sqlPair) {
|
|
1181
|
+
return {
|
|
1182
|
+
kind: "sql",
|
|
1183
|
+
name,
|
|
1184
|
+
alias,
|
|
1185
|
+
upPath: sqlPair.upPath,
|
|
1186
|
+
downPath: sqlPair.downPath,
|
|
1187
|
+
dataPath: null,
|
|
1188
|
+
schemaPath: null
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
const module2 = resolveModule(name, migrationsDir);
|
|
1192
|
+
if (!module2) {
|
|
1193
|
+
throw new Error(`Seed implementation not found for "${name}" under ${migrationsDir}`);
|
|
1194
|
+
}
|
|
1195
|
+
const dataDir = (_b = options.dataDir) != null ? _b : (_a = migrationConfig.seeds) == null ? void 0 : _a.dataDir;
|
|
1196
|
+
let dataPath = null;
|
|
1197
|
+
let schemaPath = null;
|
|
1198
|
+
if (dataDir) {
|
|
1199
|
+
dataPath = walkForFile(dataDir, `${alias}.json`);
|
|
1200
|
+
schemaPath = walkForFile(dataDir, `${alias}.schema.json`);
|
|
1201
|
+
}
|
|
1202
|
+
return {
|
|
1203
|
+
kind: module2.kind,
|
|
1204
|
+
name,
|
|
1205
|
+
alias,
|
|
1206
|
+
upPath: module2.modulePath,
|
|
1207
|
+
downPath: module2.modulePath,
|
|
1208
|
+
dataPath,
|
|
1209
|
+
schemaPath
|
|
1210
|
+
};
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
function loadJson(filePath) {
|
|
1214
|
+
return __async(this, null, function* () {
|
|
1215
|
+
const content = yield import_fs4.default.promises.readFile(filePath, "utf8");
|
|
1216
|
+
return JSON.parse(content);
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
function createValidator() {
|
|
1220
|
+
const ajv = new import_ajv.default({ allErrors: true, strict: false });
|
|
1221
|
+
(0, import_ajv_formats.default)(ajv);
|
|
1222
|
+
return ajv;
|
|
1223
|
+
}
|
|
1224
|
+
function validateData(schemaPath, data, validate, log) {
|
|
1225
|
+
return __async(this, null, function* () {
|
|
1226
|
+
if (!validate || !schemaPath) return;
|
|
1227
|
+
const content = yield import_fs4.default.promises.readFile(schemaPath, "utf8");
|
|
1228
|
+
const schema = JSON.parse(content);
|
|
1229
|
+
const ajv = createValidator();
|
|
1230
|
+
const validateFn = ajv.compile(schema);
|
|
1231
|
+
const ok = validateFn(data);
|
|
1232
|
+
if (!ok) {
|
|
1233
|
+
log == null ? void 0 : log(`Validation failed for seed data (${schemaPath})`);
|
|
1234
|
+
throw new Error(`Seed data validation failed: ${ajv.errorsText(validateFn.errors || [])}`);
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
function runSqlSeed(runner, resolved, direction) {
|
|
1239
|
+
return __async(this, null, function* () {
|
|
1240
|
+
const sqlPath = direction === "up" ? resolved.upPath : resolved.downPath;
|
|
1241
|
+
const sql = yield import_fs4.default.promises.readFile(sqlPath, "utf8");
|
|
1242
|
+
yield runner.query(sql);
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
function runModuleSeed(runner, resolved, migrationConfig, options, direction) {
|
|
1246
|
+
return __async(this, null, function* () {
|
|
1247
|
+
const { log } = options;
|
|
1248
|
+
const module2 = yield import(import_path2.default.resolve(resolved.upPath));
|
|
1249
|
+
const handler = module2[direction];
|
|
1250
|
+
if (typeof handler !== "function") {
|
|
1251
|
+
throw new Error(`Seed module "${resolved.name}" does not export ${direction}()`);
|
|
1252
|
+
}
|
|
1253
|
+
let data = null;
|
|
1254
|
+
if (options.preloadedData && Object.prototype.hasOwnProperty.call(options.preloadedData, resolved.name)) {
|
|
1255
|
+
data = options.preloadedData[resolved.name];
|
|
1256
|
+
} else if (resolved.dataPath) {
|
|
1257
|
+
data = yield loadJson(resolved.dataPath);
|
|
1258
|
+
}
|
|
1259
|
+
yield validateData(resolved.schemaPath, data, options.validate, log);
|
|
1260
|
+
const ctx = {
|
|
1261
|
+
data,
|
|
1262
|
+
log,
|
|
1263
|
+
dialect: migrationConfig.database || "sql"
|
|
1264
|
+
};
|
|
1265
|
+
yield handler(runner, ctx);
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function runSingleSeed(runner, migrationConfig, name, options, direction) {
|
|
1269
|
+
return __async(this, null, function* () {
|
|
1270
|
+
const resolved = yield resolveSeed(name, migrationConfig, options);
|
|
1271
|
+
if (resolved.kind === "sql") {
|
|
1272
|
+
yield runSqlSeed(runner, resolved, direction);
|
|
1273
|
+
} else {
|
|
1274
|
+
yield runModuleSeed(runner, resolved, migrationConfig, options, direction);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
function withTransactionalMode(runner, migrationConfig, names, options, direction) {
|
|
1279
|
+
return __async(this, null, function* () {
|
|
1280
|
+
const mode = options.transactional;
|
|
1281
|
+
if (mode === "runner") {
|
|
1282
|
+
yield runner.query("BEGIN");
|
|
1283
|
+
try {
|
|
1284
|
+
for (const name of names) {
|
|
1285
|
+
yield runSingleSeed(runner, migrationConfig, name, options, direction);
|
|
1286
|
+
}
|
|
1287
|
+
yield runner.query("COMMIT");
|
|
1288
|
+
} catch (err) {
|
|
1289
|
+
try {
|
|
1290
|
+
yield runner.query("ROLLBACK");
|
|
1291
|
+
} catch (e) {
|
|
1292
|
+
}
|
|
1293
|
+
throw err;
|
|
1294
|
+
}
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
if (mode === "seed") {
|
|
1298
|
+
for (const name of names) {
|
|
1299
|
+
yield runner.query("BEGIN");
|
|
1300
|
+
try {
|
|
1301
|
+
yield runSingleSeed(runner, migrationConfig, name, options, direction);
|
|
1302
|
+
yield runner.query("COMMIT");
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
try {
|
|
1305
|
+
yield runner.query("ROLLBACK");
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
}
|
|
1308
|
+
throw err;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
for (const name of names) {
|
|
1314
|
+
yield runSingleSeed(runner, migrationConfig, name, options, direction);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
function runSeedsWithRunner(runner, migrationConfig, direction, options) {
|
|
1319
|
+
return __async(this, null, function* () {
|
|
1320
|
+
const { names, finalOptions } = mergeSeedConfig(migrationConfig, options);
|
|
1321
|
+
yield withTransactionalMode(runner, migrationConfig, names, finalOptions, direction);
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1004
1325
|
// cli.ts
|
|
1005
1326
|
var args = MigrationCLIFactory.setup(process.argv);
|
|
1006
1327
|
if (!args.commands || args.commands.length === 0) {
|
|
@@ -1047,8 +1368,9 @@ pending_runner.then((runner) => __async(null, null, function* () {
|
|
|
1047
1368
|
const migrations_rollback = yield runner.getMigrations();
|
|
1048
1369
|
let downgraded = yield migration_filter(migrations_rollback, true);
|
|
1049
1370
|
downgraded = downgraded.reverse();
|
|
1050
|
-
if (args.flags.
|
|
1051
|
-
|
|
1371
|
+
if (args.flags.all) {
|
|
1372
|
+
} else {
|
|
1373
|
+
const increment = parseInt(args.flags.increment || "1");
|
|
1052
1374
|
downgraded = downgraded.slice(0, increment);
|
|
1053
1375
|
}
|
|
1054
1376
|
console.log(`Rolling back ${downgraded.length} migration(s)`);
|
|
@@ -1096,6 +1418,84 @@ pending_runner.then((runner) => __async(null, null, function* () {
|
|
|
1096
1418
|
printTable(yield Promise.all(table));
|
|
1097
1419
|
runner.close();
|
|
1098
1420
|
break;
|
|
1421
|
+
case "query":
|
|
1422
|
+
const sql_query = args.flags.query || commands[1];
|
|
1423
|
+
const format = (args.flags.format || "table").toLowerCase();
|
|
1424
|
+
if (!sql_query) {
|
|
1425
|
+
throw new CLIError('SQL query is required. Usage: proper query "SELECT * FROM table"');
|
|
1426
|
+
}
|
|
1427
|
+
const trimmed_query = sql_query.trim().toUpperCase();
|
|
1428
|
+
const is_select = trimmed_query.startsWith("SELECT");
|
|
1429
|
+
const is_cte = trimmed_query.startsWith("WITH");
|
|
1430
|
+
if (!is_select && !is_cte) {
|
|
1431
|
+
throw new CLIError("Only SELECT queries and CTEs (WITH clauses) are allowed");
|
|
1432
|
+
}
|
|
1433
|
+
const dangerous_keywords = ["INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER", "TRUNCATE", "GRANT", "REVOKE"];
|
|
1434
|
+
const has_dangerous_keyword = dangerous_keywords.some((keyword) => {
|
|
1435
|
+
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
1436
|
+
return regex.test(sql_query);
|
|
1437
|
+
});
|
|
1438
|
+
if (has_dangerous_keyword) {
|
|
1439
|
+
throw new CLIError("Query contains disallowed keywords. Only SELECT and CTE queries are permitted");
|
|
1440
|
+
}
|
|
1441
|
+
if (!["table", "json"].includes(format)) {
|
|
1442
|
+
throw new CLIError("Invalid format. Use --format table or --format json");
|
|
1443
|
+
}
|
|
1444
|
+
try {
|
|
1445
|
+
const [results] = yield runner.query(sql_query);
|
|
1446
|
+
if (Array.isArray(results) && results.length > 0) {
|
|
1447
|
+
if (format === "json") {
|
|
1448
|
+
console.log(JSON.stringify(results, null, 2));
|
|
1449
|
+
} else {
|
|
1450
|
+
const { printTable: printTable2 } = require("console-table-printer");
|
|
1451
|
+
printTable2(results);
|
|
1452
|
+
}
|
|
1453
|
+
console.log(`
|
|
1454
|
+
Returned ${results.length} row(s)`);
|
|
1455
|
+
} else if (Array.isArray(results) && results.length === 0) {
|
|
1456
|
+
console.log("Query returned 0 rows");
|
|
1457
|
+
} else {
|
|
1458
|
+
console.log("Query executed successfully");
|
|
1459
|
+
}
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
throw new CLIError(`Query execution failed: ${error.message}`);
|
|
1462
|
+
}
|
|
1463
|
+
yield runner.close();
|
|
1464
|
+
break;
|
|
1465
|
+
case "seed": {
|
|
1466
|
+
const subCommands = commands.slice(1);
|
|
1467
|
+
const actionFlag = (args.flags.action || "").toString().toLowerCase();
|
|
1468
|
+
const firstSub = (subCommands[0] || "").toString().toLowerCase();
|
|
1469
|
+
const action = actionFlag === "down" || firstSub === "down" ? "down" : "up";
|
|
1470
|
+
const positionalNames = actionFlag ? subCommands : firstSub === "up" || firstSub === "down" ? subCommands.slice(1) : subCommands;
|
|
1471
|
+
const aliasFlag = args.flags.alias;
|
|
1472
|
+
const aliasMap = {};
|
|
1473
|
+
if (aliasFlag) {
|
|
1474
|
+
const aliases = Array.isArray(aliasFlag) ? aliasFlag : [aliasFlag];
|
|
1475
|
+
for (const entry of aliases) {
|
|
1476
|
+
const eq = entry.indexOf("=");
|
|
1477
|
+
if (eq > 0) {
|
|
1478
|
+
const key = entry.slice(0, eq);
|
|
1479
|
+
const value = entry.slice(eq + 1);
|
|
1480
|
+
if (key) aliasMap[key] = value;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
const seedOptions = {
|
|
1485
|
+
migrationsDir: args.flags.migrationsDir || args.flags["migrations-dir"],
|
|
1486
|
+
dataDir: args.flags.dataDir || args.flags["data-dir"],
|
|
1487
|
+
validate: args.flags.validate === void 0 ? true : String(args.flags.validate).toLowerCase() !== "false",
|
|
1488
|
+
transactional: args.flags.transactional,
|
|
1489
|
+
aliasMap: Object.keys(aliasMap).length ? aliasMap : void 0,
|
|
1490
|
+
names: positionalNames.length ? positionalNames : void 0,
|
|
1491
|
+
log: (msg) => console.log(msg)
|
|
1492
|
+
};
|
|
1493
|
+
const reader = new FileMigrationConfigReader(config_file);
|
|
1494
|
+
const migrationConfig = reader.loadFile();
|
|
1495
|
+
yield runSeedsWithRunner(runner, migrationConfig, action, seedOptions);
|
|
1496
|
+
yield runner.close();
|
|
1497
|
+
break;
|
|
1498
|
+
}
|
|
1099
1499
|
default:
|
|
1100
1500
|
throw CLIError.unknownCommand(command);
|
|
1101
1501
|
}
|
|
@@ -1111,7 +1511,7 @@ function printUsage() {
|
|
|
1111
1511
|
console.log(`
|
|
1112
1512
|
SQL Proper - Database migration tool
|
|
1113
1513
|
|
|
1114
|
-
Usage:
|
|
1514
|
+
Usage:
|
|
1115
1515
|
proper <command> [options]
|
|
1116
1516
|
|
|
1117
1517
|
Commands:
|
|
@@ -1121,20 +1521,27 @@ Commands:
|
|
|
1121
1521
|
create Create a new migration
|
|
1122
1522
|
init Initialize a new config file
|
|
1123
1523
|
status Show migration status
|
|
1524
|
+
query Execute a SQL query and display results
|
|
1124
1525
|
help Show this help message
|
|
1125
1526
|
|
|
1126
1527
|
Options:
|
|
1127
1528
|
-c, --config Specify the config file (default: proper.json)
|
|
1128
1529
|
--increment <n> Limit the number of migrations to apply or roll back
|
|
1530
|
+
--all Roll back all completed migrations (down command only)
|
|
1531
|
+
--format <fmt> Output format for query command: 'table' (default) or 'json'
|
|
1129
1532
|
|
|
1130
1533
|
Examples:
|
|
1131
|
-
proper up
|
|
1132
|
-
proper up --increment 1
|
|
1133
|
-
proper down
|
|
1134
|
-
proper
|
|
1135
|
-
proper
|
|
1136
|
-
proper
|
|
1137
|
-
proper
|
|
1534
|
+
proper up Apply all pending migrations
|
|
1535
|
+
proper up --increment 1 Apply only the next pending migration
|
|
1536
|
+
proper down Roll back the last applied migration (default: 1)
|
|
1537
|
+
proper down --increment 3 Roll back the last 3 applied migrations
|
|
1538
|
+
proper down --all Roll back all completed migrations
|
|
1539
|
+
proper create my_migration Create a new migration named "my_migration"
|
|
1540
|
+
proper init Create a new config file
|
|
1541
|
+
proper status Show the status of all migrations
|
|
1542
|
+
proper query "select * from users" Execute a SQL query
|
|
1543
|
+
proper query "select * from users" --format json Execute a query and output as JSON
|
|
1544
|
+
proper -c custom.json up Use a custom config file
|
|
1138
1545
|
`);
|
|
1139
1546
|
}
|
|
1140
1547
|
//# sourceMappingURL=cli.js.map
|