@noego/proper 0.2.1 → 0.3.0
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 +90 -40
- package/bin/cli.js.map +1 -1
- package/bin/cli.mjs +103 -43
- package/bin/cli.mjs.map +1 -1
- package/bin/index.d.mts +5 -0
- package/bin/index.d.ts +5 -0
- package/bin/index.js +90 -40
- package/bin/index.js.map +1 -1
- package/bin/index.mjs +90 -40
- package/bin/index.mjs.map +1 -1
- package/package.json +1 -1
- package/readme.md +12 -0
package/bin/index.d.mts
CHANGED
|
@@ -6,6 +6,11 @@ interface MigrationConfig {
|
|
|
6
6
|
migration_table: string;
|
|
7
7
|
migration_folder: string;
|
|
8
8
|
database: string;
|
|
9
|
+
/**
|
|
10
|
+
* Exact canonical migration keys whose historical rounded stamps are
|
|
11
|
+
* explicitly allowed. Proper remains strict for every other migration.
|
|
12
|
+
*/
|
|
13
|
+
legacy_migration_keys?: readonly string[];
|
|
9
14
|
/**
|
|
10
15
|
* Folder containing one-shot ledger patch files. Defaults to a `patches`
|
|
11
16
|
* sibling of `migration_folder` (e.g. `migrations` -> `patches`,
|
package/bin/index.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ interface MigrationConfig {
|
|
|
6
6
|
migration_table: string;
|
|
7
7
|
migration_folder: string;
|
|
8
8
|
database: string;
|
|
9
|
+
/**
|
|
10
|
+
* Exact canonical migration keys whose historical rounded stamps are
|
|
11
|
+
* explicitly allowed. Proper remains strict for every other migration.
|
|
12
|
+
*/
|
|
13
|
+
legacy_migration_keys?: readonly string[];
|
|
9
14
|
/**
|
|
10
15
|
* Folder containing one-shot ledger patch files. Defaults to a `patches`
|
|
11
16
|
* sibling of `migration_folder` (e.g. `migrations` -> `patches`,
|
package/bin/index.js
CHANGED
|
@@ -90,9 +90,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
90
90
|
// framework/MigrationRunner.ts
|
|
91
91
|
var import_fs6 = __toESM(require("fs"));
|
|
92
92
|
|
|
93
|
-
// framework/MigrationDirectoryReader.ts
|
|
94
|
-
var import_fs2 = __toESM(require("fs"));
|
|
95
|
-
|
|
96
93
|
// framework/errors.ts
|
|
97
94
|
var MigrationError = class _MigrationError extends Error {
|
|
98
95
|
constructor(message) {
|
|
@@ -157,6 +154,16 @@ var DatabaseConnectionError = class _DatabaseConnectionError extends MigrationEr
|
|
|
157
154
|
return new _DatabaseConnectionError(`Authentication failed for ${dbType} database`);
|
|
158
155
|
}
|
|
159
156
|
};
|
|
157
|
+
var MigrationStampError = class _MigrationStampError extends MigrationError {
|
|
158
|
+
constructor(migrationKey) {
|
|
159
|
+
super(
|
|
160
|
+
`Migration '${migrationKey}' cannot be migrated: its timestamp ends in "000", so it does not look like it was generated by 'proper create'. Recreate it with 'proper create <name>' and move the SQL into the generated files.`
|
|
161
|
+
);
|
|
162
|
+
this.migrationKey = migrationKey;
|
|
163
|
+
this.name = "MigrationStampError";
|
|
164
|
+
Object.setPrototypeOf(this, _MigrationStampError.prototype);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
160
167
|
var MigrationExecutionError = class _MigrationExecutionError extends MigrationError {
|
|
161
168
|
constructor(message, migrationName, sql, originalError) {
|
|
162
169
|
let fullMessage = `Migration Execution Error${migrationName ? ` in '${migrationName}'` : ""}: ${message}`;
|
|
@@ -285,6 +292,71 @@ var CLIError = class _CLIError extends MigrationError {
|
|
|
285
292
|
}
|
|
286
293
|
};
|
|
287
294
|
|
|
295
|
+
// framework/MigrationManifest.ts
|
|
296
|
+
var import_fs = __toESM(require("fs"));
|
|
297
|
+
var import_path = __toESM(require("path"));
|
|
298
|
+
var MIGRATION_FILE_REGEX = /(?:\.(mysql|sqlite|pg))?\.(up|down)\.(sql|js)$/i;
|
|
299
|
+
function isMigrationFile(fileName) {
|
|
300
|
+
return MIGRATION_FILE_REGEX.test(fileName);
|
|
301
|
+
}
|
|
302
|
+
function canonicalMigrationKey(value) {
|
|
303
|
+
return value.replace(MIGRATION_FILE_REGEX, "").toLowerCase();
|
|
304
|
+
}
|
|
305
|
+
function mintMigrationStamp(now = Date.now()) {
|
|
306
|
+
return now % 10 === 0 ? now + 1 : now;
|
|
307
|
+
}
|
|
308
|
+
function isHandStampedKey(key) {
|
|
309
|
+
const stamp = key.split("_")[0];
|
|
310
|
+
return /^\d+$/.test(stamp) && /000$/.test(stamp);
|
|
311
|
+
}
|
|
312
|
+
function resolveMigrationFile(directory, baseName, direction, dialect) {
|
|
313
|
+
const dialectExt = dialect === "sql" ? "mysql" : dialect;
|
|
314
|
+
const dialectFile = import_path.default.join(directory, `${baseName}.${dialectExt}.${direction}.sql`);
|
|
315
|
+
if (import_fs.default.existsSync(dialectFile)) return dialectFile;
|
|
316
|
+
const genericFile = import_path.default.join(directory, `${baseName}.${direction}.sql`);
|
|
317
|
+
if (import_fs.default.existsSync(genericFile)) return genericFile;
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
function loadMigrationManifest(directory, dialect) {
|
|
321
|
+
const manifest = /* @__PURE__ */ new Map();
|
|
322
|
+
if (!import_fs.default.existsSync(directory)) return manifest;
|
|
323
|
+
const files = import_fs.default.readdirSync(directory, { withFileTypes: true }).filter((f) => f.isFile()).map((f) => f.name).filter(isMigrationFile);
|
|
324
|
+
const uniqueKeys = /* @__PURE__ */ new Set();
|
|
325
|
+
files.forEach((file) => uniqueKeys.add(canonicalMigrationKey(file)));
|
|
326
|
+
uniqueKeys.forEach((key) => {
|
|
327
|
+
manifest.set(key, {
|
|
328
|
+
key,
|
|
329
|
+
upFile: resolveMigrationFile(directory, key, "up", dialect),
|
|
330
|
+
downFile: resolveMigrationFile(directory, key, "down", dialect)
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
return manifest;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// framework/MigrationConfig.ts
|
|
337
|
+
function validateLegacyMigrationKeys(value) {
|
|
338
|
+
if (value === void 0) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (!Array.isArray(value) || value.some((key) => !isExactCanonicalMigrationKey(key))) {
|
|
342
|
+
throw new ConfigurationError(
|
|
343
|
+
"legacy_migration_keys must be an array of nonempty exact canonical migration keys; filenames, paths, wildcards, and nonstrings are not allowed"
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function isExactCanonicalMigrationKey(value) {
|
|
348
|
+
if (typeof value !== "string" || value.length === 0 || value !== value.trim()) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
if (value.includes("/") || value.includes("\\") || ["*", "?", "[", "]", "{", "}"].some((token) => value.includes(token))) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
return !isMigrationFile(value) && canonicalMigrationKey(value) === value;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// framework/MigrationDirectoryReader.ts
|
|
358
|
+
var import_fs2 = __toESM(require("fs"));
|
|
359
|
+
|
|
288
360
|
// framework/MigrationNode.ts
|
|
289
361
|
var MigrationNode = class {
|
|
290
362
|
constructor(name) {
|
|
@@ -416,40 +488,6 @@ var SqlMigrationBuilder = class {
|
|
|
416
488
|
}
|
|
417
489
|
};
|
|
418
490
|
|
|
419
|
-
// framework/MigrationManifest.ts
|
|
420
|
-
var import_fs = __toESM(require("fs"));
|
|
421
|
-
var import_path = __toESM(require("path"));
|
|
422
|
-
var MIGRATION_FILE_REGEX = /(?:\.(mysql|sqlite|pg))?\.(up|down)\.(sql|js)$/i;
|
|
423
|
-
function isMigrationFile(fileName) {
|
|
424
|
-
return MIGRATION_FILE_REGEX.test(fileName);
|
|
425
|
-
}
|
|
426
|
-
function canonicalMigrationKey(value) {
|
|
427
|
-
return value.replace(MIGRATION_FILE_REGEX, "").toLowerCase();
|
|
428
|
-
}
|
|
429
|
-
function resolveMigrationFile(directory, baseName, direction, dialect) {
|
|
430
|
-
const dialectExt = dialect === "sql" ? "mysql" : dialect;
|
|
431
|
-
const dialectFile = import_path.default.join(directory, `${baseName}.${dialectExt}.${direction}.sql`);
|
|
432
|
-
if (import_fs.default.existsSync(dialectFile)) return dialectFile;
|
|
433
|
-
const genericFile = import_path.default.join(directory, `${baseName}.${direction}.sql`);
|
|
434
|
-
if (import_fs.default.existsSync(genericFile)) return genericFile;
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
function loadMigrationManifest(directory, dialect) {
|
|
438
|
-
const manifest = /* @__PURE__ */ new Map();
|
|
439
|
-
if (!import_fs.default.existsSync(directory)) return manifest;
|
|
440
|
-
const files = import_fs.default.readdirSync(directory, { withFileTypes: true }).filter((f) => f.isFile()).map((f) => f.name).filter(isMigrationFile);
|
|
441
|
-
const uniqueKeys = /* @__PURE__ */ new Set();
|
|
442
|
-
files.forEach((file) => uniqueKeys.add(canonicalMigrationKey(file)));
|
|
443
|
-
uniqueKeys.forEach((key) => {
|
|
444
|
-
manifest.set(key, {
|
|
445
|
-
key,
|
|
446
|
-
upFile: resolveMigrationFile(directory, key, "up", dialect),
|
|
447
|
-
downFile: resolveMigrationFile(directory, key, "down", dialect)
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
return manifest;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
491
|
// framework/MigrationDirectoryReader.ts
|
|
454
492
|
var MigrationDirectoryReader = class {
|
|
455
493
|
constructor(directory, read_strategy, sqlrunner, dialect = "sql") {
|
|
@@ -1337,7 +1375,7 @@ var PatchCreator = class _PatchCreator {
|
|
|
1337
1375
|
if (!import_fs5.default.existsSync(this.patchFolder)) {
|
|
1338
1376
|
import_fs5.default.mkdirSync(this.patchFolder, { recursive: true });
|
|
1339
1377
|
}
|
|
1340
|
-
let stamp =
|
|
1378
|
+
let stamp = mintMigrationStamp();
|
|
1341
1379
|
for (let attempt = 0; attempt < 1e3; attempt++) {
|
|
1342
1380
|
const filePath = import_path4.default.join(this.patchFolder, `${stamp}_${normalized}.yaml`);
|
|
1343
1381
|
try {
|
|
@@ -1345,7 +1383,7 @@ var PatchCreator = class _PatchCreator {
|
|
|
1345
1383
|
return filePath;
|
|
1346
1384
|
} catch (error) {
|
|
1347
1385
|
if (error && error.code === "EEXIST") {
|
|
1348
|
-
stamp
|
|
1386
|
+
stamp = mintMigrationStamp(stamp + 1);
|
|
1349
1387
|
continue;
|
|
1350
1388
|
}
|
|
1351
1389
|
throw error;
|
|
@@ -1834,6 +1872,7 @@ var MySQLMigrationRunner = class {
|
|
|
1834
1872
|
*/
|
|
1835
1873
|
this.preflightPromise = null;
|
|
1836
1874
|
this.lastPatchResults = [];
|
|
1875
|
+
validateLegacyMigrationKeys(config.legacy_migration_keys);
|
|
1837
1876
|
}
|
|
1838
1877
|
setup() {
|
|
1839
1878
|
return __async(this, null, function* () {
|
|
@@ -1939,7 +1978,17 @@ var MySQLMigrationRunner = class {
|
|
|
1939
1978
|
}
|
|
1940
1979
|
migrate(migrationNodes, forward) {
|
|
1941
1980
|
return __async(this, null, function* () {
|
|
1981
|
+
var _a;
|
|
1942
1982
|
yield this.setup();
|
|
1983
|
+
if (forward) {
|
|
1984
|
+
const legacyMigrationKeys = new Set((_a = this.config.legacy_migration_keys) != null ? _a : []);
|
|
1985
|
+
for (const node of migrationNodes) {
|
|
1986
|
+
const key = node.get_key();
|
|
1987
|
+
if (isHandStampedKey(key) && !legacyMigrationKeys.has(key) && !(yield node.status()).completed) {
|
|
1988
|
+
throw new MigrationStampError(key);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1943
1992
|
for (let node of migrationNodes) {
|
|
1944
1993
|
try {
|
|
1945
1994
|
if (forward) {
|
|
@@ -2035,6 +2084,7 @@ var FileMigrationConfigReader = class {
|
|
|
2035
2084
|
try {
|
|
2036
2085
|
const fileContent = import_fs6.default.readFileSync(this.configFile);
|
|
2037
2086
|
const config = JSON.parse(fileContent.toString());
|
|
2087
|
+
validateLegacyMigrationKeys(config.legacy_migration_keys);
|
|
2038
2088
|
if (!config.migration_folder) {
|
|
2039
2089
|
throw ConfigurationError.missingRequiredProperty("migration_folder");
|
|
2040
2090
|
}
|
|
@@ -2077,7 +2127,7 @@ var MigrationCreator = class {
|
|
|
2077
2127
|
if (!import_fs6.default.existsSync(this.config.migration_folder)) {
|
|
2078
2128
|
import_fs6.default.mkdirSync(this.config.migration_folder, { recursive: true });
|
|
2079
2129
|
}
|
|
2080
|
-
const now_timestamp =
|
|
2130
|
+
const now_timestamp = mintMigrationStamp();
|
|
2081
2131
|
const filename_up = `${now_timestamp}_${name}.up.sql`;
|
|
2082
2132
|
const filename_down = `${now_timestamp}_${name}.down.sql`;
|
|
2083
2133
|
import_fs6.default.writeFileSync(`${this.config.migration_folder}/${filename_up}`, `
|