@noego/proper 0.0.1

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 ADDED
@@ -0,0 +1,935 @@
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
21
+
22
+ // framework/MigrationRunner.ts
23
+ import fs3 from "fs";
24
+ import mysql from "mysql2/promise";
25
+ import * as sqlite from "sqlite";
26
+ import * as sqlite3 from "sqlite3";
27
+
28
+ // framework/MigrationDirectoryReader.ts
29
+ import fs from "fs";
30
+ import path from "path";
31
+
32
+ // framework/errors.ts
33
+ var MigrationError = class _MigrationError extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = "MigrationError";
37
+ Object.setPrototypeOf(this, _MigrationError.prototype);
38
+ }
39
+ };
40
+ var ConfigurationError = class _ConfigurationError extends MigrationError {
41
+ constructor(message) {
42
+ super(`Configuration Error: ${message}`);
43
+ this.name = "ConfigurationError";
44
+ Object.setPrototypeOf(this, _ConfigurationError.prototype);
45
+ }
46
+ /**
47
+ * Helper method for missing database configuration
48
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
49
+ * @returns A ConfigurationError with appropriate message
50
+ */
51
+ static missingDatabaseConfiguration(dbType) {
52
+ return new _ConfigurationError(`Missing ${dbType} configuration`);
53
+ }
54
+ /**
55
+ * Helper method for unknown database type
56
+ * @param dbType The unknown database type
57
+ * @returns A ConfigurationError with appropriate message
58
+ */
59
+ static unknownDatabaseType(dbType) {
60
+ return new _ConfigurationError(`Unknown database type: ${dbType}`);
61
+ }
62
+ /**
63
+ * Helper method for missing required configuration properties
64
+ * @param property The name of the missing property
65
+ * @returns A ConfigurationError with appropriate message
66
+ */
67
+ static missingRequiredProperty(property) {
68
+ return new _ConfigurationError(`Missing required property: ${property}`);
69
+ }
70
+ };
71
+ var DatabaseConnectionError = class _DatabaseConnectionError extends MigrationError {
72
+ constructor(message) {
73
+ super(`Database Connection Error: ${message}`);
74
+ this.name = "DatabaseConnectionError";
75
+ Object.setPrototypeOf(this, _DatabaseConnectionError.prototype);
76
+ }
77
+ /**
78
+ * Helper method for connection errors
79
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
80
+ * @param details Additional error details
81
+ * @returns A DatabaseConnectionError with appropriate message
82
+ */
83
+ static connectionFailed(dbType, details) {
84
+ const message = details ? `Failed to connect to ${dbType} database: ${details}` : `Failed to connect to ${dbType} database`;
85
+ return new _DatabaseConnectionError(message);
86
+ }
87
+ /**
88
+ * Helper method for authentication errors
89
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
90
+ * @returns A DatabaseConnectionError with appropriate message
91
+ */
92
+ static authenticationFailed(dbType) {
93
+ return new _DatabaseConnectionError(`Authentication failed for ${dbType} database`);
94
+ }
95
+ };
96
+ var MigrationExecutionError = class _MigrationExecutionError extends MigrationError {
97
+ constructor(message, migrationName, sql, originalError) {
98
+ let fullMessage = `Migration Execution Error${migrationName ? ` in '${migrationName}'` : ""}: ${message}`;
99
+ if (originalError) {
100
+ fullMessage += `
101
+ Original Error:
102
+ ${originalError.message}`;
103
+ }
104
+ super(fullMessage);
105
+ this.migrationName = migrationName;
106
+ this.sql = sql;
107
+ this.originalError = originalError;
108
+ this.name = "MigrationExecutionError";
109
+ Object.setPrototypeOf(this, _MigrationExecutionError.prototype);
110
+ }
111
+ /**
112
+ * Helper method for SQL execution errors
113
+ * @param migrationName The name of the migration
114
+ * @param sql The SQL that caused the error
115
+ * @param originalError The original error thrown by the database driver
116
+ * @returns A MigrationExecutionError with appropriate message
117
+ */
118
+ static sqlExecutionFailed(migrationName, sql, originalError) {
119
+ return new _MigrationExecutionError(
120
+ originalError.message,
121
+ migrationName,
122
+ sql,
123
+ originalError
124
+ );
125
+ }
126
+ /**
127
+ * Helper method for missing migration file errors
128
+ * @param filename The missing file
129
+ * @returns A MigrationExecutionError with appropriate message
130
+ */
131
+ static missingMigrationFile(filename) {
132
+ return new _MigrationExecutionError(`Migration file not found: ${filename}`);
133
+ }
134
+ /**
135
+ * Helper method for invalid migration file format errors
136
+ * @param filename The invalid file
137
+ * @param details Additional error details
138
+ * @returns A MigrationExecutionError with appropriate message
139
+ */
140
+ static invalidMigrationFile(filename, details) {
141
+ const message = details ? `Invalid migration file format in ${filename}: ${details}` : `Invalid migration file format in ${filename}`;
142
+ return new _MigrationExecutionError(message);
143
+ }
144
+ };
145
+ var CLIError = class _CLIError extends MigrationError {
146
+ constructor(message) {
147
+ super(`CLI Error: ${message}`);
148
+ this.name = "CLIError";
149
+ Object.setPrototypeOf(this, _CLIError.prototype);
150
+ }
151
+ /**
152
+ * Helper method for missing command errors
153
+ * @returns A CLIError with appropriate message
154
+ */
155
+ static missingCommand() {
156
+ return new _CLIError("No command specified. Run with --help for usage information.");
157
+ }
158
+ /**
159
+ * Helper method for unknown command errors
160
+ * @param command The unknown command
161
+ * @returns A CLIError with appropriate message
162
+ */
163
+ static unknownCommand(command) {
164
+ return new _CLIError(`Unknown command: ${command}. Run with --help for usage information.`);
165
+ }
166
+ /**
167
+ * Helper method for missing required argument errors
168
+ * @param argument The missing argument
169
+ * @returns A CLIError with appropriate message
170
+ */
171
+ static missingRequiredArgument(argument) {
172
+ return new _CLIError(`Missing required argument: ${argument}`);
173
+ }
174
+ };
175
+
176
+ // framework/MigrationNode.ts
177
+ var MigrationNode = class {
178
+ constructor(name) {
179
+ this.name = name || "";
180
+ }
181
+ };
182
+ var SqlMigrationNode = class extends MigrationNode {
183
+ constructor(conn, table, key, up_file, sql_up, down_file, sql_down) {
184
+ super(key);
185
+ this.conn = conn;
186
+ this.table = table;
187
+ this.key = key;
188
+ this.up_file = up_file;
189
+ this.sql_up = sql_up;
190
+ this.down_file = down_file;
191
+ this.sql_down = sql_down;
192
+ }
193
+ up_sql() {
194
+ return this.sql_up;
195
+ }
196
+ down_sql() {
197
+ return this.sql_down;
198
+ }
199
+ get_key() {
200
+ return this.key;
201
+ }
202
+ status() {
203
+ return __async(this, null, function* () {
204
+ try {
205
+ const result = yield this.conn.query(`
206
+ select *
207
+ from ${this.table}
208
+ where migration_key = ?;
209
+ `, [this.key]);
210
+ if (result.length > 0 && result[0].length > 0) {
211
+ return {
212
+ completed: true
213
+ };
214
+ } else {
215
+ return {
216
+ completed: false
217
+ };
218
+ }
219
+ } catch (error) {
220
+ throw new MigrationExecutionError(
221
+ `Error checking status for migration`,
222
+ this.key,
223
+ void 0,
224
+ error instanceof Error ? error : new Error(String(error))
225
+ );
226
+ }
227
+ });
228
+ }
229
+ up() {
230
+ return __async(this, null, function* () {
231
+ try {
232
+ yield this.conn.execute(this.sql_up);
233
+ } catch (error) {
234
+ throw new MigrationExecutionError(
235
+ `Error executing UP migration`,
236
+ this.key,
237
+ this.sql_up,
238
+ error instanceof Error ? error : new Error(String(error))
239
+ );
240
+ }
241
+ try {
242
+ yield this.conn.execute(`
243
+ insert into ${this.table} (migration_key,up,down)
244
+ values (?,?,?)
245
+ `, [this.key, this.up_file, this.down_file]);
246
+ } catch (error) {
247
+ throw new MigrationExecutionError(
248
+ `Error recording migration completion`,
249
+ this.key,
250
+ void 0,
251
+ error instanceof Error ? error : new Error(String(error))
252
+ );
253
+ }
254
+ });
255
+ }
256
+ down() {
257
+ return __async(this, null, function* () {
258
+ try {
259
+ yield this.conn.execute(this.sql_down);
260
+ } catch (error) {
261
+ throw new MigrationExecutionError(
262
+ `Error executing DOWN migration`,
263
+ this.key,
264
+ this.sql_down,
265
+ error instanceof Error ? error : new Error(String(error))
266
+ );
267
+ }
268
+ try {
269
+ yield this.conn.execute(`
270
+ delete from ${this.table}
271
+ where migration_key = ?
272
+ `, [this.key]);
273
+ } catch (error) {
274
+ throw new MigrationExecutionError(
275
+ `Error removing migration record`,
276
+ this.key,
277
+ void 0,
278
+ error instanceof Error ? error : new Error(String(error))
279
+ );
280
+ }
281
+ });
282
+ }
283
+ };
284
+
285
+ // framework/SqlMigrationBuilder.ts
286
+ var SqlMigrationBuilder = class {
287
+ constructor(key) {
288
+ this.key = key;
289
+ this.up = "";
290
+ this.up_file = "";
291
+ this.down = "";
292
+ this.down_file = "";
293
+ }
294
+ set_up(file, up) {
295
+ this.up_file = file;
296
+ this.up = up;
297
+ }
298
+ set_down(file, down) {
299
+ this.down_file = file;
300
+ this.down = down;
301
+ }
302
+ build(table, conn) {
303
+ return new SqlMigrationNode(conn, table, this.key, this.up_file, this.up, this.down_file, this.down);
304
+ }
305
+ };
306
+
307
+ // framework/MigrationDirectoryReader.ts
308
+ var MigrationDirectoryReader = class {
309
+ constructor(directory, read_strategy, sqlrunner) {
310
+ this.directory = directory;
311
+ this.read_strategy = read_strategy;
312
+ this.sqlrunner = sqlrunner;
313
+ }
314
+ loadMigrations(table, connection) {
315
+ fs.existsSync(this.directory) || fs.mkdirSync(this.directory);
316
+ const dir_content = fs.readdirSync(this.directory, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name);
317
+ let migration_files = dir_content.map((file) => {
318
+ return {
319
+ migration_key: file.replace(/\.(up|down)\.(sql|js)/i, "").toLowerCase(),
320
+ directory: this.directory,
321
+ relative_path: path.join(this.directory, file),
322
+ file
323
+ };
324
+ }).sort();
325
+ const migration_sorter = {};
326
+ migration_files.forEach((migration) => {
327
+ const builder = migration_sorter[migration.migration_key] = migration_sorter[migration.migration_key] || new SqlMigrationBuilder(migration.migration_key);
328
+ this.loadMigration(builder, migration.relative_path);
329
+ });
330
+ const keys = Object.keys(migration_sorter);
331
+ keys.sort();
332
+ const built = keys.map((key) => {
333
+ return migration_sorter[key].build(table, this.sqlrunner);
334
+ });
335
+ return built;
336
+ }
337
+ loadMigration(builder, file) {
338
+ const is_sql = /sql$/i.test(file);
339
+ const is_js = /js$/i.test(file);
340
+ const is_up = /up\.(js|sql)/i.test(file);
341
+ const is_down = /down\.(js|sql)/i.test(file);
342
+ if (is_sql && is_up) {
343
+ const content = this.sql_up(file);
344
+ builder.set_up(file, content);
345
+ } else if (is_sql && is_down) {
346
+ const content = this.sql_down(file);
347
+ builder.set_down(file, content);
348
+ } else {
349
+ throw new Error(`Invalid migration file: ${file}`);
350
+ }
351
+ return builder;
352
+ }
353
+ sql_up(file) {
354
+ let content = fs.readFileSync(file).toString();
355
+ content = this.read_strategy(content);
356
+ return content.trim();
357
+ }
358
+ sql_down(file) {
359
+ let content = fs.readFileSync(file).toString();
360
+ content = this.read_strategy(content);
361
+ return content.trim();
362
+ }
363
+ };
364
+
365
+ // framework/MigrationSetup.ts
366
+ import fs2 from "fs";
367
+ var MigrationSetup = class {
368
+ constructor(sqlrunner, config) {
369
+ this.sqlrunner = sqlrunner;
370
+ this.config = config;
371
+ }
372
+ setup() {
373
+ return __async(this, null, function* () {
374
+ fs2.existsSync(this.config.migration_folder) || fs2.mkdirSync(this.config.migration_folder);
375
+ const tableName = this.config.migration_table;
376
+ const createTableSql = this.config.database === "sqlite" ? `CREATE TABLE IF NOT EXISTS ${tableName} (
377
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
378
+ migration_key TEXT,
379
+ up TEXT,
380
+ down TEXT,
381
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
382
+ )` : `CREATE TABLE IF NOT EXISTS ${tableName} (
383
+ id INT AUTO_INCREMENT PRIMARY KEY,
384
+ migration_key VARCHAR(255),
385
+ up VARCHAR(255),
386
+ down VARCHAR(255),
387
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
388
+ )`;
389
+ yield this.sqlrunner.query(createTableSql);
390
+ });
391
+ }
392
+ teardown() {
393
+ return __async(this, null, function* () {
394
+ yield this.sqlrunner.execute(`DROP TABLE ${this.config.migration_table}`);
395
+ });
396
+ }
397
+ };
398
+
399
+ // framework/MigrationFilter.ts
400
+ function migration_filter(_0) {
401
+ return __async(this, arguments, function* (migrations, completed = true, keep = []) {
402
+ if (migrations.length === 0) {
403
+ return keep;
404
+ }
405
+ const [first, ...rest] = migrations;
406
+ const status = yield first.status();
407
+ if (status.completed == completed) {
408
+ keep.push(first);
409
+ }
410
+ return migration_filter(rest, completed, keep);
411
+ });
412
+ }
413
+
414
+ // framework/MigrationDialectParser.ts
415
+ function MySqlDialectParser(sql) {
416
+ const lines = sql.split("\n");
417
+ let result = "";
418
+ let isInMySQLBlock = true;
419
+ const startSQLRegex = /^\s*--\s*\[\s*(sql|mysql)\s*\]\s*$/i;
420
+ const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
421
+ for (const line of lines) {
422
+ const trimmed = line.trim();
423
+ if (startSQLRegex.test(trimmed)) {
424
+ isInMySQLBlock = true;
425
+ result += line + "\n";
426
+ continue;
427
+ }
428
+ if (anyDialectRegex.test(trimmed) && !startSQLRegex.test(trimmed)) {
429
+ isInMySQLBlock = false;
430
+ continue;
431
+ }
432
+ if (!isInMySQLBlock && line.toLowerCase().includes("create index")) {
433
+ isInMySQLBlock = true;
434
+ }
435
+ if (isInMySQLBlock) {
436
+ result += line + "\n";
437
+ }
438
+ }
439
+ return result;
440
+ }
441
+ function SqliteDialectParser(sql) {
442
+ const lines = sql.split("\n");
443
+ let result = "";
444
+ let isInSqliteBlock = true;
445
+ const startSqliteRegex = /^\s*--\s*\[\s*sqlite\s*\]\s*$/i;
446
+ const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
447
+ for (const line of lines) {
448
+ const trimmed = line.trim();
449
+ if (startSqliteRegex.test(trimmed)) {
450
+ isInSqliteBlock = true;
451
+ result += line + "\n";
452
+ continue;
453
+ } else if (anyDialectRegex.test(trimmed) && !startSqliteRegex.test(trimmed)) {
454
+ isInSqliteBlock = false;
455
+ continue;
456
+ }
457
+ if (!isInSqliteBlock && line.toLowerCase().includes("create index")) {
458
+ isInSqliteBlock = true;
459
+ }
460
+ if (isInSqliteBlock) {
461
+ result += line + "\n";
462
+ }
463
+ }
464
+ return result;
465
+ }
466
+
467
+ // framework/SQLRunner.ts
468
+ var BaseSQLRunner = class {
469
+ /**
470
+ * Ensures that both MySQL and SQLite return the same tuple shape that callers
471
+ * expect: `[rowsOrResult, extra]`. For SQLite there is no `extra` metadata
472
+ * comparable to MySQL's `FieldPacket[]`, so we just use `undefined`.
473
+ */
474
+ query(_0) {
475
+ return __async(this, arguments, function* (sql, params = []) {
476
+ const result = yield this._query(sql, params);
477
+ return Array.isArray(result) && result.length === 2 ? result : [result, void 0];
478
+ });
479
+ }
480
+ execute(_0) {
481
+ return __async(this, arguments, function* (sql, params = []) {
482
+ const result = yield this._execute(sql, params);
483
+ return Array.isArray(result) && result.length === 2 ? result : [result, void 0];
484
+ });
485
+ }
486
+ end() {
487
+ return __async(this, null, function* () {
488
+ yield this._end();
489
+ });
490
+ }
491
+ };
492
+ var SQLRunner = class extends BaseSQLRunner {
493
+ constructor(connection) {
494
+ super();
495
+ this.connection = connection;
496
+ }
497
+ // The MySQL driver already returns the correct tuple shapes.
498
+ _query(_0) {
499
+ return __async(this, arguments, function* (sql, params = []) {
500
+ return yield this.connection.query(sql, params);
501
+ });
502
+ }
503
+ _execute(_0) {
504
+ return __async(this, arguments, function* (sql, params = []) {
505
+ return yield this.connection.execute(sql, params);
506
+ });
507
+ }
508
+ _end() {
509
+ return __async(this, null, function* () {
510
+ if (this.connection) {
511
+ yield this.connection.end();
512
+ }
513
+ });
514
+ }
515
+ };
516
+ var SQLiteRunner = class extends BaseSQLRunner {
517
+ constructor(connection) {
518
+ super();
519
+ this.connection = connection;
520
+ }
521
+ _query(_0) {
522
+ return __async(this, arguments, function* (sql, params = []) {
523
+ const stmt = yield this.connection.prepare(sql);
524
+ try {
525
+ const rows = yield stmt.all(...params);
526
+ return rows;
527
+ } finally {
528
+ yield stmt.finalize();
529
+ }
530
+ });
531
+ }
532
+ _execute(_0) {
533
+ return __async(this, arguments, function* (sql, params = []) {
534
+ if (this.isMultiStatement(sql)) {
535
+ return yield this.executeMultiStatement(sql, params).catch((err) => {
536
+ console.error(`Error executing multi-statement SQL:
537
+ ${sql}
538
+ `, err);
539
+ throw err;
540
+ });
541
+ }
542
+ const stmt = yield this.connection.prepare(sql);
543
+ try {
544
+ const info = yield stmt.run(...params);
545
+ return info;
546
+ } finally {
547
+ yield stmt.finalize();
548
+ }
549
+ });
550
+ }
551
+ isMultiStatement(sql) {
552
+ return sql.split(";").length > 1;
553
+ }
554
+ executeMultiStatement(_0) {
555
+ return __async(this, arguments, function* (sql, params = []) {
556
+ const statements = sql.split(";").map(
557
+ (s) => s.split("\n").map(
558
+ (s2) => this.removeComments(s2)
559
+ ).filter((s2) => s2.trim() != "").join("\n")
560
+ ).filter((s) => s.trim() !== "");
561
+ const infos = yield statements.reduce((prev, statement) => __async(this, null, function* () {
562
+ const infos2 = yield prev;
563
+ const stmt = yield this.connection.prepare(`${statement};`);
564
+ try {
565
+ const info = yield stmt.run(...params);
566
+ infos2.push(info);
567
+ return infos2;
568
+ } finally {
569
+ yield stmt.finalize();
570
+ }
571
+ }), Promise.resolve([null])).then((infos2) => {
572
+ return infos2.filter((info) => info !== null);
573
+ });
574
+ return infos.reduce((acc, latest) => {
575
+ if (latest) {
576
+ acc.stmt = latest.stmt;
577
+ acc.lastID = latest.lastID;
578
+ acc.changes += latest.changes;
579
+ }
580
+ return acc;
581
+ });
582
+ });
583
+ }
584
+ removeComments(sql) {
585
+ sql = sql.replace(/--.*$/gm, "");
586
+ return sql;
587
+ }
588
+ _end() {
589
+ return __async(this, null, function* () {
590
+ if (this.connection) {
591
+ yield this.connection.close();
592
+ }
593
+ });
594
+ }
595
+ };
596
+
597
+ // framework/MigrationRunner.ts
598
+ function toError(error) {
599
+ if (error instanceof Error) return error;
600
+ return new Error(String(error));
601
+ }
602
+ var MigrationRunnerFactory = class _MigrationRunnerFactory {
603
+ static create(configFile, conn) {
604
+ return __async(this, null, function* () {
605
+ const configReader = new FileMigrationConfigReader(configFile);
606
+ const config = configReader.loadFile();
607
+ if (!conn) {
608
+ conn = yield this.createConnection(config);
609
+ }
610
+ return new _MigrationRunnerFactory().create(config, conn);
611
+ });
612
+ }
613
+ static createConnection(config) {
614
+ return __async(this, null, function* () {
615
+ let conn = null;
616
+ switch (config.database) {
617
+ case "sql":
618
+ if (!config.sql) {
619
+ throw ConfigurationError.missingDatabaseConfiguration("sql");
620
+ }
621
+ const settings = Object.assign({
622
+ password: process.env.SQL_PASSWORD
623
+ }, config.sql);
624
+ try {
625
+ conn = yield mysql.createConnection(settings);
626
+ return conn;
627
+ } catch (error) {
628
+ throw DatabaseConnectionError.connectionFailed("sql", toError(error).message);
629
+ }
630
+ case "sqlite":
631
+ if (!config.sqlite) {
632
+ throw ConfigurationError.missingDatabaseConfiguration("sqlite");
633
+ }
634
+ try {
635
+ conn = yield sqlite.open({
636
+ filename: config.sqlite.database,
637
+ driver: sqlite3.Database
638
+ });
639
+ return conn;
640
+ } catch (error) {
641
+ throw DatabaseConnectionError.connectionFailed("sqlite", toError(error).message);
642
+ }
643
+ default:
644
+ throw ConfigurationError.unknownDatabaseType(config.database);
645
+ }
646
+ });
647
+ }
648
+ static createEmpty(configFile) {
649
+ return __async(this, null, function* () {
650
+ const configReader = new FileMigrationConfigReader(configFile);
651
+ const config = configReader.loadFile();
652
+ return new _MigrationRunnerFactory().createEmpty(config);
653
+ });
654
+ }
655
+ create(config, conn) {
656
+ return __async(this, null, function* () {
657
+ let sqlrunner;
658
+ switch (config.database) {
659
+ case "sql":
660
+ sqlrunner = new SQLRunner(conn);
661
+ break;
662
+ case "sqlite":
663
+ sqlrunner = new SQLiteRunner(conn);
664
+ break;
665
+ default:
666
+ throw ConfigurationError.unknownDatabaseType(config.database);
667
+ }
668
+ const setup = new MigrationSetup(sqlrunner, config);
669
+ const read_strategy = this.getReadStategy(config);
670
+ const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
671
+ yield setup.setup();
672
+ return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, conn);
673
+ });
674
+ }
675
+ createEmpty(config) {
676
+ return __async(this, null, function* () {
677
+ const conn = null;
678
+ let sqlrunner;
679
+ switch (config.database) {
680
+ case "sql":
681
+ sqlrunner = new SQLRunner(conn);
682
+ break;
683
+ case "sqlite":
684
+ sqlrunner = new SQLiteRunner(conn);
685
+ break;
686
+ default:
687
+ throw ConfigurationError.unknownDatabaseType(config.database);
688
+ }
689
+ const setup = new MigrationSetup(sqlrunner, config);
690
+ const read_strategy = this.getReadStategy(config);
691
+ const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
692
+ return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, conn);
693
+ });
694
+ }
695
+ getReadStategy(config) {
696
+ switch (config.database) {
697
+ case "sql":
698
+ return MySqlDialectParser;
699
+ case "sqlite":
700
+ return SqliteDialectParser;
701
+ default:
702
+ throw ConfigurationError.unknownDatabaseType(config.database);
703
+ }
704
+ }
705
+ };
706
+ var MySQLMigrationRunner = class {
707
+ constructor(config, directory, setupRunner, sqlrunner, connection) {
708
+ this.config = config;
709
+ this.directory = directory;
710
+ this.setupRunner = setupRunner;
711
+ this.sqlrunner = sqlrunner;
712
+ this.connection = connection;
713
+ }
714
+ setup() {
715
+ return __async(this, null, function* () {
716
+ try {
717
+ yield this.setupRunner.setup();
718
+ } catch (error) {
719
+ throw new MigrationExecutionError("Failed to set up migration database", void 0, void 0, toError(error));
720
+ }
721
+ });
722
+ }
723
+ terminate() {
724
+ return __async(this, null, function* () {
725
+ try {
726
+ yield this.setupRunner.teardown();
727
+ } catch (error) {
728
+ throw new MigrationExecutionError("Failed to tear down migration database", void 0, void 0, toError(error));
729
+ }
730
+ });
731
+ }
732
+ getMigrationsHistory() {
733
+ return __async(this, null, function* () {
734
+ try {
735
+ const results = yield this.sqlrunner.query(`
736
+ select *
737
+ from ${this.config.migration_table}
738
+ `);
739
+ return results[0];
740
+ } catch (error) {
741
+ throw new MigrationExecutionError("Failed to get migration history", void 0, void 0, toError(error));
742
+ }
743
+ });
744
+ }
745
+ getMigrations() {
746
+ return __async(this, null, function* () {
747
+ try {
748
+ return this.directory.loadMigrations(this.config.migration_table, this.connection);
749
+ } catch (error) {
750
+ throw new MigrationExecutionError("Failed to load migrations", void 0, void 0, toError(error));
751
+ }
752
+ });
753
+ }
754
+ getPendingMigrations() {
755
+ return __async(this, null, function* () {
756
+ try {
757
+ const migrations = yield this.getMigrations();
758
+ return migration_filter(migrations, false);
759
+ } catch (error) {
760
+ if (error instanceof MigrationExecutionError) {
761
+ throw error;
762
+ }
763
+ throw new MigrationExecutionError("Failed to get pending migrations", void 0, void 0, toError(error));
764
+ }
765
+ });
766
+ }
767
+ getCompletedMigrations() {
768
+ return __async(this, null, function* () {
769
+ try {
770
+ const migrations = yield this.getMigrations();
771
+ return migration_filter(migrations, true);
772
+ } catch (error) {
773
+ if (error instanceof MigrationExecutionError) {
774
+ throw error;
775
+ }
776
+ throw new MigrationExecutionError("Failed to get completed migrations", void 0, void 0, toError(error));
777
+ }
778
+ });
779
+ }
780
+ migrate(migrationNodes, forward) {
781
+ return __async(this, null, function* () {
782
+ for (let node of migrationNodes) {
783
+ try {
784
+ if (forward) {
785
+ yield node.up();
786
+ } else {
787
+ yield node.down();
788
+ }
789
+ } catch (error) {
790
+ throw new MigrationExecutionError(
791
+ `Failed to ${forward ? "apply" : "rollback"} migration`,
792
+ node.name || String(node),
793
+ forward ? node.up_sql() : node.down_sql(),
794
+ toError(error)
795
+ );
796
+ }
797
+ }
798
+ });
799
+ }
800
+ reset() {
801
+ return __async(this, null, function* () {
802
+ try {
803
+ let migrations = yield this.getMigrations();
804
+ const rollback = yield migration_filter(migrations, true);
805
+ yield this.migrate(rollback.reverse(), false);
806
+ migrations = yield this.getMigrations();
807
+ const rollforward = yield migration_filter(migrations, false);
808
+ yield this.migrate(rollforward, true);
809
+ } catch (error) {
810
+ if (error instanceof MigrationExecutionError) {
811
+ throw error;
812
+ }
813
+ throw new MigrationExecutionError("Failed to reset migrations", void 0, void 0, toError(error));
814
+ }
815
+ });
816
+ }
817
+ createMigration(name) {
818
+ try {
819
+ const creator = new MigrationCreator(this.config);
820
+ creator.create(name);
821
+ } catch (error) {
822
+ throw new MigrationExecutionError(`Failed to create migration: ${name}`, void 0, void 0, toError(error));
823
+ }
824
+ }
825
+ close() {
826
+ return __async(this, null, function* () {
827
+ if (this.sqlrunner) {
828
+ try {
829
+ yield this.sqlrunner.end();
830
+ } catch (error) {
831
+ throw new DatabaseConnectionError(`Failed to close database connection: ${toError(error).message}`);
832
+ }
833
+ }
834
+ });
835
+ }
836
+ init(config_file) {
837
+ return __async(this, null, function* () {
838
+ try {
839
+ console.log(`Checking for ${config_file}`);
840
+ const config_exist = fs3.existsSync(config_file);
841
+ if (!config_exist) {
842
+ console.log(`Creating ${config_file}`);
843
+ const default_config = {
844
+ "migration_folder": "migrations",
845
+ "migration_table": "proper_migrations",
846
+ "database": "sql",
847
+ "sql": {
848
+ "host": "localhost",
849
+ "user": "root",
850
+ "database": "proper",
851
+ "password": ""
852
+ }
853
+ };
854
+ fs3.writeFileSync(config_file, JSON.stringify(default_config, null, 2));
855
+ console.log(`Created ${config_file}`);
856
+ }
857
+ } catch (error) {
858
+ throw new ConfigurationError(`Failed to initialize config file: ${toError(error).message}`);
859
+ }
860
+ });
861
+ }
862
+ };
863
+ var FileMigrationConfigReader = class {
864
+ constructor(configFile) {
865
+ this.configFile = configFile;
866
+ }
867
+ loadFile() {
868
+ try {
869
+ const fileContent = fs3.readFileSync(this.configFile);
870
+ const config = JSON.parse(fileContent.toString());
871
+ if (!config.migration_folder) {
872
+ throw ConfigurationError.missingRequiredProperty("migration_folder");
873
+ }
874
+ if (!config.migration_table) {
875
+ throw ConfigurationError.missingRequiredProperty("migration_table");
876
+ }
877
+ if (!config.database) {
878
+ if (config.sql) {
879
+ config.database = "sql";
880
+ } else if (config.sqlite) {
881
+ config.database = "sqlite";
882
+ } else {
883
+ throw ConfigurationError.missingRequiredProperty("database");
884
+ }
885
+ }
886
+ return config;
887
+ } catch (error) {
888
+ if (error instanceof ConfigurationError) {
889
+ throw error;
890
+ }
891
+ const err = toError(error);
892
+ if (err.message.includes("ENOENT")) {
893
+ throw new ConfigurationError(`Config file not found: ${this.configFile}`);
894
+ }
895
+ throw new ConfigurationError(`Failed to load config file: ${err.message}`);
896
+ }
897
+ }
898
+ };
899
+ var MigrationCreator = class {
900
+ constructor(config) {
901
+ this.config = config;
902
+ }
903
+ create(name) {
904
+ if (!name) {
905
+ throw new CLIError("Migration name is required");
906
+ }
907
+ try {
908
+ if (!fs3.existsSync(this.config.migration_folder)) {
909
+ fs3.mkdirSync(this.config.migration_folder, { recursive: true });
910
+ }
911
+ const now_timestamp = Date.now();
912
+ const filename_up = `${now_timestamp}_${name}.up.sql`;
913
+ const filename_down = `${now_timestamp}_${name}.down.sql`;
914
+ fs3.writeFileSync(`${this.config.migration_folder}/${filename_up}`, `
915
+ -- Write your up migration here
916
+ `.trim());
917
+ fs3.writeFileSync(`${this.config.migration_folder}/${filename_down}`, `
918
+ -- Write your down migration here
919
+ `.trim());
920
+ console.log(`Created migration files:`);
921
+ console.log(` ${filename_up}`);
922
+ console.log(` ${filename_down}`);
923
+ } catch (error) {
924
+ if (error instanceof CLIError) {
925
+ throw error;
926
+ }
927
+ throw new MigrationExecutionError(`Failed to create migration files: ${toError(error).message}`);
928
+ }
929
+ }
930
+ };
931
+ export {
932
+ MySQLMigrationRunner as MigrationRunner,
933
+ MigrationRunnerFactory
934
+ };
935
+ //# sourceMappingURL=index.mjs.map