@magic-mustard/sqlsync
Version:
SQLSync simplifies database schema evolution by allowing a declarative approach to table management
146 lines (145 loc) • 6.85 kB
JavaScript
;
// config/index.ts
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Config = void 0;
/**
* ConfigLoader class for loading and validating sqlsync.yaml configuration files.
*/
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const yaml = __importStar(require("js-yaml"));
const logger_1 = require("../utils/logger");
/**
* Default configuration filename.
*/
const DefaultConfigFilename = 'sqlsync.yaml';
/**
* ConfigLoader implements the ConfigLoader interface from CLI types,
* providing functionality to load and validate configuration from a yaml file.
*/
class Config {
/**
* Loads the configuration from the specified path or defaults to sqlsync.yaml in the current directory.
* @param configPath Path to the configuration file. If not provided, defaults to sqlsync.yaml in the current directory.
* @returns The parsed and validated SqlSyncConfig object.
* @throws Error if the file is not found, cannot be parsed, or is invalid.
*/
load(configPath = path.join(process.cwd(), DefaultConfigFilename)) {
console.log(`[DEBUG config/index.ts] loadConfig received raw configPath: ${configPath}`);
const absoluteConfigPath = path.resolve(configPath);
console.log(`Loading config from: ${absoluteConfigPath}`);
if (!fs.existsSync(absoluteConfigPath)) {
throw new Error(`Configuration file not found at ${absoluteConfigPath}`);
}
const fileContents = fs.readFileSync(absoluteConfigPath, 'utf8');
let parsedConfig;
try {
parsedConfig = yaml.load(fileContents);
}
catch (e) {
throw new Error(`Error parsing YAML file ${absoluteConfigPath}: ${e.message}`);
}
if (!parsedConfig || typeof parsedConfig !== 'object') {
throw new Error(`Invalid configuration format in ${absoluteConfigPath}: Expected an object.`);
}
// Perform validation
this.validateConfig(parsedConfig, absoluteConfigPath);
// Log success message in green
logger_1.logger.success(`Configuration successfully loaded from ${absoluteConfigPath}`);
return parsedConfig;
}
/**
* Returns the absolute directory containing the loaded config file.
*/
static getConfigRoot(configPath = path.join(process.cwd(), DefaultConfigFilename)) {
return path.dirname(path.resolve(configPath));
}
/**
* Validates the configuration object to ensure it meets the required structure and types.
* @param config The configuration object to validate.
* @param configPath The path to the configuration file (for error messages).
* @throws Error if the configuration is invalid.
*/
validateConfig(config, configPath) {
// Validate top-level config object
if (!config.config || typeof config.config !== 'object') {
throw new Error(`Invalid configuration in ${configPath}: "config" object is required.`);
}
// Validate config.migrations
if (!config.config.migrations ||
typeof config.config.migrations !== 'object') {
throw new Error(`Invalid configuration in ${configPath}: "config.migrations" object is required.`);
}
if (!config.config.migrations.outputDir) {
throw new Error(`Invalid configuration in ${configPath}: "config.migrations.outputDir" is required.`);
}
if (typeof config.config.migrations.outputDir !== 'string') {
throw new Error(`Invalid configuration in ${configPath}: "config.migrations.outputDir" should be a string.`);
}
if (config.config.migrations.maxRollbacks !== undefined &&
typeof config.config.migrations.maxRollbacks !== 'number') {
throw new Error(`Invalid configuration in ${configPath}: "config.migrations.maxRollbacks" should be a number.`);
}
// Validate schema
if (!config.schema || typeof config.schema !== 'object') {
throw new Error(`Invalid configuration in ${configPath}: "schema" object is required.`);
}
}
/**
* Validates a folder configuration to ensure it meets the required structure and does not contain nested folders.
* @param folderConfig The folder configuration to validate.
* @param folderPath The path to the folder in the configuration (for error messages).
* @param configPath The path to the configuration file (for error messages).
* @throws Error if the folder configuration is invalid.
*/
validateFolderConfig(folderConfig, folderPath, configPath) {
if (typeof folderConfig !== 'object') {
throw new Error(`Invalid configuration in ${configPath}: "${folderPath}" should be an object.`);
}
if (folderConfig.order) {
if (!Array.isArray(folderConfig.order)) {
throw new Error(`Invalid configuration in ${configPath}: "${folderPath}.order" should be an array.`);
}
if (!folderConfig.order.every((item) => typeof item === 'string')) {
throw new Error(`Invalid configuration in ${configPath}: All elements in "${folderPath}.order" should be strings.`);
}
}
if (folderConfig.folders) {
throw new Error(`Invalid configuration in ${configPath}: Nested folders are not allowed in "${folderPath}".`);
}
}
}
exports.Config = Config;