Skip to content

schemagen

import "github.com/alehatsman/mooncake/internal/schemagen"

Package schemagen provides JSON Schema generation from action metadata.

This package generates JSON Schema, OpenAPI specifications, and TypeScript definitions from mooncake's action registry and Go struct definitions.

The generator uses multiple sources: 1. Action metadata from actions.List() (platforms, capabilities) 2. Go struct tags from config structs (types, descriptions) 3. Custom schema hints for complex validations

Usage:

schema := schemagen.Generate()
json, _ := schema.MarshalJSON()

Index

Variables

EnhancedDescriptions adds detailed descriptions to properties.

var EnhancedDescriptions = map[string]map[string]string{
    "os.service": {
        "name":          "Service name (systemd: nginx, launchd: com.example.app)",
        "state":         "Desired service state",
        "enabled":       "Enable service to start on boot (systemd: enable/disable, launchd: bootstrap/bootout)",
        "daemon_reload": "Run 'systemctl daemon-reload' after unit file changes (systemd only)",
    },
    "file.write": {
        "path":  "File, directory, or symlink path (required)",
        "state": "Desired file state (file/present: file exists, absent: removed, directory: dir exists, link: symlink, hardlink: hard link, touch: update timestamp, perms: change permissions only)",
        "mode":  "File permissions (e.g., '0644', '0755')",
        "owner": "File owner (username or UID)",
        "group": "File group (groupname or GID)",
    },
    "shell": {
        "cmd":          "Shell command to execute (required)",
        "interpreter":  "Shell interpreter binary (any executable on PATH). Default: bash on Unix, powershell on Windows. On Windows, 'cmd' dispatches with /c, others with -Command.",
        "stdin":        "Input to provide to the command via stdin",
        "capture":      "Capture command output (default: true). When false, output is only streamed",
        "run_as_admin": "Windows only — assert the mooncake process is elevated; fail the step if it isn't. Does not attempt UAC. Ignored on Unix.",
        "error_action": "Windows + PowerShell only — sets $ErrorActionPreference (Stop, Continue, SilentlyContinue, ...). Default: Stop. Ignored elsewhere.",
    },
    "pkg": {
        "name":         "Package name (single package)",
        "names":        "Multiple packages to install/remove",
        "state":        "Package state (present: installed, absent: removed, latest: install or upgrade)",
        "manager":      "Package manager (auto-detected if empty: apt, dnf, yum, pacman, zypper, apk, brew, port, choco, scoop)",
        "update_cache": "Update package cache before operation (e.g., apt-get update)",
    },
}

FieldOverrides hand-tunes specific (action, field) pairs whose JSON Schema type isn't directly derivable from the Go struct (e.g. union types supported by custom UnmarshalYAML).

Applied after the reflection-based property extraction.

var FieldOverrides = map[string]func(prop *Property){
    "package.names": func(prop *Property) {

        prop.Type = ""
        prop.Items = nil
        prop.OneOf = []*Property{
            {Type: "array", Items: &Property{Type: "string"}},
            {Type: "string"},
        }
    },
    "os.sysctl.value": func(prop *Property) {

        prop.Type = ""
        prop.OneOf = []*Property{
            {Type: "string"},
            {Type: "integer"},
            {Type: "boolean"},
        }
    },
}

KnownEnums maps action.field paths to their enum values. Keys use spec-21 dot-namespaced action names.

var KnownEnums = map[string][]string{

    "os.service.state": {"started", "stopped", "restarted", "reloaded"},

    "file.write.state": {"file", "present", "absent", "directory", "link", "hardlink", "touch", "perms"},

    "pkg.state": {"present", "absent", "latest"},

    "os.cron.state":   {"present", "absent"},
    "os.sysctl.state": {"present", "absent"},

    "os.systemd.scope": {"system", "user"},
}

KnownPatterns maps field names to regex patterns for validation.

var KnownPatterns = map[string]string{

    "timeout": `^[0-9]+(ns|us|µs|ms|s|m|h)$`,
    "delay":   `^[0-9]+(ns|us|µs|ms|s|m|h)$`,

    "mode": `^[0-7]{3,4}$`,
}

