sentinel

package module
v1.0.4 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 5 Imported by: 11

README

sentinel

CI Status codecov Go Report Card CodeQL Go Reference License Go Version Release

Zero-dependency struct introspection for Go.

Extract struct metadata once, cache it permanently, and discover relationships between types.

Structured Type Intelligence

type User struct {
    ID      string   `json:"id" db:"id" validate:"required"`
    Email   string   `json:"email" validate:"required,email"`
    Profile *Profile
    Orders  []Order
}

metadata := sentinel.Scan[User]()
// metadata.TypeName → "User"
// metadata.FQDN     → "github.com/app/models.User"
// metadata.Fields   → []FieldMetadata (4 fields)
// metadata.Relationships → []TypeRelationship (2 relationships)

field := metadata.Fields[0]
// field.Name  → "ID"
// field.Type  → "string"
// field.Kind  → "scalar"
// field.Tags  → {"json": "id", "db": "id", "validate": "required"}
// field.Index → []int{0}

One call extracts metadata for User and every type it touches — Profile, Order, and anything they reference. All cached permanently.

types := sentinel.Browse()
// [
//   "github.com/app/models.User",
//   "github.com/app/models.Profile",
//   "github.com/app/models.Order",
// ]

relationships := sentinel.GetRelationships[User]()
// []TypeRelationship (2 relationships)

rel := relationships[0]
// rel.From      → "github.com/app/models.User"
// rel.To        → "github.com/app/models.Profile"
// rel.Field     → "Profile"
// rel.Kind      → "reference"
// rel.ToPackage → "github.com/app/models"

Types don't change at runtime. Neither does their metadata.

Install

go get github.com/zoobz-io/sentinel@latest

Requires Go 1.24+.

Quick Start

package main

import (
    "fmt"
    "github.com/zoobz-io/sentinel"
)

type Order struct {
    ID     string  `json:"id" db:"order_id" validate:"required"`
    Total  float64 `json:"total" validate:"gte=0"`
    Status string  `json:"status"`
}

type User struct {
    ID     string  `json:"id" db:"user_id"`
    Email  string  `json:"email" validate:"required,email"`
    Orders []Order
}

func main() {
    // Scan extracts User, Order, and their relationship
    metadata := sentinel.Scan[User]()

    // Type information
    fmt.Println(metadata.TypeName) // "User"
    fmt.Println(metadata.FQDN)     // "main.User" (reflects actual package path)

    // Field metadata
    for _, field := range metadata.Fields {
        fmt.Printf("%s (%s): %v\n", field.Name, field.Kind, field.Tags)
    }
    // ID (scalar): map[json:id db:user_id]
    // Email (scalar): map[json:email validate:required,email]
    // Orders (slice): map[]

    // Relationships
    for _, rel := range metadata.Relationships {
        fmt.Printf("%s → %s (%s)\n", metadata.TypeName, rel.To, rel.Kind)
    }
    // User → main.Order (collection)

    // Everything is cached
    fmt.Println(sentinel.Browse())
    // [main.User main.Order]

    // Export the full schema
    schema := sentinel.Schema()
    fmt.Printf("%d types cached\n", len(schema))
}

Capabilities

Feature Description Docs
Metadata Extraction Fields, types, indices, categories, struct tags Concepts
Relationship Discovery References, collections, embeddings, maps Scanning
Permanent Caching Extract once, cached forever Architecture
Custom Tags Register additional struct tags Tags
Module-Aware Scanning Recursive extraction within module boundaries Scanning
Schema Export Schema() returns all cached metadata API

Why sentinel?

  • Zero dependencies — only the Go standard library
  • Permanent caching — types are immutable at runtime, so metadata is cached once
  • Type-safe genericsInspect[T]() catches type errors at compile time
  • Relationship discovery — automatically maps references, collections, embeddings, and maps
  • Module-aware scanningScan[T]() recursively extracts related types within your module
  • Thread-safe — concurrent access after initial extraction

Type-Driven Generation

Sentinel metadata enables a pattern: define types once, generate everything else.

Your struct definitions become the single source of truth. Downstream tools consume sentinel's metadata to generate:

  • Entity diagrams — Visualize domain models directly from type relationships
  • Database schemas — Generate DDL and type-safe queries from struct tags
  • API documentation — Produce OpenAPI specs from request/response types

Documentation

  • Learn
  • Guides
  • Integrations
    • erd — entity relationship diagrams
    • soy — SQL injection-safe queries
    • rocco — OpenAPI generation
  • Reference
    • API — complete function documentation
    • Types — Metadata, FieldMetadata, TypeRelationship

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT License — see LICENSE for details.

Documentation

Overview

Package sentinel provides struct metadata extraction and relationship discovery for Go.

Index

Constants

View Source
const (
	RelationshipReference  = "reference"  // Direct field reference (e.g., Profile *Profile)
	RelationshipCollection = "collection" // Slice/array of types (e.g., Orders []Order)
	RelationshipEmbedding  = "embedding"  // Anonymous field embedding
	RelationshipMap        = "map"        // Map with struct values
)

