|null */ private ?array $cache = null; public function __construct(private readonly string $packagesDir) { } public static function fromRoot(string $root): self { return new self(rtrim($root, '/') . '/packages'); } /** @return array */ public function packages(): array { if ($this->cache !== null) { return $this->cache; } $packages = []; foreach (glob($this->packagesDir . '/*/duckbrain.json') ?: [] as $manifestPath) { $manifest = Manifest::fromFile($manifestPath); $packages[$manifest->name] = $manifest; } ksort($packages); return $this->cache = $packages; } public function has(string $name): bool { return isset($this->packages()[$name]); } public function get(string $name): Manifest { if (!$this->has($name)) { throw new ToolboxException("Paquete desconocido en el catálogo: '{$name}'"); } return $this->packages()[$name]; } }