KnownRanges maps field names to min/max constraints.

var KnownRanges = map[string]struct{ Min, Max float64 }{
    "attempts": {Min: 0, Max: 100},
}

type Definition

Definition represents a schema definition (typically for an action).

type Definition struct {
    Type        string               `json:"type" yaml:"type"`
    Description string               `json:"description,omitempty" yaml:"description,omitempty"`
    Properties  map[string]*Property `json:"properties,omitempty" yaml:"properties,omitempty"`
    Required    []string             `json:"required,omitempty" yaml:"required,omitempty"`
    OneOf       []*OneOfConstraint   `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
    AnyOf       []*SchemaRef         `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
    AllOf       []*SchemaRef         `json:"allOf,omitempty" yaml:"allOf,omitempty"`

    // additionalProperties controls whether unknown properties are allowed
    AdditionalProperties *bool `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`

    // Custom extensions for mooncake
    XPlatforms             []string `json:"x-platforms,omitempty" yaml:"x-platforms,omitempty"`
    XRequiresSudo          bool     `json:"x-requires-sudo,omitempty" yaml:"x-requires-sudo,omitempty"`
    XImplementsCheck       bool     `json:"x-implements-check,omitempty" yaml:"x-implements-check,omitempty"`
    XImplementsDiff        bool     `json:"x-implements-diff,omitempty" yaml:"x-implements-diff,omitempty"`
    XImplementsCost        bool     `json:"x-implements-cost,omitempty" yaml:"x-implements-cost,omitempty"`
    XImplementsReverse     bool     `json:"x-implements-reverse,omitempty" yaml:"x-implements-reverse,omitempty"`
    XImplementsPermissions bool     `json:"x-implements-permissions,omitempty" yaml:"x-implements-permissions,omitempty"`
    XCategory              string   `json:"x-category,omitempty" yaml:"x-category,omitempty"`
    XSupportsDryRun        bool     `json:"x-supports-dry-run,omitempty" yaml:"x-supports-dry-run,omitempty"`
    XSupportsBecome        bool     `json:"x-supports-become,omitempty" yaml:"x-supports-become,omitempty"`
    XVersion               string   `json:"x-version,omitempty" yaml:"x-version,omitempty"`
    XEmitsEvents           []string `json:"x-emits-events,omitempty" yaml:"x-emits-events,omitempty"`
}

type Generator

Generator generates JSON Schema from action metadata and struct definitions.

type Generator struct {
    // contains filtered or unexported fields
}

func NewGenerator

func NewGenerator(opts GeneratorOptions) *Generator

NewGenerator creates a new schema generator with options.

func (*Generator) Generate

func (g *Generator) Generate() (*Schema, error)

Generate creates a complete JSON Schema from the action registry.

func (*Generator) GenerateOpenAPI

func (g *Generator) GenerateOpenAPI() (*OpenAPISpec, error)

GenerateOpenAPI generates an OpenAPI 3.0 specification.

func (*Generator) GenerateTypeScript

func (g *Generator) GenerateTypeScript() (string, error)

GenerateTypeScript generates TypeScript definitions.

type GeneratorOptions

GeneratorOptions configures schema generation behavior.

type GeneratorOptions struct {
    // IncludeExamples adds example values to properties
    IncludeExamples bool

    // IncludeExtensions adds custom x- extensions
    IncludeExtensions bool

    // StrictValidation generates stricter validation rules
    StrictValidation bool

    // OutputFormat specifies the output format (json, yaml, openapi, typescript)
    OutputFormat string
}

type NotConstraint

NotConstraint represents a "not" constraint with anyOf clauses.

type NotConstraint struct {
    AnyOf []*RequiredConstraint `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
}

type OneOfConstraint

OneOfConstraint represents a oneOf constraint with required and not clauses. It can also represent complete schema alternatives (for root-level oneOf).

type OneOfConstraint struct {
    // For mutual exclusion constraints at step level
    Required   []string             `json:"required,omitempty" yaml:"required,omitempty"`
    Properties map[string]*Property `json:"properties,omitempty" yaml:"properties,omitempty"`
    Not        *NotConstraint       `json:"not,omitempty" yaml:"not,omitempty"`

    // For root-level oneOf alternatives (complete schemas)
    Type        string     `json:"type,omitempty" yaml:"type,omitempty"`
    Items       *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"`
    Ref         string     `json:"$ref,omitempty" yaml:"$ref,omitempty"`
    Description string     `json:"description,omitempty" yaml:"description,omitempty"`
}

