Skip to content

🛠️ Create / Edit / List Models

Structures in pkg/domain used for package management (create, edit, update) and package list/popular package queries. These types serve as both API request bodies and response bodies, corresponding to Packagist's https://packagist.org/api/... write endpoints and https://packagist.org/packages/list.json read endpoint.

Type Overview

TypeRoleRequires Credentials
PackageCreateRequest / PackageCreateResponseCreate package✅ Required
PackageEditRequest / PackageEditResponseEdit package✅ Required
PackageUpdateRequest / PackageUpdateResponseUpdate package✅ Required
PackageListResponsePackage name list❌ Public
PackageListWithDataResponse / PackageDataPackage list with additional data❌ Public
PopularPackagesResponse / PopularPackagePopular package list❌ Public

Write operations require API credentials

Create/edit/update packages are write operations that require credentials via client.WithAPICredentials(username, apiToken), otherwise Packagist will reject them.


✏️ Create Package

PackageCreateRequest

go
type PackageCreateRequest struct {
    Repository string `json:"repository"`
}
FieldTypeDescription
RepositorystringRepository URL of the package to submit (e.g., https://github.com/symfony/console)

PackageCreateResponse

go
type PackageCreateResponse struct {
    Status string `json:"status"`
}
FieldTypeDescription
StatusstringOperation status (e.g., success)

📝 Edit Package

PackageEditRequest

go
type PackageEditRequest struct {
    Repository string `json:"repository"`
}
FieldTypeDescription
RepositorystringNew repository URL to replace the package's original repository address

PackageEditResponse

go
type PackageEditResponse struct {
    Status string `json:"status"`
}
FieldTypeDescription
StatusstringOperation status

Create vs Edit

  • Create (CreatePackage): Register a new package to Packagist, only need to pass repository URL.
  • Edit (EditPackage): Modify existing package's repository address, need to specify both package name (in method parameter) and new repository URL.
  • Update (UpdatePackage): Trigger Packagist to re-fetch metadata for a package, usually without changing repository address.

🔄 Update Package

PackageUpdateRequest

go
type PackageUpdateRequest struct {
    Repository string `json:"repository"`
}
FieldTypeDescription
RepositorystringPackage name or repository identifier to trigger update

PackageUpdateResponse

go
type PackageUpdateResponse struct {
    Status string   `json:"status"`
    Jobs   []string `json:"jobs,omitempty"`
}
FieldTypeDescription
StatusstringOperation status
Jobs[]stringList of triggered background job IDs (can be used to poll update progress)

📋 Package Name List

PackageListResponse

Corresponds to https://packagist.org/packages/list.json, returns all package names in Packagist.

go
type PackageListResponse struct {
    PackageNames []string `json:"packageNames"`
}
FieldTypeDescription
PackageNames[]stringPackage name list

📋 Package List with Additional Data

PackageListWithDataResponse

Corresponds to https://packagist.org/packages/list.json?fields[]=repository&fields[]=type, returns mapping from package names to additional data.

go
type PackageListWithDataResponse struct {
    Packages map[string]PackageData `json:"package"`
}
FieldTypeDescription
Packagesmap[string]PackageDataPackage info mapping, key is package name, value is additional data

PackageData

go
type PackageData struct {
    Type       string      `json:"type,omitempty"`
    Repository string      `json:"repository,omitempty"`
    Abandoned  interface{} `json:"abandoned,omitempty"`
}
FieldTypeDescription
TypestringPackage type (e.g., library)
RepositorystringRepository URL
Abandonedinterface{}Whether abandoned: can be bool (false/true) or string (recommended replacement package name)

Abandoned is a union type

Abandoned in JSON can be either boolean or string:

  • false / true: indicates not abandoned / abandoned (no replacement package)
  • "vendor/replacement": abandoned, recommends migrating to this replacement package

Therefore Go uses interface{} to receive it. Type assertion is needed when using:

go
switch v := data.Abandoned.(type) {
case bool:
    if v { fmt.Println("Abandoned, no replacement package") }
case string:
    fmt.Printf("Abandoned, replacement package: %s\n", v)
}

PopularPackagesResponse

Corresponds to https://packagist.org/explore/popular.json, returns popular package list.

go
type PopularPackagesResponse struct {
    Packages []PopularPackage `json:"packages"`
    Total    int              `json:"total"`
    Next     string           `json:"next,omitempty"`
}
FieldTypeDescription
Packages[]PopularPackagePopular package info list
TotalintTotal popular package count
NextstringNext page URL, empty if none

PopularPackage

go
type PopularPackage struct {
    Name        string `json:"name"`
    Description string `json:"description"`
    URL         string `json:"url"`
    Downloads   int    `json:"downloads"`
    Favers      int    `json:"favers"`
}
FieldTypeDescription
NamestringPackage name
DescriptionstringPackage description
URLstringPackage page URL
DownloadsintDownload count
FaversintFavorite count

Difference from SearchResult

PopularPackage fields are nearly identical to domain.SearchResult, but semantically different: former comes from "popular packages" list (sorted by favorites/downloads), latter from keyword search. Both are defined separately to allow independent evolution.


🚀 Examples

Create Package (requires credentials)

go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/scagogogo/composer-skills/pkg/client"
    "github.com/scagogogo/composer-skills/pkg/domain"
)

func main() {
    c := client.NewComposerClient(30*time.Second,
        client.WithAPICredentials("your-username", "your-api-token"),
    )

    resp, err := c.CreatePackage(context.Background(), &domain.PackageCreateRequest{
        Repository: "https://github.com/your-org/your-package",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Create result: %s\n", resp.Status)
}

List Packages with Additional Data

go
list, _ := c.ListPackagesWithData([]string{"repository", "type"})
for name, data := range list.Packages {
    fmt.Printf("%s  type=%s  repo=%s\n", name, data.Type, data.Repository)
}
go
popular, _ := c.ListPopularPackages(20)
for _, p := range popular.Packages {
    fmt.Printf("%-30s%-6d downloads:%d\n", p.Name, p.Favers, p.Downloads)
}

Released under the MIT License