@bdkinc/knex-ibmi 0.5.9 → 0.5.11
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/README.md +476 -453
- package/dist/cli.cjs +58 -19
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +65 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +65 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -36,6 +36,9 @@ var import_fs2 = require("fs");
|
|
|
36
36
|
var import_fs = __toESM(require("fs"));
|
|
37
37
|
var import_path = __toESM(require("path"));
|
|
38
38
|
var import_url = require("url");
|
|
39
|
+
function buildTsRuntimeHelpMessage(fileName) {
|
|
40
|
+
return `TypeScript migration '${fileName}' requires a TypeScript runtime loader. Run with a TS-capable runtime (for example: \`node --import tsx\`) or precompile migrations to JavaScript.`;
|
|
41
|
+
}
|
|
39
42
|
var IBMiMigrationRunner = class {
|
|
40
43
|
constructor(knex2, config) {
|
|
41
44
|
__publicField(this, "knex");
|
|
@@ -45,9 +48,13 @@ var IBMiMigrationRunner = class {
|
|
|
45
48
|
directory: "./migrations",
|
|
46
49
|
tableName: "KNEX_MIGRATIONS",
|
|
47
50
|
schemaName: void 0,
|
|
48
|
-
extension: "js",
|
|
49
51
|
...config
|
|
50
52
|
};
|
|
53
|
+
if (typeof config?.extension === "string") {
|
|
54
|
+
console.warn(
|
|
55
|
+
"\u26A0\uFE0F IBMiMigrationRunner config 'extension' is ignored for discovery. The runner always discovers .js/.ts/.mjs/.cjs migration files."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
getFullTableName() {
|
|
53
60
|
return this.config.schemaName ? `${this.config.schemaName}.${this.config.tableName}` : this.config.tableName;
|
|
@@ -94,7 +101,18 @@ var IBMiMigrationRunner = class {
|
|
|
94
101
|
try {
|
|
95
102
|
const migrationPath = this.getMigrationPath(migrationFile);
|
|
96
103
|
const fileUrl = (0, import_url.pathToFileURL)(migrationPath).href;
|
|
97
|
-
|
|
104
|
+
let migration;
|
|
105
|
+
try {
|
|
106
|
+
const moduleNs = await import(`${fileUrl}?t=${Date.now()}`);
|
|
107
|
+
migration = moduleNs.default ?? moduleNs;
|
|
108
|
+
} catch (importError) {
|
|
109
|
+
const isTsMigration = migrationFile.toLowerCase().endsWith(".ts");
|
|
110
|
+
const message = String(importError?.message || importError || "");
|
|
111
|
+
if (isTsMigration && (message.includes("Unknown file extension") || message.includes("Cannot use import statement") || message.includes("Unexpected token"))) {
|
|
112
|
+
throw new Error(buildTsRuntimeHelpMessage(migrationFile));
|
|
113
|
+
}
|
|
114
|
+
throw importError;
|
|
115
|
+
}
|
|
98
116
|
if (!migration.up || typeof migration.up !== "function") {
|
|
99
117
|
throw new Error(`Migration ${migrationFile} has no 'up' function`);
|
|
100
118
|
}
|
|
@@ -142,7 +160,18 @@ var IBMiMigrationRunner = class {
|
|
|
142
160
|
try {
|
|
143
161
|
const migrationPath = this.getMigrationPath(migrationFile);
|
|
144
162
|
const fileUrl = (0, import_url.pathToFileURL)(migrationPath).href;
|
|
145
|
-
|
|
163
|
+
let migration;
|
|
164
|
+
try {
|
|
165
|
+
const moduleNs = await import(`${fileUrl}?t=${Date.now()}`);
|
|
166
|
+
migration = moduleNs.default ?? moduleNs;
|
|
167
|
+
} catch (importError) {
|
|
168
|
+
const isTsMigration = migrationFile.toLowerCase().endsWith(".ts");
|
|
169
|
+
const message = String(importError?.message || importError || "");
|
|
170
|
+
if (isTsMigration && (message.includes("Unknown file extension") || message.includes("Cannot use import statement") || message.includes("Unexpected token"))) {
|
|
171
|
+
throw new Error(buildTsRuntimeHelpMessage(migrationFile));
|
|
172
|
+
}
|
|
173
|
+
throw importError;
|
|
174
|
+
}
|
|
146
175
|
if (migration.down && typeof migration.down === "function") {
|
|
147
176
|
console.log(` \u26A1 Executing rollback...`);
|
|
148
177
|
await migration.down(this.knex);
|
|
@@ -213,17 +242,12 @@ var IBMiMigrationRunner = class {
|
|
|
213
242
|
}
|
|
214
243
|
}
|
|
215
244
|
getMigrationFiles() {
|
|
216
|
-
const { directory
|
|
245
|
+
const { directory } = this.config;
|
|
217
246
|
if (!import_fs.default.existsSync(directory)) {
|
|
218
247
|
throw new Error(`Migration directory does not exist: ${directory}`);
|
|
219
248
|
}
|
|
220
249
|
const validExtensions = ["js", "ts", "mjs", "cjs"];
|
|
221
|
-
return import_fs.default.readdirSync(directory).filter((file) => {
|
|
222
|
-
if (extension && extension !== "js") {
|
|
223
|
-
return file.endsWith(`.${extension}`);
|
|
224
|
-
}
|
|
225
|
-
return validExtensions.some((ext) => file.endsWith(`.${ext}`));
|
|
226
|
-
}).sort();
|
|
250
|
+
return import_fs.default.readdirSync(directory).filter((file) => validExtensions.some((ext) => file.endsWith(`.${ext}`))).sort();
|
|
227
251
|
}
|
|
228
252
|
getMigrationPath(filename) {
|
|
229
253
|
return import_path.default.resolve(this.config.directory, filename);
|
|
@@ -258,17 +282,19 @@ function showHelp() {
|
|
|
258
282
|
console.log(
|
|
259
283
|
" --knexfile <file> - Specify knexfile path (default: ./knexfile.js)"
|
|
260
284
|
);
|
|
285
|
+
console.log(" - Supports both .js and .ts knexfiles");
|
|
261
286
|
console.log(
|
|
262
|
-
"
|
|
287
|
+
" -x <extension> - File extension for new migrations (js|ts)"
|
|
263
288
|
);
|
|
264
289
|
console.log(
|
|
265
|
-
"
|
|
290
|
+
" --steps <number> - Number of migration batches to rollback"
|
|
266
291
|
);
|
|
267
292
|
console.log(" --help - Show this help message");
|
|
268
293
|
console.log("");
|
|
269
294
|
console.log("Examples:");
|
|
270
295
|
console.log(" ibmi-migrations migrate:latest");
|
|
271
296
|
console.log(" ibmi-migrations migrate:rollback");
|
|
297
|
+
console.log(" ibmi-migrations migrate:rollback --steps 2");
|
|
272
298
|
console.log(" ibmi-migrations migrate:status --env production");
|
|
273
299
|
console.log(" ibmi-migrations migrate:latest --knexfile knexfile.ts");
|
|
274
300
|
console.log(" ibmi-migrations migrate:make create_users_table");
|
|
@@ -283,6 +309,7 @@ function parseArgs() {
|
|
|
283
309
|
knexfile: "./knexfile.js",
|
|
284
310
|
help: false,
|
|
285
311
|
extension: "js",
|
|
312
|
+
steps: 1,
|
|
286
313
|
migrationName: null
|
|
287
314
|
};
|
|
288
315
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -298,8 +325,16 @@ function parseArgs() {
|
|
|
298
325
|
} else if (arg === "-x" && args[i + 1]) {
|
|
299
326
|
parsed.extension = args[i + 1];
|
|
300
327
|
i++;
|
|
328
|
+
} else if ((arg === "--steps" || arg === "-s") && args[i + 1]) {
|
|
329
|
+
const parsedSteps = Number.parseInt(args[i + 1], 10);
|
|
330
|
+
if (!Number.isNaN(parsedSteps) && parsedSteps > 0) {
|
|
331
|
+
parsed.steps = parsedSteps;
|
|
332
|
+
}
|
|
333
|
+
i++;
|
|
301
334
|
} else if (!parsed.command) {
|
|
302
335
|
parsed.command = arg;
|
|
336
|
+
} else if (parsed.command === "migrate:rollback" && /^\d+$/.test(arg) && parsed.steps === 1) {
|
|
337
|
+
parsed.steps = Number.parseInt(arg, 10);
|
|
303
338
|
} else if (parsed.command === "migrate:make" && !parsed.migrationName) {
|
|
304
339
|
parsed.migrationName = arg;
|
|
305
340
|
}
|
|
@@ -385,6 +420,14 @@ async function loadKnexfile(knexfilePath, environment) {
|
|
|
385
420
|
return envConfig;
|
|
386
421
|
} catch (error) {
|
|
387
422
|
const message = error instanceof Error ? error.message : String(error);
|
|
423
|
+
const tsFile = knexfilePath.toLowerCase().endsWith(".ts");
|
|
424
|
+
if (tsFile && (message.includes("Unknown file extension") || message.includes("Cannot use import statement") || message.includes("Unexpected token"))) {
|
|
425
|
+
console.error("\u274C Failed to load TypeScript knexfile:", knexfilePath);
|
|
426
|
+
console.error(
|
|
427
|
+
"Run with a TS-capable runtime loader (for example: `node --import tsx`) or use a compiled JavaScript knexfile."
|
|
428
|
+
);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
388
431
|
console.error("\u274C Failed to load knexfile:", message);
|
|
389
432
|
console.error(`Make sure you have a valid knexfile at: ${knexfilePath}`);
|
|
390
433
|
process.exit(1);
|
|
@@ -455,8 +498,7 @@ async function main() {
|
|
|
455
498
|
const migrationConfig = {
|
|
456
499
|
directory: config.migrations?.directory || "./migrations",
|
|
457
500
|
tableName: config.migrations?.tableName || "KNEX_MIGRATIONS",
|
|
458
|
-
schemaName: config.migrations?.schemaName
|
|
459
|
-
extension: config.migrations?.extension || "js"
|
|
501
|
+
schemaName: config.migrations?.schemaName
|
|
460
502
|
};
|
|
461
503
|
const migrationRunner = createIBMiMigrationRunner(db, migrationConfig);
|
|
462
504
|
switch (command) {
|
|
@@ -465,11 +507,8 @@ async function main() {
|
|
|
465
507
|
await migrationRunner.latest();
|
|
466
508
|
break;
|
|
467
509
|
case "migrate:rollback":
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
) || 1;
|
|
471
|
-
console.log(`\u{1F504} Rolling back ${steps} migration batch(es)...`);
|
|
472
|
-
await migrationRunner.rollback(steps);
|
|
510
|
+
console.log(`\u{1F504} Rolling back ${args.steps} migration batch(es)...`);
|
|
511
|
+
await migrationRunner.rollback(args.steps);
|
|
473
512
|
break;
|
|
474
513
|
case "migrate:status":
|
|
475
514
|
console.log("\u{1F4CB} Migration Status Report");
|
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,10 @@ interface IBMiMigrationConfig {
|
|
|
5
5
|
directory: string;
|
|
6
6
|
tableName: string;
|
|
7
7
|
schemaName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Deprecated for runner discovery. Kept for backward compatibility.
|
|
10
|
+
* The runner discovers .js/.ts/.mjs/.cjs migrations regardless of this value.
|
|
11
|
+
*/
|
|
8
12
|
extension?: string;
|
|
9
13
|
}
|
|
10
14
|
declare class IBMiMigrationRunner {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ interface IBMiMigrationConfig {
|
|
|
5
5
|
directory: string;
|
|
6
6
|
tableName: string;
|
|
7
7
|
schemaName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Deprecated for runner discovery. Kept for backward compatibility.
|
|
10
|
+
* The runner discovers .js/.ts/.mjs/.cjs migrations regardless of this value.
|
|
11
|
+
*/
|
|
8
12
|
extension?: string;
|
|
9
13
|
}
|
|
10
14
|
declare class IBMiMigrationRunner {
|
package/dist/index.js
CHANGED
|
@@ -508,7 +508,8 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
508
508
|
updateSql: baseUpdateSql,
|
|
509
509
|
selectColumns,
|
|
510
510
|
whereClause: where,
|
|
511
|
-
tableName: this.tableName
|
|
511
|
+
tableName: this.tableName,
|
|
512
|
+
setBindingCount: updates.map((fragment) => (fragment.match(/\?/g) || []).length).reduce((sum, count) => sum + count, 0)
|
|
512
513
|
}
|
|
513
514
|
};
|
|
514
515
|
}
|
|
@@ -573,6 +574,9 @@ var import_node_stream = require("stream");
|
|
|
573
574
|
var import_fs = __toESM(require("fs"));
|
|
574
575
|
var import_path = __toESM(require("path"));
|
|
575
576
|
var import_url = require("url");
|
|
577
|
+
function buildTsRuntimeHelpMessage(fileName) {
|
|
578
|
+
return `TypeScript migration '${fileName}' requires a TypeScript runtime loader. Run with a TS-capable runtime (for example: \`node --import tsx\`) or precompile migrations to JavaScript.`;
|
|
579
|
+
}
|
|
576
580
|
var IBMiMigrationRunner = class {
|
|
577
581
|
constructor(knex2, config) {
|
|
578
582
|
__publicField(this, "knex");
|
|
@@ -582,9 +586,13 @@ var IBMiMigrationRunner = class {
|
|
|
582
586
|
directory: "./migrations",
|
|
583
587
|
tableName: "KNEX_MIGRATIONS",
|
|
584
588
|
schemaName: void 0,
|
|
585
|
-
extension: "js",
|
|
586
589
|
...config
|
|
587
590
|
};
|
|
591
|
+
if (typeof config?.extension === "string") {
|
|
592
|
+
console.warn(
|
|
593
|
+
"\u26A0\uFE0F IBMiMigrationRunner config 'extension' is ignored for discovery. The runner always discovers .js/.ts/.mjs/.cjs migration files."
|
|
594
|
+
);
|
|
595
|
+
}
|
|
588
596
|
}
|
|
589
597
|
getFullTableName() {
|
|
590
598
|
return this.config.schemaName ? `${this.config.schemaName}.${this.config.tableName}` : this.config.tableName;
|
|
@@ -631,7 +639,18 @@ var IBMiMigrationRunner = class {
|
|
|
631
639
|
try {
|
|
632
640
|
const migrationPath = this.getMigrationPath(migrationFile);
|
|
633
641
|
const fileUrl = (0, import_url.pathToFileURL)(migrationPath).href;
|
|
634
|
-
|
|
642
|
+
let migration;
|
|
643
|
+
try {
|
|
644
|
+
const moduleNs = await import(`${fileUrl}?t=${Date.now()}`);
|
|
645
|
+
migration = moduleNs.default ?? moduleNs;
|
|
646
|
+
} catch (importError) {
|
|
647
|
+
const isTsMigration = migrationFile.toLowerCase().endsWith(".ts");
|
|
648
|
+
const message = String(importError?.message || importError || "");
|
|
649
|
+
if (isTsMigration && (message.includes("Unknown file extension") || message.includes("Cannot use import statement") || message.includes("Unexpected token"))) {
|
|
650
|
+
throw new Error(buildTsRuntimeHelpMessage(migrationFile));
|
|
651
|
+
}
|
|
652
|
+
throw importError;
|
|
653
|
+
}
|
|
635
654
|
if (!migration.up || typeof migration.up !== "function") {
|
|
636
655
|
throw new Error(`Migration ${migrationFile} has no 'up' function`);
|
|
637
656
|
}
|
|
@@ -679,7 +698,18 @@ var IBMiMigrationRunner = class {
|
|
|
679
698
|
try {
|
|
680
699
|
const migrationPath = this.getMigrationPath(migrationFile);
|
|
681
700
|
const fileUrl = (0, import_url.pathToFileURL)(migrationPath).href;
|
|
682
|
-
|
|
701
|
+
let migration;
|
|
702
|
+
try {
|
|
703
|
+
const moduleNs = await import(`${fileUrl}?t=${Date.now()}`);
|
|
704
|
+
migration = moduleNs.default ?? moduleNs;
|
|
705
|
+
} catch (importError) {
|
|
706
|
+
const isTsMigration = migrationFile.toLowerCase().endsWith(".ts");
|
|
707
|
+
const message = String(importError?.message || importError || "");
|
|
708
|
+
if (isTsMigration && (message.includes("Unknown file extension") || message.includes("Cannot use import statement") || message.includes("Unexpected token"))) {
|
|
709
|
+
throw new Error(buildTsRuntimeHelpMessage(migrationFile));
|
|
710
|
+
}
|
|
711
|
+
throw importError;
|
|
712
|
+
}
|
|
683
713
|
if (migration.down && typeof migration.down === "function") {
|
|
684
714
|
console.log(` \u26A1 Executing rollback...`);
|
|
685
715
|
await migration.down(this.knex);
|
|
@@ -750,17 +780,12 @@ var IBMiMigrationRunner = class {
|
|
|
750
780
|
}
|
|
751
781
|
}
|
|
752
782
|
getMigrationFiles() {
|
|
753
|
-
const { directory
|
|
783
|
+
const { directory } = this.config;
|
|
754
784
|
if (!import_fs.default.existsSync(directory)) {
|
|
755
785
|
throw new Error(`Migration directory does not exist: ${directory}`);
|
|
756
786
|
}
|
|
757
787
|
const validExtensions = ["js", "ts", "mjs", "cjs"];
|
|
758
|
-
return import_fs.default.readdirSync(directory).filter((file) => {
|
|
759
|
-
if (extension && extension !== "js") {
|
|
760
|
-
return file.endsWith(`.${extension}`);
|
|
761
|
-
}
|
|
762
|
-
return validExtensions.some((ext) => file.endsWith(`.${ext}`));
|
|
763
|
-
}).sort();
|
|
788
|
+
return import_fs.default.readdirSync(directory).filter((file) => validExtensions.some((ext) => file.endsWith(`.${ext}`))).sort();
|
|
764
789
|
}
|
|
765
790
|
getMigrationPath(filename) {
|
|
766
791
|
return import_path.default.resolve(this.config.directory, filename);
|
|
@@ -1004,7 +1029,13 @@ var DB2Client = class extends import_knex.default.Client {
|
|
|
1004
1029
|
*/
|
|
1005
1030
|
async executeUpdateReturning(connection, obj) {
|
|
1006
1031
|
const { _ibmiUpdateReturning } = obj;
|
|
1007
|
-
const {
|
|
1032
|
+
const {
|
|
1033
|
+
updateSql,
|
|
1034
|
+
selectColumns,
|
|
1035
|
+
whereClause,
|
|
1036
|
+
tableName,
|
|
1037
|
+
setBindingCount
|
|
1038
|
+
} = _ibmiUpdateReturning;
|
|
1008
1039
|
this.printDebug(
|
|
1009
1040
|
"Executing UPDATE with returning using transaction approach"
|
|
1010
1041
|
);
|
|
@@ -1016,10 +1047,8 @@ var DB2Client = class extends import_knex.default.Client {
|
|
|
1016
1047
|
};
|
|
1017
1048
|
await this.executeStatementQuery(connection, updateObj);
|
|
1018
1049
|
const selectSql = whereClause ? `select ${selectColumns} from ${tableName} ${whereClause}` : `select ${selectColumns} from ${tableName}`;
|
|
1019
|
-
const
|
|
1020
|
-
const
|
|
1021
|
-
const setBindingCount = (setClausePart.match(/\?/g) || []).length;
|
|
1022
|
-
const whereBindings = obj.bindings ? obj.bindings.slice(setBindingCount) : [];
|
|
1050
|
+
const inferredSetBindingCount = typeof setBindingCount === "number" ? setBindingCount : (updateSql.split(" where ")[0].match(/\?/g) || []).length;
|
|
1051
|
+
const whereBindings = obj.bindings ? obj.bindings.slice(inferredSetBindingCount) : [];
|
|
1023
1052
|
const selectObj = {
|
|
1024
1053
|
sql: selectSql,
|
|
1025
1054
|
bindings: whereBindings,
|
|
@@ -1425,11 +1454,25 @@ var DB2Client = class extends import_knex.default.Client {
|
|
|
1425
1454
|
validateResponse(obj) {
|
|
1426
1455
|
if (!obj.response) {
|
|
1427
1456
|
this.printDebug("response undefined " + this.safeStringify(obj));
|
|
1428
|
-
return
|
|
1457
|
+
return this.processSqlMethod({
|
|
1458
|
+
...obj,
|
|
1459
|
+
response: { rows: [], rowCount: 0 }
|
|
1460
|
+
});
|
|
1429
1461
|
}
|
|
1430
1462
|
if (!obj.response.rows) {
|
|
1431
|
-
|
|
1432
|
-
|
|
1463
|
+
const usesRowCountOnly = !obj.select && (obj.sqlMethod === "del" /* DELETE */ || obj.sqlMethod === "delete" /* DELETE_ALT */ || obj.sqlMethod === "update" /* UPDATE */ || obj.sqlMethod === "counter" /* COUNTER */);
|
|
1464
|
+
if (usesRowCountOnly) {
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
this.printWarn("rows undefined " + this.safeStringify(obj));
|
|
1468
|
+
return this.processSqlMethod({
|
|
1469
|
+
...obj,
|
|
1470
|
+
response: {
|
|
1471
|
+
...obj.response,
|
|
1472
|
+
rows: [],
|
|
1473
|
+
rowCount: obj.response.rowCount ?? 0
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1433
1476
|
}
|
|
1434
1477
|
return null;
|
|
1435
1478
|
}
|
|
@@ -1474,8 +1517,7 @@ var DB2Client = class extends import_knex.default.Client {
|
|
|
1474
1517
|
return queryObject;
|
|
1475
1518
|
} catch (retryError) {
|
|
1476
1519
|
this.printError(`Retry failed: ${retryError.message}`);
|
|
1477
|
-
|
|
1478
|
-
return queryObject;
|
|
1520
|
+
throw this.wrapError(retryError, `${method}_retry`, queryObject);
|
|
1479
1521
|
}
|
|
1480
1522
|
}
|
|
1481
1523
|
/**
|
|
@@ -1520,7 +1562,8 @@ var DB2Client = class extends import_knex.default.Client {
|
|
|
1520
1562
|
return errorMessage.includes("sql") || errorMessage.includes("syntax") || errorMessage.includes("table") || errorMessage.includes("column");
|
|
1521
1563
|
}
|
|
1522
1564
|
processSqlMethod(obj) {
|
|
1523
|
-
const
|
|
1565
|
+
const rows = obj.response?.rows ?? [];
|
|
1566
|
+
const rowCount = obj.response?.rowCount;
|
|
1524
1567
|
switch (obj.sqlMethod) {
|
|
1525
1568
|
case "select" /* SELECT */:
|
|
1526
1569
|
return rows;
|