first commit.

This commit is contained in:
kj
2026-10-05 15:08:39 -03:00
commit b7799f9fff
93 changed files with 8966 additions and 0 deletions
@@ -0,0 +1,86 @@
<?php
namespace Toolbox;
/**
* Mapa de propiedad: cada ruta instalada pertenece al core o a un componente.
*/
final class Ownership
{
/** @var array<string, string> path => owner id ('core' o 'pkg:comp') */
private array $owners = [];
/** @var array<string, list<string>> path => owners en conflicto */
private array $collisions = [];
public static function fromLock(Lock $lock): self
{
$ownership = new self();
$core = $lock->core();
foreach (($core['files'] ?? []) as $path => $hash) {
$ownership->claim('core', $path);
}
foreach ($lock->components() as $id => $entry) {
foreach (($entry['files'] ?? []) as $path => $hash) {
$ownership->claim($id, $path);
}
}
return $ownership;
}
private function claim(string $owner, string $path): void
{
if (!isset($this->owners[$path])) {
$this->owners[$path] = $owner;
return;
}
if ($this->owners[$path] !== $owner) {
$this->collisions[$path][] = $this->owners[$path];
$this->collisions[$path][] = $owner;
}
}
public function ownerOf(string $path): ?string
{
return $this->owners[$path] ?? null;
}
/** @return list<string> */
public function pathsOf(string $owner): array
{
return array_keys(array_filter($this->owners, fn ($o) => $o === $owner));
}
/**
* @return array<string, list<string>>
*/
public function collisions(): array
{
$result = [];
foreach ($this->collisions as $path => $owners) {
$result[$path] = array_values(array_unique($owners));
}
return $result;
}
public function assertNoCollisions(): void
{
$collisions = $this->collisions();
if ($collisions === []) {
return;
}
$parts = [];
foreach ($collisions as $path => $owners) {
$parts[] = $path . ' (' . implode(', ', $owners) . ')';
}
throw new ToolboxException('Colisión de propiedad: ' . implode('; ', $parts));
}
}