Skip to content

🏗️ 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

go
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/):

go
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

MethodSignatureDescription
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) errorDownload package index and write to file

Package

go
type Package struct {
    Name string
}

Lightweight package model returned by List, only contains package name.

Example

go
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:

go
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.

Released under the MIT License