// Package engineversion defines the engine version registry domain // model owned by Game Master. // // The registry mirrors the durable shape of the `engine_versions` // PostgreSQL table (see // `galaxy/gamemaster/internal/adapters/postgres/migrations/00001_init.sql`) // and the user-visible status enum frozen in // `galaxy/gamemaster/api/internal-openapi.yaml`. // // `Options` is intentionally kept opaque ([]byte holding raw JSON) so // the v1 service does not impose a Go-side schema on the engine-owned // document. Schema-aware handling lands when an engine version actually // requires it; until then the registry is a pass-through store. package engineversion import ( "errors" "fmt" "strings" "time" ) // Status identifies one engine-version registry state. type Status string const ( // StatusActive marks a version as deployable. Lobby's start flow // resolves image refs only against active versions. StatusActive Status = "active" // StatusDeprecated marks a version as no longer offered for new // starts. Already-running games on a deprecated version are // unaffected; the runtime stays bound to the version it started on. StatusDeprecated Status = "deprecated" ) // IsKnown reports whether status belongs to the frozen engine-version // status vocabulary. func (status Status) IsKnown() bool { switch status { case StatusActive, StatusDeprecated: return true default: return false } } // AllStatuses returns the frozen list of every engine-version status // value. The slice order is stable across calls. func AllStatuses() []Status { return []Status{StatusActive, StatusDeprecated} } // EngineVersion stores one row of the `engine_versions` registry table. // Options carries the raw `jsonb` document verbatim so the registry // stays decoupled from any engine-side schema. type EngineVersion struct { // Version stores the canonical semver string (primary key). Version string // ImageRef stores the Docker reference of the engine image. ImageRef string // Options stores the engine-side options document as raw JSON. Empty // is treated as `{}` by adapters that hydrate the column. Options []byte // Status reports whether the version is deployable (`active`) or // no longer offered for new starts (`deprecated`). Status Status // CreatedAt stores the wall-clock at which the row was created. CreatedAt time.Time // UpdatedAt stores the wall-clock of the most recent mutation. UpdatedAt time.Time } // Validate reports whether record satisfies the engine-version // invariants implied by `engine_versions_status_chk` and the README // §Engine Version Registry surface. func (record EngineVersion) Validate() error { if strings.TrimSpace(record.Version) == "" { return fmt.Errorf("version must not be empty") } if strings.TrimSpace(record.ImageRef) == "" { return fmt.Errorf("image ref must not be empty") } if !record.Status.IsKnown() { return fmt.Errorf("status %q is unsupported", record.Status) } if record.CreatedAt.IsZero() { return fmt.Errorf("created at must not be zero") } if record.UpdatedAt.IsZero() { return fmt.Errorf("updated at must not be zero") } if record.UpdatedAt.Before(record.CreatedAt) { return fmt.Errorf("updated at must not be before created at") } return nil } // ErrNotFound reports that an engine-version lookup failed because no // matching row exists. var ErrNotFound = errors.New("engine version not found") // ErrInUse reports that a hard-delete or deprecate operation was // rejected because the version is still referenced by a non-finished // runtime record. var ErrInUse = errors.New("engine version in use") // ErrConflict reports that an engine-version mutation could not be // applied because a row with the same primary key already exists. // Adapters surface a PostgreSQL unique-violation through this sentinel // so the service layer maps it to a `conflict` REST envelope. var ErrConflict = errors.New("engine version already exists") // ErrInvalidSemver reports that a semver string did not parse against // `golang.org/x/mod/semver`'s grammar. var ErrInvalidSemver = errors.New("invalid semver")