From ea8b7a933193955bdad44b25c51935941e4eaa40 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Fri, 30 Oct 2026 16:44:54 -0300 Subject: [PATCH 1/3] docs: complete Foxy PHPDoc descriptions for the hosted API reference. --- CHANGELOG.md | 1 + src/Asset/AbstractAssetManager.php | 27 ++++++++++ src/Asset/AbstractManifestAssetManager.php | 47 ++++++++++++++++ src/Asset/AssetManagerFinder.php | 8 +++ src/Asset/AssetManagerInterface.php | 3 ++ src/Asset/AssetPackage.php | 20 +++++++ src/Asset/AssetPackageInterface.php | 3 ++ src/Asset/BunManager.php | 24 +++++++++ src/Asset/DenoManager.php | 36 +++++++++++++ src/Asset/NativeManager.php | 14 +++++ src/Asset/NpmManager.php | 21 ++++++++ src/Asset/PnpmManager.php | 21 ++++++++ src/Asset/YarnManager.php | 27 ++++++++++ src/Audit/AuditFinding.php | 23 ++++++-- src/Audit/AuditFormat.php | 3 ++ src/Audit/AuditFormatter.php | 5 ++ src/Audit/AuditParserInterface.php | 5 ++ src/Audit/AuditProcessResult.php | 10 ++++ src/Audit/AuditReport.php | 21 +++++++- src/Audit/AuditRequest.php | 9 ++++ src/Audit/AuditRunner.php | 9 ++++ src/Audit/AuditRunnerInterface.php | 6 +++ src/Audit/CveEnricher.php | 3 ++ src/Audit/CveResolution.php | 8 ++- src/Audit/CveStatus.php | 3 ++ src/Audit/GitHubAdvisoryCveResolver.php | 3 ++ src/Audit/Parser/AbstractAuditParser.php | 54 +++++++++++++++++++ .../Parser/AbstractBulkAdvisoryParser.php | 3 ++ src/Audit/Parser/BunAuditParser.php | 3 ++ src/Audit/Parser/DenoAuditParser.php | 9 ++++ src/Audit/Parser/NativeAuditParser.php | 3 ++ src/Audit/Parser/NpmAuditParser.php | 9 ++++ src/Audit/Parser/PnpmAuditParser.php | 9 ++++ src/Audit/Parser/YarnAuditParser.php | 9 ++++ src/Audit/Severity.php | 9 ++++ src/Command/AuditCommand.php | 16 ++++++ src/Command/FoxyCommandProvider.php | 9 +++- src/Config/Config.php | 9 +++- src/Config/ConfigBuilder.php | 3 ++ src/Converter/SemverConverter.php | 6 +++ src/Converter/SemverUtil.php | 6 +++ src/Converter/VersionConverterInterface.php | 3 ++ src/Event/AbstractSolveEvent.php | 5 ++ src/Event/GetAssetsEvent.php | 5 ++ src/Event/PostSolveEvent.php | 5 ++ src/Event/PreSolveEvent.php | 5 ++ src/Exception/ExceptionInterface.php | 3 ++ src/Exception/RuntimeException.php | 3 ++ src/Fallback/AssetFallback.php | 17 ++++++ src/Fallback/ComposerFallback.php | 10 ++++ src/Fallback/FallbackInterface.php | 3 ++ src/Foxy.php | 24 +++++++++ src/FoxyEvents.php | 3 ++ src/Json/JsonFile.php | 9 ++++ src/Json/JsonFormatter.php | 3 ++ src/Native/DependencyResolver.php | 2 + src/Native/InstalledState.php | 2 + src/Native/LocalPackage.php | 2 + src/Native/LockFile.php | 2 + src/Native/NativeInstaller.php | 2 + src/Native/NpmRegistry.php | 2 + src/Native/PackageMetadata.php | 2 + src/Native/PackageVersion.php | 2 + src/Native/Requirement.php | 2 + src/Native/ResolvedPackage.php | 2 + src/Native/TarballExtractor.php | 2 + src/Solver/Solver.php | 11 ++++ src/Solver/SolverInterface.php | 3 ++ src/Util/AssetUtil.php | 3 ++ src/Util/ComposerUtil.php | 3 ++ src/Util/PackageUtil.php | 3 ++ 71 files changed, 650 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1bfd18..2b02100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.4.1 Under development - docs: add AI guidance and the Foxy integration skill. +- docs: complete Foxy PHPDoc descriptions for the hosted API reference. ## 0.4.0 October 11, 2026 diff --git a/src/Asset/AbstractAssetManager.php b/src/Asset/AbstractAssetManager.php index 1aa3998..d628ada 100644 --- a/src/Asset/AbstractAssetManager.php +++ b/src/Asset/AbstractAssetManager.php @@ -20,10 +20,22 @@ use function sprintf; use function trim; +/** + * Runs an external frontend package manager and validates its executable version. + */ abstract class AbstractAssetManager extends AbstractManifestAssetManager { private string|null $version = ''; + /** + * Creates a manager that executes frontend commands for the configured asset manifest. + * + * @param IOInterface $io Composer output used for progress messages. + * @param Config $config Foxy configuration. + * @param ProcessExecutor $executor Process executor used to invoke the manager. + * @param Filesystem $fs Filesystem used by manifest handling. + * @param VersionConverterInterface|null $versionConverter Converter for manager version output. + */ public function __construct( IOInterface $io, Config $config, @@ -41,6 +53,9 @@ public function __construct( */ abstract protected function getAuditCommand(bool $noDev): string; + /** + * Runs the manager audit command against the current lock file. + */ public function audit(bool $noDev): AuditProcessResult { if (!$this->hasLockFile()) { @@ -72,11 +87,17 @@ function () use ($noDev): AuditProcessResult { ); } + /** + * Returns whether the manager executable reports a detectable version. + */ public function isAvailable(): bool { return null !== $this->getVersion(); } + /** + * Installs or updates frontend dependencies according to the manager state and configuration. + */ public function run(): int { if (!$this->config->isEnabled('run-asset-manager')) { @@ -100,6 +121,9 @@ public function run(): int ); } + /** + * Validates the installed manager version against Foxy's supported and configured constraints. + */ public function validate(): void { $version = $this->getVersion(); @@ -209,6 +233,9 @@ protected function getUpdateCommand(): string return $this->buildCommand('update', 'update'); } + /** + * Returns the normalized manager version, or `null` when it cannot be detected. + */ protected function getVersion(): string|null { if ($this->version === '' && $this->versionConverter !== null) { diff --git a/src/Asset/AbstractManifestAssetManager.php b/src/Asset/AbstractManifestAssetManager.php index 462e466..c540959 100644 --- a/src/Asset/AbstractManifestAssetManager.php +++ b/src/Asset/AbstractManifestAssetManager.php @@ -29,8 +29,18 @@ abstract class AbstractManifestAssetManager implements AssetManagerInterface { final public const string NODE_MODULES_PATH = './node_modules'; + /** + * Whether an existing frontend installation may be updated. + */ protected bool $updatable = true; + /** + * Creates a manager with the services shared by asset manifest operations. + * + * @param IOInterface $io Composer output used for progress messages. + * @param Config $config Foxy configuration. + * @param Filesystem $fs Filesystem used to resolve and update manifest paths. + */ public function __construct( protected IOInterface $io, protected Config $config, @@ -38,6 +48,8 @@ public function __construct( ) {} /** + * Merges Composer asset dependencies into the frontend package manifest. + * * @throws Exception|ParsingException */ public function addDependencies(RootPackageInterface $rootPackage, array $dependencies): AssetPackageInterface @@ -64,31 +76,49 @@ public function addDependencies(RootPackageInterface $rootPackage, array $depend return $assetPackage->write(); } + /** + * Returns the frontend package manifest path. + */ public function getPackageJsonPath(): string { return $this->getRootPackagePath($this->getPackageName()); } + /** + * Returns the frontend package manifest filename. + */ public function getPackageName(): string { return 'package.json'; } + /** + * Returns whether the manager lock file exists. + */ public function hasLockFile(): bool { return file_exists($this->getLockFilePath()); } + /** + * Returns whether both the dependency directory and package manifest exist. + */ public function isInstalled(): bool { return is_dir($this->getNodeModulesPath()) && file_exists($this->getPackageJsonPath()); } + /** + * Returns whether an existing frontend installation may be updated. + */ public function isUpdatable(): bool { return $this->updatable && $this->isInstalled(); } + /** + * Enables or disables updates of an existing frontend installation. + */ public function setUpdatable(bool $updatable): static { $this->updatable = $updatable; @@ -97,6 +127,8 @@ public function setUpdatable(bool $updatable): static } /** + * Handles dependencies already present in the frontend installation. + * * @param list $names the asset package name of composer dependencies. */ protected function actionWhenComposerDependenciesAreAlreadyInstalled(array $names): void @@ -117,6 +149,9 @@ protected function actionWhenComposerDependenciesAreMerged( // do nothing by default } + /** + * Returns the manager lock-file path under the root package directory. + */ protected function getLockFilePath(): string { return $this->getRootPackagePath($this->getLockPackageName()); @@ -132,6 +167,9 @@ protected function getManagerConfig(): Config return $this->config->forManager($this->getName()); } + /** + * Returns the configured manager working directory, or `null` to use the process default. + */ protected function getManagerWorkingDirectory(): string|null { if (null === $this->getConfiguredRootPackageDir()) { @@ -149,11 +187,17 @@ protected function getManagerWorkingDirectory(): string|null return $rootPackageDir; } + /** + * Returns the `node_modules` path under the root package directory. + */ protected function getNodeModulesPath(): string { return $this->getRootPackagePath(ltrim(self::NODE_MODULES_PATH, './')); } + /** + * Returns the resolved root package directory. + */ protected function getRootPackageDir(): string { $rootPackageDir = $this->getConfiguredRootPackageDir(); @@ -177,6 +221,9 @@ protected function getRootPackageDir(): string return rtrim($this->getCurrentDirectory(), '/\\') . DIRECTORY_SEPARATOR . $rootPackageDir; } + /** + * Resolves a path under the root package directory. + */ protected function getRootPackagePath(string $path): string { return rtrim($this->getRootPackageDir(), '/\\') . DIRECTORY_SEPARATOR . $path; diff --git a/src/Asset/AssetManagerFinder.php b/src/Asset/AssetManagerFinder.php index 75b3278..6b7af40 100644 --- a/src/Asset/AssetManagerFinder.php +++ b/src/Asset/AssetManagerFinder.php @@ -8,6 +8,9 @@ use function count; +/** + * Selects a configured asset manager by name, lock file, or executable availability. + */ final class AssetManagerFinder { /** @@ -16,6 +19,8 @@ final class AssetManagerFinder private array $managers = []; /** + * Registers the asset managers available for selection. + * * @param AssetManagerInterface[] $managers The asset managers */ public function __construct(array $managers = []) @@ -25,6 +30,9 @@ public function __construct(array $managers = []) } } + /** + * Registers an asset manager by its unique name. + */ public function addManager(AssetManagerInterface $manager): void { $this->managers[$manager->getName()] = $manager; diff --git a/src/Asset/AssetManagerInterface.php b/src/Asset/AssetManagerInterface.php index af22732..e22d808 100644 --- a/src/Asset/AssetManagerInterface.php +++ b/src/Asset/AssetManagerInterface.php @@ -8,6 +8,9 @@ use Foxy\Audit\{AuditParserInterface, AuditProcessResult}; use Foxy\Exception\RuntimeException; +/** + * Defines frontend dependency manager operations used by the Foxy solver. + */ interface AssetManagerInterface { /** diff --git a/src/Asset/AssetPackage.php b/src/Asset/AssetPackage.php index 5e1e9c5..9b60b39 100644 --- a/src/Asset/AssetPackage.php +++ b/src/Asset/AssetPackage.php @@ -21,6 +21,9 @@ use const DIRECTORY_SEPARATOR; +/** + * Maintains Composer-provided dependencies in a frontend package manifest. + */ final class AssetPackage implements AssetPackageInterface { public const string COMPOSER_PREFIX = '@composer-asset/'; @@ -32,6 +35,8 @@ final class AssetPackage implements AssetPackageInterface private array $package = []; /** + * Loads the frontend package manifest and injects metadata inherited from the Composer root package. + * * @param RootPackageInterface $rootPackage The composer root package * @param JsonFile $jsonFile The JSON file * @@ -52,6 +57,8 @@ public function __construct( } /** + * Adds Composer asset dependencies and returns the names that were already installed. + * * @param array $dependencies The dependencies * * @return list The existing packages. @@ -80,6 +87,9 @@ public function addNewDependencies(array $dependencies): array return $existingPackages; } + /** + * Returns Composer-managed dependencies currently declared in the package manifest. + */ public function getInstalledDependencies(): array { $installedAssets = []; @@ -99,12 +109,17 @@ public function getInstalledDependencies(): array return $installedAssets; } + /** + * Returns the complete frontend package manifest. + */ public function getPackage(): array { return $this->package; } /** + * Removes Composer-managed dependencies absent from the current dependency set. + * * @psalm-suppress MixedArrayAccess */ public function removeUnusedDependencies(array $dependencies): self @@ -120,6 +135,9 @@ public function removeUnusedDependencies(array $dependencies): self return $this; } + /** + * Replaces the in-memory frontend package manifest. + */ public function setPackage(array $package): self { $this->package = $package; @@ -128,6 +146,8 @@ public function setPackage(array $package): self } /** + * Writes the in-memory package manifest to its JSON file. + * * @throws Exception */ public function write(): self diff --git a/src/Asset/AssetPackageInterface.php b/src/Asset/AssetPackageInterface.php index 9a916be..e3cc7f9 100644 --- a/src/Asset/AssetPackageInterface.php +++ b/src/Asset/AssetPackageInterface.php @@ -4,6 +4,9 @@ namespace Foxy\Asset; +/** + * Defines mutable access to a frontend package manifest and its Composer-managed dependencies. + */ interface AssetPackageInterface { /** diff --git a/src/Asset/BunManager.php b/src/Asset/BunManager.php index 820b26d..722dde2 100644 --- a/src/Asset/BunManager.php +++ b/src/Asset/BunManager.php @@ -38,33 +38,54 @@ use const DIRECTORY_SEPARATOR; +/** + * Integrates the Bun package manager with Foxy's install, update, and audit workflows. + */ final class BunManager extends AbstractAssetManager { + /** + * Returns the parser for Bun audit JSON output. + */ public function getAuditParser(): AuditParserInterface { return new BunAuditParser(); } + /** + * Returns Bun's text lock-file name. + */ public function getLockPackageName(): string { return 'bun.lock'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'bun'; } + /** + * Returns the Bun versions supported by this integration. + */ public function getVersionConstraint(): string { return '^1.4.0'; } + /** + * Returns whether the package manifest, dependency directory, and Bun lock file exist. + */ public function isInstalled(): bool { return parent::isInstalled() && $this->hasLockFile(); } + /** + * Returns the Bun audit command for all or production dependencies. + */ protected function getAuditCommand(bool $noDev): string { $command = ['audit', '--json']; @@ -84,6 +105,9 @@ protected function getDefaultBinary(): string return Platform::isWindows() ? 'bun.exe' : 'bun'; } + /** + * Rejects Bun and npm configuration that would narrow or ignore audit results. + */ protected function validateAuditConfiguration(bool $noDev): void { foreach ($this->getNpmrcPaths() as $path) { diff --git a/src/Asset/DenoManager.php b/src/Asset/DenoManager.php index 40474ff..e1396ed 100644 --- a/src/Asset/DenoManager.php +++ b/src/Asset/DenoManager.php @@ -17,35 +17,56 @@ use function str_starts_with; use function substr; +/** + * Integrates Deno with Foxy's dependency workspace, update, and audit workflows. + */ final class DenoManager extends AbstractAssetManager { private const array AUDIT_ENVIRONMENT = ['NO_COLOR' => '1']; + /** + * Returns the parser for Deno audit JSON output. + */ public function getAuditParser(): AuditParserInterface { return new DenoAuditParser(); } + /** + * Returns Deno's lock-file name. + */ public function getLockPackageName(): string { return 'deno.lock'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'deno'; } + /** + * Returns the Deno versions supported by this integration. + */ public function getVersionConstraint(): string { return '^2.9.7'; } + /** + * Returns whether the package manifest, dependency directory, and Deno lock file exist. + */ public function isInstalled(): bool { return parent::isInstalled() && $this->hasLockFile(); } + /** + * Synchronizes Deno workspace members with the merged Composer asset dependencies. + */ protected function actionWhenComposerDependenciesAreMerged( AssetPackageInterface $assetPackage, array $previousDependencies, @@ -96,11 +117,17 @@ protected function actionWhenComposerDependenciesAreMerged( $assetPackage->setPackage($package); } + /** + * Returns the Deno audit command for the complete workspace. + */ protected function getAuditCommand(bool $noDev): string { return $this->buildUnconfiguredCommand(['audit', '--level=low']); } + /** + * Returns environment overrides that disable color in Deno audit output. + */ protected function getAuditEnvironment(): array { return self::AUDIT_ENVIRONMENT; @@ -114,6 +141,9 @@ protected function getDefaultBinary(): string return Platform::isWindows() ? 'deno.exe' : 'deno'; } + /** + * Returns the command that refreshes the lock file and then installs dependencies. + */ protected function getUpdateCommand(): string { return $this->buildCommand('update', ['update', '--lockfile-only', '--recursive']) @@ -121,11 +151,17 @@ protected function getUpdateCommand(): string . $this->getInstallCommand(); } + /** + * Extracts the version number from Deno's version output. + */ protected function normalizeVersionOutput(string $output): string { return 1 === preg_match('/^deno (\S+)/', $output, $matches) ? $matches[1] : $output; } + /** + * Rejects production-only audits because Deno cannot exclude development dependencies. + */ protected function validateAuditConfiguration(bool $noDev): void { if ($noDev) { diff --git a/src/Asset/NativeManager.php b/src/Asset/NativeManager.php index 9645682..164e0df 100644 --- a/src/Asset/NativeManager.php +++ b/src/Asset/NativeManager.php @@ -40,6 +40,8 @@ final class NativeManager extends AbstractManifestAssetManager private const string FILESYSTEM_ROOT = '{^(?:[A-Za-z]:|//[^/]+/[^/]+)?/?$}'; /** + * Creates the native manager with its installer and npm registry client. + * * @param IOInterface $io Output for the progress line. * @param Config $config Foxy configuration. * @param Filesystem $fs Filesystem used by the manifest handling. @@ -97,21 +99,33 @@ public function audit(bool $noDev): AuditProcessResult return new AuditProcessResult([] === $this->getAuditParser()->parse($document) ? 0 : 1, $document, ''); } + /** + * Returns the parser for npm bulk-advisory documents. + */ public function getAuditParser(): AuditParserInterface { return new NativeAuditParser(); } + /** + * Returns the native manager lock-file name. + */ public function getLockPackageName(): string { return 'foxy.lock'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'native'; } + /** + * Returns the unrestricted version constraint because this manager runs in PHP. + */ public function getVersionConstraint(): string { return '*'; diff --git a/src/Asset/NpmManager.php b/src/Asset/NpmManager.php index 0716244..ca1809b 100644 --- a/src/Asset/NpmManager.php +++ b/src/Asset/NpmManager.php @@ -18,28 +18,46 @@ use function str_replace; use function trim; +/** + * Integrates npm with Foxy's install, update, workspace, and audit workflows. + */ final class NpmManager extends AbstractAssetManager { + /** + * Returns the parser for npm audit JSON output. + */ public function getAuditParser(): AuditParserInterface { return new NpmAuditParser(); } + /** + * Returns npm's lock-file name. + */ public function getLockPackageName(): string { return 'package-lock.json'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'npm'; } + /** + * Returns the npm versions supported by this integration. + */ public function getVersionConstraint(): string { return '>=10.9.8'; } + /** + * Removes already-installed Composer assets so npm installs their updated manifest paths. + */ protected function actionWhenComposerDependenciesAreAlreadyInstalled(array $names): void { foreach ($names as $name) { @@ -47,6 +65,9 @@ protected function actionWhenComposerDependenciesAreAlreadyInstalled(array $name } } + /** + * Returns an npm audit command that covers the root package and every locked workspace. + */ protected function getAuditCommand(bool $noDev): string { $command = [ diff --git a/src/Asset/PnpmManager.php b/src/Asset/PnpmManager.php index 935a407..b2725f2 100644 --- a/src/Asset/PnpmManager.php +++ b/src/Asset/PnpmManager.php @@ -8,33 +8,54 @@ use Foxy\Audit\AuditParserInterface; use Foxy\Audit\Parser\PnpmAuditParser; +/** + * Integrates pnpm with Foxy's install, update, and audit workflows. + */ final class PnpmManager extends AbstractAssetManager { + /** + * Returns the parser for pnpm audit JSON output. + */ public function getAuditParser(): AuditParserInterface { return new PnpmAuditParser(); } + /** + * Returns pnpm's lock-file name. + */ public function getLockPackageName(): string { return 'pnpm-lock.yaml'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'pnpm'; } + /** + * Returns the pnpm versions supported by this integration. + */ public function getVersionConstraint(): string { return '^11.23.0'; } + /** + * Returns whether the package manifest, dependency directory, and pnpm lock file exist. + */ public function isInstalled(): bool { return parent::isInstalled() && file_exists($this->getLockFilePath()); } + /** + * Returns the pnpm audit command for all or production dependencies. + */ protected function getAuditCommand(bool $noDev): string { $command = [ diff --git a/src/Asset/YarnManager.php b/src/Asset/YarnManager.php index 63634ea..e0ed84a 100644 --- a/src/Asset/YarnManager.php +++ b/src/Asset/YarnManager.php @@ -7,6 +7,9 @@ use Foxy\Audit\AuditParserInterface; use Foxy\Audit\Parser\YarnAuditParser; +/** + * Integrates Yarn with Foxy's install, update, Plug'n'Play, and audit workflows. + */ final class YarnManager extends AbstractAssetManager { private const array AUDIT_ENVIRONMENT = [ @@ -14,26 +17,41 @@ final class YarnManager extends AbstractAssetManager 'YARN_NPM_AUDIT_IGNORE_ADVISORIES' => '__FOXY_AUDIT_NO_MATCH__', ]; + /** + * Returns the parser for Yarn audit JSON output. + */ public function getAuditParser(): AuditParserInterface { return new YarnAuditParser(); } + /** + * Returns Yarn's lock-file name. + */ public function getLockPackageName(): string { return 'yarn.lock'; } + /** + * Returns the manager name used in Foxy configuration. + */ public function getName(): string { return 'yarn'; } + /** + * Returns the Yarn versions supported by this integration. + */ public function getVersionConstraint(): string { return '^4.18.0'; } + /** + * Returns whether the manifest and lock file exist with either `node_modules` or Plug'n'Play output. + */ public function isInstalled(): bool { return file_exists($this->getPackageJsonPath()) @@ -44,6 +62,9 @@ public function isInstalled(): bool ); } + /** + * Returns the Yarn recursive audit command for all or production dependencies. + */ protected function getAuditCommand(bool $noDev): string { $command = ['npm', 'audit', '--all', '--recursive', '--json', '--no-deprecations', '--severity', 'info']; @@ -55,11 +76,17 @@ protected function getAuditCommand(bool $noDev): string return $this->buildUnconfiguredCommand($command); } + /** + * Returns environment overrides that prevent exclusions from Yarn audit results. + */ protected function getAuditEnvironment(): array { return self::AUDIT_ENVIRONMENT; } + /** + * Returns Yarn's dependency update command. + */ protected function getUpdateCommand(): string { return $this->buildCommand('update', 'up'); diff --git a/src/Audit/AuditFinding.php b/src/Audit/AuditFinding.php index 990994c..909f418 100644 --- a/src/Audit/AuditFinding.php +++ b/src/Audit/AuditFinding.php @@ -4,12 +4,25 @@ namespace Foxy\Audit; +/** + * Represents one normalized frontend dependency security advisory. + */ final readonly class AuditFinding { /** - * @param list $cves - * @param list $affectedVersions - * @param list $dependencyPaths + * Creates an immutable audit finding. + * + * @param string $package Package affected by the advisory. + * @param Severity $severity Normalized advisory severity. + * @param string $advisoryId Preferred advisory identifier, such as a GHSA identifier. + * @param string|null $sourceId Identifier reported by the package manager. + * @param string $title Human-readable advisory title. + * @param string $vulnerableVersions Version constraint reported as vulnerable. + * @param string|null $url Advisory details URL. + * @param list $cves Associated CVE identifiers. + * @param CveStatus $cveStatus Status of CVE resolution for this advisory. + * @param list $affectedVersions Installed versions affected by the advisory. + * @param list $dependencyPaths Dependency paths that include the vulnerable package. */ public function __construct( public string $package, @@ -26,7 +39,9 @@ public function __construct( ) {} /** - * @param list $cves + * Returns a copy with the supplied CVE resolution. + * + * @param list $cves Resolved CVE identifiers. */ public function withCveResolution(array $cves, CveStatus $status): self { diff --git a/src/Audit/AuditFormat.php b/src/Audit/AuditFormat.php index 302022c..e8657db 100644 --- a/src/Audit/AuditFormat.php +++ b/src/Audit/AuditFormat.php @@ -4,6 +4,9 @@ namespace Foxy\Audit; +/** + * Output formats supported by the audit command. + */ enum AuditFormat: string { case JSON = 'json'; diff --git a/src/Audit/AuditFormatter.php b/src/Audit/AuditFormatter.php index f19d11f..dadc82d 100644 --- a/src/Audit/AuditFormatter.php +++ b/src/Audit/AuditFormatter.php @@ -19,9 +19,14 @@ use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; +/** + * Renders normalized audit reports for console output. + */ final readonly class AuditFormatter { /** + * Writes the report in the selected output format. + * * @throws JsonException */ public function write( diff --git a/src/Audit/AuditParserInterface.php b/src/Audit/AuditParserInterface.php index 5d4b26b..13f65ba 100644 --- a/src/Audit/AuditParserInterface.php +++ b/src/Audit/AuditParserInterface.php @@ -4,9 +4,14 @@ namespace Foxy\Audit; +/** + * Parses a package manager's audit output into normalized findings. + */ interface AuditParserInterface { /** + * Parses the complete audit command output. + * * @return list */ public function parse(string $output): array; diff --git a/src/Audit/AuditProcessResult.php b/src/Audit/AuditProcessResult.php index 510d9ed..ad6aa40 100644 --- a/src/Audit/AuditProcessResult.php +++ b/src/Audit/AuditProcessResult.php @@ -4,7 +4,17 @@ namespace Foxy\Audit; +/** + * Contains the raw result of a package manager audit process. + */ final readonly class AuditProcessResult { + /** + * Creates an immutable process result. + * + * @param int $exitCode Process exit status. + * @param string $output Standard output captured from the process. + * @param string $errorOutput Standard error captured from the process. + */ public function __construct(public int $exitCode, public string $output, public string $errorOutput) {} } diff --git a/src/Audit/AuditReport.php b/src/Audit/AuditReport.php index 6470c15..86ee464 100644 --- a/src/Audit/AuditReport.php +++ b/src/Audit/AuditReport.php @@ -7,13 +7,23 @@ use function array_unique; use function count; +/** + * Contains normalized findings and diagnostics from one package manager audit. + */ final readonly class AuditReport { /** - * @param list $findings + * Creates an immutable audit report. + * + * @param string $manager Package manager that produced the report. + * @param list $findings Normalized security findings. + * @param string $diagnostics Diagnostic output emitted by the package manager. */ public function __construct(public string $manager, public array $findings, public string $diagnostics = '') {} + /** + * Returns the number of distinct affected packages. + */ public function countPackages(): int { return count( @@ -27,6 +37,8 @@ public function countPackages(): int } /** + * Counts findings for every supported severity. + * * @return array */ public function countSeverities(): array @@ -46,6 +58,9 @@ public function countSeverities(): array return $counts; } + /** + * Whether any finding meets or exceeds the minimum severity. + */ public function hasFindingAtLeast(Severity $minimumSeverity): bool { foreach ($this->findings as $finding) { @@ -58,7 +73,9 @@ public function hasFindingAtLeast(Severity $minimumSeverity): bool } /** - * @param list $findings + * Returns a copy with the supplied findings. + * + * @param list $findings Replacement findings. */ public function withFindings(array $findings): self { diff --git a/src/Audit/AuditRequest.php b/src/Audit/AuditRequest.php index 12c41af..d66db31 100644 --- a/src/Audit/AuditRequest.php +++ b/src/Audit/AuditRequest.php @@ -4,7 +4,16 @@ namespace Foxy\Audit; +/** + * Describes the scope and failure threshold of an audit run. + */ final readonly class AuditRequest { + /** + * Creates an immutable audit request. + * + * @param Severity $minimumSeverity Lowest severity that makes the audit report vulnerable. + * @param bool $noDev Whether development dependencies are excluded. + */ public function __construct(public Severity $minimumSeverity = Severity::LOW, public bool $noDev = false) {} } diff --git a/src/Audit/AuditRunner.php b/src/Audit/AuditRunner.php index a0d4324..4e60143 100644 --- a/src/Audit/AuditRunner.php +++ b/src/Audit/AuditRunner.php @@ -12,10 +12,19 @@ use function trim; use function usort; +/** + * Runs and validates a package manager audit before normalizing its findings. + */ final readonly class AuditRunner implements AuditRunnerInterface { + /** + * Creates an audit runner for the selected asset manager. + */ public function __construct(private AssetManagerInterface $manager) {} + /** + * Runs the configured package manager audit and returns a normalized report. + */ public function audit(AuditRequest $request): AuditReport { $manager = $this->manager->getName(); diff --git a/src/Audit/AuditRunnerInterface.php b/src/Audit/AuditRunnerInterface.php index 91fa512..0121842 100644 --- a/src/Audit/AuditRunnerInterface.php +++ b/src/Audit/AuditRunnerInterface.php @@ -4,7 +4,13 @@ namespace Foxy\Audit; +/** + * Runs a package manager audit and returns normalized findings. + */ interface AuditRunnerInterface { + /** + * Audits the selected dependency set. + */ public function audit(AuditRequest $request): AuditReport; } diff --git a/src/Audit/CveEnricher.php b/src/Audit/CveEnricher.php index eb4cdff..a065ff2 100644 --- a/src/Audit/CveEnricher.php +++ b/src/Audit/CveEnricher.php @@ -17,6 +17,9 @@ */ final readonly class CveEnricher { + /** + * Creates an enricher backed by the supplied CVE resolver. + */ public function __construct(private CveResolverInterface $resolver) {} /** diff --git a/src/Audit/CveResolution.php b/src/Audit/CveResolution.php index 0269843..9762a82 100644 --- a/src/Audit/CveResolution.php +++ b/src/Audit/CveResolution.php @@ -4,10 +4,16 @@ namespace Foxy\Audit; +/** + * Contains the CVE identifiers resolved for one security advisory. + */ final readonly class CveResolution { /** - * @param list $cves + * Creates an immutable CVE resolution. + * + * @param list $cves Resolved CVE identifiers. + * @param CveStatus $status Outcome of the CVE lookup. */ public function __construct(public array $cves, public CveStatus $status) {} } diff --git a/src/Audit/CveStatus.php b/src/Audit/CveStatus.php index 52ecc88..f1e2c6b 100644 --- a/src/Audit/CveStatus.php +++ b/src/Audit/CveStatus.php @@ -4,6 +4,9 @@ namespace Foxy\Audit; +/** + * Outcomes of resolving CVE identifiers for an advisory. + */ enum CveStatus: string { case NONE_ASSIGNED = 'none_assigned'; diff --git a/src/Audit/GitHubAdvisoryCveResolver.php b/src/Audit/GitHubAdvisoryCveResolver.php index 2f0b9cd..9929df4 100644 --- a/src/Audit/GitHubAdvisoryCveResolver.php +++ b/src/Audit/GitHubAdvisoryCveResolver.php @@ -29,6 +29,9 @@ { private const string API_URL = 'https://api.github.com/advisories/%s'; + /** + * Creates a resolver using Composer's asynchronous HTTP downloader. + */ public function __construct(private HttpDownloader $httpDownloader) {} /** diff --git a/src/Audit/Parser/AbstractAuditParser.php b/src/Audit/Parser/AbstractAuditParser.php index b07c002..b1be842 100644 --- a/src/Audit/Parser/AbstractAuditParser.php +++ b/src/Audit/Parser/AbstractAuditParser.php @@ -25,13 +25,21 @@ use const JSON_THROW_ON_ERROR; +/** + * Provides strict validation and normalization helpers for audit output parsers. + */ abstract class AbstractAuditParser { private const int MAX_OUTPUT_BYTES = 16 * 1024 * 1024; + /** + * Returns the package manager name used in malformed-output errors. + */ abstract protected function getManagerName(): string; /** + * Rejects a JSON document that represents a package manager error. + * * @param array $data */ final protected function assertNotErrorDocument(array $data): void @@ -43,6 +51,9 @@ final protected function assertNotErrorDocument(array $data): void } } + /** + * Rejects audit output that exceeds the parser's size limit. + */ final protected function assertOutputSize(string $output): void { if (strlen($output) > self::MAX_OUTPUT_BYTES) { @@ -53,6 +64,8 @@ final protected function assertOutputSize(string $output): void } /** + * Decodes an audit JSON object after validating its size and shape. + * * @return array */ final protected function decodeObject(string $output): array @@ -78,11 +91,17 @@ final protected function decodeObject(string $output): array return get_object_vars($data); } + /** + * Selects a GHSA identifier from the candidate or URL, falling back to the source identifier. + */ final protected function getAdvisoryId(string $sourceId, string|null $candidate, string|null $url): string { return $this->getGhsaId($candidate) ?? $this->getGhsaId($url) ?? $sourceId; } + /** + * Returns a required boolean field. + */ final protected function getBoolean(array $data, string $key, string $context): bool { $value = $data[$key] ?? null; @@ -97,6 +116,8 @@ final protected function getBoolean(array $data, string $key, string $context): } /** + * Returns validated, normalized CVE identifiers. + * * @return list */ final protected function getCves(mixed $value, string $context): array @@ -118,11 +139,17 @@ final protected function getCves(mixed $value, string $context): array return $this->uniqueStrings(array_map(strtoupper(...), $cves)); } + /** + * Extracts and normalizes a GHSA identifier from a value or URL. + */ final protected function getGhsaId(string|null $value): string|null { return null === $value ? null : AuditNormalizer::normalizeGhsaReference($value); } + /** + * Returns a required non-negative integer field. + */ final protected function getNonNegativeInteger(array $data, string $key, string $context): int { $value = $data[$key] ?? null; @@ -137,6 +164,8 @@ final protected function getNonNegativeInteger(array $data, string $key, string } /** + * Converts a decoded JSON object into its property map. + * * @return array */ final protected function getObject(mixed $value, string $context): array @@ -150,6 +179,9 @@ final protected function getObject(mixed $value, string $context): array return get_object_vars($value); } + /** + * Returns a sanitized optional string field, or `null` when absent or empty. + */ final protected function getOptionalString(array $data, string $key, string $context): string|null { if (!isset($data[$key])) { @@ -167,6 +199,9 @@ final protected function getOptionalString(array $data, string $key, string $con return trim($value) === '' ? null : $this->sanitizeString($value); } + /** + * Returns a supported normalized severity value. + */ final protected function getSeverity(mixed $value, string $context): Severity { if (!is_string($value) || null === $severity = Severity::tryFrom(strtolower($value))) { @@ -178,6 +213,9 @@ final protected function getSeverity(mixed $value, string $context): Severity return $severity; } + /** + * Returns the sum of severity counts and optionally validates the reported total. + */ final protected function getSeverityCount(array $metadata, string $context, bool $requireTotal): int { $counts = $this->getObject($metadata['vulnerabilities'] ?? null, $context . '.vulnerabilities'); @@ -196,6 +234,9 @@ final protected function getSeverityCount(array $metadata, string $context, bool return $total; } + /** + * Returns a non-empty source identifier from a string or integer value. + */ final protected function getSourceId(mixed $value, string $context): string { if (!is_int($value) && !is_string($value)) { @@ -215,6 +256,9 @@ final protected function getSourceId(mixed $value, string $context): string return $sourceId; } + /** + * Returns a required sanitized string field. + */ final protected function getString(array $data, string $key, string $context, bool $allowEmpty = false): string { $value = $data[$key] ?? null; @@ -229,6 +273,8 @@ final protected function getString(array $data, string $key, string $context, bo } /** + * Returns a normalized list containing only strings. + * * @return list */ final protected function getStringList(mixed $value, string $context): array @@ -253,6 +299,9 @@ final protected function getStringList(mixed $value, string $context): array return $this->uniqueStrings($value); } + /** + * Creates a package-manager-specific malformed-output exception. + */ final protected function malformed(string $reason, Throwable|null $previous = null): RuntimeException { return new RuntimeException( @@ -261,12 +310,17 @@ final protected function malformed(string $reason, Throwable|null $previous = nu ); } + /** + * Replaces control characters and trims surrounding whitespace. + */ final protected function sanitizeString(string $value): string { return trim((string) preg_replace('/[\x00-\x1F\x7F]+/u', ' ', $value)); } /** + * Returns unique strings in deterministic order. + * * @param list $values * * @return list diff --git a/src/Audit/Parser/AbstractBulkAdvisoryParser.php b/src/Audit/Parser/AbstractBulkAdvisoryParser.php index 669da51..55ddcd9 100644 --- a/src/Audit/Parser/AbstractBulkAdvisoryParser.php +++ b/src/Audit/Parser/AbstractBulkAdvisoryParser.php @@ -18,6 +18,9 @@ */ abstract class AbstractBulkAdvisoryParser extends AbstractAuditParser implements AuditParserInterface { + /** + * Parses a bulk advisories JSON object into normalized findings. + */ public function parse(string $output): array { $packages = $this->decodeObject($output); diff --git a/src/Audit/Parser/BunAuditParser.php b/src/Audit/Parser/BunAuditParser.php index 8620476..d5d8779 100644 --- a/src/Audit/Parser/BunAuditParser.php +++ b/src/Audit/Parser/BunAuditParser.php @@ -9,6 +9,9 @@ */ final class BunAuditParser extends AbstractBulkAdvisoryParser { + /** + * Returns the Bun manager name. + */ protected function getManagerName(): string { return 'bun'; diff --git a/src/Audit/Parser/DenoAuditParser.php b/src/Audit/Parser/DenoAuditParser.php index 6b68ce6..7763cf1 100644 --- a/src/Audit/Parser/DenoAuditParser.php +++ b/src/Audit/Parser/DenoAuditParser.php @@ -19,6 +19,9 @@ use function substr; use function trim; +/** + * Parses the human-readable report produced by `deno audit`. + */ final class DenoAuditParser extends AbstractAuditParser implements AuditParserInterface { /** @@ -51,6 +54,9 @@ final class DenoAuditParser extends AbstractAuditParser implements AuditParserIn 'actions' => "\u{2570} Actions: ", ]; + /** + * Parses a complete Deno audit report. + */ public function parse(string $output): array { $this->assertOutputSize($output); @@ -110,6 +116,9 @@ public function parse(string $output): array return $findings; } + /** + * Returns the Deno manager name. + */ protected function getManagerName(): string { return 'deno'; diff --git a/src/Audit/Parser/NativeAuditParser.php b/src/Audit/Parser/NativeAuditParser.php index af77616..665df23 100644 --- a/src/Audit/Parser/NativeAuditParser.php +++ b/src/Audit/Parser/NativeAuditParser.php @@ -9,6 +9,9 @@ */ final class NativeAuditParser extends AbstractBulkAdvisoryParser { + /** + * Returns the native manager name. + */ protected function getManagerName(): string { return 'native'; diff --git a/src/Audit/Parser/NpmAuditParser.php b/src/Audit/Parser/NpmAuditParser.php index d13b0ae..e5c96d9 100644 --- a/src/Audit/Parser/NpmAuditParser.php +++ b/src/Audit/Parser/NpmAuditParser.php @@ -15,8 +15,14 @@ use function is_string; use function sprintf; +/** + * Parses the JSON report produced by npm audit report version 2. + */ final class NpmAuditParser extends AbstractAuditParser implements AuditParserInterface { + /** + * Parses a complete npm audit report. + */ public function parse(string $output): array { $data = $this->decodeObject($output); @@ -37,6 +43,9 @@ public function parse(string $output): array return $findings; } + /** + * Returns the npm manager name. + */ protected function getManagerName(): string { return 'npm'; diff --git a/src/Audit/Parser/PnpmAuditParser.php b/src/Audit/Parser/PnpmAuditParser.php index af07791..ab39be4 100644 --- a/src/Audit/Parser/PnpmAuditParser.php +++ b/src/Audit/Parser/PnpmAuditParser.php @@ -13,8 +13,14 @@ use function is_int; use function sprintf; +/** + * Parses the JSON advisory report produced by `pnpm audit`. + */ final class PnpmAuditParser extends AbstractAuditParser implements AuditParserInterface { + /** + * Parses a complete pnpm audit report. + */ public function parse(string $output): array { $data = $this->decodeObject($output); @@ -61,6 +67,9 @@ public function parse(string $output): array return $findings; } + /** + * Returns the pnpm manager name. + */ protected function getManagerName(): string { return 'pnpm'; diff --git a/src/Audit/Parser/YarnAuditParser.php b/src/Audit/Parser/YarnAuditParser.php index 7aabedf..afd8d1b 100644 --- a/src/Audit/Parser/YarnAuditParser.php +++ b/src/Audit/Parser/YarnAuditParser.php @@ -17,8 +17,14 @@ use const JSON_THROW_ON_ERROR; +/** + * Parses the newline-delimited JSON report produced by Yarn audit. + */ final class YarnAuditParser extends AbstractAuditParser implements AuditParserInterface { + /** + * Parses a complete Yarn audit report. + */ public function parse(string $output): array { $this->assertOutputSize($output); @@ -70,6 +76,9 @@ public function parse(string $output): array return $findings; } + /** + * Returns the Yarn manager name. + */ protected function getManagerName(): string { return 'yarn'; diff --git a/src/Audit/Severity.php b/src/Audit/Severity.php index 2830fc6..9961c1d 100644 --- a/src/Audit/Severity.php +++ b/src/Audit/Severity.php @@ -4,6 +4,9 @@ namespace Foxy\Audit; +/** + * Normalized security advisory severity levels. + */ enum Severity: string { case CRITICAL = 'critical'; @@ -12,11 +15,17 @@ enum Severity: string case LOW = 'low'; case MODERATE = 'moderate'; + /** + * Whether this severity meets or exceeds the threshold. + */ public function isAtLeast(self $threshold): bool { return $this->weight() >= $threshold->weight(); } + /** + * Returns the relative ordering weight of this severity. + */ public function weight(): int { return match ($this) { diff --git a/src/Command/AuditCommand.php b/src/Command/AuditCommand.php index be44727..4f28e92 100644 --- a/src/Command/AuditCommand.php +++ b/src/Command/AuditCommand.php @@ -24,12 +24,22 @@ use function preg_replace; use function trim; +/** + * Audits frontend dependencies and renders normalized vulnerability findings. + */ final class AuditCommand extends BaseCommand { public const int STATUS_FAILED = 2; public const int STATUS_OK = 0; public const int STATUS_VULNERABLE = 1; + /** + * Creates the audit command and its optional CVE resolver. + * + * @param AuditRunnerInterface $runner Runs the selected package manager audit. + * @param AuditFormatter $formatter Renders the normalized report. + * @param CveResolverInterface|null $cveResolver Resolves GHSA advisories to CVE identifiers. + */ public function __construct( private readonly AuditRunnerInterface $runner, private readonly AuditFormatter $formatter = new AuditFormatter(), @@ -38,6 +48,9 @@ public function __construct( parent::__construct(); } + /** + * Configures the audit command options and help text. + */ protected function configure(): void { $this @@ -76,6 +89,9 @@ protected function configure(): void ); } + /** + * Runs the audit and returns its stable command status. + */ protected function execute(InputInterface $input, OutputInterface $output): int { $formatValue = $input->getOption('format'); diff --git a/src/Command/FoxyCommandProvider.php b/src/Command/FoxyCommandProvider.php index be6d2df..661b9f5 100644 --- a/src/Command/FoxyCommandProvider.php +++ b/src/Command/FoxyCommandProvider.php @@ -11,6 +11,9 @@ use Foxy\Exception\{Message, RuntimeException}; use Foxy\Foxy; +/** + * Provides Foxy's Composer console commands. + */ final readonly class FoxyCommandProvider implements CommandProvider { private Composer $composer; @@ -18,7 +21,9 @@ private Foxy $plugin; /** - * @param array $arguments + * Creates the provider from Composer's capability arguments. + * + * @param array $arguments Composer, I/O, and plugin instances supplied by Composer. */ public function __construct(array $arguments) { @@ -38,6 +43,8 @@ public function __construct(array $arguments) } /** + * Returns the Foxy commands configured with the active Composer context. + * * @return list */ public function getCommands(): array diff --git a/src/Config/Config.php b/src/Config/Config.php index d5056e0..6af6e67 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -19,6 +19,9 @@ use function strtoupper; use function trim; +/** + * Resolves Foxy configuration from package values, defaults, and environment overrides. + */ class Config { private array $cacheEnv = []; @@ -26,8 +29,10 @@ class Config private string|null $scopedManager = null; /** - * @param array $config The config. - * @param array $defaults The default values. + * Creates a configuration resolver. + * + * @param array $config Package and global configuration values. + * @param array $defaults Default values used when a key is not configured. */ public function __construct(private readonly array $config, private readonly array $defaults = []) {} diff --git a/src/Config/ConfigBuilder.php b/src/Config/ConfigBuilder.php index 61123e1..a068919 100644 --- a/src/Config/ConfigBuilder.php +++ b/src/Config/ConfigBuilder.php @@ -12,6 +12,9 @@ use function is_array; use function is_string; +/** + * Builds Foxy configuration from Composer's project and global settings. + */ abstract class ConfigBuilder { /** diff --git a/src/Converter/SemverConverter.php b/src/Converter/SemverConverter.php index f969e82..428a151 100644 --- a/src/Converter/SemverConverter.php +++ b/src/Converter/SemverConverter.php @@ -11,8 +11,14 @@ use function strlen; use function substr; +/** + * Converts asset-manager version constraints to Composer-compatible constraints. + */ final class SemverConverter implements VersionConverterInterface { + /** + * Converts an asset-manager version constraint to Composer syntax. + */ public function convertVersion(string|null $version = null): string { if (in_array($version, [null, '', 'latest'], true)) { diff --git a/src/Converter/SemverUtil.php b/src/Converter/SemverUtil.php index df6e363..19a17ff 100644 --- a/src/Converter/SemverUtil.php +++ b/src/Converter/SemverUtil.php @@ -17,6 +17,9 @@ use function strtolower; use function substr; +/** + * Normalizes asset-manager semantic versions for Composer. + */ abstract class SemverUtil { /** @@ -35,6 +38,9 @@ public static function convertDateVersion(string $version): string return $version; } + /** + * Converts prerelease and build metadata to Composer's normalized stability notation. + */ public static function convertVersionMetadata(string $version): string { $pattern = self::createPattern('([a-zA-Z]+|(\-|\+)[a-zA-Z]+|(\-|\+)[0-9]+)'); diff --git a/src/Converter/VersionConverterInterface.php b/src/Converter/VersionConverterInterface.php index c69f895..bd229c4 100644 --- a/src/Converter/VersionConverterInterface.php +++ b/src/Converter/VersionConverterInterface.php @@ -4,6 +4,9 @@ namespace Foxy\Converter; +/** + * Converts asset-manager version constraints to Composer constraints. + */ interface VersionConverterInterface { /** diff --git a/src/Event/AbstractSolveEvent.php b/src/Event/AbstractSolveEvent.php index ef3c3f6..119b0f9 100644 --- a/src/Event/AbstractSolveEvent.php +++ b/src/Event/AbstractSolveEvent.php @@ -7,9 +7,14 @@ use Composer\EventDispatcher\Event; use Composer\Package\PackageInterface; +/** + * Carries the asset directory and installed packages through the solve lifecycle. + */ abstract class AbstractSolveEvent extends Event { /** + * Creates a solve lifecycle event. + * * @param string $name The event name. * @param string $assetDir The directory of mock assets. * @param PackageInterface[] $packages All installed Composer packages. diff --git a/src/Event/GetAssetsEvent.php b/src/Event/GetAssetsEvent.php index 0a4d0a7..580aca2 100644 --- a/src/Event/GetAssetsEvent.php +++ b/src/Event/GetAssetsEvent.php @@ -7,9 +7,14 @@ use Composer\Package\PackageInterface; use Foxy\FoxyEvents; +/** + * Allows listeners to contribute asset packages before dependency solving. + */ final class GetAssetsEvent extends AbstractSolveEvent { /** + * Creates the asset collection event. + * * @param string $assetDir The directory of mock assets. * @param PackageInterface[] $packages All installed Composer packages. * @param array $assets The map of asset package name and the asset package path. diff --git a/src/Event/PostSolveEvent.php b/src/Event/PostSolveEvent.php index 384fca4..c1f9e04 100644 --- a/src/Event/PostSolveEvent.php +++ b/src/Event/PostSolveEvent.php @@ -7,9 +7,14 @@ use Composer\Package\PackageInterface; use Foxy\FoxyEvents; +/** + * Reports the asset-manager result after dependency solving. + */ final class PostSolveEvent extends AbstractSolveEvent { /** + * Creates the post-solve event. + * * @param string $assetDir The directory of mock assets. * @param PackageInterface[] $packages All installed Composer packages. * @param int $runResult The process result of asset manager execution. diff --git a/src/Event/PreSolveEvent.php b/src/Event/PreSolveEvent.php index f50ff29..e5b8b54 100644 --- a/src/Event/PreSolveEvent.php +++ b/src/Event/PreSolveEvent.php @@ -7,9 +7,14 @@ use Composer\Package\PackageInterface; use Foxy\FoxyEvents; +/** + * Announces that asset dependency solving is about to start. + */ final class PreSolveEvent extends AbstractSolveEvent { /** + * Creates the pre-solve event. + * * @param string $assetDir The directory of mock assets. * @param PackageInterface[] $packages All installed Composer packages. */ diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index 7366b1d..fda3c88 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -4,4 +4,7 @@ namespace Foxy\Exception; +/** + * Identifies exceptions raised by Foxy. + */ interface ExceptionInterface {} diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index 84c1b0e..bacdfcd 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -4,4 +4,7 @@ namespace Foxy\Exception; +/** + * Represents a runtime failure in Foxy. + */ class RuntimeException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Fallback/AssetFallback.php b/src/Fallback/AssetFallback.php index 7185eb9..909a30a 100644 --- a/src/Fallback/AssetFallback.php +++ b/src/Fallback/AssetFallback.php @@ -13,6 +13,9 @@ use function is_file; use function is_link; +/** + * Restores an asset manifest to the state captured before dependency solving. + */ final class AssetFallback implements FallbackInterface { private readonly Filesystem $fs; @@ -21,6 +24,14 @@ final class AssetFallback implements FallbackInterface private bool $snapshotSaved = false; + /** + * Creates a fallback for one asset manifest path. + * + * @param IOInterface $io Composer I/O used to report restoration. + * @param Config $config Foxy configuration controlling asset fallback. + * @param string $path Asset manifest path to snapshot. + * @param Filesystem|null $fs Filesystem implementation, or `null` to create the Composer default. + */ public function __construct( private readonly IOInterface $io, private readonly Config $config, @@ -30,6 +41,9 @@ public function __construct( $this->fs = $fs ?? new Filesystem(); } + /** + * Restores the captured manifest, or removes it when Foxy created it. + */ public function restore(): void { if (!$this->isEnabled() || !$this->snapshotSaved) { @@ -51,6 +65,9 @@ public function restore(): void } } + /** + * Captures the current manifest state when asset fallback is enabled. + */ public function save(): self { $this->resetSnapshot(); diff --git a/src/Fallback/ComposerFallback.php b/src/Fallback/ComposerFallback.php index f12b68b..2dac283 100644 --- a/src/Fallback/ComposerFallback.php +++ b/src/Fallback/ComposerFallback.php @@ -21,6 +21,9 @@ use function is_link; +/** + * Restores Composer's lock file and installed dependencies after an asset failure. + */ final class ComposerFallback implements FallbackInterface { private readonly Filesystem $fs; @@ -40,6 +43,8 @@ final class ComposerFallback implements FallbackInterface private bool $vendorDirExisted = false; /** + * Creates a Composer state fallback. + * * @param Composer $composer The composer. * @param IOInterface $io The IO. * @param Config $config The config. @@ -59,6 +64,8 @@ public function __construct( } /** + * Restores the captured Composer state without running project scripts. + * * @throws Exception */ public function restore(): void @@ -79,6 +86,9 @@ public function restore(): void $this->restorePreviousLockFile(); } + /** + * Captures the lock file and vendor-directory state when Composer fallback is enabled. + */ public function save(): self { $this->resetSnapshot(); diff --git a/src/Fallback/FallbackInterface.php b/src/Fallback/FallbackInterface.php index 9d04a82..a344bcd 100644 --- a/src/Fallback/FallbackInterface.php +++ b/src/Fallback/FallbackInterface.php @@ -4,6 +4,9 @@ namespace Foxy\Fallback; +/** + * Captures and restores state around asset dependency solving. + */ interface FallbackInterface { /** diff --git a/src/Foxy.php b/src/Foxy.php index e709fc3..cc71a11 100644 --- a/src/Foxy.php +++ b/src/Foxy.php @@ -36,6 +36,9 @@ use function str_contains; +/** + * Integrates frontend dependency managers with Composer's install and update lifecycle. + */ final class Foxy implements PluginInterface, EventSubscriberInterface, Capable, AuditRunnerInterface { final public const string REQUIRED_COMPOSER_VERSION = '^2.10.2'; @@ -91,6 +94,8 @@ final class Foxy implements PluginInterface, EventSubscriberInterface, Capable, private SolverInterface $solver; /** + * Initializes Foxy for the active Composer project. + * * @throws ParsingException */ public function activate(Composer $composer, IOInterface $io): void @@ -114,6 +119,9 @@ public function activate(Composer $composer, IOInterface $io): void $this->composerFallback = new ComposerFallback($composer, $io, $this->config, $input, $fs); } + /** + * Audits the asset dependencies through the selected manager. + */ public function audit(AuditRequest $request): AuditReport { if (!isset($this->config) || !$this->isEnabled()) { @@ -125,16 +133,25 @@ public function audit(AuditRequest $request): AuditReport return (new AuditRunner($this->getAssetManager()))->audit($request); } + /** + * Handles plugin deactivation; Foxy keeps no deactivation state. + */ public function deactivate(Composer $composer, IOInterface $io): void { // Do nothing } + /** + * Declares Foxy's Composer command-provider capability. + */ public function getCapabilities(): array { return [ComposerCommandProvider::class => FoxyCommandProvider::class]; } + /** + * Returns the Composer events that initialize and run asset solving. + */ public static function getSubscribedEvents(): array { return [ @@ -183,6 +200,8 @@ public function initOnInstall(PackageEvent $event): void } /** + * Replaces the solver used for Composer lifecycle events. + * * @param SolverInterface $solver The solver instance. */ public function setSolver(SolverInterface $solver): void @@ -191,6 +210,8 @@ public function setSolver(SolverInterface $solver): void } /** + * Solves asset dependencies for the current Composer install or update event. + * * @param Event $event The composer script event. */ public function solveAssets(Event $event): void @@ -205,6 +226,9 @@ public function solveAssets(Event $event): void $solver->solve($event->getComposer(), $event->getIO()); } + /** + * Handles plugin removal; Foxy keeps no plugin-specific uninstall state. + */ public function uninstall(Composer $composer, IOInterface $io): void { // Do nothing diff --git a/src/FoxyEvents.php b/src/FoxyEvents.php index 7bbb317..718d8d6 100644 --- a/src/FoxyEvents.php +++ b/src/FoxyEvents.php @@ -4,6 +4,9 @@ namespace Foxy; +/** + * Defines the lifecycle events dispatched while Foxy solves asset dependencies. + */ abstract class FoxyEvents { /** diff --git a/src/Json/JsonFile.php b/src/Json/JsonFile.php index ab97c37..6073730 100644 --- a/src/Json/JsonFile.php +++ b/src/Json/JsonFile.php @@ -11,6 +11,9 @@ use function is_array; use function is_string; +/** + * Preserves empty arrays and objects while reading and writing package manifests. + */ final class JsonFile extends \Composer\Json\JsonFile { private const int DEFAULT_OPTIONS = JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE; @@ -67,6 +70,9 @@ public function getArrayKeys(): array return $this->arrayKeys; } + /** + * Reads the JSON document and records how its empty containers are represented. + */ public function read(): array { $data = parent::read(); @@ -76,6 +82,9 @@ public function read(): array return is_array($data) ? $data : []; } + /** + * Writes package data while preserving empty-array keys from the original document. + */ public function write(array $hash, int $options = self::DEFAULT_OPTIONS): void { $arrayKeys = $this->collectEmptyArrayKeys($hash); diff --git a/src/Json/JsonFormatter.php b/src/Json/JsonFormatter.php index 89d57e8..a4fda3f 100644 --- a/src/Json/JsonFormatter.php +++ b/src/Json/JsonFormatter.php @@ -16,6 +16,9 @@ use function str_replace; use function strlen; +/** + * Formats JSON while retaining selected empty containers as arrays. + */ final class JsonFormatter { public const string ARRAY_KEYS_REGEX = '/["\']([\w\-.]+)["\']\s*:\s*\[\s*]/'; diff --git a/src/Native/DependencyResolver.php b/src/Native/DependencyResolver.php index 5a7e461..7779402 100644 --- a/src/Native/DependencyResolver.php +++ b/src/Native/DependencyResolver.php @@ -51,6 +51,8 @@ private const string OPTIONAL_SKIPPED = 'The optional dependency "%s" (%s) was skipped: %s'; /** + * Creates a resolver backed by the npm registry. + * * @param NpmRegistryInterface $registry Registry that provides the package metadata. * @param IOInterface $io Output that receives the deprecation and skipped-dependency warnings. */ diff --git a/src/Native/InstalledState.php b/src/Native/InstalledState.php index 5867b91..645dfc3 100644 --- a/src/Native/InstalledState.php +++ b/src/Native/InstalledState.php @@ -30,6 +30,8 @@ private const string MALFORMED = 'The installed state file "%s" is malformed and was ignored.'; /** + * Creates an installed-state store for one native install directory. + * * @param string $path State file path. * @param IOInterface $io Output for the verbose notice of a malformed file. */ diff --git a/src/Native/LocalPackage.php b/src/Native/LocalPackage.php index b5b3da9..b5571f4 100644 --- a/src/Native/LocalPackage.php +++ b/src/Native/LocalPackage.php @@ -11,6 +11,8 @@ final readonly class LocalPackage { /** + * Describes a local package selected by the native resolver. + * * @param string $name Package name, the source of its requirements. * @param string $version Version declared by its `package.json` (`0.0.0` when absent). * @param string $path Local package directory: the manifest's `file:` value without the prefix, as written. diff --git a/src/Native/LockFile.php b/src/Native/LockFile.php index 2f12ab6..0bfae8b 100644 --- a/src/Native/LockFile.php +++ b/src/Native/LockFile.php @@ -39,6 +39,8 @@ ]; /** + * Creates a native dependency lock-file store. + * * @param string $path Lock file path. */ public function __construct(private string $path) {} diff --git a/src/Native/NativeInstaller.php b/src/Native/NativeInstaller.php index 7f1aedb..8f027ae 100644 --- a/src/Native/NativeInstaller.php +++ b/src/Native/NativeInstaller.php @@ -57,6 +57,8 @@ private const string TEMPORARY_DIRECTORY = '.foxy-tmp'; /** + * Creates the native package installer. + * * @param IOInterface $io Output that receives one line per changed package, and per kept package at verbose level. * @param Filesystem $fs Filesystem used to copy, move, and remove package directories. * @param NpmRegistryInterface $registry Registry that provides the tarballs. diff --git a/src/Native/NpmRegistry.php b/src/Native/NpmRegistry.php index 23e76b3..1cccbfc 100644 --- a/src/Native/NpmRegistry.php +++ b/src/Native/NpmRegistry.php @@ -61,6 +61,8 @@ private const int RETRY_AFTER_LIMIT = 60; /** + * Creates an npm registry client with cache and retry support. + * * @param HttpDownloader $httpDownloader Downloader that performs the requests; {@see prefetchTarballs()} needs the * async-enabled instance returned by `Composer::getLoop()->getHttpDownloader()`. * @param Cache $cache Tarball cache. diff --git a/src/Native/PackageMetadata.php b/src/Native/PackageMetadata.php index 6d11098..8ac4e93 100644 --- a/src/Native/PackageMetadata.php +++ b/src/Native/PackageMetadata.php @@ -21,6 +21,8 @@ final readonly class PackageMetadata { /** + * Describes the installable versions returned for an npm package. + * * @param string $name Package name. * @param array $distTags Dist-tag names mapped to versions. * @param array $versions Versions keyed by version string, limited to the versions that diff --git a/src/Native/PackageVersion.php b/src/Native/PackageVersion.php index 28c0fa0..15591e5 100644 --- a/src/Native/PackageVersion.php +++ b/src/Native/PackageVersion.php @@ -10,6 +10,8 @@ final readonly class PackageVersion { /** + * Describes one installable npm package version. + * * @param string $name Package name. * @param string $version Version string, exactly as keyed in the registry `versions` object. * @param string $tarball Tarball URL (`dist.tarball`). diff --git a/src/Native/Requirement.php b/src/Native/Requirement.php index de9e15f..2a93a66 100644 --- a/src/Native/Requirement.php +++ b/src/Native/Requirement.php @@ -11,6 +11,8 @@ final readonly class Requirement { /** + * Describes one dependency constraint for the native resolver. + * * @param string $name Package name, scoped names included (`@scope/name`). * @param string $spec npm specification as written by the declaring manifest (range, version, or dist-tag). * @param string $source Declaring origin: `''` for the root manifest, the local package name, or `name@version`. diff --git a/src/Native/ResolvedPackage.php b/src/Native/ResolvedPackage.php index cac6601..1d679e4 100644 --- a/src/Native/ResolvedPackage.php +++ b/src/Native/ResolvedPackage.php @@ -10,6 +10,8 @@ final readonly class ResolvedPackage { /** + * Describes a registry or local package selected for installation. + * * @param string $name Package name. * @param string $version Installed version. * @param string|null $resolved Tarball URL of a registry package. diff --git a/src/Native/TarballExtractor.php b/src/Native/TarballExtractor.php index 93e5493..b691226 100644 --- a/src/Native/TarballExtractor.php +++ b/src/Native/TarballExtractor.php @@ -78,6 +78,8 @@ private const array MAGICS = ["ustar\0", 'ustar ']; /** + * Creates a tarball extractor using Composer's filesystem abstraction. + * * @param Filesystem $fs Filesystem used to create directories. */ public function __construct(private Filesystem $fs) {} diff --git a/src/Solver/Solver.php b/src/Solver/Solver.php index c5c114e..48b2ae0 100644 --- a/src/Solver/Solver.php +++ b/src/Solver/Solver.php @@ -34,13 +34,19 @@ use function str_starts_with; use function trim; +/** + * Merges Composer-provided assets and runs the selected frontend dependency manager. + */ final readonly class Solver implements SolverInterface { private const string MANAGED_MARKER = '.foxy-managed'; /** + * Creates the asset dependency solver and its rollback collaborators. + * * @param AssetManagerInterface $assetManager The asset manager instance. * @param Config $config The config instance. + * @param Filesystem $fs Filesystem used to prepare the managed asset directory. * @param FallbackInterface|null $composerFallback The composer fallback instance. * @param FallbackInterface|null $assetFallback Fallback restored when the asset manager fails to merge the * dependencies, throws while running, or returns a non-zero code, or `null` for none. @@ -53,6 +59,9 @@ public function __construct( private FallbackInterface|null $assetFallback = null, ) {} + /** + * Allows the selected manager to update existing asset dependencies. + */ public function setUpdatable($updatable): self { $this->assetManager->setUpdatable($updatable); @@ -61,6 +70,8 @@ public function setUpdatable($updatable): self } /** + * Merges package assets and runs the selected frontend dependency manager. + * * @throws Exception */ public function solve(Composer $composer, IOInterface $io): void diff --git a/src/Solver/SolverInterface.php b/src/Solver/SolverInterface.php index cc47ef8..fc6c041 100644 --- a/src/Solver/SolverInterface.php +++ b/src/Solver/SolverInterface.php @@ -7,6 +7,9 @@ use Composer\Composer; use Composer\IO\IOInterface; +/** + * Resolves and installs asset dependencies during Composer operations. + */ interface SolverInterface { /** diff --git a/src/Util/AssetUtil.php b/src/Util/AssetUtil.php index 0d95e56..498aab0 100644 --- a/src/Util/AssetUtil.php +++ b/src/Util/AssetUtil.php @@ -26,6 +26,9 @@ use function str_starts_with; use function strtr; +/** + * Reads and normalizes asset metadata exposed by Composer packages. + */ final class AssetUtil { /** diff --git a/src/Util/ComposerUtil.php b/src/Util/ComposerUtil.php index 5eb7096..49325a7 100644 --- a/src/Util/ComposerUtil.php +++ b/src/Util/ComposerUtil.php @@ -10,6 +10,9 @@ use function preg_match; use function str_contains; +/** + * Validates Composer runtime requirements used by Foxy. + */ final class ComposerUtil { /** diff --git a/src/Util/PackageUtil.php b/src/Util/PackageUtil.php index 0cc9486..3669d82 100644 --- a/src/Util/PackageUtil.php +++ b/src/Util/PackageUtil.php @@ -7,6 +7,9 @@ use Composer\Package\AliasPackage; use Composer\Package\Loader\ArrayLoader; +/** + * Hydrates Composer package records stored in lock data. + */ final class PackageUtil { /** From c431274b303ff169f2be36eaa870fc6776e28e04 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Fri, 30 Oct 2026 17:09:27 -0300 Subject: [PATCH 2/3] fix: update PHPUnit configuration and enhance mock handling for Composer utilities. --- .github/workflows/build.yml | 2 +- phpunit.xml.dist | 2 +- tests/Solver/SolverTest.php | 30 +++++++++++++++-------- tests/Support/InternalMockerExtension.php | 12 +++++++++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f39234e..599744b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,6 @@ jobs: phpunit: uses: php-forge/actions/.github/workflows/phpunit.yml@a574dbe95fc370db1b7b5ea546cd5ff5b70304d6 # v3.0.0 with: - php-version: '["8.3","8.4","8.5"]' + php-version: '["8.3","8.4","8.5","8.6"]' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index bb962a2..86ec142 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,7 @@ - + ./src diff --git a/tests/Solver/SolverTest.php b/tests/Solver/SolverTest.php index ce86471..bc72163 100644 --- a/tests/Solver/SolverTest.php +++ b/tests/Solver/SolverTest.php @@ -1416,12 +1416,7 @@ public function testSolveRestoresComposerWhenAssetManifestCannotBeWritten(): voi $targetContent = JsonFile::encode(['name' => '@composer-asset/foo--bar', 'version' => '1.0.0']) . "\n"; - MockerState::addCondition( - 'Composer\\Json', - 'file_put_contents', - [$target, $targetContent, 0, null], - false, - ); + $this->mockManifestWrite($target, $targetContent, false); $this->manager ->expects(self::never()) @@ -1509,10 +1504,9 @@ public function testSolveRestoresComposerWhenAssetManifestIsTruncated(): void $truncatedTargetContent = substr($targetContent, 0, -2); - MockerState::addCondition( - 'Composer\\Json', - 'file_put_contents', - [$target, $targetContent, 0, null], + $this->mockManifestWrite( + $target, + $targetContent, static function (string $filename, mixed $data, int $flags, mixed $context): false { self::assertNotFalse( file_put_contents($filename, substr((string) $data, 0, -2), $flags, $context), @@ -2003,6 +1997,22 @@ private function invokeSolverMethodOn(SolverInterface $solver, string $method, m return (new ReflectionClass($solver))->getMethod($method)->invoke($solver, ...$arguments); } + /** + * Mocks the direct `file_put_contents()` write of a JSON manifest for every supported Composer version. + * + * Composer before 2.11 writes from `Composer\Json`. Composer 2.11+ replaces the file through a temporary file in + * `Composer\Util` and only writes directly when the target is read-only, so the target is reported as read-only. + */ + private function mockManifestWrite(string $target, string $content, mixed $result): void + { + foreach (['Composer\\Json', 'Composer\\Util'] as $namespace) { + MockerState::addCondition($namespace, 'file_put_contents', [$target, $content, 0, null], $result); + } + + MockerState::addCondition('Composer\\Util', 'file_exists', [$target], true); + MockerState::addCondition('Composer\\Util', 'is_writable', [$target], false); + } + /** * Replaces the event dispatcher with one that runs the callback when `foxy.post-solve` is dispatched. */ diff --git a/tests/Support/InternalMockerExtension.php b/tests/Support/InternalMockerExtension.php index 343aa0e..5a6d276 100644 --- a/tests/Support/InternalMockerExtension.php +++ b/tests/Support/InternalMockerExtension.php @@ -44,6 +44,18 @@ public static function load(): void 'namespace' => 'Composer\\Json', 'name' => 'file_put_contents', ], + [ + 'namespace' => 'Composer\\Util', + 'name' => 'file_exists', + ], + [ + 'namespace' => 'Composer\\Util', + 'name' => 'file_put_contents', + ], + [ + 'namespace' => 'Composer\\Util', + 'name' => 'is_writable', + ], [ 'namespace' => 'Foxy\\Asset', 'name' => 'getcwd', From a5fd973b282b72fc8c5599b2dad0d68beae7e2e4 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Fri, 30 Oct 2026 17:17:46 -0300 Subject: [PATCH 3/3] Apply fixed Coderabbitai review. --- .github/workflows/build.yml | 2 +- src/Asset/DenoManager.php | 2 +- src/Converter/SemverConverter.php | 4 ++-- src/Converter/VersionConverterInterface.php | 2 +- src/Fallback/ComposerFallback.php | 4 +++- src/Solver/Solver.php | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 599744b..f39234e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,6 @@ jobs: phpunit: uses: php-forge/actions/.github/workflows/phpunit.yml@a574dbe95fc370db1b7b5ea546cd5ff5b70304d6 # v3.0.0 with: - php-version: '["8.3","8.4","8.5","8.6"]' + php-version: '["8.3","8.4","8.5"]' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/src/Asset/DenoManager.php b/src/Asset/DenoManager.php index e1396ed..1d92063 100644 --- a/src/Asset/DenoManager.php +++ b/src/Asset/DenoManager.php @@ -25,7 +25,7 @@ final class DenoManager extends AbstractAssetManager private const array AUDIT_ENVIRONMENT = ['NO_COLOR' => '1']; /** - * Returns the parser for Deno audit JSON output. + * Returns the parser for the human-readable text report produced by `deno audit`. */ public function getAuditParser(): AuditParserInterface { diff --git a/src/Converter/SemverConverter.php b/src/Converter/SemverConverter.php index 428a151..ad86904 100644 --- a/src/Converter/SemverConverter.php +++ b/src/Converter/SemverConverter.php @@ -12,12 +12,12 @@ use function substr; /** - * Converts asset-manager version constraints to Composer-compatible constraints. + * Converts an asset-manager version to a Composer-compatible version. */ final class SemverConverter implements VersionConverterInterface { /** - * Converts an asset-manager version constraint to Composer syntax. + * Converts an asset-manager version to Composer-compatible syntax. */ public function convertVersion(string|null $version = null): string { diff --git a/src/Converter/VersionConverterInterface.php b/src/Converter/VersionConverterInterface.php index bd229c4..bca830b 100644 --- a/src/Converter/VersionConverterInterface.php +++ b/src/Converter/VersionConverterInterface.php @@ -5,7 +5,7 @@ namespace Foxy\Converter; /** - * Converts asset-manager version constraints to Composer constraints. + * Converts an asset-manager version to a Composer-compatible version. */ interface VersionConverterInterface { diff --git a/src/Fallback/ComposerFallback.php b/src/Fallback/ComposerFallback.php index 2dac283..afd3adf 100644 --- a/src/Fallback/ComposerFallback.php +++ b/src/Fallback/ComposerFallback.php @@ -64,7 +64,9 @@ public function __construct( } /** - * Restores the captured Composer state without running project scripts. + * Restores the captured Composer state. + * + * The default installer suppresses project scripts; an injected installer retains its event-dispatcher behavior. * * @throws Exception */ diff --git a/src/Solver/Solver.php b/src/Solver/Solver.php index 48b2ae0..f2f8347 100644 --- a/src/Solver/Solver.php +++ b/src/Solver/Solver.php @@ -42,7 +42,7 @@ private const string MANAGED_MARKER = '.foxy-managed'; /** - * Creates the asset dependency solver and its rollback collaborators. + * Creates the asset dependency solver with optional fallbacks; `null` disables each corresponding rollback path. * * @param AssetManagerInterface $assetManager The asset manager instance. * @param Config $config The config instance.