go-scm/manifest/manifest.go
Virgil a0fac1341b
Some checks failed
Security Scan / security (push) Failing after 10s
Test / test (push) Successful in 2m11s
chore(ax): add usage docs to exported APIs
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-30 14:11:15 +00:00

129 lines
4.7 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package manifest
import (
coreerr "dappco.re/go/core/log"
"gopkg.in/yaml.v3"
)
// Manifest represents a .core/manifest.yaml application manifest.
type Manifest struct {
Code string `yaml:"code" json:"code"`
Name string `yaml:"name" json:"name"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Version string `yaml:"version" json:"version"`
Author string `yaml:"author,omitempty" json:"author,omitempty"`
Licence string `yaml:"licence,omitempty" json:"licence,omitempty"`
Sign string `yaml:"sign,omitempty" json:"sign,omitempty"`
Layout string `yaml:"layout,omitempty" json:"layout,omitempty"`
Slots map[string]string `yaml:"slots,omitempty" json:"slots,omitempty"`
// Provider fields — used by runtime provider loading.
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` // API route prefix, e.g. /api/v1/cool-widget
Port int `yaml:"port,omitempty" json:"port,omitempty"` // Listen port (0 = auto-assign)
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"` // Path to provider binary (relative to provider dir)
Args []string `yaml:"args,omitempty" json:"args,omitempty"` // Additional CLI args for the binary
Element *ElementSpec `yaml:"element,omitempty" json:"element,omitempty"` // Custom element for GUI rendering
Spec string `yaml:"spec,omitempty" json:"spec,omitempty"` // Path to OpenAPI spec file
Permissions Permissions `yaml:"permissions,omitempty" json:"permissions,omitempty"`
Modules []string `yaml:"modules,omitempty" json:"modules,omitempty"`
Daemons map[string]DaemonSpec `yaml:"daemons,omitempty" json:"daemons,omitempty"`
}
// ElementSpec describes a web component for GUI rendering.
type ElementSpec struct {
// Tag is the custom element tag name, e.g. "core-cool-widget".
Tag string `yaml:"tag" json:"tag"`
// Source is the path to the JS bundle (relative to provider dir).
Source string `yaml:"source" json:"source"`
}
// IsProvider returns true if this manifest declares provider fields
// (namespace and binary), indicating it is a runtime provider.
// Usage: IsProvider(...)
func (m *Manifest) IsProvider() bool {
return m.Namespace != "" && m.Binary != ""
}
// Permissions declares the I/O capabilities a module requires.
type Permissions struct {
Read []string `yaml:"read" json:"read"`
Write []string `yaml:"write" json:"write"`
Net []string `yaml:"net" json:"net"`
Run []string `yaml:"run" json:"run"`
}
// DaemonSpec describes a long-running process managed by the runtime.
type DaemonSpec struct {
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"`
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
Health string `yaml:"health,omitempty" json:"health,omitempty"`
Default bool `yaml:"default,omitempty" json:"default,omitempty"`
}
// Parse decodes YAML bytes into a Manifest.
//
// m, err := manifest.Parse(yamlBytes)
//
// Usage: Parse(...)
func Parse(data []byte) (*Manifest, error) {
var m Manifest
if err := yaml.Unmarshal(data, &m); err != nil {
return nil, coreerr.E("manifest.Parse", "unmarshal failed", err)
}
return &m, nil
}
// SlotNames returns a deduplicated list of component names from slots.
// Usage: SlotNames(...)
func (m *Manifest) SlotNames() []string {
seen := make(map[string]bool)
var names []string
for _, name := range m.Slots {
if !seen[name] {
seen[name] = true
names = append(names, name)
}
}
return names
}
// DefaultDaemon returns the name, spec, and true for the default daemon.
// A daemon is the default if it has Default:true, or if it is the only daemon
// in the map. If multiple daemons have Default:true, returns false (ambiguous).
// Returns empty values and false if no default can be determined.
// Usage: DefaultDaemon(...)
func (m *Manifest) DefaultDaemon() (string, DaemonSpec, bool) {
if len(m.Daemons) == 0 {
return "", DaemonSpec{}, false
}
// Look for an explicit default; reject ambiguous multiple defaults.
var defaultName string
var defaultSpec DaemonSpec
for name, spec := range m.Daemons {
if spec.Default {
if defaultName != "" {
// Multiple defaults — ambiguous.
return "", DaemonSpec{}, false
}
defaultName = name
defaultSpec = spec
}
}
if defaultName != "" {
return defaultName, defaultSpec, true
}
// If exactly one daemon exists, treat it as the implicit default.
if len(m.Daemons) == 1 {
for name, spec := range m.Daemons {
return name, spec, true
}
}
return "", DaemonSpec{}, false
}