@deepbrainspace/nx-surrealdb
Version:
NX plugin for SurrealDB migrations with modular architecture
161 lines • 5.99 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DependencyResolver = void 0;
const tslib_1 = require("tslib");
const config_loader_1 = require("../configuration/config-loader");
const fs = tslib_1.__importStar(require("fs/promises"));
class DependencyResolver {
constructor(basePath) {
this.basePath = basePath;
this.config = null;
this.dependencyGraph = new Map();
}
async initialize(configPath) {
this.config = await config_loader_1.ConfigLoader.loadConfig(this.basePath, configPath);
if (!this.config) {
// Auto-discover modules if no config exists
await this.autoDiscoverModules();
}
this.buildDependencyGraph();
}
async autoDiscoverModules() {
try {
const entries = await fs.readdir(this.basePath, { withFileTypes: true });
const modules = entries
.filter(entry => entry.isDirectory())
.map(entry => entry.name)
.filter(name => /^\d{1,4}_/.test(name))
.sort();
if (modules.length > 0) {
this.config = config_loader_1.ConfigLoader.createDefaultConfig(modules);
// console.log(`Auto-discovered ${modules.length} modules:`, modules.join(', '));
}
else {
this.config = { modules: {} };
}
}
catch {
// console.warn(`Failed to auto-discover modules`);
this.config = { modules: {} };
}
}
buildDependencyGraph() {
if (!this.config)
return;
this.dependencyGraph.clear();
// First pass: create all nodes
for (const [moduleId, moduleConfig] of Object.entries(this.config.modules)) {
this.dependencyGraph.set(moduleId, {
moduleId,
config: moduleConfig,
dependencies: [...moduleConfig.dependencies],
dependents: [],
});
}
// Second pass: build dependents lists
for (const [moduleId, moduleConfig] of Object.entries(this.config.modules)) {
for (const dependency of moduleConfig.dependencies) {
const depNode = this.dependencyGraph.get(dependency);
if (depNode) {
depNode.dependents.push(moduleId);
}
}
}
}
getExecutionOrder(targetModules) {
if (!this.config)
return [];
const requestedModules = targetModules || Object.keys(this.config.modules);
const result = [];
const visited = new Set();
const visiting = new Set();
const visit = (moduleId) => {
if (visited.has(moduleId))
return;
if (visiting.has(moduleId)) {
throw new Error(`Circular dependency detected involving ${moduleId}`);
}
const node = this.dependencyGraph.get(moduleId);
if (!node)
return;
visiting.add(moduleId);
// Visit all dependencies first (dependencies are always included)
for (const dependency of node.dependencies) {
visit(dependency);
}
visiting.delete(moduleId);
visited.add(moduleId);
result.push(moduleId);
};
// Visit all requested modules
for (const moduleId of requestedModules) {
visit(moduleId);
}
return result;
}
getRollbackOrder(targetModules) {
// Rollback is the reverse of execution order
return this.getExecutionOrder(targetModules).reverse();
}
validateRollback(moduleId, targetModules) {
const node = this.dependencyGraph.get(moduleId);
if (!node) {
return {
canRollback: false,
blockedBy: [],
reason: `Module ${moduleId} not found in dependency graph`,
};
}
// Check if any dependents would be affected
// Only consider dependents that are NOT also being rolled back
const affectedDependents = node.dependents.filter(dependent => {
// If targetModules is specified, exclude dependents that are also in the rollback list
return !targetModules || !targetModules.includes(dependent);
});
if (affectedDependents.length > 0) {
return {
canRollback: false,
blockedBy: affectedDependents,
reason: `Cannot rollback ${moduleId} because it has active dependents: ${affectedDependents.join(', ')}`,
};
}
return {
canRollback: true,
blockedBy: [],
};
}
validateModuleExists(moduleId) {
return this.dependencyGraph.has(moduleId);
}
getModuleDependencies(moduleId) {
const node = this.dependencyGraph.get(moduleId);
return node ? [...node.dependencies] : [];
}
getModuleDependents(moduleId) {
const node = this.dependencyGraph.get(moduleId);
return node ? [...node.dependents] : [];
}
getAllModules() {
return Array.from(this.dependencyGraph.keys()).sort();
}
getResolutionResult(targetModules) {
return {
executionOrder: this.getExecutionOrder(targetModules),
dependencyGraph: new Map(this.dependencyGraph),
};
}
getConfig() {
return this.config;
}
hasConfig() {
return this.config !== null && Object.keys(this.config.modules).length > 0;
}
// Static utility method for testing
static async createResolver(basePath, configPath) {
const resolver = new DependencyResolver(basePath);
await resolver.initialize(configPath);
return resolver;
}
}
exports.DependencyResolver = DependencyResolver;
//# sourceMappingURL=dependency-resolver.js.map