🏗️ Repository Layer
pkg/repository is a lower-level HTTP call implementation for Packagist API. It directly interfaces with Packagist REST endpoints, returning deserialized domain models.
When to Use
- You need finer-grained control (custom
context.Context, proxy). - You want to call Packagist endpoints directly, without going through
pkg/client.ComposerClient. - You're building package mirror or index download service.
pkg/client and pkg/repository are two parallel Packagist access implementations.
ComposerClient initiates HTTP requests itself (based on repoURL/baseURL), covering complete high-level API; Repository provides context-based lower-level methods. For daily development use Client, when needing context pass-through or proxy control use Repository layer.
Core Types
Repository
type Repository struct {
options *Options
}Repository holds Options (server address, proxy), all methods initiate HTTP requests through it. Since options field is unexported, external packages construct with zero value literal (consistent with usage in examples/):
repo := &repository.Repository{}After construction you can call various methods — methods take complete endpoint URL as parameters (e.g., Statistics internally requests https://packagist.org/statistics.json), don't depend on options server address.
About Proxy
Repository.getBytes enables proxy when options.Proxy is non-empty, but options field is unexported, external currently cannot set proxy directly. If you need proxy, please use pkg/client (supports WithRepoURL etc. options), or construct within package via test helper.
Method List
| Method | Signature | Description |
|---|---|---|
Statistics | (ctx context.Context) (*domain.StatisticsResponse, error) | Get Packagist global statistics |
List | (ctx context.Context) ([]*Package, error) | List all package names |
ListSecurityAdvisories | (ctx context.Context, updatedSince time.Time) (*domain.AdvisoriesResponse, error) | List security advisories updated after specified time |
ListAdvisories | (ctx context.Context, packageName string) ([]*domain.Advisory, error) | List security advisories for a package |
DownloadIndex | (ctx context.Context) ([]byte, error) | Download package index raw bytes |
DownloadIndexToFile | (ctx context.Context, filepath string) error | Download package index and write to file |
Package
type Package struct {
Name string
}Lightweight package model returned by List, only contains package name.
Example
package main
import (
"context"
"fmt"
"time"
"github.com/scagogogo/composer-skills/pkg/repository"
)
func main() {
repo := &repository.Repository{}
// Global statistics
stats, _ := repo.Statistics(context.Background())
fmt.Printf("Total packages: %d\n", stats.Totals.Packages)
// Incremental security advisories (last 24 hours)
since := time.Now().Add(-24 * time.Hour)
advs, _ := repo.ListSecurityAdvisories(context.Background(), since)
fmt.Printf("Last 24h advisories: %d\n", len(advs.Advisories))
// Download package index to local file
_ = repo.DownloadIndexToFile(context.Background(), "/tmp/packagist-index.json")
}Internal Mechanism
Repository internally uses generic function getJson[T] to unify request and deserialization:
func getJson[T any](ctx context.Context, repository *Repository, targetUrl string) (T, error)Therefore adding a new endpoint only requires declaring return type and reusing getJson, no need for repetitive HTTP boilerplate code.
Difference between ListAdvisories and ListSecurityAdvisories
ListSecurityAdvisories incrementally fetches all advisories by update time; ListAdvisories queries single package's advisories by package name.
Advanced Topics
- Custom proxy and server address: see Options.
- Index download commonly used for building Package Mirror.
- High-level facade comparison: see Client.