type OpenAPIComponents

OpenAPIComponents contains reusable components.

type OpenAPIComponents struct {
    Schemas map[string]*OpenAPISchema `json:"schemas,omitempty" yaml:"schemas,omitempty"`
}

type OpenAPIContact

OpenAPIContact contains contact information.

type OpenAPIContact struct {
    Name  string `json:"name,omitempty" yaml:"name,omitempty"`
    URL   string `json:"url,omitempty" yaml:"url,omitempty"`
    Email string `json:"email,omitempty" yaml:"email,omitempty"`
}

type OpenAPIExample

OpenAPIExample represents an example.

type OpenAPIExample struct {
    Summary     string      `json:"summary,omitempty" yaml:"summary,omitempty"`
    Description string      `json:"description,omitempty" yaml:"description,omitempty"`
    Value       interface{} `json:"value,omitempty" yaml:"value,omitempty"`
}

type OpenAPIInfo

OpenAPIInfo contains API metadata.

type OpenAPIInfo struct {
    Title       string          `json:"title" yaml:"title"`
    Description string          `json:"description,omitempty" yaml:"description,omitempty"`
    Version     string          `json:"version" yaml:"version"`
    Contact     *OpenAPIContact `json:"contact,omitempty" yaml:"contact,omitempty"`
    License     *OpenAPILicense `json:"license,omitempty" yaml:"license,omitempty"`
}

type OpenAPILicense

OpenAPILicense contains license information.

type OpenAPILicense struct {
    Name string `json:"name" yaml:"name"`
    URL  string `json:"url,omitempty" yaml:"url,omitempty"`
}

type OpenAPIMediaType

OpenAPIMediaType describes a media type.

type OpenAPIMediaType struct {
    Schema   *OpenAPISchema            `json:"schema,omitempty" yaml:"schema,omitempty"`
    Example  interface{}               `json:"example,omitempty" yaml:"example,omitempty"`
    Examples map[string]OpenAPIExample `json:"examples,omitempty" yaml:"examples,omitempty"`
}

type OpenAPIOperation

OpenAPIOperation describes a single API operation.

