@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
JavaScript
"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