Documentation
¶
Overview ¶
Package custom implements user-defined mod sources configured declaratively via YAML files in <configDir>/sources/. See the design doc: docs/plans/2026-07-13-custom-sources-design.md
Index ¶
- Constants
- func New(def SourceDefinition) (source.ModSource, error)
- type API
- func (a *API) AuthURL() string
- func (a *API) Capabilities() source.Capabilities
- func (a *API) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
- func (a *API) DownloadHeaders(fileURL string) map[string]string
- func (a *API) ExchangeToken(ctx context.Context, code string) (*source.Token, error)
- func (a *API) GetDependencies(ctx context.Context, mod *domain.Mod) ([]domain.ModReference, error)
- func (a *API) GetDownloadURL(ctx context.Context, mod *domain.Mod, fileID string) (string, error)
- func (a *API) GetMod(ctx context.Context, gameID, modID string) (*domain.Mod, error)
- func (a *API) GetModFiles(ctx context.Context, mod *domain.Mod) ([]domain.DownloadableFile, error)
- func (a *API) ID() string
- func (a *API) IsAuthenticated() bool
- func (a *API) Name() string
- func (a *API) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
- func (a *API) SetAPIKey(key string)
- func (a *API) TypeLabel() string
- type APIConfig
- type APIEndpoints
- type APIKeyConfig
- type APIMappings
- type AuthConfig
- type Directory
- func (d *Directory) AuthURL() string
- func (d *Directory) Capabilities() source.Capabilities
- func (d *Directory) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
- func (d *Directory) ExchangeToken(ctx context.Context, code string) (*source.Token, error)
- func (d *Directory) GetDependencies(ctx context.Context, mod *domain.Mod) ([]domain.ModReference, error)
- func (d *Directory) GetDownloadURL(ctx context.Context, mod *domain.Mod, fileID string) (string, error)
- func (d *Directory) GetMod(ctx context.Context, gameID, modID string) (*domain.Mod, error)
- func (d *Directory) GetModFiles(ctx context.Context, mod *domain.Mod) ([]domain.DownloadableFile, error)
- func (d *Directory) ID() string
- func (d *Directory) Name() string
- func (d *Directory) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
- func (d *Directory) TypeLabel() string
- type DirectoryConfig
- type EndpointConfig
- type Manifest
- func (m *Manifest) AuthURL() string
- func (m *Manifest) Capabilities() source.Capabilities
- func (m *Manifest) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
- func (m *Manifest) DownloadHeaders(fileURL string) map[string]string
- func (m *Manifest) ExchangeToken(ctx context.Context, code string) (*source.Token, error)
- func (m *Manifest) GetDependencies(ctx context.Context, mod *domain.Mod) ([]domain.ModReference, error)
- func (m *Manifest) GetDownloadURL(ctx context.Context, mod *domain.Mod, fileID string) (string, error)
- func (m *Manifest) GetMod(ctx context.Context, gameID, modID string) (*domain.Mod, error)
- func (m *Manifest) GetModFiles(ctx context.Context, mod *domain.Mod) ([]domain.DownloadableFile, error)
- func (m *Manifest) ID() string
- func (m *Manifest) IsAuthenticated() bool
- func (m *Manifest) Name() string
- func (m *Manifest) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
- func (m *Manifest) SetAPIKey(key string)
- func (m *Manifest) TypeLabel() string
- type ManifestConfig
- type SourceDefinition
Constants ¶
const ( TypeDirectory = "directory" TypeManifest = "manifest" TypeAPI = "api" )
Source type identifiers for SourceDefinition.Type.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(def SourceDefinition) (source.ModSource, error)
New constructs a ModSource from a validated definition. The default case is defensive: SourceDefinition.Validate already rejects unknown type strings, so it should be unreachable in practice, but New still returns a clear error instead of panicking if it is ever reached.
Types ¶
type API ¶ added in v1.9.0
type API struct {
// contains filtered or unexported fields
}
API is a ModSource backed by a declaratively-described GET+JSON REST API (design §4). Endpoints that the definition omits surface as ErrNotSupported capability gaps rather than errors at load time.
func NewAPI ¶ added in v1.9.0
func NewAPI(def SourceDefinition) (*API, error)
NewAPI constructs an api source from a validated definition. It performs no I/O — a valid definition always registers; request problems surface as operation errors.
func (*API) AuthURL ¶ added in v1.9.0
AuthURL implements source.ModSource; api sources use API keys, not OAuth.
func (*API) Capabilities ¶ added in v1.9.0
func (a *API) Capabilities() source.Capabilities
Capabilities implements source.CapabilityReporter: an undefined endpoint is an unsupported capability (design §4/§7).
func (*API) CheckUpdates ¶ added in v1.9.0
func (a *API) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
CheckUpdates implements source.ModSource generically via get_mod + version comparison (design §4). Per-mod fetch failures are collected and returned alongside partial results so a single flaky mod page doesn't hide the rest — and doesn't get silently skipped either.
func (*API) DownloadHeaders ¶ added in v1.9.0
DownloadHeaders implements source.DownloadHeaderProvider: header-mode keys go only to downloads on the API's own origin (design §9).
func (*API) ExchangeToken ¶ added in v1.9.0
ExchangeToken implements source.ModSource.
func (*API) GetDependencies ¶ added in v1.9.0
GetDependencies implements source.ModSource; always unsupported in v1 (design §4).
func (*API) GetDownloadURL ¶ added in v1.9.0
GetDownloadURL implements source.ModSource via the download_url endpoint. Query-mode keys are appended only for same-origin download URLs (design §9).
func (*API) GetMod ¶ added in v1.9.0
GetMod implements source.ModSource via the get_mod endpoint. gameID feeds the {game_id} placeholder and is echoed onto the returned mod for downstream attribution (the persisted row is normalized by the installer).
func (*API) GetModFiles ¶ added in v1.9.0
GetModFiles implements source.ModSource via the mod_files endpoint.
func (*API) IsAuthenticated ¶ added in v1.9.0
IsAuthenticated reports whether an API key is configured.
func (*API) Search ¶ added in v1.9.0
func (a *API) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
Search implements source.ModSource by executing the search endpoint template and mapping the results (design §4). An undefined search endpoint is an unsupported capability.
type APIConfig ¶
type APIConfig struct {
BaseURL string `yaml:"base_url"`
PageStart *int `yaml:"page_start"` // nil = default 1; explicit 0 respected
Auth *AuthConfig `yaml:"auth"`
Endpoints APIEndpoints `yaml:"endpoints"`
Mappings APIMappings `yaml:"mappings"`
}
APIConfig configures a declarative REST source (expanded in Phase 4).
type APIEndpoints ¶ added in v1.9.0
type APIEndpoints struct {
Search *EndpointConfig `yaml:"search"`
GetMod *EndpointConfig `yaml:"get_mod"`
ModFiles *EndpointConfig `yaml:"mod_files"`
DownloadURL *EndpointConfig `yaml:"download_url"`
}
APIEndpoints defines optional endpoint configurations for an API source.
type APIKeyConfig ¶ added in v1.7.0
type APIKeyConfig struct {
In string `yaml:"in"` // "header" or "query"
Name string `yaml:"name"` // header name or query parameter name
}
APIKeyConfig says where the API key is attached on requests.
type APIMappings ¶ added in v1.9.0
type APIMappings struct {
Mod map[string]string `yaml:"mod"` // domain field key -> JSON dot-path
File map[string]string `yaml:"file"`
}
APIMappings maps domain field keys to JSON dot-paths.
type AuthConfig ¶ added in v1.7.0
type AuthConfig struct {
APIKey *APIKeyConfig `yaml:"api_key"`
}
AuthConfig configures optional API-key authentication for a custom source. The key itself is never stored in the definition; it comes from the LMM_<ID>_API_KEY env var or the DB token store at startup.
type Directory ¶
type Directory struct {
// contains filtered or unexported fields
}
Directory is a ModSource backed by a local directory: each subdirectory (or .zip/.jar archive) is one mod. The directory is rescanned on each operation so edits show up without restarting lmm; scans are local and cheap.
func NewDirectory ¶
func NewDirectory(def SourceDefinition) (*Directory, error)
NewDirectory constructs a directory source from a validated definition. The configured path must exist and be a directory.
func (*Directory) Capabilities ¶
func (d *Directory) Capabilities() source.Capabilities
Capabilities implements source.CapabilityReporter.
func (*Directory) CheckUpdates ¶
func (d *Directory) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
CheckUpdates implements source.ModSource by comparing installed versions to the current scan.
func (*Directory) ExchangeToken ¶
ExchangeToken implements source.ModSource.
func (*Directory) GetDependencies ¶
func (d *Directory) GetDependencies(ctx context.Context, mod *domain.Mod) ([]domain.ModReference, error)
GetDependencies implements source.ModSource; directory mods declare none.
func (*Directory) GetDownloadURL ¶
func (d *Directory) GetDownloadURL(ctx context.Context, mod *domain.Mod, fileID string) (string, error)
GetDownloadURL implements source.ModSource, returning a file:// URL that Service.DownloadModToCache ingests by local copy instead of HTTP download.
func (*Directory) GetMod ¶
GetMod implements source.ModSource. gameID is accepted from any game and not used to look up the mod (see Search); it is echoed onto the returned mod so downstream installs are attributed to the correct game.
func (*Directory) GetModFiles ¶
func (d *Directory) GetModFiles(ctx context.Context, mod *domain.Mod) ([]domain.DownloadableFile, error)
GetModFiles implements source.ModSource: every mod has exactly one synthetic file ("main") representing its directory or archive.
func (*Directory) Search ¶
func (d *Directory) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
Search implements source.ModSource with client-side matching: case-insensitive substring on name and summary; name matches rank first, then alphabetical. GameID is accepted from any game (a directory source applies to any game that maps it — it does not filter by GameID) but is echoed onto every returned mod so downstream installs are attributed to the correct game.
type DirectoryConfig ¶
type DirectoryConfig struct {
Path string `yaml:"path"`
}
DirectoryConfig configures a local-directory source.
type EndpointConfig ¶ added in v1.9.0
type EndpointConfig struct {
Path string `yaml:"path"` // required; may contain {placeholders}
List string `yaml:"list"` // dot-path to results array (required for search & mod_files)
Total string `yaml:"total"` // optional dot-path to total count (search only)
Field string `yaml:"field"` // dot-path to a scalar (required for download_url)
}
EndpointConfig configures a single API endpoint.
type Manifest ¶ added in v1.7.0
type Manifest struct {
// contains filtered or unexported fields
}
Manifest is a ModSource backed by a published mod-list document (design §3). Remote manifests are fetched on demand and cached in memory for the configured TTL; local paths are read on every operation (cheap). Construction is pure: a valid definition always registers, and fetch/parse problems surface as operation errors naming the manifest URL.
func NewManifest ¶ added in v1.7.0
func NewManifest(def SourceDefinition) (*Manifest, error)
NewManifest constructs a manifest source from a validated definition. It performs no I/O — the manifest is first fetched when an operation needs it.
func (*Manifest) AuthURL ¶ added in v1.7.0
AuthURL implements source.ModSource; manifest sources use API keys, not OAuth.
func (*Manifest) Capabilities ¶ added in v1.7.0
func (m *Manifest) Capabilities() source.Capabilities
Capabilities implements source.CapabilityReporter. Auth reflects whether the definition declares an auth block.
func (*Manifest) CheckUpdates ¶ added in v1.7.0
func (m *Manifest) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)
CheckUpdates implements source.ModSource by comparing installed versions to the current manifest.
func (*Manifest) DownloadHeaders ¶ added in v1.7.0
DownloadHeaders implements source.DownloadHeaderProvider. Header-mode auth applies the same key to file downloads as to manifest fetches (design §6), but for remote manifests only when the file URL is same-origin (scheme and host, via sameOrigin) with the manifest — a manifest pointing files at a third-party CDN, or downgrading from https to http on the same host, must not ship the repo's key there. Local-path manifests are user-authored and trusted, so their configured key attaches regardless of host.
func (*Manifest) ExchangeToken ¶ added in v1.7.0
ExchangeToken implements source.ModSource.
func (*Manifest) GetDependencies ¶ added in v1.7.0
func (m *Manifest) GetDependencies(ctx context.Context, mod *domain.Mod) ([]domain.ModReference, error)
GetDependencies implements source.ModSource: manifest dependencies are IDs within this source, returned as ModReferences for the resolver.
func (*Manifest) GetDownloadURL ¶ added in v1.7.0
func (m *Manifest) GetDownloadURL(ctx context.Context, mod *domain.Mod, fileID string) (string, error)
GetDownloadURL implements source.ModSource. Query-mode auth is appended here — for remote manifests, only when the file URL is same-origin with the manifest (see sameOrigin); a manifest pointing files at a third-party CDN must not ship the repo's key there via the URL either. Header-mode auth rides via DownloadHeaders (see DownloadHeaderProvider) under the same rule.
func (*Manifest) GetMod ¶ added in v1.7.0
GetMod implements source.ModSource. gameID does not filter (install-by-ID works from any game); it is echoed onto the returned mod for attribution.
func (*Manifest) GetModFiles ¶ added in v1.7.0
func (m *Manifest) GetModFiles(ctx context.Context, mod *domain.Mod) ([]domain.DownloadableFile, error)
GetModFiles implements source.ModSource, mapping manifest file entries — including declared sha256 checksums — onto DownloadableFiles.
func (*Manifest) IsAuthenticated ¶ added in v1.7.0
IsAuthenticated reports whether an API key is configured. Only meaningful when the definition declares auth (Capabilities().Auth).
func (*Manifest) Search ¶ added in v1.7.0
func (m *Manifest) Search(ctx context.Context, query source.SearchQuery) (source.SearchResult, error)
Search implements source.ModSource with the shared client-side semantics (design §5), filtered by the manifest's per-mod game_ids.
type ManifestConfig ¶
type ManifestConfig struct {
URL string `yaml:"url"`
Refresh string `yaml:"refresh"` // Go duration string, e.g. "15m"; empty = default
Auth *AuthConfig `yaml:"auth"`
}
ManifestConfig configures a manifest source (Phase 3).
type SourceDefinition ¶
type SourceDefinition struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Type string `yaml:"type"`
AllowHTTP bool `yaml:"allow_http"`
Directory *DirectoryConfig `yaml:"directory"`
Manifest *ManifestConfig `yaml:"manifest"`
API *APIConfig `yaml:"api"`
}
SourceDefinition is one user-defined source, parsed from a YAML file in <configDir>/sources/. Exactly one of Directory/Manifest/API must be set, matching Type.
func (*SourceDefinition) Validate ¶
func (d *SourceDefinition) Validate() error
Validate checks the definition for structural errors. It does not touch the filesystem or network; existence checks happen when the source is constructed.