type OpenAPIOperation struct {
    Summary     string                     `json:"summary,omitempty" yaml:"summary,omitempty"`
    Description string                     `json:"description,omitempty" yaml:"description,omitempty"`
    OperationID string                     `json:"operationId,omitempty" yaml:"operationId,omitempty"`
    Tags        []string                   `json:"tags,omitempty" yaml:"tags,omitempty"`
    RequestBody *OpenAPIRequestBody        `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
    Responses   map[string]OpenAPIResponse `json:"responses" yaml:"responses"`
}

type OpenAPIPath

OpenAPIPath represents API path operations.

type OpenAPIPath struct {
    Summary     string            `json:"summary,omitempty" yaml:"summary,omitempty"`
    Description string            `json:"description,omitempty" yaml:"description,omitempty"`
    Get         *OpenAPIOperation `json:"get,omitempty" yaml:"get,omitempty"`
    Post        *OpenAPIOperation `json:"post,omitempty" yaml:"post,omitempty"`
}

type OpenAPIRequestBody

OpenAPIRequestBody describes a request body.

type OpenAPIRequestBody struct {
    Description string                      `json:"description,omitempty" yaml:"description,omitempty"`
    Required    bool                        `json:"required,omitempty" yaml:"required,omitempty"`
    Content     map[string]OpenAPIMediaType `json:"content" yaml:"content"`
}

type OpenAPIResponse

OpenAPIResponse describes a response.

type OpenAPIResponse struct {
    Description string                      `json:"description" yaml:"description"`
    Content     map[string]OpenAPIMediaType `json:"content,omitempty" yaml:"content,omitempty"`
}

type OpenAPISchema

OpenAPISchema represents a schema (simplified from JSON Schema). OpenAPI 3.0 uses a subset of JSON Schema with some modifications.

type OpenAPISchema struct {
    Type        string                    `json:"type,omitempty" yaml:"type,omitempty"`
    Description string                    `json:"description,omitempty" yaml:"description,omitempty"`
    Ref         string                    `json:"$ref,omitempty" yaml:"$ref,omitempty"`
    Properties  map[string]*OpenAPISchema `json:"properties,omitempty" yaml:"properties,omitempty"`
    Items       *OpenAPISchema            `json:"items,omitempty" yaml:"items,omitempty"`
    Required    []string                  `json:"required,omitempty" yaml:"required,omitempty"`
    Enum        []interface{}             `json:"enum,omitempty" yaml:"enum,omitempty"`
    Default     interface{}               `json:"default,omitempty" yaml:"default,omitempty"`
    Example     interface{}               `json:"example,omitempty" yaml:"example,omitempty"`

    // OneOf, AnyOf, AllOf support
    OneOf []*OpenAPISchema `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
    AnyOf []*OpenAPISchema `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
    AllOf []*OpenAPISchema `json:"allOf,omitempty" yaml:"allOf,omitempty"`
    Not   *OpenAPISchema   `json:"not,omitempty" yaml:"not,omitempty"`

    // Validation
    Minimum   *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
    Maximum   *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
    MinLength *int     `json:"minLength,omitempty" yaml:"minLength,omitempty"`
    MaxLength *int     `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
    Pattern   string   `json:"pattern,omitempty" yaml:"pattern,omitempty"`
    Format    string   `json:"format,omitempty" yaml:"format,omitempty"`

    AdditionalProperties *bool `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`

    // Custom extensions (x- prefixed fields are allowed in OpenAPI)
    XPlatforms             []string `json:"x-platforms,omitempty" yaml:"x-platforms,omitempty"`
    XRequiresSudo          bool     `json:"x-requires-sudo,omitempty" yaml:"x-requires-sudo,omitempty"`
    XImplementsCheck       bool     `json:"x-implements-check,omitempty" yaml:"x-implements-check,omitempty"`
    XImplementsDiff        bool     `json:"x-implements-diff,omitempty" yaml:"x-implements-diff,omitempty"`
    XImplementsCost        bool     `json:"x-implements-cost,omitempty" yaml:"x-implements-cost,omitempty"`
    XImplementsReverse     bool     `json:"x-implements-reverse,omitempty" yaml:"x-implements-reverse,omitempty"`
    XImplementsPermissions bool     `json:"x-implements-permissions,omitempty" yaml:"x-implements-permissions,omitempty"`
    XCategory              string   `json:"x-category,omitempty" yaml:"x-category,omitempty"`
    XSupportsDryRun        bool     `json:"x-supports-dry-run,omitempty" yaml:"x-supports-dry-run,omitempty"`
    XSupportsBecome        bool     `json:"x-supports-become,omitempty" yaml:"x-supports-become,omitempty"`
    XVersion               string   `json:"x-version,omitempty" yaml:"x-version,omitempty"`
    XEmitsEvents           []string `json:"x-emits-events,omitempty" yaml:"x-emits-events,omitempty"`
}

type OpenAPIServer

OpenAPIServer describes a server.

type OpenAPIServer struct {
    URL         string `json:"url" yaml:"url"`
    Description string `json:"description,omitempty" yaml:"description,omitempty"`
}

type OpenAPISpec

OpenAPISpec represents an OpenAPI 3.0 specification document.

type OpenAPISpec struct {
    OpenAPI    string                 `json:"openapi" yaml:"openapi"`
    Info       OpenAPIInfo            `json:"info" yaml:"info"`
    Servers    []OpenAPIServer        `json:"servers,omitempty" yaml:"servers,omitempty"`
    Paths      map[string]OpenAPIPath `json:"paths,omitempty" yaml:"paths,omitempty"`
    Components OpenAPIComponents      `json:"components" yaml:"components"`
}

type Property

Property represents a schema property (field in an action struct).

