@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/cli.js ADDED
@@ -0,0 +1,1140 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __objRest = (source, exclude) => {
11
+ var target = {};
12
+ for (var prop in source)
13
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
14
+ target[prop] = source[prop];
15
+ if (source != null && __getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(source)) {
17
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
18
+ target[prop] = source[prop];
19
+ }
20
+ return target;
21
+ };
22
+ var __copyProps = (to, from, except, desc) => {
23
+ if (from && typeof from === "object" || typeof from === "function") {
24
+ for (let key of __getOwnPropNames(from))
25
+ if (!__hasOwnProp.call(to, key) && key !== except)
26
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
27
+ }
28
+ return to;
29
+ };
30
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
31
+ // If the importer is in node compatibility mode or this is not an ESM
32
+ // file that has been converted to a CommonJS file using a Babel-
33
+ // compatible transform (i.e. "__esModule" has not been set), then set
34
+ // "default" to the CommonJS "module.exports" for node compatibility.
35
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
36
+ mod
37
+ ));
38
+ var __async = (__this, __arguments, generator) => {
39
+ return new Promise((resolve, reject) => {
40
+ var fulfilled = (value) => {
41
+ try {
42
+ step(generator.next(value));
43
+ } catch (e) {
44
+ reject(e);
45
+ }
46
+ };
47
+ var rejected = (value) => {
48
+ try {
49
+ step(generator.throw(value));
50
+ } catch (e) {
51
+ reject(e);
52
+ }
53
+ };
54
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
55
+ step((generator = generator.apply(__this, __arguments)).next());
56
+ });
57
+ };
58
+
59
+ // cli.ts
60
+ var import_register = require("source-map-support/register");
61
+
62
+ // framework/MigrationCLi.ts
63
+ var import_minimist = __toESM(require("minimist"));
64
+ var MigrationCLIFactory = class {
65
+ /**
66
+ * Parse command line arguments into commands and flags
67
+ * @param argv Optional array of command line arguments. If not provided, process.argv will be used.
68
+ * @returns Object containing parsed commands and flags
69
+ */
70
+ static setup(argv) {
71
+ var _a;
72
+ const args2 = argv || process.argv;
73
+ let startIndex = 0;
74
+ if ((_a = args2[0]) == null ? void 0 : _a.includes("node")) {
75
+ startIndex = 2;
76
+ } else if (args2[0] && /\.(js|ts|mjs|cjs)$/i.test(args2[0])) {
77
+ startIndex = 1;
78
+ } else if (args2[0] && ["proper", "migration", "sql-proper"].includes(args2[0].toLowerCase())) {
79
+ startIndex = 1;
80
+ }
81
+ const cli = (0, import_minimist.default)(args2.slice(startIndex), {
82
+ alias: {
83
+ c: "config"
84
+ }
85
+ });
86
+ const _b = cli, { _: commands2 } = _b, flags = __objRest(_b, ["_"]);
87
+ return {
88
+ commands: commands2,
89
+ flags
90
+ };
91
+ }
92
+ };
93
+
94
+ // framework/MigrationFilter.ts
95
+ function migration_filter(_0) {
96
+ return __async(this, arguments, function* (migrations, completed = true, keep = []) {
97
+ if (migrations.length === 0) {
98
+ return keep;
99
+ }
100
+ const [first, ...rest] = migrations;
101
+ const status = yield first.status();
102
+ if (status.completed == completed) {
103
+ keep.push(first);
104
+ }
105
+ return migration_filter(rest, completed, keep);
106
+ });
107
+ }
108
+
109
+ // framework/MigrationRunner.ts
110
+ var import_fs3 = __toESM(require("fs"));
111
+ var import_promise = __toESM(require("mysql2/promise"));
112
+ var sqlite = __toESM(require("sqlite"));
113
+ var sqlite3 = __toESM(require("sqlite3"));
114
+
115
+ // framework/MigrationDirectoryReader.ts
116
+ var import_fs = __toESM(require("fs"));
117
+ var import_path = __toESM(require("path"));
118
+
119
+ // framework/errors.ts
120
+ var MigrationError = class _MigrationError extends Error {
121
+ constructor(message) {
122
+ super(message);
123
+ this.name = "MigrationError";
124
+ Object.setPrototypeOf(this, _MigrationError.prototype);
125
+ }
126
+ };
127
+ var ConfigurationError = class _ConfigurationError extends MigrationError {
128
+ constructor(message) {
129
+ super(`Configuration Error: ${message}`);
130
+ this.name = "ConfigurationError";
131
+ Object.setPrototypeOf(this, _ConfigurationError.prototype);
132
+ }
133
+ /**
134
+ * Helper method for missing database configuration
135
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
136
+ * @returns A ConfigurationError with appropriate message
137
+ */
138
+ static missingDatabaseConfiguration(dbType) {
139
+ return new _ConfigurationError(`Missing ${dbType} configuration`);
140
+ }
141
+ /**
142
+ * Helper method for unknown database type
143
+ * @param dbType The unknown database type
144
+ * @returns A ConfigurationError with appropriate message
145
+ */
146
+ static unknownDatabaseType(dbType) {
147
+ return new _ConfigurationError(`Unknown database type: ${dbType}`);
148
+ }
149
+ /**
150
+ * Helper method for missing required configuration properties
151
+ * @param property The name of the missing property
152
+ * @returns A ConfigurationError with appropriate message
153
+ */
154
+ static missingRequiredProperty(property) {
155
+ return new _ConfigurationError(`Missing required property: ${property}`);
156
+ }
157
+ };
158
+ var DatabaseConnectionError = class _DatabaseConnectionError extends MigrationError {
159
+ constructor(message) {
160
+ super(`Database Connection Error: ${message}`);
161
+ this.name = "DatabaseConnectionError";
162
+ Object.setPrototypeOf(this, _DatabaseConnectionError.prototype);
163
+ }
164
+ /**
165
+ * Helper method for connection errors
166
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
167
+ * @param details Additional error details
168
+ * @returns A DatabaseConnectionError with appropriate message
169
+ */
170
+ static connectionFailed(dbType, details) {
171
+ const message = details ? `Failed to connect to ${dbType} database: ${details}` : `Failed to connect to ${dbType} database`;
172
+ return new _DatabaseConnectionError(message);
173
+ }
174
+ /**
175
+ * Helper method for authentication errors
176
+ * @param dbType The database type (e.g., 'sql', 'sqlite')
177
+ * @returns A DatabaseConnectionError with appropriate message
178
+ */
179
+ static authenticationFailed(dbType) {
180
+ return new _DatabaseConnectionError(`Authentication failed for ${dbType} database`);
181
+ }
182
+ };
183
+ var MigrationExecutionError = class _MigrationExecutionError extends MigrationError {
184
+ constructor(message, migrationName, sql, originalError) {
185
+ let fullMessage = `Migration Execution Error${migrationName ? ` in '${migrationName}'` : ""}: ${message}`;
186
+ if (originalError) {
187
+ fullMessage += `
188
+ Original Error:
189
+ ${originalError.message}`;
190
+ }
191
+ super(fullMessage);
192
+ this.migrationName = migrationName;
193
+ this.sql = sql;
194
+ this.originalError = originalError;
195
+ this.name = "MigrationExecutionError";
196
+ Object.setPrototypeOf(this, _MigrationExecutionError.prototype);
197
+ }
198
+ /**
199
+ * Helper method for SQL execution errors
200
+ * @param migrationName The name of the migration
201
+ * @param sql The SQL that caused the error
202
+ * @param originalError The original error thrown by the database driver
203
+ * @returns A MigrationExecutionError with appropriate message
204
+ */
205
+ static sqlExecutionFailed(migrationName, sql, originalError) {
206
+ return new _MigrationExecutionError(
207
+ originalError.message,
208
+ migrationName,
209
+ sql,
210
+ originalError
211
+ );
212
+ }
213
+ /**
214
+ * Helper method for missing migration file errors
215
+ * @param filename The missing file
216
+ * @returns A MigrationExecutionError with appropriate message
217
+ */
218
+ static missingMigrationFile(filename) {
219
+ return new _MigrationExecutionError(`Migration file not found: ${filename}`);
220
+ }
221
+ /**
222
+ * Helper method for invalid migration file format errors
223
+ * @param filename The invalid file
224
+ * @param details Additional error details
225
+ * @returns A MigrationExecutionError with appropriate message
226
+ */
227
+ static invalidMigrationFile(filename, details) {
228
+ const message = details ? `Invalid migration file format in ${filename}: ${details}` : `Invalid migration file format in ${filename}`;
229
+ return new _MigrationExecutionError(message);
230
+ }
231
+ };
232
+ var CLIError = class _CLIError extends MigrationError {
233
+ constructor(message) {
234
+ super(`CLI Error: ${message}`);
235
+ this.name = "CLIError";
236
+ Object.setPrototypeOf(this, _CLIError.prototype);
237
+ }
238
+ /**
239
+ * Helper method for missing command errors
240
+ * @returns A CLIError with appropriate message
241
+ */
242
+ static missingCommand() {
243
+ return new _CLIError("No command specified. Run with --help for usage information.");
244
+ }
245
+ /**
246
+ * Helper method for unknown command errors
247
+ * @param command The unknown command
248
+ * @returns A CLIError with appropriate message
249
+ */
250
+ static unknownCommand(command2) {
251
+ return new _CLIError(`Unknown command: ${command2}. Run with --help for usage information.`);
252
+ }
253
+ /**
254
+ * Helper method for missing required argument errors
255
+ * @param argument The missing argument
256
+ * @returns A CLIError with appropriate message
257
+ */
258
+ static missingRequiredArgument(argument) {
259
+ return new _CLIError(`Missing required argument: ${argument}`);
260
+ }
261
+ };
262
+
263
+ // framework/MigrationNode.ts
264
+ var MigrationNode = class {
265
+ constructor(name) {
266
+ this.name = name || "";
267
+ }
268
+ };
269
+ var SqlMigrationNode = class extends MigrationNode {
270
+ constructor(conn, table, key, up_file, sql_up, down_file, sql_down) {
271
+ super(key);
272
+ this.conn = conn;
273
+ this.table = table;
274
+ this.key = key;
275
+ this.up_file = up_file;
276
+ this.sql_up = sql_up;
277
+ this.down_file = down_file;
278
+ this.sql_down = sql_down;
279
+ }
280
+ up_sql() {
281
+ return this.sql_up;
282
+ }
283
+ down_sql() {
284
+ return this.sql_down;
285
+ }
286
+ get_key() {
287
+ return this.key;
288
+ }
289
+ status() {
290
+ return __async(this, null, function* () {
291
+ try {
292
+ const result = yield this.conn.query(`
293
+ select *
294
+ from ${this.table}
295
+ where migration_key = ?;
296
+ `, [this.key]);
297
+ if (result.length > 0 && result[0].length > 0) {
298
+ return {
299
+ completed: true
300
+ };
301
+ } else {
302
+ return {
303
+ completed: false
304
+ };
305
+ }
306
+ } catch (error) {
307
+ throw new MigrationExecutionError(
308
+ `Error checking status for migration`,
309
+ this.key,
310
+ void 0,
311
+ error instanceof Error ? error : new Error(String(error))
312
+ );
313
+ }
314
+ });
315
+ }
316
+ up() {
317
+ return __async(this, null, function* () {
318
+ try {
319
+ yield this.conn.execute(this.sql_up);
320
+ } catch (error) {
321
+ throw new MigrationExecutionError(
322
+ `Error executing UP migration`,
323
+ this.key,
324
+ this.sql_up,
325
+ error instanceof Error ? error : new Error(String(error))
326
+ );
327
+ }
328
+ try {
329
+ yield this.conn.execute(`
330
+ insert into ${this.table} (migration_key,up,down)
331
+ values (?,?,?)
332
+ `, [this.key, this.up_file, this.down_file]);
333
+ } catch (error) {
334
+ throw new MigrationExecutionError(
335
+ `Error recording migration completion`,
336
+ this.key,
337
+ void 0,
338
+ error instanceof Error ? error : new Error(String(error))
339
+ );
340
+ }
341
+ });
342
+ }
343
+ down() {
344
+ return __async(this, null, function* () {
345
+ try {
346
+ yield this.conn.execute(this.sql_down);
347
+ } catch (error) {
348
+ throw new MigrationExecutionError(
349
+ `Error executing DOWN migration`,
350
+ this.key,
351
+ this.sql_down,
352
+ error instanceof Error ? error : new Error(String(error))
353
+ );
354
+ }
355
+ try {
356
+ yield this.conn.execute(`
357
+ delete from ${this.table}
358
+ where migration_key = ?
359
+ `, [this.key]);
360
+ } catch (error) {
361
+ throw new MigrationExecutionError(
362
+ `Error removing migration record`,
363
+ this.key,
364
+ void 0,
365
+ error instanceof Error ? error : new Error(String(error))
366
+ );
367
+ }
368
+ });
369
+ }
370
+ };
371
+
372
+ // framework/SqlMigrationBuilder.ts
373
+ var SqlMigrationBuilder = class {
374
+ constructor(key) {
375
+ this.key = key;
376
+ this.up = "";
377
+ this.up_file = "";
378
+ this.down = "";
379
+ this.down_file = "";
380
+ }
381
+ set_up(file, up) {
382
+ this.up_file = file;
383
+ this.up = up;
384
+ }
385
+ set_down(file, down) {
386
+ this.down_file = file;
387
+ this.down = down;
388
+ }
389
+ build(table, conn) {
390
+ return new SqlMigrationNode(conn, table, this.key, this.up_file, this.up, this.down_file, this.down);
391
+ }
392
+ };
393
+
394
+ // framework/MigrationDirectoryReader.ts
395
+ var MigrationDirectoryReader = class {
396
+ constructor(directory, read_strategy, sqlrunner) {
397
+ this.directory = directory;
398
+ this.read_strategy = read_strategy;
399
+ this.sqlrunner = sqlrunner;
400
+ }
401
+ loadMigrations(table, connection) {
402
+ import_fs.default.existsSync(this.directory) || import_fs.default.mkdirSync(this.directory);
403
+ const dir_content = import_fs.default.readdirSync(this.directory, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name);
404
+ let migration_files = dir_content.map((file) => {
405
+ return {
406
+ migration_key: file.replace(/\.(up|down)\.(sql|js)/i, "").toLowerCase(),
407
+ directory: this.directory,
408
+ relative_path: import_path.default.join(this.directory, file),
409
+ file
410
+ };
411
+ }).sort();
412
+ const migration_sorter = {};
413
+ migration_files.forEach((migration) => {
414
+ const builder = migration_sorter[migration.migration_key] = migration_sorter[migration.migration_key] || new SqlMigrationBuilder(migration.migration_key);
415
+ this.loadMigration(builder, migration.relative_path);
416
+ });
417
+ const keys = Object.keys(migration_sorter);
418
+ keys.sort();
419
+ const built = keys.map((key) => {
420
+ return migration_sorter[key].build(table, this.sqlrunner);
421
+ });
422
+ return built;
423
+ }
424
+ loadMigration(builder, file) {
425
+ const is_sql = /sql$/i.test(file);
426
+ const is_js = /js$/i.test(file);
427
+ const is_up = /up\.(js|sql)/i.test(file);
428
+ const is_down = /down\.(js|sql)/i.test(file);
429
+ if (is_sql && is_up) {
430
+ const content = this.sql_up(file);
431
+ builder.set_up(file, content);
432
+ } else if (is_sql && is_down) {
433
+ const content = this.sql_down(file);
434
+ builder.set_down(file, content);
435
+ } else {
436
+ throw new Error(`Invalid migration file: ${file}`);
437
+ }
438
+ return builder;
439
+ }
440
+ sql_up(file) {
441
+ let content = import_fs.default.readFileSync(file).toString();
442
+ content = this.read_strategy(content);
443
+ return content.trim();
444
+ }
445
+ sql_down(file) {
446
+ let content = import_fs.default.readFileSync(file).toString();
447
+ content = this.read_strategy(content);
448
+ return content.trim();
449
+ }
450
+ };
451
+
452
+ // framework/MigrationSetup.ts
453
+ var import_fs2 = __toESM(require("fs"));
454
+ var MigrationSetup = class {
455
+ constructor(sqlrunner, config) {
456
+ this.sqlrunner = sqlrunner;
457
+ this.config = config;
458
+ }
459
+ setup() {
460
+ return __async(this, null, function* () {
461
+ import_fs2.default.existsSync(this.config.migration_folder) || import_fs2.default.mkdirSync(this.config.migration_folder);
462
+ const tableName = this.config.migration_table;
463
+ const createTableSql = this.config.database === "sqlite" ? `CREATE TABLE IF NOT EXISTS ${tableName} (
464
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
465
+ migration_key TEXT,
466
+ up TEXT,
467
+ down TEXT,
468
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
469
+ )` : `CREATE TABLE IF NOT EXISTS ${tableName} (
470
+ id INT AUTO_INCREMENT PRIMARY KEY,
471
+ migration_key VARCHAR(255),
472
+ up VARCHAR(255),
473
+ down VARCHAR(255),
474
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
475
+ )`;
476
+ yield this.sqlrunner.query(createTableSql);
477
+ });
478
+ }
479
+ teardown() {
480
+ return __async(this, null, function* () {
481
+ yield this.sqlrunner.execute(`DROP TABLE ${this.config.migration_table}`);
482
+ });
483
+ }
484
+ };
485
+
486
+ // framework/MigrationDialectParser.ts
487
+ function MySqlDialectParser(sql) {
488
+ const lines = sql.split("\n");
489
+ let result = "";
490
+ let isInMySQLBlock = true;
491
+ const startSQLRegex = /^\s*--\s*\[\s*(sql|mysql)\s*\]\s*$/i;
492
+ const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
493
+ for (const line of lines) {
494
+ const trimmed = line.trim();
495
+ if (startSQLRegex.test(trimmed)) {
496
+ isInMySQLBlock = true;
497
+ result += line + "\n";
498
+ continue;
499
+ }
500
+ if (anyDialectRegex.test(trimmed) && !startSQLRegex.test(trimmed)) {
501
+ isInMySQLBlock = false;
502
+ continue;
503
+ }
504
+ if (!isInMySQLBlock && line.toLowerCase().includes("create index")) {
505
+ isInMySQLBlock = true;
506
+ }
507
+ if (isInMySQLBlock) {
508
+ result += line + "\n";
509
+ }
510
+ }
511
+ return result;
512
+ }
513
+ function SqliteDialectParser(sql) {
514
+ const lines = sql.split("\n");
515
+ let result = "";
516
+ let isInSqliteBlock = true;
517
+ const startSqliteRegex = /^\s*--\s*\[\s*sqlite\s*\]\s*$/i;
518
+ const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
519
+ for (const line of lines) {
520
+ const trimmed = line.trim();
521
+ if (startSqliteRegex.test(trimmed)) {
522
+ isInSqliteBlock = true;
523
+ result += line + "\n";
524
+ continue;
525
+ } else if (anyDialectRegex.test(trimmed) && !startSqliteRegex.test(trimmed)) {
526
+ isInSqliteBlock = false;
527
+ continue;
528
+ }
529
+ if (!isInSqliteBlock && line.toLowerCase().includes("create index")) {
530
+ isInSqliteBlock = true;
531
+ }
532
+ if (isInSqliteBlock) {
533
+ result += line + "\n";
534
+ }
535
+ }
536
+ return result;
537
+ }
538
+
539
+ // framework/SQLRunner.ts
540
+ var BaseSQLRunner = class {
541
+ /**
542
+ * Ensures that both MySQL and SQLite return the same tuple shape that callers
543
+ * expect: `[rowsOrResult, extra]`. For SQLite there is no `extra` metadata
544
+ * comparable to MySQL's `FieldPacket[]`, so we just use `undefined`.
545
+ */
546
+ query(_0) {
547
+ return __async(this, arguments, function* (sql, params = []) {
548
+ const result = yield this._query(sql, params);
549
+ return Array.isArray(result) && result.length === 2 ? result : [result, void 0];
550
+ });
551
+ }
552
+ execute(_0) {
553
+ return __async(this, arguments, function* (sql, params = []) {
554
+ const result = yield this._execute(sql, params);
555
+ return Array.isArray(result) && result.length === 2 ? result : [result, void 0];
556
+ });
557
+ }
558
+ end() {
559
+ return __async(this, null, function* () {
560
+ yield this._end();
561
+ });
562
+ }
563
+ };
564
+ var SQLRunner = class extends BaseSQLRunner {
565
+ constructor(connection) {
566
+ super();
567
+ this.connection = connection;
568
+ }
569
+ // The MySQL driver already returns the correct tuple shapes.
570
+ _query(_0) {
571
+ return __async(this, arguments, function* (sql, params = []) {
572
+ return yield this.connection.query(sql, params);
573
+ });
574
+ }
575
+ _execute(_0) {
576
+ return __async(this, arguments, function* (sql, params = []) {
577
+ return yield this.connection.execute(sql, params);
578
+ });
579
+ }
580
+ _end() {
581
+ return __async(this, null, function* () {
582
+ if (this.connection) {
583
+ yield this.connection.end();
584
+ }
585
+ });
586
+ }
587
+ };
588
+ var SQLiteRunner = class extends BaseSQLRunner {
589
+ constructor(connection) {
590
+ super();
591
+ this.connection = connection;
592
+ }
593
+ _query(_0) {
594
+ return __async(this, arguments, function* (sql, params = []) {
595
+ const stmt = yield this.connection.prepare(sql);
596
+ try {
597
+ const rows = yield stmt.all(...params);
598
+ return rows;
599
+ } finally {
600
+ yield stmt.finalize();
601
+ }
602
+ });
603
+ }
604
+ _execute(_0) {
605
+ return __async(this, arguments, function* (sql, params = []) {
606
+ if (this.isMultiStatement(sql)) {
607
+ return yield this.executeMultiStatement(sql, params).catch((err) => {
608
+ console.error(`Error executing multi-statement SQL:
609
+ ${sql}
610
+ `, err);
611
+ throw err;
612
+ });
613
+ }
614
+ const stmt = yield this.connection.prepare(sql);
615
+ try {
616
+ const info = yield stmt.run(...params);
617
+ return info;
618
+ } finally {
619
+ yield stmt.finalize();
620
+ }
621
+ });
622
+ }
623
+ isMultiStatement(sql) {
624
+ return sql.split(";").length > 1;
625
+ }
626
+ executeMultiStatement(_0) {
627
+ return __async(this, arguments, function* (sql, params = []) {
628
+ const statements = sql.split(";").map(
629
+ (s) => s.split("\n").map(
630
+ (s2) => this.removeComments(s2)
631
+ ).filter((s2) => s2.trim() != "").join("\n")
632
+ ).filter((s) => s.trim() !== "");
633
+ const infos = yield statements.reduce((prev, statement) => __async(this, null, function* () {
634
+ const infos2 = yield prev;
635
+ const stmt = yield this.connection.prepare(`${statement};`);
636
+ try {
637
+ const info = yield stmt.run(...params);
638
+ infos2.push(info);
639
+ return infos2;
640
+ } finally {
641
+ yield stmt.finalize();
642
+ }
643
+ }), Promise.resolve([null])).then((infos2) => {
644
+ return infos2.filter((info) => info !== null);
645
+ });
646
+ return infos.reduce((acc, latest) => {
647
+ if (latest) {
648
+ acc.stmt = latest.stmt;
649
+ acc.lastID = latest.lastID;
650
+ acc.changes += latest.changes;
651
+ }
652
+ return acc;
653
+ });
654
+ });
655
+ }
656
+ removeComments(sql) {
657
+ sql = sql.replace(/--.*$/gm, "");
658
+ return sql;
659
+ }
660
+ _end() {
661
+ return __async(this, null, function* () {
662
+ if (this.connection) {
663
+ yield this.connection.close();
664
+ }
665
+ });
666
+ }
667
+ };
668
+
669
+ // framework/MigrationRunner.ts
670
+ function toError(error) {
671
+ if (error instanceof Error) return error;
672
+ return new Error(String(error));
673
+ }
674
+ var MigrationRunnerFactory = class _MigrationRunnerFactory {
675
+ static create(configFile, conn) {
676
+ return __async(this, null, function* () {
677
+ const configReader = new FileMigrationConfigReader(configFile);
678
+ const config = configReader.loadFile();
679
+ if (!conn) {
680
+ conn = yield this.createConnection(config);
681
+ }
682
+ return new _MigrationRunnerFactory().create(config, conn);
683
+ });
684
+ }
685
+ static createConnection(config) {
686
+ return __async(this, null, function* () {
687
+ let conn = null;
688
+ switch (config.database) {
689
+ case "sql":
690
+ if (!config.sql) {
691
+ throw ConfigurationError.missingDatabaseConfiguration("sql");
692
+ }
693
+ const settings = Object.assign({
694
+ password: process.env.SQL_PASSWORD
695
+ }, config.sql);
696
+ try {
697
+ conn = yield import_promise.default.createConnection(settings);
698
+ return conn;
699
+ } catch (error) {
700
+ throw DatabaseConnectionError.connectionFailed("sql", toError(error).message);
701
+ }
702
+ case "sqlite":
703
+ if (!config.sqlite) {
704
+ throw ConfigurationError.missingDatabaseConfiguration("sqlite");
705
+ }
706
+ try {
707
+ conn = yield sqlite.open({
708
+ filename: config.sqlite.database,
709
+ driver: sqlite3.Database
710
+ });
711
+ return conn;
712
+ } catch (error) {
713
+ throw DatabaseConnectionError.connectionFailed("sqlite", toError(error).message);
714
+ }
715
+ default:
716
+ throw ConfigurationError.unknownDatabaseType(config.database);
717
+ }
718
+ });
719
+ }
720
+ static createEmpty(configFile) {
721
+ return __async(this, null, function* () {
722
+ const configReader = new FileMigrationConfigReader(configFile);
723
+ const config = configReader.loadFile();
724
+ return new _MigrationRunnerFactory().createEmpty(config);
725
+ });
726
+ }
727
+ create(config, conn) {
728
+ return __async(this, null, function* () {
729
+ let sqlrunner;
730
+ switch (config.database) {
731
+ case "sql":
732
+ sqlrunner = new SQLRunner(conn);
733
+ break;
734
+ case "sqlite":
735
+ sqlrunner = new SQLiteRunner(conn);
736
+ break;
737
+ default:
738
+ throw ConfigurationError.unknownDatabaseType(config.database);
739
+ }
740
+ const setup = new MigrationSetup(sqlrunner, config);
741
+ const read_strategy = this.getReadStategy(config);
742
+ const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
743
+ yield setup.setup();
744
+ return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, conn);
745
+ });
746
+ }
747
+ createEmpty(config) {
748
+ return __async(this, null, function* () {
749
+ const conn = null;
750
+ let sqlrunner;
751
+ switch (config.database) {
752
+ case "sql":
753
+ sqlrunner = new SQLRunner(conn);
754
+ break;
755
+ case "sqlite":
756
+ sqlrunner = new SQLiteRunner(conn);
757
+ break;
758
+ default:
759
+ throw ConfigurationError.unknownDatabaseType(config.database);
760
+ }
761
+ const setup = new MigrationSetup(sqlrunner, config);
762
+ const read_strategy = this.getReadStategy(config);
763
+ const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner);
764
+ return new MySQLMigrationRunner(config, migration_files, setup, sqlrunner, conn);
765
+ });
766
+ }
767
+ getReadStategy(config) {
768
+ switch (config.database) {
769
+ case "sql":
770
+ return MySqlDialectParser;
771
+ case "sqlite":
772
+ return SqliteDialectParser;
773
+ default:
774
+ throw ConfigurationError.unknownDatabaseType(config.database);
775
+ }
776
+ }
777
+ };
778
+ var MySQLMigrationRunner = class {
779
+ constructor(config, directory, setupRunner, sqlrunner, connection) {
780
+ this.config = config;
781
+ this.directory = directory;
782
+ this.setupRunner = setupRunner;
783
+ this.sqlrunner = sqlrunner;
784
+ this.connection = connection;
785
+ }
786
+ setup() {
787
+ return __async(this, null, function* () {
788
+ try {
789
+ yield this.setupRunner.setup();
790
+ } catch (error) {
791
+ throw new MigrationExecutionError("Failed to set up migration database", void 0, void 0, toError(error));
792
+ }
793
+ });
794
+ }
795
+ terminate() {
796
+ return __async(this, null, function* () {
797
+ try {
798
+ yield this.setupRunner.teardown();
799
+ } catch (error) {
800
+ throw new MigrationExecutionError("Failed to tear down migration database", void 0, void 0, toError(error));
801
+ }
802
+ });
803
+ }
804
+ getMigrationsHistory() {
805
+ return __async(this, null, function* () {
806
+ try {
807
+ const results = yield this.sqlrunner.query(`
808
+ select *
809
+ from ${this.config.migration_table}
810
+ `);
811
+ return results[0];
812
+ } catch (error) {
813
+ throw new MigrationExecutionError("Failed to get migration history", void 0, void 0, toError(error));
814
+ }
815
+ });
816
+ }
817
+ getMigrations() {
818
+ return __async(this, null, function* () {
819
+ try {
820
+ return this.directory.loadMigrations(this.config.migration_table, this.connection);
821
+ } catch (error) {
822
+ throw new MigrationExecutionError("Failed to load migrations", void 0, void 0, toError(error));
823
+ }
824
+ });
825
+ }
826
+ getPendingMigrations() {
827
+ return __async(this, null, function* () {
828
+ try {
829
+ const migrations = yield this.getMigrations();
830
+ return migration_filter(migrations, false);
831
+ } catch (error) {
832
+ if (error instanceof MigrationExecutionError) {
833
+ throw error;
834
+ }
835
+ throw new MigrationExecutionError("Failed to get pending migrations", void 0, void 0, toError(error));
836
+ }
837
+ });
838
+ }
839
+ getCompletedMigrations() {
840
+ return __async(this, null, function* () {
841
+ try {
842
+ const migrations = yield this.getMigrations();
843
+ return migration_filter(migrations, true);
844
+ } catch (error) {
845
+ if (error instanceof MigrationExecutionError) {
846
+ throw error;
847
+ }
848
+ throw new MigrationExecutionError("Failed to get completed migrations", void 0, void 0, toError(error));
849
+ }
850
+ });
851
+ }
852
+ migrate(migrationNodes, forward) {
853
+ return __async(this, null, function* () {
854
+ for (let node of migrationNodes) {
855
+ try {
856
+ if (forward) {
857
+ yield node.up();
858
+ } else {
859
+ yield node.down();
860
+ }
861
+ } catch (error) {
862
+ throw new MigrationExecutionError(
863
+ `Failed to ${forward ? "apply" : "rollback"} migration`,
864
+ node.name || String(node),
865
+ forward ? node.up_sql() : node.down_sql(),
866
+ toError(error)
867
+ );
868
+ }
869
+ }
870
+ });
871
+ }
872
+ reset() {
873
+ return __async(this, null, function* () {
874
+ try {
875
+ let migrations = yield this.getMigrations();
876
+ const rollback = yield migration_filter(migrations, true);
877
+ yield this.migrate(rollback.reverse(), false);
878
+ migrations = yield this.getMigrations();
879
+ const rollforward = yield migration_filter(migrations, false);
880
+ yield this.migrate(rollforward, true);
881
+ } catch (error) {
882
+ if (error instanceof MigrationExecutionError) {
883
+ throw error;
884
+ }
885
+ throw new MigrationExecutionError("Failed to reset migrations", void 0, void 0, toError(error));
886
+ }
887
+ });
888
+ }
889
+ createMigration(name) {
890
+ try {
891
+ const creator = new MigrationCreator(this.config);
892
+ creator.create(name);
893
+ } catch (error) {
894
+ throw new MigrationExecutionError(`Failed to create migration: ${name}`, void 0, void 0, toError(error));
895
+ }
896
+ }
897
+ close() {
898
+ return __async(this, null, function* () {
899
+ if (this.sqlrunner) {
900
+ try {
901
+ yield this.sqlrunner.end();
902
+ } catch (error) {
903
+ throw new DatabaseConnectionError(`Failed to close database connection: ${toError(error).message}`);
904
+ }
905
+ }
906
+ });
907
+ }
908
+ init(config_file2) {
909
+ return __async(this, null, function* () {
910
+ try {
911
+ console.log(`Checking for ${config_file2}`);
912
+ const config_exist = import_fs3.default.existsSync(config_file2);
913
+ if (!config_exist) {
914
+ console.log(`Creating ${config_file2}`);
915
+ const default_config = {
916
+ "migration_folder": "migrations",
917
+ "migration_table": "proper_migrations",
918
+ "database": "sql",
919
+ "sql": {
920
+ "host": "localhost",
921
+ "user": "root",
922
+ "database": "proper",
923
+ "password": ""
924
+ }
925
+ };
926
+ import_fs3.default.writeFileSync(config_file2, JSON.stringify(default_config, null, 2));
927
+ console.log(`Created ${config_file2}`);
928
+ }
929
+ } catch (error) {
930
+ throw new ConfigurationError(`Failed to initialize config file: ${toError(error).message}`);
931
+ }
932
+ });
933
+ }
934
+ };
935
+ var FileMigrationConfigReader = class {
936
+ constructor(configFile) {
937
+ this.configFile = configFile;
938
+ }
939
+ loadFile() {
940
+ try {
941
+ const fileContent = import_fs3.default.readFileSync(this.configFile);
942
+ const config = JSON.parse(fileContent.toString());
943
+ if (!config.migration_folder) {
944
+ throw ConfigurationError.missingRequiredProperty("migration_folder");
945
+ }
946
+ if (!config.migration_table) {
947
+ throw ConfigurationError.missingRequiredProperty("migration_table");
948
+ }
949
+ if (!config.database) {
950
+ if (config.sql) {
951
+ config.database = "sql";
952
+ } else if (config.sqlite) {
953
+ config.database = "sqlite";
954
+ } else {
955
+ throw ConfigurationError.missingRequiredProperty("database");
956
+ }
957
+ }
958
+ return config;
959
+ } catch (error) {
960
+ if (error instanceof ConfigurationError) {
961
+ throw error;
962
+ }
963
+ const err = toError(error);
964
+ if (err.message.includes("ENOENT")) {
965
+ throw new ConfigurationError(`Config file not found: ${this.configFile}`);
966
+ }
967
+ throw new ConfigurationError(`Failed to load config file: ${err.message}`);
968
+ }
969
+ }
970
+ };
971
+ var MigrationCreator = class {
972
+ constructor(config) {
973
+ this.config = config;
974
+ }
975
+ create(name) {
976
+ if (!name) {
977
+ throw new CLIError("Migration name is required");
978
+ }
979
+ try {
980
+ if (!import_fs3.default.existsSync(this.config.migration_folder)) {
981
+ import_fs3.default.mkdirSync(this.config.migration_folder, { recursive: true });
982
+ }
983
+ const now_timestamp = Date.now();
984
+ const filename_up = `${now_timestamp}_${name}.up.sql`;
985
+ const filename_down = `${now_timestamp}_${name}.down.sql`;
986
+ import_fs3.default.writeFileSync(`${this.config.migration_folder}/${filename_up}`, `
987
+ -- Write your up migration here
988
+ `.trim());
989
+ import_fs3.default.writeFileSync(`${this.config.migration_folder}/${filename_down}`, `
990
+ -- Write your down migration here
991
+ `.trim());
992
+ console.log(`Created migration files:`);
993
+ console.log(` ${filename_up}`);
994
+ console.log(` ${filename_down}`);
995
+ } catch (error) {
996
+ if (error instanceof CLIError) {
997
+ throw error;
998
+ }
999
+ throw new MigrationExecutionError(`Failed to create migration files: ${toError(error).message}`);
1000
+ }
1001
+ }
1002
+ };
1003
+
1004
+ // cli.ts
1005
+ var args = MigrationCLIFactory.setup(process.argv);
1006
+ if (!args.commands || args.commands.length === 0) {
1007
+ console.error("Error: No command specified");
1008
+ printUsage();
1009
+ process.exit(1);
1010
+ }
1011
+ var commands = args.commands;
1012
+ var command = commands[0];
1013
+ var load_database = !["init", "create", "help"].includes(command.toLowerCase());
1014
+ var config_file = args.flags.config || "proper.json";
1015
+ if (command.toLowerCase() === "help") {
1016
+ printUsage();
1017
+ process.exit(0);
1018
+ }
1019
+ console.log(`Loading database: ${load_database}`);
1020
+ var pending_runner = load_database ? MigrationRunnerFactory.create(config_file) : MigrationRunnerFactory.createEmpty(config_file);
1021
+ pending_runner.then((runner) => __async(null, null, function* () {
1022
+ try {
1023
+ if (load_database) {
1024
+ yield runner.setup();
1025
+ }
1026
+ switch (command.toLowerCase()) {
1027
+ case "up":
1028
+ const migrations_forward = yield runner.getMigrations();
1029
+ let upgraded = yield migration_filter(migrations_forward, false);
1030
+ if (args.flags.increment) {
1031
+ const increment = parseInt(args.flags.increment);
1032
+ upgraded = upgraded.slice(0, increment);
1033
+ }
1034
+ console.log(`Applying ${upgraded.length} migration(s)`);
1035
+ if (upgraded.length > 0) {
1036
+ for (const migration of upgraded) {
1037
+ console.log(` - ${migration.name}`);
1038
+ }
1039
+ yield runner.migrate(upgraded, true);
1040
+ console.log("Migration completed successfully");
1041
+ } else {
1042
+ console.log("No pending migrations");
1043
+ }
1044
+ yield runner.close();
1045
+ break;
1046
+ case "down":
1047
+ const migrations_rollback = yield runner.getMigrations();
1048
+ let downgraded = yield migration_filter(migrations_rollback, true);
1049
+ downgraded = downgraded.reverse();
1050
+ if (args.flags.increment) {
1051
+ const increment = parseInt(args.flags.increment);
1052
+ downgraded = downgraded.slice(0, increment);
1053
+ }
1054
+ console.log(`Rolling back ${downgraded.length} migration(s)`);
1055
+ if (downgraded.length > 0) {
1056
+ for (const migration of downgraded) {
1057
+ console.log(` - ${migration.name}`);
1058
+ }
1059
+ yield runner.migrate(downgraded, false);
1060
+ console.log("Rollback completed successfully");
1061
+ } else {
1062
+ console.log("No migrations to roll back");
1063
+ }
1064
+ yield runner.close();
1065
+ break;
1066
+ case "reset":
1067
+ console.log("Resetting all migrations...");
1068
+ yield runner.reset();
1069
+ console.log("Reset completed successfully");
1070
+ yield runner.close();
1071
+ break;
1072
+ case "create":
1073
+ let filename = args.flags.name || commands[1];
1074
+ if (!filename) {
1075
+ throw new CLIError("Migration name is required");
1076
+ }
1077
+ filename = filename.replace(/\s/g, "_");
1078
+ runner.createMigration(filename);
1079
+ runner.close();
1080
+ break;
1081
+ case "init":
1082
+ yield runner.init(config_file);
1083
+ runner.close();
1084
+ break;
1085
+ case "status":
1086
+ const { printTable } = require("console-table-printer");
1087
+ const migrations = yield runner.getMigrations();
1088
+ console.log("Migration Status:");
1089
+ const table = migrations.map((m) => __async(null, null, function* () {
1090
+ const status = yield m.status();
1091
+ return {
1092
+ key: m.get_key(),
1093
+ status: status.completed ? "completed" : "pending"
1094
+ };
1095
+ }));
1096
+ printTable(yield Promise.all(table));
1097
+ runner.close();
1098
+ break;
1099
+ default:
1100
+ throw CLIError.unknownCommand(command);
1101
+ }
1102
+ } catch (error) {
1103
+ console.error(`Error: ${error.message}`);
1104
+ if (error.stack && process.env.DEBUG) {
1105
+ console.error(error.stack);
1106
+ }
1107
+ process.exit(1);
1108
+ }
1109
+ }));
1110
+ function printUsage() {
1111
+ console.log(`
1112
+ SQL Proper - Database migration tool
1113
+
1114
+ Usage:
1115
+ proper <command> [options]
1116
+
1117
+ Commands:
1118
+ up Apply pending migrations
1119
+ down Roll back completed migrations
1120
+ reset Roll back all migrations and reapply them
1121
+ create Create a new migration
1122
+ init Initialize a new config file
1123
+ status Show migration status
1124
+ help Show this help message
1125
+
1126
+ Options:
1127
+ -c, --config Specify the config file (default: proper.json)
1128
+ --increment <n> Limit the number of migrations to apply or roll back
1129
+
1130
+ Examples:
1131
+ proper up Apply all pending migrations
1132
+ proper up --increment 1 Apply only the next pending migration
1133
+ proper down Roll back the last applied migration
1134
+ proper create my_migration Create a new migration named "my_migration"
1135
+ proper init Create a new config file
1136
+ proper status Show the status of all migrations
1137
+ proper -c custom.json up Use a custom config file
1138
+ `);
1139
+ }
1140
+ //# sourceMappingURL=cli.js.map