📊 Statistics Models
Structures in pkg/domain describing Packagist statistics and change tracking data. They correspond to the following APIs:
https://packagist.org/statistics.json→StatisticsResponsehttps://packagist.org/packages/<vendor>/<name>/stats.json→PackageStatsResponsehttps://packagist.org/metadata/changes.json?since=<timestamp>→ChangeTrackingResponse
Type Overview
| Type | Role | Source API |
|---|---|---|
StatisticsResponse | Repository overall statistics top-level | /statistics.json |
Totals | Repository totals (downloads/packages/versions) | Nested in StatisticsResponse |
PackageStatsResponse | Single package download stats | /<package>/stats.json |
ChangeTrackingResponse | Metadata change tracking response | /metadata/changes.json |
ChangeAction | Single change action | Nested in ChangeTrackingResponse |
📈 StatisticsResponse
Top-level structure for Packagist repository overall statistics.
type StatisticsResponse struct {
Totals Totals `json:"totals"`
}| Field | Type | Description |
|---|---|---|
Totals | Totals | Repository total statistics, see next section |
🔢 Totals
Repository overall statistics.
type Totals struct {
Downloads int64 `json:"downloads"`
Packages int `json:"packages"`
Versions int `json:"versions"`
}| Field | Type | Description | Example |
|---|---|---|---|
Downloads | int64 | Total download count for all packages | 10000000000 |
Packages | int | Total package count in repository | 300000 |
Versions | int | Total version count for all packages | 2500000 |
Downloads uses int64
Whole-site downloads can reach tens of billions, exceeding 32-bit int range, so Downloads explicitly uses int64. Do not assign to int variables when accumulating or comparing (will overflow on 32-bit platforms).
📉 PackageStatsResponse
Single package download statistics. Downloads field reuses PackageDownloads type from package.md.
type PackageStatsResponse struct {
Downloads PackageDownloads `json:"downloads"`
Versions []string `json:"versions"`
Date string `json:"date"`
}| Field | Type | Description |
|---|---|---|
Downloads | PackageDownloads | Download stats (Total/Monthly/Daily) |
Versions | []string | Available version list |
Date | string | Statistics start date |
🔄 ChangeTrackingResponse
Metadata change tracking response. Used for incremental sync: pass a since timestamp, Packagist returns all package change actions after that time point.
type ChangeTrackingResponse struct {
Error string `json:"error,omitempty"`
Timestamp int64 `json:"timestamp"`
Actions []ChangeAction `json:"actions,omitempty"`
}| Field | Type | Description |
|---|---|---|
Error | string | Error message returned when since parameter missing or invalid (empty when normal) |
Timestamp | int64 | Current response timestamp (Unix seconds) |
Actions | []ChangeAction | Change action list, see next section |
Incremental sync mode
Typical usage: locally save last Timestamp, pass it as since next request, gradually catch up with update/delete in Actions. If Error is non-empty, means since expired or invalid, need full resync.
⚡ ChangeAction
Single change action.
type ChangeAction struct {
Type string `json:"type"`
Package string `json:"package"`
Time int64 `json:"time"`
}| Field | Type | Description | Value |
|---|---|---|---|
Type | string | Action type | "update" or "delete" |
Package | string | Package name being operated on | symfony/console |
Time | int64 | Unix timestamp when action occurred | 1700000000 |
🚀 Examples
Repository Total Statistics
package main
import (
"fmt"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
func main() {
c := client.NewComposerClient(30 * time.Second)
stats, err := c.GetStatistics()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total packages: %d\n", stats.Totals.Packages)
fmt.Printf("Total versions: %d\n", stats.Totals.Versions)
fmt.Printf("Total downloads: %d\n", stats.Totals.Downloads)
}Single Package Download Stats
stats, _ := c.GetPackageStats("monolog/monolog")
fmt.Printf("Today downloads: %d, this month: %d, total: %d\n",
stats.Downloads.Daily, stats.Downloads.Monthly, stats.Downloads.Total)Incremental Change Tracking
package main
import (
"context"
"fmt"
"log"
"github.com/scagogogo/composer-skills/pkg/client"
)
func main() {
c := client.NewComposerClient(60)
var since int64 = 1700000000 // Last sync timestamp
changes, err := c.GetPackageChanges(context.Background(), since)
if err != nil {
log.Fatal(err)
}
if changes.Error != "" {
log.Fatalf("API returned error: %s (since may be expired, need full resync)", changes.Error)
}
fmt.Printf("Current timestamp: %d, change count: %d\n", changes.Timestamp, len(changes.Actions))
for _, a := range changes.Actions {
fmt.Printf(" [%s] %s @ %d\n", a.Type, a.Package, a.Time)
}
// Next time use changes.Timestamp as new since
}📚 Related Documentation
- 🔙 Back to Domain Overview
- 📦
PackageDownloadsdefinition → package.md - 🛠️ Create/list models → create-package.md