> */ private array $modules = []; /** @var array */ private array $configuration = []; public function __construct( private readonly string $tenantIdentifier, private readonly string $identityIdentifier, ) { } public function type(): ModuleContextType { return ModuleContextType::Browser; } public function registerModule( ModuleInstanceInterface $module, string $namespace, ?string $boot = null, ): void { if (trim($namespace) === '' || ($boot !== null && trim($boot) === '')) { throw new InvalidArgumentException('Browser namespace must not be empty and boot path must be null or non-empty'); } $handle = $module->handle(); if (isset($this->modules[$handle])) { throw new LogicException("Browser module '{$handle}' is already registered"); } $entry = [ 'handle' => $handle, 'namespace' => $namespace, 'version' => $module->version(), 'label' => $module->label(), 'author' => $module->author(), 'description' => $module->description(), ]; if ($boot !== null) { $entry['boot'] = $boot; } $this->modules[$handle] = $entry; } public function set(string $key, mixed $value): void { if (trim($key) === '') { throw new InvalidArgumentException('Browser configuration key must not be empty'); } if (in_array($key, self::RESERVED_KEYS, true) || array_key_exists($key, $this->configuration)) { throw new LogicException("Browser configuration '{$key}' is already reserved or registered"); } $this->configuration[$key] = $value; } public function tenantIdentifier(): string { return $this->tenantIdentifier; } public function identityIdentifier(): string { return $this->identityIdentifier; } public function modules(): array { return $this->modules; } public function configuration(): array { return $this->configuration; } }