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