@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 +1140 -0
- package/bin/cli.js.map +1 -0
- package/bin/cli.mjs +1192 -0
- package/bin/cli.mjs.map +1 -0
- package/bin/index.js +971 -0
- package/bin/index.js.map +1 -0
- package/bin/index.mjs +935 -0
- package/bin/index.mjs.map +1 -0
- package/package.json +55 -0
- package/readme.md +236 -0
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
|