ordinality
Version:
Universal migrations tools
72 lines (70 loc) • 11.1 kB
JavaScript
import { existsSync } from 'fs';
import path from 'path';
import { z } from 'zod';
import { mkdir, readFile, writeFile } from 'fs/promises';
// TODO: lock file and wait unlock to ensure consistent state when multiple instances in run
export class MigrationFileStorage {
filename;
options;
constructor(filename, options = {}) {
this.filename = filename;
this.options = options;
}
getCodec() {
return (this.options.codec ?? {
encode(data) {
return Buffer.from(JSON.stringify(data, undefined, 2));
},
decode(buffer) {
return JSON.parse(buffer.toString('utf-8'));
},
});
}
getMigrationScheme() {
return z.object({
name: z.string(),
meta: this.options.metaScheme ?? z.void().nullish().optional(),
});
}
getStorageScheme() {
return z.object({
migrations: this.getMigrationScheme().array(),
});
}
async fetchState() {
if (!existsSync(this.filename))
return { migrations: [] };
const fileBuffer = await readFile(this.filename);
const jsonState = this.getCodec().decode(fileBuffer);
return this.getStorageScheme().parse(jsonState);
}
async list() {
const state = await this.fetchState();
return state.migrations.map((migration) => migration.name);
}
async log(uid, context) {
const newState = await this.fetchState();
// Ensure unique name
for (const migration of newState.migrations) {
if (migration.name === uid)
throw new Error(`Migration with name ${uid} is already applied`);
}
// Add and validate
newState.migrations.push({ name: uid, meta: context.meta });
await mkdir(path.dirname(this.filename), { recursive: true });
const verifiedState = this.getStorageScheme().parse(newState);
await writeFile(this.filename, this.getCodec().encode(verifiedState));
}
async unlog(uid) {
const state = await this.fetchState();
// Update migrations list
const filteredMigrations = state.migrations.filter((migration) => migration.name === uid);
if (state.migrations.length === filteredMigrations.length)
throw new Error(`Migration with name ${uid} is not found`);
state.migrations = filteredMigrations;
await mkdir(path.dirname(this.filename), { recursive: true });
const verifiedState = this.getStorageScheme().parse(state);
await writeFile(this.filename, this.getCodec().encode(verifiedState));
}
}
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0b3JhZ2UvTWlncmF0aW9uRmlsZVN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNoQyxPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFDeEIsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUd4QixPQUFPLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFlekQsNEZBQTRGO0FBQzVGLE1BQU0sT0FBTyxvQkFBb0I7SUFJZDtJQUNBO0lBRmxCLFlBQ2tCLFFBQWdCLEVBQ2hCLFVBWWIsRUFBRTtRQWJXLGFBQVEsR0FBUixRQUFRLENBQVE7UUFDaEIsWUFBTyxHQUFQLE9BQU8sQ0FZbEI7SUFDSixDQUFDO0lBRU0sUUFBUTtRQUNqQixPQUFPLENBQ04sSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLElBQUk7WUFDckIsTUFBTSxDQUFDLElBQUk7Z0JBQ1YsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3hELENBQUM7WUFDRCxNQUFNLENBQUMsTUFBTTtnQkFDWixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQzdDLENBQUM7U0FDRCxDQUNELENBQUM7SUFDSCxDQUFDO0lBRVMsa0JBQWtCO1FBQzNCLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQztZQUNmLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO1lBQ2hCLElBQUksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsUUFBUSxFQUFFO1NBQzlELENBQUMsQ0FBQztJQUNKLENB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Q0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztJQUN2RSxDQUFDO0NBQ0QiLCJmaWxlIjoic3RvcmFnZS9NaWdyYXRpb25GaWxlU3RvcmFnZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4aXN0c1N5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG5pbXBvcnQgeyBNaWdyYXRpb25Db250ZXh0IH0gZnJvbSAnLi4vTWlncmF0aW9uJztcbmltcG9ydCB7IG1rZGlyLCByZWFkRmlsZSwgd3JpdGVGaWxlIH0gZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHR5cGUgeyBNaWdyYXRpb25TdG9yYWdlIH0gZnJvbSAnLi9NaWdyYXRpb25TdG9yYWdlJztcblxuZXhwb3J0IHR5cGUgQ29kZWM8VD4gPSB7XG5cdGVuY29kZTogKGRhdGE6IFQpID0+IEJ1ZmZlcjtcblx0ZGVjb2RlOiAoYnVmZmVyOiBCdWZmZXIpID0+IFQ7XG59O1xuXG5leHBvcnQgdHlwZSBTdGF0ZTxNID0gdm9pZD4gPSB7XG5cdG1pZ3JhdGlvbnM6IHtcblx0XHRuYW1lOiBzdHJpbmc7XG5cdFx0bWV0YTogTTtcblx0fVtdO1xufTtcblxuLy8gVE9ETzogbG9jayBmaWxlIGFuZCB3YWl0IHVubG9jayB0byBlbnN1cmUgY29uc2lzdGVudCBzdGF0ZSB3aGVuIG11bHRpcGxlIGluc3RhbmNlcyBpbiBydW5cbmV4cG9ydCBjbGFzcyBNaWdyYXRpb25GaWxlU3RvcmFnZTxDIGV4dGVuZHMgTWlncmF0aW9uQ29udGV4dDxhbnksIGFueT4+XG5cdGltcGxlbWVudHMgTWlncmF0aW9uU3RvcmFnZTxDPlxue1xuXHRjb25zdHJ1Y3Rvcihcblx0XHRwcml2YXRlIHJlYWRvbmx5IGZpbGVuYW1lOiBzdHJpbmcsXG5cdFx0cHJpdmF0ZSByZWFkb25seSBvcHRpb25zOiB7XG5cdFx0XHQvKipcblx0XHRcdCAqIFNjaGVtZSB2YWxpZGF0b3IgZm9yIGEgbWlncmF0aW9uIG1ldGFcblx0XHRcdCAqL1xuXHRcdFx0bWV0YVNjaGVtZT86IENbJ21ldGEnXSBleHRlbmRzIHouVHlwZU9mPGluZmVyIFg+ID8gWCA6IG5ldmVyO1xuXG5cdFx0XHQvKipcblx0XHRcdCAqIEN1c3RvbSBsb2dpYyB0byBlbmNvZGUvZGVjb2RlIGRhdGFcblx0XHRcdCAqXG5cdFx0XHQgKiBCeSBkZWZhdWx0IGRhdGEgd2lsbCBiZSBlbmNvZGVkIGFzIEpTT04gc3RyaW5nXG5cdFx0XHQgKi9cblx0XHRcdGNvZGVjPzogQ29kZWM8U3RhdGU8Q1snbWV0YSddPj47XG5cdFx0fSA9IHt9LFxuXHQpIHt9XG5cblx0cHJvdGVjdGVkIGdldENvZGVjKCk6IENvZGVjPFN0YXRlPENbJ21ldGEnXT4+IHtcblx0XHRyZXR1cm4gKFxuXHRcdFx0dGhpcy5vcHRpb25zLmNvZGVjID8/IHtcblx0XHRcdFx0ZW5jb2RlKGRhdGEpIHtcblx0XHRcdFx0XHRyZXR1cm4gQnVmZmVyLmZyb20oSlNPTi5zdHJpbmdpZnkoZGF0YSwgdW5kZWZpbmVkLCAyKSk7XG5cdFx0XHRcdH0sXG5cdFx0XHRcdGRlY29kZShidWZmZXIpIHtcblx0XHRcdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShidWZmZXIudG9TdHJpbmcoJ3V0Zi04JykpO1xuXHRcdFx0XHR9LFxuXHRcdFx0fVxuXHRcdCk7XG5cdH1cblxuXHRwcm90ZWN0ZWQgZ2V0TWlncmF0aW9uU2NoZW1lKCkge1xuXHRcdHJldHVybiB6Lm9iamVjdCh7XG5cdFx0XHRuYW1lOiB6LnN0cmluZygpLFxuXHRcdFx0bWV0YTogdGhpcy5vcHRpb25zLm1ldGFTY2hlbWUgPz8gei52b2lkKCkubnVsbGlzaCgpLm9wdGlvbmFsKCksXG5cdFx0fSk7XG5cdH1cblxuXHRwcm90ZWN0ZWQgZ2V0U3RvcmFnZVNjaGVtZSgpIHtcblx0XHRyZXR1cm4gei5vYmplY3Qoe1xuXHRcdFx0bWlncmF0aW9uczogdGhpcy5nZXRNaWdyYXRpb25TY2hlbWUoKS5hcnJheSgpLFxuXHRcdH0pO1xuXHR9XG5cblx0cHJvdGVjdGVkIGFzeW5jIGZldGNoU3RhdGUoKSB7XG5cdFx0aWYgKCFleGlzdHNTeW5jKHRoaXMuZmlsZW5hbWUpKSByZXR1cm4geyBtaWdyYXRpb25zOiBbXSB9O1xuXG5cdFx0Y29uc3QgZmlsZUJ1ZmZlciA9IGF3YWl0IHJlYWRGaWxlKHRoaXMuZmlsZW5hbWUpO1xuXHRcdGNvbnN0IGpzb25TdGF0ZSA9IHRoaXMuZ2V0Q29kZWMoKS5kZWNvZGUoZmlsZUJ1ZmZlcik7XG5cblx0XHRyZXR1cm4gdGhpcy5nZXRTdG9yYWdlU2NoZW1lKCkucGFyc2UoanNvblN0YXRlKSBhcyBTdGF0ZTxDWydtZXRhJ10+O1xuXHR9XG5cblx0YXN5bmMgbGlzdCgpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG5cdFx0Y29uc3Qgc3RhdGUgPSBhd2FpdCB0aGlzLmZldGNoU3RhdGUoKTtcblxuXHRcdHJldHVybiBzdGF0ZS5taWdyYXRpb25zLm1hcCgobWlncmF0aW9uKSA9PiBtaWdyYXRpb24ubmFtZSk7XG5cdH1cblxuXHRhc3luYyBsb2codWlkOiBzdHJpbmcsIGNvbnRleHQ6IEMpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCBuZXdTdGF0ZSA9IGF3YWl0IHRoaXMuZmV0Y2hTdGF0ZSgpO1xuXG5cdFx0Ly8gRW5zdXJlIHVuaXF1ZSBuYW1lXG5cdFx0Zm9yIChjb25zdCBtaWdyYXRpb24gb2YgbmV3U3RhdGUubWlncmF0aW9ucykge1xuXHRcdFx0aWYgKG1pZ3JhdGlvbi5uYW1lID09PSB1aWQpXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcihgTWlncmF0aW9uIHdpdGggbmFtZSAke3VpZH0gaXMgYWxyZWFkeSBhcHBsaWVkYCk7XG5cdFx0fVxuXG5cdFx0Ly8gQWRkIGFuZCB2YWxpZGF0ZVxuXHRcdG5ld1N0YXRlLm1pZ3JhdGlvbnMucHVzaCh7IG5hbWU6IHVpZCwgbWV0YTogY29udGV4dC5tZXRhIH0pO1xuXG5cdFx0YXdhaXQgbWtkaXIocGF0aC5kaXJuYW1lKHRoaXMuZmlsZW5hbWUpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcblxuXHRcdGNvbnN0IHZlcmlmaWVkU3RhdGUgPSB0aGlzLmdldFN0b3JhZ2VTY2hlbWUoKS5wYXJzZShuZXdTdGF0ZSkgYXMgU3RhdGU8Q1snbWV0YSddPjtcblx0XHRhd2FpdCB3cml0ZUZpbGUodGhpcy5maWxlbmFtZSwgdGhpcy5nZXRDb2RlYygpLmVuY29kZSh2ZXJpZmllZFN0YXRlKSk7XG5cdH1cblxuXHRhc3luYyB1bmxvZyh1aWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGNvbnN0IHN0YXRlID0gYXdhaXQgdGhpcy5mZXRjaFN0YXRlKCk7XG5cblx0XHQvLyBVcGRhdGUgbWlncmF0aW9ucyBsaXN0XG5cdFx0Y29uc3QgZmlsdGVyZWRNaWdyYXRpb25zID0gc3RhdGUubWlncmF0aW9ucy5maWx0ZXIoXG5cdFx0XHQobWlncmF0aW9uKSA9PiBtaWdyYXRpb24ubmFtZSA9PT0gdWlkLFxuXHRcdCk7XG5cdFx0aWYgKHN0YXRlLm1pZ3JhdGlvbnMubGVuZ3RoID09PSBmaWx0ZXJlZE1pZ3JhdGlvbnMubGVuZ3RoKVxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKGBNaWdyYXRpb24gd2l0aCBuYW1lICR7dWlkfSBpcyBub3QgZm91bmRgKTtcblxuXHRcdHN0YXRlLm1pZ3JhdGlvbnMgPSBmaWx0ZXJlZE1pZ3JhdGlvbnM7XG5cblx0XHRhd2FpdCBta2RpcihwYXRoLmRpcm5hbWUodGhpcy5maWxlbmFtZSksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuXG5cdFx0Y29uc3QgdmVyaWZpZWRTdGF0ZSA9IHRoaXMuZ2V0U3RvcmFnZVNjaGVtZSgpLnBhcnNlKHN0YXRlKSBhcyBTdGF0ZTxDWydtZXRhJ10+O1xuXHRcdGF3YWl0IHdyaXRlRmlsZSh0aGlzLmZpbGVuYW1lLCB0aGlzLmdldENvZGVjKCkuZW5jb2RlKHZlcmlmaWVkU3RhdGUpKTtcblx0fVxufVxuIl19