UNPKG

@memberjunction/react-runtime

Version:

Platform-agnostic React component runtime for MemberJunction. Provides core compilation, registry, and execution capabilities for React components in any JavaScript environment.

443 lines 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LibraryDependencyResolver = void 0; class LibraryDependencyResolver { constructor(options) { this.debug = false; this.debug = options?.debug || false; } parseDependencies(json) { const dependencies = new Map(); if (!json || json.trim() === '') { return dependencies; } try { const parsed = JSON.parse(json); if (typeof parsed === 'object' && parsed !== null) { for (const [name, version] of Object.entries(parsed)) { if (typeof version === 'string') { dependencies.set(name, version); } } } } catch (error) { console.error('Failed to parse dependencies JSON:', error); } return dependencies; } buildDependencyGraph(libraries) { const nodes = new Map(); const roots = new Set(); for (const library of libraries) { const dependencies = this.parseDependencies(library.Dependencies); nodes.set(library.Name, { library, dependencies, dependents: new Set() }); roots.add(library.Name); } for (const [name, node] of nodes) { for (const [depName, depVersion] of node.dependencies) { const depNode = nodes.get(depName); if (depNode) { roots.delete(depName); depNode.dependents.add(name); } else if (this.debug) { console.warn(`Dependency '${depName}' not found for library '${name}'`); } } } return { nodes, roots }; } detectCycles(graph) { const cycles = []; const visited = new Set(); const recursionStack = new Set(); const path = []; const detectCyclesUtil = (nodeName) => { visited.add(nodeName); recursionStack.add(nodeName); path.push(nodeName); const node = graph.nodes.get(nodeName); if (node) { for (const [depName] of node.dependencies) { if (!visited.has(depName)) { detectCyclesUtil(depName); } else if (recursionStack.has(depName)) { const cycleStartIndex = path.indexOf(depName); const cycle = [...path.slice(cycleStartIndex), depName]; cycles.push(cycle); } } } path.pop(); recursionStack.delete(nodeName); }; for (const nodeName of graph.nodes.keys()) { if (!visited.has(nodeName)) { detectCyclesUtil(nodeName); } } return cycles; } topologicalSort(graph) { const result = []; const visited = new Set(); const tempMarked = new Set(); const visit = (nodeName) => { if (tempMarked.has(nodeName)) { return false; } if (visited.has(nodeName)) { return true; } tempMarked.add(nodeName); const node = graph.nodes.get(nodeName); if (node) { for (const [depName] of node.dependencies) { if (graph.nodes.has(depName)) { if (!visit(depName)) { return false; } } } result.push(node.library); } tempMarked.delete(nodeName); visited.add(nodeName); return true; }; for (const nodeName of graph.nodes.keys()) { if (!visited.has(nodeName)) { if (!visit(nodeName)) { console.error(`Cycle detected involving library: ${nodeName}`); } } } return result; } parseVersion(version) { const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+(.+))?$/); if (!match) { return null; } return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10), patch: parseInt(match[3], 10), prerelease: match[4], build: match[5] }; } parseVersionRange(spec) { if (spec === '*' || spec === '' || spec === 'latest') { return { type: 'any', raw: spec }; } if (spec.startsWith('~')) { const version = this.parseVersion(spec.substring(1)); return { type: 'tilde', version: version || undefined, raw: spec }; } if (spec.startsWith('^')) { const version = this.parseVersion(spec.substring(1)); return { type: 'caret', version: version || undefined, raw: spec }; } const version = this.parseVersion(spec); if (version) { return { type: 'exact', version, raw: spec }; } return { type: 'any', raw: spec }; } versionSatisfiesRange(version, range) { if (range.type === 'any') { return true; } if (!range.version) { return false; } const rangeVer = range.version; switch (range.type) { case 'exact': return version.major === rangeVer.major && version.minor === rangeVer.minor && version.patch === rangeVer.patch; case 'tilde': if (version.major !== rangeVer.major) return false; if (version.minor !== rangeVer.minor) return false; return version.patch >= rangeVer.patch; case 'caret': if (rangeVer.major === 0) { if (version.major !== 0) return false; if (rangeVer.minor === 0) { return version.minor === 0 && version.patch === rangeVer.patch; } if (version.minor !== rangeVer.minor) return false; return version.patch >= rangeVer.patch; } if (version.major !== rangeVer.major) return false; if (version.minor < rangeVer.minor) return false; if (version.minor === rangeVer.minor) { return version.patch >= rangeVer.patch; } return true; default: return false; } } compareVersions(a, b) { if (a.major !== b.major) return a.major - b.major; if (a.minor !== b.minor) return a.minor - b.minor; if (a.patch !== b.patch) return a.patch - b.patch; if (!a.prerelease && b.prerelease) return 1; if (a.prerelease && !b.prerelease) return -1; if (a.prerelease && b.prerelease) { return a.prerelease.localeCompare(b.prerelease); } return 0; } resolveVersionConflicts(requirements, availableLibraries) { if (requirements.length === 0) { throw new Error('No version requirements provided'); } const libraryName = requirements[0].library; const warnings = []; const availableVersions = availableLibraries .filter(lib => lib.Name === libraryName && lib.Version) .map(lib => ({ library: lib, version: this.parseVersion(lib.Version) })) .filter(item => item.version !== null) .sort((a, b) => this.compareVersions(b.version, a.version)); if (availableVersions.length === 0) { throw new Error(`No versions available for library '${libraryName}'`); } const ranges = requirements.map(req => ({ ...req, range: this.parseVersionRange(req.versionSpec) })); for (const { library, version } of availableVersions) { let satisfiesAll = true; const satisfiedRequirements = []; for (const req of ranges) { if (this.versionSatisfiesRange(version, req.range)) { satisfiedRequirements.push(req); } else { satisfiesAll = false; warnings.push(`Version ${library.Version} does not satisfy '${req.versionSpec}' required by ${req.requestedBy}`); break; } } if (satisfiesAll) { return { library: libraryName, version: library.Version, satisfies: requirements, warnings: warnings.length > 0 ? warnings : undefined }; } } const latest = availableVersions[0]; warnings.push(`Could not find a version that satisfies all requirements. Using ${latest.library.Version}`); return { library: libraryName, version: latest.library.Version, satisfies: [], warnings }; } getLoadOrder(requestedLibs, allLibs, options) { const errors = []; const warnings = []; const validRequestedLibs = requestedLibs.filter(lib => { if (!lib || typeof lib !== 'string') { const warning = `Invalid library name: ${lib} (type: ${typeof lib})`; warnings.push(warning); if (this.debug || options?.debug) { console.warn(`⚠️ ${warning}`); } return false; } return true; }); if (this.debug || options?.debug) { console.log('🔍 Getting load order for requested libraries:'); console.log(' 📝 Requested (raw):', requestedLibs); console.log(' 📝 Requested (valid):', validRequestedLibs); console.log(' 📚 Total available libraries:', allLibs.length); } const libMap = new Map(); for (const lib of allLibs) { if (!lib?.Name) { warnings.push(`Library with missing name found in available libraries`); continue; } const key = lib.Name.toLowerCase(); if (!libMap.has(key)) { libMap.set(key, []); } libMap.get(key).push(lib); } const needed = new Set(); const toProcess = [...validRequestedLibs]; const processed = new Set(); const versionRequirements = new Map(); let depth = 0; const maxDepth = options?.maxDepth || 10; while (toProcess.length > 0 && depth < maxDepth) { const current = toProcess.shift(); if (!current || typeof current !== 'string') { const warning = `Unexpected invalid library name during processing: ${current}`; warnings.push(warning); if (this.debug || options?.debug) { console.warn(`⚠️ ${warning}`); } continue; } if (processed.has(current)) continue; processed.add(current); needed.add(current); const libVersions = libMap.get(current.toLowerCase()); if (!libVersions || libVersions.length === 0) { if (this.debug || options?.debug) { console.log(` ❌ Library '${current}' not found in available libraries`); } errors.push(`Library '${current}' not found`); continue; } const lib = libVersions[0]; const deps = this.parseDependencies(lib.Dependencies); if ((this.debug || options?.debug) && deps.size > 0) { console.log(` 📌 ${current} requires:`, Array.from(deps.entries())); } for (const [depName, depVersion] of deps) { needed.add(depName); if (!versionRequirements.has(depName)) { versionRequirements.set(depName, []); } versionRequirements.get(depName).push({ library: depName, versionSpec: depVersion, requestedBy: current }); if (!processed.has(depName)) { toProcess.push(depName); } } depth++; } if (depth >= maxDepth) { warnings.push(`Maximum dependency depth (${maxDepth}) reached`); } const resolvedLibraries = []; for (const libName of needed) { const requirements = versionRequirements.get(libName) || []; const versions = libMap.get(libName.toLowerCase()) || []; if (versions.length === 0) { errors.push(`Library '${libName}' not found`); continue; } if (requirements.length > 0) { try { const resolved = this.resolveVersionConflicts(requirements, versions); const selectedLib = versions.find(lib => lib.Version === resolved.version); if (selectedLib) { resolvedLibraries.push(selectedLib); if (resolved.warnings) { warnings.push(...resolved.warnings); } } } catch (error) { errors.push(error.message); resolvedLibraries.push(versions[0]); } } else { resolvedLibraries.push(versions[0]); } } const graph = this.buildDependencyGraph(resolvedLibraries); const cycles = this.detectCycles(graph); if (cycles.length > 0) { errors.push(`Circular dependencies detected: ${cycles.map(c => c.join(' -> ')).join(', ')}`); return { success: false, cycles, errors, warnings: warnings.length > 0 ? warnings : undefined }; } const sorted = this.topologicalSort(graph); if (this.debug || options?.debug) { console.log('✅ Load order determined:', sorted.map(lib => `${lib.Name}@${lib.Version}`)); } return { success: errors.length === 0, order: sorted, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined }; } getDirectDependencies(library) { return this.parseDependencies(library.Dependencies); } getTransitiveDependencies(libraryName, allLibs, maxDepth = 10) { const dependencies = new Set(); const toProcess = [libraryName]; const processed = new Set(); let depth = 0; const libMap = new Map(); for (const lib of allLibs) { libMap.set(lib.Name.toLowerCase(), lib); } while (toProcess.length > 0 && depth < maxDepth) { const current = toProcess.shift(); if (processed.has(current)) continue; processed.add(current); const lib = libMap.get(current.toLowerCase()); if (!lib) continue; const deps = this.parseDependencies(lib.Dependencies); for (const [depName] of deps) { dependencies.add(depName); if (!processed.has(depName)) { toProcess.push(depName); } } depth++; } return dependencies; } } exports.LibraryDependencyResolver = LibraryDependencyResolver; //# sourceMappingURL=library-dependency-resolver.js.map