{"id":30886673,"url":"https://github.com/smnandre/packapi","last_synced_at":"2025-09-08T13:08:34.616Z","repository":{"id":313580212,"uuid":"1051918929","full_name":"smnandre/packapi","owner":"smnandre","description":null,"archived":false,"fork":false,"pushed_at":"2025-09-07T02:14:57.000Z","size":137,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-07T04:16:52.983Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smnandre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["smnandre"]}},"created_at":"2025-09-07T02:12:04.000Z","updated_at":"2025-09-07T04:14:16.000Z","dependencies_parsed_at":"2025-09-07T04:28:08.528Z","dependency_job_id":null,"html_url":"https://github.com/smnandre/packapi","commit_stats":null,"previous_names":["smnandre/packapi"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/smnandre/packapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnandre%2Fpackapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnandre%2Fpackapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnandre%2Fpackapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnandre%2Fpackapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smnandre","download_url":"https://codeload.github.com/smnandre/packapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnandre%2Fpackapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274130227,"owners_count":25227272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-08T13:08:31.871Z","updated_at":"2025-09-08T13:08:34.608Z","avatar_url":"https://github.com/smnandre.png","language":"PHP","funding_links":["https://github.com/sponsors/smnandre"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003e\u003cimg src=\".github/packapi.png\" alt=\"Pack API\" width=\"100%\" /\u003e\u003c/h1\u003e\n\n\u0026nbsp; ![PHP Version](https://img.shields.io/badge/PHP-8.3+-7A8593?logoColor=7A8593\u0026labelColor=161406)\n\u0026nbsp; ![CI](https://img.shields.io/github/actions/workflow/status/smnandre/packapi/CI.yaml?branch=main\u0026label=Tests\u0026labelColor=161406\u0026color=7A8593)\n\u0026nbsp; ![Release](https://img.shields.io/github/v/release/smnandre/packapi?label=Stable\u0026labelColor=161406\u0026color=7A8593)\n\u0026nbsp; [![GitHub Sponsors](https://img.shields.io/github/sponsors/smnandre?logo=githubsponsors\u0026logoColor=7A8593\u0026label=%20Sponsor\u0026labelColor=161406\u0026color=7A8593)](https://github.com/sponsors/smnandre)\n\u0026nbsp; ![License](https://img.shields.io/github/license/smnandre/packapi?label=License\u0026labelColor=161406\u0026color=7A8593)\n\n\u003c/div\u003e\n\nGet insights from **Composer**, **NPM**, **GitHub**, and more via a unified, strongly‑typed API.\n\n\n## Features\n\n- **Multi‑ecosystem**: Composer, NPM, GitHub, jsDelivr, OSV, BundlePhobia\n- **Analyses**: Metadata, downloads, security, activity, quality\n- **Strong typing**: PHP 8.3+ with strict types\n- **Extensible**: Provider/factory architecture\n- **Well‑tested**: Extensive automated test suite\n- **Lean deps**: Symfony components and PSR interfaces\n- **HTTP/3 (QUIC)** support with graceful fallback\n\n## Quick Start\n\n### Installation\n\n```bash\ncomposer require smnandre/packapi\n```\n\n### Basic Usage\n\n```php\nuse PackApi\\Bridge\\Packagist\\PackagistProviderFactory;\nuse PackApi\\Http\\HttpClientFactory;\nuse PackApi\\Inspector\\{MetadataInspector, DownloadStatsInspector};\nuse PackApi\\Package\\ComposerPackage;\n\n$http = new HttpClientFactory();\n$packagist = new PackagistProviderFactory($http);\n\n$metadata = new MetadataInspector([\n    $packagist-\u003ecreateMetadataProvider(),\n]);\n$downloads = new DownloadStatsInspector([\n    $packagist-\u003ecreateStatsProvider(),\n]);\n\n$package = new ComposerPackage('symfony/console');\n$meta = $metadata-\u003egetMetadata($package);\n$stats = $downloads-\u003egetStats($package);\n\necho 'Package: '.($meta?-\u003ename ?? 'N/A').\"\\n\";\necho 'Monthly downloads: '.($stats?-\u003eget('monthly')?-\u003egetCount() ?? 'N/A').\"\\n\";\n```\n\nFor activity, content, quality, and OSV security, add the relevant provider factories (GitHub, jsDelivr, OSV) and pass them to the corresponding inspectors.\n\n## Supported Package Types\n\n| Ecosystem    | Package Type      | Metadata | Downloads | Security | Activity | Content | Bundle Size |\n|--------------|-------------------|----------|-----------|----------|----------|---------|-------------|\n| **Composer** | `ComposerPackage` | Yes      | Yes       | Yes      | Yes      | Yes     | No          |\n| **NPM**      | `NpmPackage`      | Yes      | Yes       | Yes      | Yes      | Yes     | Yes         |\n| **GitHub**   | Any with repo URL | Yes      | Yes       | Yes      | Yes      | Yes     | No          |\n\n## Usage Examples\n\n### Package Metadata Analysis\n\n```php\nuse PackApi\\Inspector\\MetadataInspector;\nuse PackApi\\Bridge\\Packagist\\PackagistProviderFactory;\nuse PackApi\\Package\\ComposerPackage;\n\n$factory = new PackagistProviderFactory($httpClient);\n$inspector = new MetadataInspector([\n    $factory-\u003ecreateMetadataProvider()\n]);\n\n$package = new ComposerPackage('laravel/framework');\n$metadata = $inspector-\u003egetMetadata($package);\n\necho $metadata-\u003egetName() . \"\\n\";        // laravel/framework  \necho $metadata-\u003egetDescription() . \"\\n\"; // The Laravel Framework\necho $metadata-\u003egetLicense() . \"\\n\";     // MIT\necho $metadata-\u003egetRepository() . \"\\n\";  // https://github.com/laravel/framework\n```\n\n### Download Statistics\n\n```php\nuse PackApi\\Inspector\\DownloadStatsInspector;\nuse PackApi\\Package\\NpmPackage;\n\n$inspector = new DownloadStatsInspector([\n    $npmFactory-\u003ecreateDownloadStatsProvider(),\n    $packagistFactory-\u003ecreateStatsProvider()\n]);\n\n$package = new NpmPackage('react');\n$stats = $inspector-\u003egetStats($package);\n\n$monthly = $stats-\u003eget('monthly');\nif ($monthly) {\n    echo \"Downloads this month: \" . number_format($monthly-\u003egetCount()) . \"\\n\";\n    $days = $monthly-\u003egetEnd()-\u003ediff($monthly-\u003egetStart())-\u003edays + 1;\n    echo \"Daily average: \" . number_format($monthly-\u003egetCount() / $days) . \"\\n\";\n}\n```\n\n### Security Advisory Scanning\n\n```php\nuse PackApi\\Inspector\\SecurityInspector;\nuse PackApi\\Bridge\\OSV\\OSVProviderFactory;\nuse PackApi\\Bridge\\GitHub\\GitHubProviderFactory;\n\n$inspector = new SecurityInspector([\n    $osvFactory-\u003ecreateSecurityProvider(),      // OSV Database\n    $githubFactory-\u003ecreateSecurityProvider()   // GitHub Security Advisories\n]);\n\n$advisories = $inspector-\u003egetSecurityAdvisories($package);\n\nforeach ($advisories as $advisory) {\n    echo \"ALERT: {$advisory-\u003egetTitle()}\\n\";\n    echo \"   Severity: {$advisory-\u003egetSeverity()}\\n\";\n    echo \"   Link: {$advisory-\u003egetLink()}\\n\\n\";\n}\n```\n\n### Project Activity Analysis\n\n```php\nuse PackApi\\Inspector\\ActivityInspector;\n\n$inspector = new ActivityInspector([\n    $githubFactory-\u003ecreateActivityProvider()\n]);\n\n$activity = $inspector-\u003egetActivitySummary($package);\n\necho \"Last commit: \" . $activity-\u003egetLastCommit()?-\u003eformat('Y-m-d') . \"\\n\";\necho \"Contributors: \" . $activity-\u003egetContributors() . \"\\n\";\necho \"Open issues: \" . $activity-\u003egetOpenIssues() . \"\\n\";\necho \"Latest release: \" . $activity-\u003egetLastRelease() . \"\\n\";\n```\n\n### Package Content Analysis\n\n```php\nuse PackApi\\Inspector\\ContentInspector;\n\n$inspector = new ContentInspector([\n    $jsDelivrFactory-\u003ecreateContentProvider(),\n    $githubFactory-\u003ecreateContentProvider()\n]);\n\n$content = $inspector-\u003egetContentOverview($package);\n\necho \"Files: \" . $content-\u003egetFileCount() . \"\\n\";\necho \"Total size: \" . number_format($content-\u003egetTotalSize()) . \" bytes\\n\";\necho \"Has README: \" . ($content-\u003ehasReadme() ? 'Yes' : 'No') . \"\\n\";\necho \"Has tests: \" . ($content-\u003ehasTests() ? 'Yes' : 'No') . \"\\n\";\n```\n\n### Bundle Size Analysis (NPM)\n\n```php\nuse PackApi\\Bridge\\BundlePhobia\\BundlePhobiaProviderFactory;\n$httpFactory = new HttpClientFactory();\n$factory = new BundlePhobiaProviderFactory($httpFactory);\n$sizeProvider = $factory-\u003ecreateBundleSizeProvider();\n\n$package = new NpmPackage('lodash');\n$bundleSize = $sizeProvider-\u003egetBundleSize($package);\n\nif ($bundleSize) {\n    echo \"Bundle size: \" . $bundleSize-\u003egetFormattedSize() . \"\\n\";\n    echo \"Gzipped: \" . $bundleSize-\u003egetFormattedGzipSize() . \"\\n\";\n    echo \"Dependencies: \" . $bundleSize-\u003egetDependencyCount() . \"\\n\";\n}\n```\n\n## Configuration\n\n### HTTP/3 (QUIC)\n\nPackApi supports HTTP/3 for improved performance:\n\n```php\nuse PackApi\\Http\\HttpClientFactory;\n\n$httpFactory = new HttpClientFactory();\n$client = $httpFactory-\u003ecreateClient([\n    'enable_quic' =\u003e true  // Automatic fallback if not supported\n]);\n```\n\n### Caching\n\nEnable HTTP caching at the Symfony HTTP client level (e.g., `CachingHttpClient` with an HttpKernel `Store`). PackApi does not require a separate configuration object.\n\n### Logging\n\nPass a PSR‑3 logger to `HttpClientFactory` to log outgoing requests in examples and providers.\n\n### GitHub Authentication\n\nFor higher GitHub rate limits, provide a token:\n\n```php\n// Via environment variable\n$_ENV['GITHUB_TOKEN'] = 'ghp_your_token_here';\n\n// Pass the token to GitHubProviderFactory when creating providers\n// $github = new GitHubProviderFactory($httpFactory, $_ENV['GITHUB_TOKEN'] ?? null);\n```\n\n## Architecture\n\nPackApi uses a clean, extensible architecture:\n\n### Core Components\n\n- **Packages**: Represent different package types (`ComposerPackage`, `NpmPackage`)\n- **Inspectors**: Analyze specific aspects (metadata, downloads, security, etc.)\n- **Providers**: Fetch data from external sources (Packagist, GitHub, NPM, etc.)\n- **Models**: Strongly-typed value objects for results\n- **Builder**: Fluent API for configuration\n\n### Provider Pattern\n\nEach inspector accepts one or more providers from the corresponding factory. Providers are tried in order until one succeeds.\n\n```php\n$security = new SecurityInspector([\n    $osvFactory-\u003ecreateSecurityProvider(),\n    $githubFactory-\u003ecreateSecurityProvider(),\n    $packagistFactory-\u003ecreateSecurityProvider(),\n]);\n\n$advisories = $security-\u003egetSecurityAdvisories($package);\n```\n\n## Testing\n\nPackApi has comprehensive test coverage:\n\n```bash\n# Run tests\ncomposer test\n\n# Run with coverage\ncomposer test-coverage\n\n# Check code style\ncomposer cs\n\n# Fix code style\ncomposer cs-fix\n```\n\n## Examples\n\nRun the sample scripts in `examples/` to try PackApi quickly:\n\n- `examples/metadata-analysis.php`: print package metadata\n- `examples/download-stats-analysis.php`: print download periods\n- `examples/content-analysis.php`: analyze files and flags\n- `examples/activity-analysis.php`: summarize repo activity (set `GITHUB_TOKEN` for richer data)\n- `examples/security-analysis.php`: list security advisories (OSV/GitHub)\n- `examples/bundlephobia-size.php`: show NPM bundle sizes\n- `examples/all-analysis.php`: run a combined analysis with sensible fallbacks\n\nUsage:\n\n```bash\nphp examples/metadata-analysis.php\nphp examples/all-analysis.php\n```\n\n### Adding New Providers\n\n```php\n// 1. Implement the provider interface\nclass MyCustomProvider implements MetadataProviderInterface \n{\n    public function supports(Package $package): bool { /* ... */ }\n    public function getMetadata(Package $package): ?Metadata { /* ... */ }\n}\n\n// 2. Create a factory\nclass MyCustomProviderFactory \n{\n    public function createMetadataProvider(): MyCustomProvider \n    {\n        return new MyCustomProvider($this-\u003ehttpClient);\n    }\n}\n\n// 3. Use in inspector\n$inspector = new MetadataInspector([\n    new MyCustomProvider($httpClient)\n]);\n```\n\n## Requirements\n\n- **PHP 8.3+** (uses modern PHP features)\n- **ext-json** (for API responses)\n- **ext-curl** (for HTTP requests)\n- **ext-mbstring** (for string handling)\n\n### Optional\n\n- **ext-curl with HTTP/3** (for QUIC support)\n- **Redis/Memcached** (for distributed caching)\n\n---\n\n## Contributing\n\nContributions are welcome! Please start by creating an issue to discuss your changes.\n\n---\n\n## Credits\n\nCreated and maintained by [Simon André](https://github.com/smnandre).\n\n\u003e [!TIP]\n\u003e This library is developed and maintained by a single developer in their free time.\n\u003e\n\u003e To ensure continued maintenance and improvements, consider [sponsoring development](https://github.com/sponsors/smnandre).\n\n---\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmnandre%2Fpackapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmnandre%2Fpackapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmnandre%2Fpackapi/lists"}