@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
129 lines (128 loc) • 4.22 kB
JavaScript
/*
* @adonisjs/lucid
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { readFile, writeFile } from 'node:fs/promises';
import { basename, dirname, extname, join } from 'node:path';
/**
* Increment this version whenever the manifest shape changes in a backward
* incompatible way.
*/
export const SCHEMA_DUMP_MANIFEST_VERSION = 1;
/**
* High-level wrapper around the schema dump sidecar manifest.
*/
export class SchemaDumpManifestFile {
manifest;
/**
* Returns the default dump path for a given connection.
*/
static defaultDumpPath(connectionName) {
return `database/schema/${connectionName}-schema.sql`;
}
/**
* Returns the manifest path stored next to the SQL dump.
*/
static metaPath(dumpPath) {
const extension = extname(dumpPath);
const fileName = basename(dumpPath, extension || undefined);
return join(dirname(dumpPath), `${fileName}.meta.json`);
}
/**
* Create a new manifest instance for the provided dump metadata.
*/
static create(payload) {
return new SchemaDumpManifestFile({
version: SCHEMA_DUMP_MANIFEST_VERSION,
generatedAt: payload.generatedAt || new Date().toISOString(),
...payload,
});
}
/**
* Convert an unknown JSON value into a manifest instance. Returns `null`
* when the value does not match the expected shape.
*/
static fromJSON(value) {
if (!value || typeof value !== 'object') {
return null;
}
const manifest = value;
if (typeof manifest.version !== 'number' ||
typeof manifest.connection !== 'string' ||
typeof manifest.dumpPath !== 'string' ||
typeof manifest.generatedAt !== 'string' ||
typeof manifest.schemaTableName !== 'string' ||
typeof manifest.schemaVersionsTableName !== 'string' ||
!Array.isArray(manifest.squashedMigrationNames) ||
!manifest.squashedMigrationNames.every((name) => typeof name === 'string')) {
return null;
}
return new SchemaDumpManifestFile(manifest);
}
/**
* Read and validate a manifest file from disk.
*/
static async read(path) {
try {
const contents = await readFile(path, 'utf-8');
return this.fromJSON(JSON.parse(contents));
}
catch {
return null;
}
}
/**
* Read a manifest from disk and return it only when it matches the provided
* migration bookkeeping context.
*/
static async readForContext(path, context) {
const manifest = await this.read(path);
if (!manifest || !manifest.matchesContext(context)) {
return null;
}
return manifest;
}
#squashedSet;
constructor(manifest) {
this.manifest = manifest;
this.#squashedSet = new Set(manifest.squashedMigrationNames);
}
/**
* Persist the manifest as formatted JSON for readability and diffs.
*/
async write(path) {
await writeFile(path, JSON.stringify(this.manifest, null, 2) + '\n', 'utf-8');
}
/**
* Returns the raw manifest payload.
*/
toJSON() {
return this.manifest;
}
/**
* Returns true when the manifest was created for the provided migration
* bookkeeping context.
*/
matchesContext(payload) {
return (this.manifest.version === SCHEMA_DUMP_MANIFEST_VERSION &&
this.manifest.connection === payload.connection &&
this.manifest.schemaTableName === payload.schemaTableName &&
this.manifest.schemaVersionsTableName === payload.schemaVersionsTableName);
}
/**
* Returns true when the given migration name is part of the squashed baseline.
*/
hasSquashedMigration(name) {
return this.#squashedSet.has(name);
}
/**
* Returns the list of migration names stored inside the manifest.
*/
getSquashedMigrationNames() {
return [...this.manifest.squashedMigrationNames];
}
}