export interface AggregatedDependency { id: string title: string requiredBy: string[] requiredByKeys?: string[] required?: boolean requiredForKeys?: string[] } export interface DependencyAggregationInput { ownerKey: string dependencies: T[] } function dependencyIdentity(id: string) { const separator = id.lastIndexOf(':') return { identity: separator === -1 ? id : id.slice(0, separator), versionId: separator === -1 ? '' : id.slice(separator + 1), } } export function getActiveDependencyConflictIdentities( dependencies: Pick[], visibleOwnerKeys: Set, ) { const versions = new Map>() for (const dependency of dependencies) { if ( dependency.requiredByKeys?.length && !dependency.requiredByKeys.some((key) => visibleOwnerKeys.has(key)) ) { continue } const { identity, versionId } = dependencyIdentity(dependency.id) const identityVersions = versions.get(identity) ?? new Set() identityVersions.add(versionId) versions.set(identity, identityVersions) } return new Set( [...versions.entries()] .filter(([, versionIds]) => versionIds.size > 1) .map(([identity]) => identity), ) } export function aggregateContentSelectionDependencies( selections: DependencyAggregationInput[], conflictMessage: (dependency: T) => string, ) { const dependencies = new Map() const versionOwners = new Map>>() const dependencyByIdentity = new Map() const conflicts = new Map() const conflictIdentities = new Map() for (const selection of selections) { for (const dependency of selection.dependencies) { const { identity, versionId } = dependencyIdentity(dependency.id) const ownersByVersion = versionOwners.get(identity) ?? new Map>() const owners = ownersByVersion.get(versionId) ?? new Set() owners.add(selection.ownerKey) ownersByVersion.set(versionId, owners) versionOwners.set(identity, ownersByVersion) dependencyByIdentity.set(identity, dependency) const existing = dependencies.get(dependency.id) if (existing) { existing.requiredBy = [...new Set([...existing.requiredBy, ...dependency.requiredBy])] existing.requiredByKeys = [ ...new Set([...(existing.requiredByKeys ?? []), ...(dependency.requiredByKeys ?? [])]), ] existing.required = existing.required || dependency.required existing.requiredForKeys = [ ...new Set([ ...(existing.requiredForKeys ?? []), ...(dependency.required ? [selection.ownerKey] : []), ...(dependency.requiredForKeys ?? []), ]), ] } else { dependencies.set(dependency.id, { ...dependency, requiredForKeys: dependency.required ? [...new Set([selection.ownerKey, ...(dependency.requiredForKeys ?? [])])] : dependency.requiredForKeys, }) } } } for (const [identity, ownersByVersion] of versionOwners) { if (ownersByVersion.size < 2) continue const dependency = dependencyByIdentity.get(identity) if (!dependency) continue for (const owners of ownersByVersion.values()) { for (const ownerKey of owners) { conflicts.set(ownerKey, conflictMessage(dependency)) conflictIdentities.set(ownerKey, [ ...new Set([...(conflictIdentities.get(ownerKey) ?? []), identity]), ]) } } } return { dependencies: [...dependencies.values()], conflicts, conflictIdentities } }