@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
668 lines (667 loc) • 28.2 kB
JavaScript
"use strict";
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.WorkspaceConflictManager = void 0;
exports.createWorkspaceConflictManager = createWorkspaceConflictManager;
exports.detectWorkspaceConflicts = detectWorkspaceConflicts;
exports.autoResolveConflicts = autoResolveConflicts;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const yaml = __importStar(require("yaml"));
const error_handler_1 = require("./error-handler");
// Workspace conflict detector and resolver
class WorkspaceConflictManager {
constructor(rootPath = process.cwd()) {
this.conflicts = new Map();
this.rootPath = rootPath;
}
// Detect all conflicts in workspace definition
async detectConflicts(definition, options = {}) {
this.conflicts.clear();
const defaultOptions = {
includeWarnings: true,
checkDependencies: true,
checkPorts: true,
checkPaths: true,
checkTypes: true,
enableResolution: true,
...options
};
// Check for naming conflicts
await this.detectNamingConflicts(definition);
// Check for dependency conflicts
if (defaultOptions.checkDependencies) {
await this.detectDependencyConflicts(definition);
}
// Check for port collisions
if (defaultOptions.checkPorts) {
await this.detectPortConflicts(definition);
}
// Check for path collisions
if (defaultOptions.checkPaths) {
await this.detectPathConflicts(definition);
}
// Check for type mismatches
if (defaultOptions.checkTypes) {
await this.detectTypeConflicts(definition);
}
// Check for configuration conflicts
await this.detectConfigurationConflicts(definition);
// Generate resolutions if enabled
if (defaultOptions.enableResolution) {
await this.generateResolutions(definition);
}
return Array.from(this.conflicts.values());
}
// Resolve conflicts automatically or with guidance
async resolveConflicts(definition, conflicts, autoResolve = false) {
const result = {
resolved: [],
unresolved: [],
changes: [],
warnings: []
};
for (const conflict of conflicts) {
try {
const resolution = await this.resolveConflict(definition, conflict, autoResolve);
if (resolution) {
result.resolved.push(conflict);
result.changes.push(...resolution.changes);
result.warnings.push(...resolution.warnings);
}
else {
result.unresolved.push(conflict);
}
}
catch (error) {
result.unresolved.push(conflict);
result.warnings.push(`Failed to resolve conflict ${conflict.id}: ${error.message}`);
}
}
return result;
}
// Validate resolution preview
async previewResolution(definition, conflict, resolutionId) {
const resolution = conflict.suggestions.find(r => r.id === resolutionId);
if (!resolution) {
throw new error_handler_1.ValidationError(`Resolution ${resolutionId} not found for conflict ${conflict.id}`);
}
// Create a deep copy for preview
const preview = JSON.parse(JSON.stringify(definition));
const changes = [];
const warnings = [];
try {
await this.applyResolution(preview, conflict, resolution, changes, true);
return {
success: true,
changes,
warnings,
preview
};
}
catch (error) {
return {
success: false,
changes: [],
warnings: [error.message],
preview: definition
};
}
}
// Detect naming conflicts
async detectNamingConflicts(definition) {
const workspaceNames = Object.keys(definition.workspaces);
const duplicates = workspaceNames.filter((name, index) => workspaceNames.indexOf(name) !== index);
for (const duplicate of duplicates) {
this.conflicts.set(`naming-${duplicate}`, {
id: `naming-${duplicate}`,
type: 'naming',
severity: 'error',
description: `Duplicate workspace name: ${duplicate}`,
details: `Multiple workspaces are using the same name '${duplicate}'. Each workspace must have a unique name.`,
affectedWorkspaces: [duplicate],
suggestions: [
{
id: 'rename-workspace',
description: `Rename one of the duplicate workspaces`,
action: 'rename-workspace',
automatic: false,
riskLevel: 'low',
preview: `Rename '${duplicate}' to '${duplicate}-2'`
}
]
});
}
// Check for reserved names
const reservedNames = ['build', 'test', 'dev', 'prod', 'staging', 'config'];
for (const [name, workspace] of Object.entries(definition.workspaces)) {
if (reservedNames.includes(name.toLowerCase())) {
this.conflicts.set(`reserved-${name}`, {
id: `reserved-${name}`,
type: 'naming',
severity: 'warning',
description: `Workspace uses reserved name: ${name}`,
details: `The workspace name '${name}' conflicts with reserved system names.`,
affectedWorkspaces: [name],
suggestions: [
{
id: 'rename-workspace',
description: `Rename workspace to avoid reserved name`,
action: 'rename-workspace',
automatic: false,
riskLevel: 'low',
preview: `Rename '${name}' to '${name}-app'`
}
]
});
}
}
}
// Detect dependency conflicts
async detectDependencyConflicts(definition) {
// Check for circular dependencies
const cycles = this.findDependencyCycles(definition);
for (const cycle of cycles) {
this.conflicts.set(`cycle-${cycle.join('-')}`, {
id: `cycle-${cycle.join('-')}`,
type: 'dependency-cycle',
severity: 'error',
description: `Circular dependency detected: ${cycle.join(' → ')}`,
details: `A circular dependency exists between workspaces: ${cycle.join(' → ')} → ${cycle[0]}`,
affectedWorkspaces: cycle,
suggestions: [
{
id: 'remove-dependency',
description: `Remove dependency from ${cycle[cycle.length - 1]} to ${cycle[0]}`,
action: 'remove-dependency',
automatic: false,
riskLevel: 'medium',
preview: `Remove '${cycle[0]}' from ${cycle[cycle.length - 1]} dependencies`
},
{
id: 'extract-common',
description: `Extract common functionality to a shared library`,
action: 'split-workspace',
automatic: false,
riskLevel: 'high'
}
]
});
}
// Check for missing dependencies
for (const [workspaceName, deps] of Object.entries(definition.dependencies || {})) {
if (!definition.workspaces[workspaceName]) {
this.conflicts.set(`missing-workspace-${workspaceName}`, {
id: `missing-workspace-${workspaceName}`,
type: 'dependency-missing',
severity: 'error',
description: `Dependencies defined for non-existent workspace: ${workspaceName}`,
details: `Dependencies are defined for workspace '${workspaceName}' but this workspace doesn't exist.`,
affectedWorkspaces: [workspaceName],
suggestions: [
{
id: 'remove-dependency',
description: `Remove dependencies for non-existent workspace`,
action: 'remove-dependency',
automatic: true,
riskLevel: 'low'
}
]
});
}
for (const dep of deps) {
if (!definition.workspaces[dep.name]) {
this.conflicts.set(`missing-dep-${workspaceName}-${dep.name}`, {
id: `missing-dep-${workspaceName}-${dep.name}`,
type: 'dependency-missing',
severity: 'error',
description: `Missing dependency: ${dep.name}`,
details: `Workspace '${workspaceName}' depends on '${dep.name}' which doesn't exist.`,
affectedWorkspaces: [workspaceName, dep.name],
suggestions: [
{
id: 'remove-dependency',
description: `Remove dependency on non-existent workspace`,
action: 'remove-dependency',
automatic: true,
riskLevel: 'low'
}
]
});
}
}
}
}
// Detect port conflicts
async detectPortConflicts(definition) {
const portMap = new Map();
for (const [name, workspace] of Object.entries(definition.workspaces)) {
if (workspace.dev?.port) {
const port = workspace.dev.port;
if (!portMap.has(port)) {
portMap.set(port, []);
}
portMap.get(port).push(name);
}
}
for (const [port, workspaces] of portMap.entries()) {
if (workspaces.length > 1) {
this.conflicts.set(`port-${port}`, {
id: `port-${port}`,
type: 'port-collision',
severity: 'error',
description: `Port collision on ${port}`,
details: `Multiple workspaces are configured to use port ${port}: ${workspaces.join(', ')}`,
affectedWorkspaces: workspaces,
suggestions: [
{
id: 'auto-assign-ports',
description: `Automatically assign different ports`,
action: 'change-port',
automatic: true,
riskLevel: 'low',
preview: `Assign sequential ports starting from ${port + 1}`
}
]
});
}
}
}
// Detect path conflicts
async detectPathConflicts(definition) {
const pathMap = new Map();
for (const [name, workspace] of Object.entries(definition.workspaces)) {
if (workspace.path) {
const normalizedPath = path.normalize(workspace.path);
if (!pathMap.has(normalizedPath)) {
pathMap.set(normalizedPath, []);
}
pathMap.get(normalizedPath).push(name);
}
}
for (const [workspacePath, workspaces] of pathMap.entries()) {
if (workspaces.length > 1) {
this.conflicts.set(`path-${workspacePath}`, {
id: `path-${workspacePath}`,
type: 'path-collision',
severity: 'error',
description: `Path collision: ${workspacePath}`,
details: `Multiple workspaces are using the same path '${workspacePath}': ${workspaces.join(', ')}`,
affectedWorkspaces: workspaces,
suggestions: [
{
id: 'change-paths',
description: `Assign unique paths to each workspace`,
action: 'change-path',
automatic: false,
riskLevel: 'medium',
preview: `Move workspaces to subdirectories`
}
]
});
}
}
}
// Detect type conflicts
async detectTypeConflicts(definition) {
for (const [name, workspace] of Object.entries(definition.workspaces)) {
if (!definition.types[workspace.type]) {
this.conflicts.set(`type-${name}`, {
id: `type-${name}`,
type: 'type-mismatch',
severity: 'error',
description: `Undefined workspace type: ${workspace.type}`,
details: `Workspace '${name}' references type '${workspace.type}' which is not defined.`,
affectedWorkspaces: [name],
suggestions: [
{
id: 'define-type',
description: `Define the missing workspace type`,
action: 'auto-resolve',
automatic: true,
riskLevel: 'low',
preview: `Create type definition for '${workspace.type}'`
},
{
id: 'change-type',
description: `Change to an existing type`,
action: 'update-dependency',
automatic: false,
riskLevel: 'low'
}
]
});
}
}
}
// Detect configuration conflicts
async detectConfigurationConflicts(definition) {
// Check for conflicting build commands
const buildCommands = new Set();
for (const [name, workspace] of Object.entries(definition.workspaces)) {
const type = definition.types[workspace.type];
if (type?.build?.command && buildCommands.has(type.build.command)) {
this.conflicts.set(`build-command-${name}`, {
id: `build-command-${name}`,
type: 'build-target',
severity: 'warning',
description: `Conflicting build command: ${type.build.command}`,
details: `Multiple workspaces are using the same build command '${type.build.command}'`,
affectedWorkspaces: [name],
suggestions: [
{
id: 'unique-build-command',
description: `Use unique build commands`,
action: 'auto-resolve',
automatic: true,
riskLevel: 'low'
}
]
});
}
if (type?.build?.command) {
buildCommands.add(type.build.command);
}
}
}
// Generate resolution suggestions
async generateResolutions(definition) {
// Enhanced resolution generation based on conflict analysis
for (const conflict of this.conflicts.values()) {
if (conflict.suggestions.length === 0) {
this.generateDefaultResolutions(conflict);
}
}
}
// Generate default resolutions for conflicts without suggestions
generateDefaultResolutions(conflict) {
switch (conflict.type) {
case 'naming':
conflict.suggestions.push({
id: 'auto-rename',
description: 'Automatically generate unique names',
action: 'rename-workspace',
automatic: true,
riskLevel: 'low'
});
break;
case 'port-collision':
conflict.suggestions.push({
id: 'sequential-ports',
description: 'Assign sequential ports',
action: 'change-port',
automatic: true,
riskLevel: 'low'
});
break;
case 'dependency-missing':
conflict.suggestions.push({
id: 'remove-missing',
description: 'Remove references to missing dependencies',
action: 'remove-dependency',
automatic: true,
riskLevel: 'low'
});
break;
}
}
// Find dependency cycles using DFS
findDependencyCycles(definition) {
const cycles = [];
const visited = new Set();
const recursionStack = new Set();
const path = [];
const dfs = (workspace) => {
if (recursionStack.has(workspace)) {
// Found a cycle
const cycleStart = path.indexOf(workspace);
if (cycleStart !== -1) {
cycles.push(path.slice(cycleStart));
}
return;
}
if (visited.has(workspace)) {
return;
}
visited.add(workspace);
recursionStack.add(workspace);
path.push(workspace);
const dependencies = definition.dependencies?.[workspace] || [];
for (const dep of dependencies) {
if (definition.workspaces[dep.name]) {
dfs(dep.name);
}
}
recursionStack.delete(workspace);
path.pop();
};
for (const workspace of Object.keys(definition.workspaces)) {
if (!visited.has(workspace)) {
dfs(workspace);
}
}
return cycles;
}
// Resolve individual conflict
async resolveConflict(definition, conflict, autoResolve) {
const changes = [];
const warnings = [];
// Find automatic resolution if auto-resolve is enabled
let resolution = conflict.suggestions.find(r => r.automatic && autoResolve);
// Otherwise, use the first low-risk resolution
if (!resolution) {
resolution = conflict.suggestions.find(r => r.riskLevel === 'low');
}
if (!resolution) {
return null;
}
await this.applyResolution(definition, conflict, resolution, changes);
return { changes, warnings };
}
// Apply a specific resolution
async applyResolution(definition, conflict, resolution, changes, preview = false) {
switch (resolution.action) {
case 'rename-workspace':
await this.applyWorkspaceRename(definition, conflict, changes, preview);
break;
case 'change-port':
await this.applyPortChange(definition, conflict, changes, preview);
break;
case 'change-path':
await this.applyPathChange(definition, conflict, changes, preview);
break;
case 'remove-dependency':
await this.applyDependencyRemoval(definition, conflict, changes, preview);
break;
case 'auto-resolve':
await this.applyAutoResolve(definition, conflict, changes, preview);
break;
}
}
// Apply workspace rename resolution
async applyWorkspaceRename(definition, conflict, changes, preview = false) {
const affected = conflict.affectedWorkspaces;
for (let i = 1; i < affected.length; i++) {
const oldName = affected[i];
const newName = `${oldName}-${i + 1}`;
if (!preview) {
// Rename workspace
definition.workspaces[newName] = definition.workspaces[oldName];
delete definition.workspaces[oldName];
// Update dependencies
for (const [workspace, deps] of Object.entries(definition.dependencies || {})) {
for (const dep of deps) {
if (dep.name === oldName) {
dep.name = newName;
}
}
}
}
changes.push({
type: 'workspace',
target: oldName,
property: 'name',
oldValue: oldName,
newValue: newName,
reason: `Resolved naming conflict`
});
}
}
// Apply port change resolution
async applyPortChange(definition, conflict, changes, preview = false) {
const basePort = parseInt(conflict.id.split('-')[1]);
let currentPort = basePort + 1;
for (let i = 1; i < conflict.affectedWorkspaces.length; i++) {
const workspaceName = conflict.affectedWorkspaces[i];
const workspace = definition.workspaces[workspaceName];
if (workspace.dev) {
const oldPort = workspace.dev.port;
if (!preview) {
workspace.dev.port = currentPort;
}
changes.push({
type: 'workspace',
target: workspaceName,
property: 'dev.port',
oldValue: oldPort,
newValue: currentPort,
reason: `Resolved port collision`
});
currentPort++;
}
}
}
// Apply path change resolution
async applyPathChange(definition, conflict, changes, preview = false) {
const basePath = conflict.id.replace('path-', '');
for (let i = 1; i < conflict.affectedWorkspaces.length; i++) {
const workspaceName = conflict.affectedWorkspaces[i];
const workspace = definition.workspaces[workspaceName];
const oldPath = workspace.path;
const newPath = path.join(basePath, workspaceName);
if (!preview) {
workspace.path = newPath;
}
changes.push({
type: 'workspace',
target: workspaceName,
property: 'path',
oldValue: oldPath,
newValue: newPath,
reason: `Resolved path collision`
});
}
}
// Apply dependency removal resolution
async applyDependencyRemoval(definition, conflict, changes, preview = false) {
if (conflict.type === 'dependency-missing') {
const [, , workspace, depName] = conflict.id.split('-');
if (definition.dependencies?.[workspace]) {
const deps = definition.dependencies[workspace];
const depIndex = deps.findIndex(d => d.name === depName);
if (depIndex !== -1) {
if (!preview) {
deps.splice(depIndex, 1);
}
changes.push({
type: 'dependency',
target: workspace,
property: 'dependencies',
oldValue: depName,
newValue: null,
reason: `Removed missing dependency`
});
}
}
}
}
// Apply auto-resolve resolution
async applyAutoResolve(definition, conflict, changes, preview = false) {
if (conflict.type === 'type-mismatch') {
const workspaceName = conflict.affectedWorkspaces[0];
const workspace = definition.workspaces[workspaceName];
const typeName = workspace.type;
if (!definition.types[typeName] && !preview) {
definition.types[typeName] = {
name: typeName,
description: `Auto-generated type for ${typeName}`,
framework: 'react'
};
}
changes.push({
type: 'configuration',
target: 'types',
property: typeName,
oldValue: undefined,
newValue: definition.types[typeName],
reason: `Created missing type definition`
});
}
}
// Helper methods
async loadWorkspaceDefinition(filePath) {
if (!(await fs.pathExists(filePath))) {
throw new error_handler_1.ValidationError(`Workspace file not found: ${filePath}`);
}
const content = await fs.readFile(filePath, 'utf8');
return yaml.parse(content);
}
async saveWorkspaceDefinition(filePath, definition) {
const content = yaml.stringify(definition);
await fs.writeFile(filePath, content, 'utf8');
}
}
exports.WorkspaceConflictManager = WorkspaceConflictManager;
// Utility functions
async function createWorkspaceConflictManager(rootPath) {
return new WorkspaceConflictManager(rootPath);
}
// Quick conflict detection
async function detectWorkspaceConflicts(workspaceFile, options) {
const manager = new WorkspaceConflictManager();
const definition = await manager['loadWorkspaceDefinition'](workspaceFile);
return await manager.detectConflicts(definition, options);
}
// Auto-resolve conflicts
async function autoResolveConflicts(workspaceFile, conflicts) {
const manager = new WorkspaceConflictManager();
const definition = await manager['loadWorkspaceDefinition'](workspaceFile);
const conflictsToResolve = conflicts || await manager.detectConflicts(definition);
const result = await manager.resolveConflicts(definition, conflictsToResolve, true);
if (result.resolved.length > 0) {
await manager['saveWorkspaceDefinition'](workspaceFile, definition);
}
return result;
}