> */ private const RUNNERS = [ 'sherlock' => SherlockRunner::class, 'crtsh' => CrtShRunner::class, 'whois' => WhoisRunner::class, 'ip-api' => IpApiRunner::class, 'theharvester' => TheHarvesterRunner::class, 'holehe' => HoleheRunner::class, 'gravatar' => GravatarRunner::class, 'dns' => DnsRunner::class, 'opencorporates' => OpenCorporatesRunner::class, ]; public function has(string $sourceKey): bool { if (! isset(self::RUNNERS[$sourceKey])) { return false; } // OpenCorporates additionally needs an API token configured before // it's actually runnable — otherwise it's hidden rather than shown // as a broken "Run" button that always fails. if ($sourceKey === 'opencorporates') { return filled(config('services.opencorporates.token')); } return true; } public function resolve(string $sourceKey): Runner { if (! $this->has($sourceKey)) { throw new RunnerException("No runner registered for source [{$sourceKey}]."); } return app(self::RUNNERS[$sourceKey]); } }