type Property struct {
    Type        string               `json:"type,omitempty" yaml:"type,omitempty"`
    Description string               `json:"description,omitempty" yaml:"description,omitempty"`
    Ref         string               `json:"$ref,omitempty" yaml:"$ref,omitempty"`
    Enum        []interface{}        `json:"enum,omitempty" yaml:"enum,omitempty"`
    Default     interface{}          `json:"default,omitempty" yaml:"default,omitempty"`
    Items       *Property            `json:"items,omitempty" yaml:"items,omitempty"`
    Properties  map[string]*Property `json:"properties,omitempty" yaml:"properties,omitempty"`
    OneOf       []*Property          `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
    Required    []string             `json:"required,omitempty" yaml:"required,omitempty"`

    // Validation
    Minimum   *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
    Maximum   *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
    MinLength *int     `json:"minLength,omitempty" yaml:"minLength,omitempty"`
    MaxLength *int     `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
    Pattern   string   `json:"pattern,omitempty" yaml:"pattern,omitempty"`
    Format    string   `json:"format,omitempty" yaml:"format,omitempty"`

    // Additional metadata
    Example         interface{} `json:"example,omitempty" yaml:"example,omitempty"`
    AdditionalProps *bool       `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
}

type RequiredConstraint

RequiredConstraint represents a simple required constraint.

type RequiredConstraint struct {
    Required []string `json:"required" yaml:"required"`
}

type Schema

Schema represents a complete JSON Schema document.

type Schema struct {
    SchemaURI   string                 `json:"$schema" yaml:"$schema"`
    ID          string                 `json:"$id,omitempty" yaml:"$id,omitempty"`
    Title       string                 `json:"title" yaml:"title"`
    Description string                 `json:"description,omitempty" yaml:"description,omitempty"`
    Type        string                 `json:"type,omitempty" yaml:"type,omitempty"`
    Items       *SchemaRef             `json:"items,omitempty" yaml:"items,omitempty"`
    OneOf       []*OneOfConstraint     `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
    Definitions map[string]*Definition `json:"definitions,omitempty" yaml:"definitions,omitempty"`
}

func (*Schema) ConvertToOpenAPI

func (s *Schema) ConvertToOpenAPI() *OpenAPISpec

ConvertToOpenAPI converts a JSON Schema to OpenAPI 3.0 format.

func (*Schema) GenerateTypeScript

func (s *Schema) GenerateTypeScript() string

GenerateTypeScript generates TypeScript definitions from a schema.

func (*Schema) MarshalJSON

func (s *Schema) MarshalJSON() ([]byte, error)

MarshalJSON converts the schema to JSON.

func (*Schema) MarshalPrettyJSON

func (s *Schema) MarshalPrettyJSON() ([]byte, error)

MarshalPrettyJSON converts the schema to pretty-printed JSON.

type SchemaRef

SchemaRef represents a reference to another schema definition. Also used inside anyOf/allOf branches as a lightweight "shape with just a required clause" fragment when neither $ref nor type fits — e.g. runConfig's anyOf: [{required: [steps]}, {required: [tasks]}].

type SchemaRef struct {
    Ref         string   `json:"$ref,omitempty"`
    Type        string   `json:"type,omitempty"`
    Description string   `json:"description,omitempty"`
    Required    []string `json:"required,omitempty"`
}

type TypeScriptGenerator

TypeScriptGenerator generates TypeScript definitions from schema.

type TypeScriptGenerator struct {
    // contains filtered or unexported fields
}

type Writer

Writer handles writing schemas in different formats.

type Writer struct {
    // contains filtered or unexported fields
}

func NewWriter

func NewWriter(format string) *Writer

NewWriter creates a new writer for the specified format.

func (*Writer) Write

func (w *Writer) Write(schema *Schema, out io.Writer) error

Write writes the schema to the given writer.

func (*Writer) WriteOpenAPI

func (w *Writer) WriteOpenAPI(spec *OpenAPISpec, out io.Writer) error

WriteOpenAPI writes an OpenAPI spec to the given writer.

func (*Writer) WriteTypeScript

func (w *Writer) WriteTypeScript(tsContent string, out io.Writer) error

WriteTypeScript writes TypeScript definitions to the given writer.

Generated by gomarkdoc