Help us shape the future of Go! Take the 2026 Go Developer Survey

smapiweb

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 18 Imported by: 0

README

go-smapiweb

Go


A client for the web services of the Stardew Valley modding toolchain: smapi.io's mod update API and log parser, the SMAPI compatibility list and the StardewModDataset. Package smapiweb, module github.com/Rethunk-Tech/go-smapiweb.

The services publish no client for Go and answer in shapes that drift. This package sends the requests, bounds every body it reads, and parses leniently. It caches nothing: the Updates result says which mods were answered, so a caller keeps its own cache and decides when an answer is too old. The JSON tags on the result and dataset types are a format contract, so cached copies survive an upgrade.

Quick start

go get github.com/Rethunk-Tech/go-smapiweb

A worked example and the contracts: HUMANS.md.

Features

  • Updates: newest and unofficial versions and compatibility for any number of mods, batched, with per-mod Known.
  • CompatList: the SMAPI compatibility list in every shape it has been published in, with Lookup and BrokenOn.
  • Index and Page: which Nexus pages ship a UniqueID, and a page's files, manifests and dependencies.
  • UploadLog: share a SMAPI log through smapi.io and get its public link.
  • Every response is read under a size cap and a deadline.

Documentation

Doc Audience
HUMANS.md Running and using the package
AGENTS.md Internals, invariants, and why
CONTRIBUTING.md Process — commits, tests, docs
SECURITY.md Vulnerability disclosure
CHANGELOG.md Release notes

License

MIT — see LICENSE. © 2026 Rethunk.Tech, LLC.

Documentation

Overview

Package smapiweb is a client for the web services of the Stardew Valley modding toolchain: smapi.io's mod update API and log parser, the SMAPI compatibility list, and the StardewModDataset.

It keeps no state and makes no policy: no cache, no logging, no backoff between calls. It sends requests, bounds what it reads, and parses leniently, so callers decide what to remember and when to ask again.

Index

Constants

View Source
const (
	StatusOK         = "ok"
	StatusOptional   = "optional"
	StatusUnofficial = "unofficial"
	StatusBroken     = "broken"
	StatusObsolete   = "obsolete"
	StatusAbandoned  = "abandoned"
)

Variables

View Source
var (
	// ErrEmptyLog is UploadLog's answer to text with nothing in it. No request is sent.
	ErrEmptyLog = errors.New("the log is empty")
	// ErrLogTooLarge is UploadLog's answer to text over maxBytes. No request is sent.
	ErrLogTooLarge = errors.New("the log is too large to upload")
	// ErrLogRejected means the parser answered 200 with its page instead of a redirect, which is how it refuses a log.
	ErrLogRejected = errors.New("smapi.io did not accept the log")
	// ErrNoLogLink means the parser redirected somewhere that is not a /log/<id> page.
	ErrNoLogLink = errors.New("smapi.io did not return a log link")
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// HTTP is the client every request goes through; nil means http.DefaultClient. UploadLog adds httpkit.NoFollow
	// on a copy, so the client is never modified.
	HTTP *http.Client
	// UpdatesURL is the mod update API; "" means smapi.io's v4.0.0 endpoint.
	UpdatesURL string
	// CompatURL is the compatibility list; "" means SmapiCompatibilityList's data/mods.jsonc on its develop branch.
	CompatURL string
	// IndexURL is the dataset's page-by-UniqueID index; "" means StardewModDataset's on its main branch.
	IndexURL string
	// PageBase is the folder holding the dataset's Nexus pages; "" means StardewModDataset's on its main branch.
	PageBase string
	// LogURL is the log parser; "" means smapi.io's.
	LogURL string
}

Client talks to smapi.io and the two GitHub-hosted datasets. The zero value uses the public endpoints and http.DefaultClient. It holds no state, so one Client is safe for concurrent use and a copy is as good as the original.

func (*Client) CompatList

func (c *Client) CompatList(ctx context.Context) (CompatIndex, error)

CompatList fetches and parses the SMAPI compatibility list. It accepts every shape the list has been published in: a bare array, {"mods"} or {"data"}, with the status nested, flat or a string.

func (*Client) Index

func (c *Client) Index(ctx context.Context) (map[string][]Ref, error)

Index returns the dataset's index of which pages ship which mod, keyed by lowercased UniqueID. Entries that are not "Site:number" are skipped; an index with no usable entry is an error.

func (*Client) Page

func (c *Client) Page(ctx context.Context, id int) (Page, error)

Page returns Nexus mod page id's files. A page the dataset lacks is an empty Page and a nil error: the dataset answered, it just does not know the page.

func (*Client) Updates

func (c *Client) Updates(ctx context.Context, req UpdateRequest) ([]UpdateResult, error)

Updates asks about req.Mods in batches of 100, four at a time, and asks a failed batch once more in two halves. Results are in request order, one per mod, and each result's ID is the request's ID, not the API's casing. A mod whose batch was answered is Known (a mod the API does not list is Known with no Suggested); a mod whose batch failed, or was not sent because an earlier batch failed, has only its ID. err is nil when every batch was answered; otherwise it is errors.Join of the failed batches' errors in batch order, so a caller that needs one error takes the first.

func (*Client) UploadLog