RelationshipKind constants for different relationship types.

Variables

View Source
var ErrNotStruct = errors.New("sentinel: only struct types are supported")

ErrNotStruct is returned when a non-struct type is passed to Try* functions.

Functions

func Browse

func Browse() []string

Browse returns all type names that have been cached.

func Schema

func Schema() map[string]Metadata

Schema returns all cached metadata as a map. This is useful for generating documentation, exporting schemas, or analyzing the complete type graph of inspected types.

func Tag

func Tag(tagName string)

Tag registers a struct tag to be extracted during metadata processing. This can be called regardless of seal status.

Types

type Cache

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

Cache stores extracted metadata permanently. Since types are immutable at runtime, entries never expire.

func NewCache

func NewCache() *Cache

NewCache creates a new cache.

func (*Cache) All

func (c *Cache) All() map[string]Metadata

All returns a copy of all cached metadata.

func (*Cache) Clear

func (c *Cache) Clear()

Clear removes all entries from the cache. This should only be used in tests.

func (*Cache) Get

func (c *Cache) Get(typeName string) (Metadata, bool)

Get retrieves metadata from the cache.

func (*Cache) Keys

func (c *Cache) Keys() []string

Keys returns all cached type names.

func (*Cache) Set

func (c *Cache) Set(typeName string, metadata Metadata)

Set stores metadata in the cache.

func (*Cache) Size

func (c *Cache) Size() int

Size returns the number of cached entries.

type FieldKind

type FieldKind string

FieldKind represents the category of a field's type.

const (
	KindScalar    FieldKind = "scalar"    // Basic types: string, int, float, bool, etc.
	KindPointer   FieldKind = "pointer"   // Pointer to any type
	KindSlice     FieldKind = "slice"     // Slice or array
	KindStruct    FieldKind = "struct"    // Struct type
	KindMap       FieldKind = "map"       // Map type
	KindInterface FieldKind = "interface" // Interface type
)

FieldKind constants for type categorization.

type FieldMetadata

type FieldMetadata struct {
	ReflectType reflect.Type      `json:"-"`
	Tags        map[string]string `json:"tags,omitempty"`
	Name        string            `json:"name"`
	Type        string            `json:"type"`
	Kind        FieldKind         `json:"kind"`
	Index       []int             `json:"index"`
}

FieldMetadata captures field-level information and all struct tags.

type Metadata

type Metadata struct {
	ReflectType   reflect.Type       `json:"-"`
	FQDN          string             `json:"fqdn"`         // Fully qualified type name (e.g., "github.com/app/models.User")
	TypeName      string             `json:"type_name"`    // Simple type name (e.g., "User")
	PackageName   string             `json:"package_name"` // Package path (e.g., "github.com/app/models")
	Fields        []FieldMetadata    `json:"fields"`
	Relationships []TypeRelationship `json:"relationships,omitempty"`
}

Metadata contains comprehensive information about a user model.

func Inspect

func Inspect[T any]() Metadata

Inspect returns comprehensive metadata for a type. Panics if T is not a struct type.

func Lookup

func Lookup(typeName string) (Metadata, bool)

Lookup returns cached metadata for a type name if it exists. This allows external packages to access metadata that has already been extracted.

func Scan

func Scan[T any]() Metadata

Scan performs recursive inspection of a type and all related types within the same module. Unlike Inspect which only processes a single type, Scan will follow relationships and automatically inspect any related types that share the same module root. Panics if T is not a struct type.

func TryInspect

func TryInspect[T any]() (Metadata, error)

TryInspect returns comprehensive metadata for a type. Returns ErrNotStruct if T is not a struct type.

func TryScan

func TryScan[T any]() (Metadata, error)

TryScan performs recursive inspection of a type and all related types within the same module. Unlike TryInspect which only processes a single type, TryScan will follow relationships and automatically inspect any related types that share the same module root. Returns ErrNotStruct if T is not a struct type.

type Sentinel

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

Sentinel is the main type intelligence orchestrator. It provides metadata extraction and caching.

type TypeRelationship

type TypeRelationship struct {
	From      string `json:"from"`       // Source type name
	To        string `json:"to"`         // Target type name
	Field     string `json:"field"`      // Field creating the relationship
	Kind      string `json:"kind"`       // "reference", "collection", "embedding", "map"
	ToPackage string `json:"to_package"` // Target type's package path
}

TypeRelationship represents a relationship between two types.

func GetReferencedBy

func GetReferencedBy[T any]() []TypeRelationship

GetReferencedBy returns all types that reference the given type. This performs a reverse lookup across all cached metadata.

func GetRelationships

func GetRelationships[T any]() []TypeRelationship

GetRelationships returns all relationships from a type to other types.

Jump to

Keyboard shortcuts

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