func (c *Client) UploadLog(ctx context.Context, text string, maxBytes int) (string, error)

UploadLog posts text to the SMAPI log parser as the form field input and returns the public link, https://<host>/log/<id>. Success is a redirect to /log/<id>; the parser answers a failed or empty upload with HTTP 200 and its page, which is ErrLogRejected. Any other status is a *httpkit.StatusError. maxBytes caps len(text); 0 means no cap. The link is rebuilt from the redirect's scheme and host with the id path-escaped.

type CompatEntry

type CompatEntry struct {
	Status        string `json:"status"`
	Summary       string `json:"summary"`
	BrokeIn       string `json:"brokeIn"`
	UnofficialURL string `json:"unofficialUrl"`
	// UnofficialVersion is the version of the unofficial update, so an install at or past it is not flagged.
	UnofficialVersion string `json:"unofficialVersion"`
	Replacement       string `json:"replacement"`
}

CompatEntry is one SMAPI compatibility-list row, keyed later by UniqueID and Nexus id.

func (CompatEntry) BrokenOn

func (e CompatEntry) BrokenOn(gameVersion string) bool

BrokenOn reports whether a broken entry applies to this Stardew Valley version. A mod that broke in a later game version (a beta the player does not run) is not broken for them; a break by anything else (SMAPI, Harmony), or an unknown version on either side, counts.

type CompatIndex

type CompatIndex struct {
	ByID    map[string]CompatEntry `json:"ByID"`
	ByNexus map[int]CompatEntry    `json:"ByNexus"`
	// NexusToID and GitHubToID (repo lowercased) name the UniqueID a Nexus page or GitHub repository publishes, which
	// ties one mod's pages on different sites together.
	NexusToID  map[int]string    `json:"NexusToID"`
	GitHubToID map[string]string `json:"GitHubToID"`
}

CompatIndex maps UniqueID (lowercased) and Nexus page id onto the same entry.

func (CompatIndex) Lookup

func (idx CompatIndex) Lookup(uniqueID string, nexusID int) (CompatEntry, bool)

Lookup finds the entry for uniqueID (ignoring case), else for Nexus page nexusID; either may be zero.

type Dependency

type Dependency struct {
	UniqueID       string `json:"uniqueId"`
	MinimumVersion string `json:"minimumVersion"`
	Required       bool   `json:"required"`
}

Dependency is a Dependencies[] entry, or the ContentPackFor framework as a required one.

type File

type File struct {
	ID          int64  `json:"id"`
	Type        string `json:"type"`
	Version     string `json:"version"`
	FileName    string `json:"fileName"`
	SizeInBytes int64  `json:"sizeInBytes"`
	Mods        []Mod  `json:"mods"`
}

File is one downloadable file; its Type is Nexus' category (Main, Optional, ...). FileName is empty when the dataset gave a storage path ("4a/35/fc/<uuid>"), which names nothing a user or Nexus would recognise.

type InstalledMod

type InstalledMod struct {
	ID         string   `json:"id"`
	UpdateKeys []string `json:"updateKeys,omitempty"`
	Version    string   `json:"installedVersion,omitempty"`
}

InstalledMod is one mod to check.

type Mod

type Mod struct {
	UniqueID     string       `json:"uniqueId"`
	Name         string       `json:"name"`
	Version      string       `json:"version"`
	UpdateKeys   []string     `json:"updateKeys"`
	Dependencies []Dependency `json:"dependencies"`
}

Mod is a SMAPI manifest found inside a file.

type Page

type Page struct {
	ID        int    `json:"id"`
	Name      string `json:"name"`
	Author    string `json:"author"`
	PageURL   string `json:"pageUrl"`
	Version   string `json:"version"`
	Downloads []File `json:"downloads"`
}

Page is one Nexus mod page's current files as the dataset records them.

type Ref

type Ref struct {
	Site string `json:"site"`
	ID   int    `json:"id"`
}

Ref names a mod page: Site is "Nexus" or "CurseForge", as the dataset names it. Its JSON tags are a format contract: callers cache the encoding.

type Update

type Update struct {
	Version string `json:"version"`
	URL     string `json:"url"`
	Source  string `json:"source,omitempty"`
}

Update is SMAPI's suggested newer version.

type UpdateRequest

type UpdateRequest struct {
	APIVersion  string
	GameVersion string
	Platform    string // Android, Linux, Mac or Windows
	Mods        []InstalledMod
}

UpdateRequest describes the installed environment and the mods to check.

type UpdateResult

type UpdateResult struct {
	ID                   string  `json:"id"`
	Known                bool    `json:"known"`
	Suggested            *Update `json:"suggested,omitempty"`
	Unofficial           *Update `json:"unofficial,omitempty"`
	Compatibility        string  `json:"compatibility,omitempty"`
	CompatibilitySummary string  `json:"compatibilitySummary,omitempty"`
	BrokeIn              string  `json:"brokeIn,omitempty"`
	GitHubRepo           string  `json:"gitHubRepo,omitempty"`
}

UpdateResult is smapi.io's answer for one mod. Known is false when the mod's batch got no answer; a Known result with no Suggested has no update. Its JSON tags are a format contract: callers cache the encoding.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL