entity

package
v1.21.32 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package entity is the canonical home for clicky's entity + operation model: the entity registry and CLI generation (Entity, EntityBuilder, RegisterEntity, GenerateCLI), the command-function registries (AddCommand, the cobra annotation metadata), the RPC operation model (RPCOperation, Schema, …), and named, reusable filter/lookup definitions that work across both static (Go-struct) and dynamic (JSON-Schema) entities.

A filter is defined once — in Go via RegisterFilter, or declaratively via RegisterFilterSpec — and reused by name from any number of entities. The core abstraction (FilterSource + FilterContext) never sees a typed ListOpts, so the same definition serves a compile-time Go entity and a schema-driven dynamic entity identically.

On a static entity, attach a named filter with the typed adapter:

clicky.NewEntity[Task, TaskOpts, Task]("tasks").
	Filters(entity.Use[TaskOpts]("users").As("owner")).
	List(listTasks).
	Register()

The dependency runs one way: this package depends only on clicky subpackages (api, flags, formatters, task) plus cobra/pflag — never on the root clicky package. The root clicky package imports this one and re-exports the model via type aliases and thin wrappers (see entity_aliases.go), so callers keep using clicky.NewEntity, clicky.Entity, clicky.RegisterEntity, etc. unchanged. Host-owned globals (the CLI format flags, HTTPie argument parsing) are injected through the RenderResult / ParseArgs hooks in render.go.

Index

Constants

View Source
const (
	SourceStatic = "static"
	SourceEntity = "entity"
	SourceFunc   = "func" // Go-only; not constructible from a spec.
	SourceCustom = "custom"
)

Source kinds for the declarative FilterSourceSpec.

Variables

View Source
var ParseArgs = func(args []string) (map[string]any, error) {
	return nil, fmt.Errorf("entity.ParseArgs not configured: the root clicky package must wire it")
}

ParseArgs parses HTTPie-style positional arguments (key=value, key:=json, …) into a body map for create/update commands. Root clicky wires it in init() to its ParseArgumentsAsMap; the default fails loudly so a missing wiring surfaces rather than silently dropping arguments.

View Source
var RenderResult = func(o any) error { return nil }

RenderResult renders a command result through the host application's format pipeline — the global --format/--output flags and any configured sinks. Root clicky wires it in init() (to ParseFormatSpec + PrintAndWriteSinks against its global Flags); the default no-op lets the entity package's own unit tests run without a renderer.

It is a package-level hook rather than a direct dependency so the entity package stays free of the root clicky package (which owns the global flag state) — preserving the one-way entity → clicky-subpackages dependency.

Functions

func AddCommand

func AddCommand[T any, R any](parent *cobra.Command, opts T, fn func(opts T) (R, error)) *cobra.Command

AddCommand creates a Cobra command with automatic flag parsing from struct tags, execution, and result formatting.

Type parameter T must be a struct with tags defining flags:

  • flag:"name" - flag name (required)
  • help:"description" - flag help text
  • default:"value" - default value
  • short:"x" - short flag variant
  • required:"true" - mark flag as required
  • stdin:"true" - mark field as default for stdin input (only one field)

Supports @ prefix for file/URL loading:

  • @file.txt - read from file
  • @https://... - fetch from URL
  • For slices: one value per line (skip empty lines and # comments)
  • For slices: @file.csv:ColumnName reads a named column from a CSV (first row is the header, empty cells are skipped)
  • For slices: @file.xlsx:ColumnName / @file.xls:ColumnName reads a named column from the first worksheet (case-insensitive header match)

Supported types:

  • string, int, bool
  • duration.Duration (supports "30d", "2w", etc.)
  • time.Time (supports datamath like "now-7d", "now/d", "now-1M/M")
  • []string, []int (slices)

Datamath expressions for time.Time fields (Elasticsearch compatible):

  • now : current time
  • now-7d : 7 days ago
  • now+2h : 2 hours from now
  • now/d : start of current day
  • now-7d/d : start of day 7 days ago
  • now/w : start of this week
  • now-1M/M : start of last month
  • now/y : start of this year
  • Units: y (year), M (month), w (week), d (day), h (hour), m (minute), s (second)

Example:

type ListOptions struct {
    Role   string            `flag:"role" help:"Filter by role" short:"r"`
    Limit  int               `flag:"limit" help:"Max results" default:"50"`
    Since  time.Time         `flag:"since" help:"Created since" default:"now-30d"`
    Tags   []string          `flag:"tags" help:"Filter tags" stdin:"true"`
    MaxAge duration.Duration `flag:"max-age" help:"Max age" default:"365d"`
}

cmd := &cobra.Command{Use: "list", Short: "List users"}
clicky.AddCommand(cmd, ListOptions{}, func(opts ListOptions) (any, error) {
    return fetchUsers(opts)
})

Usage examples:

myapp list --tags @tags.txt              # load from file
myapp list --since now-30d               # datamath
echo -e "tag1\ntag2" | myapp list        # stdin
myapp list --max-age 60d --json          # with formatting

func AddCommandWithContext

func AddCommandWithContext[T any, R any](parent *cobra.Command, opts T, fn func(ctx context.Context, opts T) (R, error)) *cobra.Command

AddCommandWithContext is AddCommand whose closure receives the request-scoped context (cmd.Context() on the CLI, r.Context() over HTTP). It derives the command name from the opts struct exactly like AddCommand, then delegates to AddNamedCommandWithContext so the subcommand can resolve a per-request database/config instead of a process global.

func AddNamedCommand

func AddNamedCommand[T any, R any](name string, parent *cobra.Command, opts T, fn func(opts T) (R, error)) *cobra.Command

func AddNamedCommandWithContext

func AddNamedCommandWithContext[T any, R any](name string, parent *cobra.Command, opts T, fn func(ctx context.Context, opts T) (R, error)) *cobra.Command

AddNamedCommandWithContext is AddNamedCommand whose closure receives the request-scoped context (cmd.Context() on the CLI, r.Context() over HTTP), so the subcommand can resolve a per-request database/config instead of a process global. Same flag/arg binding and rendering as AddNamedCommand.

func GenerateCLI

func GenerateCLI(parent *cobra.Command)

GenerateCLI creates cobra subcommands for all registered entities under parent. Admin entities are nested under a shared "admin" parent command. Entities with a Parent set are nested under a shared parent cobra command, created lazily if it does not already exist.

func GetDataFunc

func GetDataFunc(cmd *cobra.Command) func(flags map[string]string, args []string) (any, error)

GetDataFunc returns the direct data function registered for a command, if any. Used by the RPC converter to wire DataFunc on RPCOperation.

func GetLookupFunc

func GetLookupFunc(cmd *cobra.Command) func(flags map[string]string, args []string) (any, error)

GetLookupFunc returns the direct lookup function registered for a command, if any.

func RegisterDynamicEntity

func RegisterDynamicEntity(spec DynamicEntitySpec)

RegisterDynamicEntity registers a schema-driven entity assembled at runtime (no compile-time Go struct). It is the single root entrypoint the entity package's dynamic builder targets; everything downstream — GenerateCLI, RPC, OpenAPI, the lookup endpoint — consumes it through the normal entity registry.

func RegisterEntity

func RegisterEntity[T EntityItem, ListOpts any, R any](e Entity[T, ListOpts, R])

RegisterEntity registers a CRUD entity. Call during init(). CLI commands and HTTP routes are generated later via GenerateCLI/GenerateRoutes.

See the github.com/flanksource/clicky/entity package for the authoring guide and the best practices for operations that work well across CLI and HTTP.

func RegisterFilter

func RegisterFilter(f NamedFilter)

RegisterFilter registers a named filter for reuse across entities. It panics on a duplicate name or an incomplete definition — registration happens at init/setup time, so a collision is a programming error that must fail loudly.

func RegisterFilterSpec

func RegisterFilterSpec(s FilterSpec)

RegisterFilterSpec registers a declaratively-defined named filter. Like RegisterFilter it is a setup-time call and panics on an invalid spec.

func RegisterSubCommand

func RegisterSubCommand(parentName string, cmd *cobra.Command)

RegisterSubCommand defers attaching cmd as a child of a parent cobra command identified by parentName. The attachment is performed by GenerateCLI after all entity parents have been created. If no command at parentName exists after entity generation, a thin parent command is created to host cmd.

parentName may be a bare command name ("policy") or a slash-delimited path ("billing/policy"). A path is resolved one segment at a time beneath root; missing intermediates are created as thin grouping commands, and existing nodes (including entity-generated parents) are reused.

Use this for non-entity cobra commands that should live under an entity-generated parent (or under an arbitrary grouping command).

func RegisterSubCommandFn

func RegisterSubCommandFn(parentName string, build func(parent *cobra.Command))

RegisterSubCommandFn defers running build against a parent cobra command identified by parentName. Use this when the subcommand needs to be constructed lazily — e.g. AddNamedCommand which both builds and attaches in one call.

parentName accepts the same bare-name-or-slash-path form as RegisterSubCommand.

Example:

clicky.RegisterSubCommandFn("correspondence", func(parent *cobra.Command) {
    clicky.AddNamedCommand("validate", parent, ValidateOptions{}, runValidate)
})

func SetCommandResponseMeta

func SetCommandResponseMeta(cmd *cobra.Command, meta ResponseOpenAPIMeta)

SetCommandResponseMeta attaches static response metadata to a command.

func Use

func Use[ListOpts any](filterName string) *attachedFilter[ListOpts]

Use attaches a registered named filter to a typed (static) entity. The returned value implements clicky.Filter[ListOpts] (plus the searchable/context/typed extensions) so it drops straight into Entity.Filters. By default it binds to the flag/field named after the filter; override with As.

Filters(entity.Use[TaskOpts]("users").As("owner"))

Use resolves the filter eagerly and panics if the name is unregistered, so a wiring mistake fails at setup rather than at request time.

Types

type ActionFlags

type ActionFlags interface {
	ClickyActionFlags()
}

ActionFlags is a marker interface implemented by options structs that declare typed cobra flags for a clicky Action. When an Action's Flags field is non-nil, the concrete type of the value is reflected for its `flag:"..."` struct tags and those flags are bound to the generated action cobra command.

Implementers attach a no-op `ClickyActionFlags()` method to an options struct, then pass a zero value of that struct in `Action.Flags`.

type ActionInfo

type ActionInfo struct {
	Name     string
	Short    string
	Method   string
	DataFunc func(flags map[string]string, args []string) (any, error)
	// ContextDataFunc, when set, is preferred over DataFunc by both the CLI run
	// path (fed cmd.Context()) and the RPC executor (fed r.Context()), mirroring
	// the entity CRUD seam. Lets an action resolve request-scoped state instead
	// of process globals.
	ContextDataFunc ContextDataFunc
	// FlagsType, if non-nil, is the struct type whose `flag:"..."` tagged
	// fields are registered as cobra flags on the generated action command
	// and populated into the flag map passed to DataFunc.
	FlagsType    reflect.Type
	ResponseType reflect.Type
	// OptionalID, when true, makes the positional <id> argument optional on
	// the generated action command — the action is invokable with no
	// argument (and the `id` passed to the run func is empty). Use for
	// actions whose target is supplied entirely through flags.
	OptionalID bool
	// ToolGroup overrides the entity's tool group for this action only. Empty
	// means the action inherits the entity's group.
	ToolGroup string
}

ActionInfo is the type-erased representation of a single-entity action.

type ActionSpec

type ActionSpec[R any] struct {
	// contains filtered or unexported fields
}

func Action

func Action[R any](name string, fn func(id string, flags map[string]string) (R, error)) *ActionSpec[R]

Action creates a typed custom operation on a single entity by ID.

func ActionWithContext

func ActionWithContext[R any](name string, fn func(ctx context.Context, id string, flags map[string]string) (R, error)) *ActionSpec[R]

ActionWithContext creates a typed action whose run closure receives the request-scoped context (cmd.Context() on the CLI, r.Context() over HTTP), so it can resolve a per-request database/config instead of a process global.

func ActionWithFlags

func ActionWithFlags[R any](name string, flags ActionFlags, fn func(id string, flags map[string]string) (R, error)) *ActionSpec[R]

ActionWithFlags creates a typed custom operation with typed action flags.

func ActionWithFlagsAndContext

func ActionWithFlagsAndContext[R any](name string, flags ActionFlags, fn func(ctx context.Context, id string, flags map[string]string) (R, error)) *ActionSpec[R]

ActionWithFlagsAndContext is ActionWithContext with typed action flags.

func (*ActionSpec[R]) WithFlags

func (a *ActionSpec[R]) WithFlags(flags ActionFlags) *ActionSpec[R]

func (*ActionSpec[R]) WithMethod

func (a *ActionSpec[R]) WithMethod(method string) *ActionSpec[R]

WithMethod overrides the inferred HTTP method for the generated RPC/OpenAPI action route. Leave empty to keep the default inference behavior.

func (*ActionSpec[R]) WithOptionalID

func (a *ActionSpec[R]) WithOptionalID() *ActionSpec[R]

WithOptionalID makes the positional <id> argument optional on the generated action command. Use for actions whose target is supplied entirely through flags; the `id` passed to the run func is then empty.

func (*ActionSpec[R]) WithShort

func (a *ActionSpec[R]) WithShort(short string) *ActionSpec[R]

func (*ActionSpec[R]) WithToolGroup

func (a *ActionSpec[R]) WithToolGroup(group string) *ActionSpec[R]

WithToolGroup overrides the entity's tool group for this action only. Empty (the default) means the action inherits the entity's group.

type BulkActionInfo

type BulkActionInfo struct {
	Name              string
	Short             string
	DataFunc          func(flags map[string]string, args []string) (any, error)
	FilterFunc        func(flags map[string]string, args []string) (any, error)
	LookupFunc        func(flags map[string]string, args []string) (any, error)
	ContextLookupFunc ContextLookupFunc
	ListType          reflect.Type
	BindCompletions   func(cmd *cobra.Command)
	ResponseType      reflect.Type
}

BulkActionInfo is the type-erased representation of a bulk action.

type BulkActionSpec

type BulkActionSpec[R any] struct {
	// contains filtered or unexported fields
}

func BulkAction

func BulkAction[R any](name string, fn func(ids []string, flags map[string]string) (R, error)) *BulkActionSpec[R]

BulkAction creates a typed custom operation on multiple entity IDs.

func BulkActionWithFilter

func BulkActionWithFilter[ListOpts any, R any](
	name string,
	run func(ids []string, flags map[string]string) (R, error),
	runFilter func(opts ListOpts, flags map[string]string) (R, error),
) *BulkActionSpec[R]

BulkActionWithFilter creates a typed bulk action that supports both explicit IDs and filtered selections.

func BulkFilterAction

func BulkFilterAction[ListOpts any, R any](name string, fn func(opts ListOpts, flags map[string]string) (R, error)) *BulkActionSpec[R]

BulkFilterAction creates a typed custom operation that runs against a typed filtered list selection instead of explicit IDs.

func (*BulkActionSpec[R]) WithShort

func (b *BulkActionSpec[R]) WithShort(short string) *BulkActionSpec[R]

type ClickyOperationMeta

type ClickyOperationMeta struct {
	SurfaceID          string   `json:"-"`
	Command            string   `json:"command,omitempty"`
	Surface            string   `json:"surface,omitempty"`
	Entity             string   `json:"-"`
	Parent             string   `json:"-"`
	Aliases            []string `json:"-"`
	Admin              bool     `json:"-"`
	Icon               string   `json:"-"`
	Title              string   `json:"-"`
	Verb               string   `json:"verb,omitempty"`
	Scope              string   `json:"scope,omitempty"`
	ActionName         string   `json:"actionName,omitempty"`
	IDParam            string   `json:"idParam,omitempty"`
	SupportsLookup     bool     `json:"supportsLookup,omitempty"`
	SupportsFilterMode bool     `json:"supportsFilterMode,omitempty"`
	Group              string   `json:"group,omitempty"`
	Order              int      `json:"-"`
}

ClickyOperationMeta is emitted as the OpenAPI operation-level x-clicky extension. SurfaceID/Aliases are internal-only fields used while building the final surface list.

type ClickySpecMeta

type ClickySpecMeta struct {
	Surfaces []ClickySurface `json:"surfaces,omitempty"`
}

ClickySpecMeta is emitted as the OpenAPI-level x-clicky extension.

type ClickySurface

type ClickySurface struct {
	Key         string `json:"key"`
	Entity      string `json:"entity"`
	Title       string `json:"title"`
	Parent      string `json:"parent,omitempty"`
	Admin       bool   `json:"admin,omitempty"`
	Description string `json:"description,omitempty"`
	// Icon is an opaque UI icon name (e.g. "database") the frontend resolves to
	// a glyph. Emitted verbatim from the entity's x-clicky-icon annotation.
	Icon  string `json:"icon,omitempty"`
	Order int    `json:"-"`
}

ClickySurface describes a UI surface resolved from clicky entity metadata.

type CommandOpenAPIMeta

type CommandOpenAPIMeta struct {
	Entity  string
	Parent  string
	Aliases []string
	Admin   bool
	// Icon is an opaque UI icon name (e.g. "database"); clicky never interprets
	// it — it is emitted verbatim on the surface for the frontend to resolve.
	Icon string
	// Title overrides the auto-generated surface title when non-empty.
	Title              string
	Verb               string
	Method             string
	Scope              string
	ActionName         string
	IDParam            string
	SupportsLookup     bool
	SupportsFilterMode bool
	// OptionalID is true when the operation's positional id is optional
	// (WithOptionalID). Such operations are invocable without an id, so the
	// generated REST path must NOT carry an {id} segment — otherwise the no-id
	// call collides with the entity's get-by-id route.
	OptionalID bool
	// ToolGroup names the group an operation belongs to. It is set on the entity
	// and inherited by every operation (a per-action override may replace it).
	// AI tool-preference layers use it to enable/disable a set of related tools
	// as one unit.
	ToolGroup string
}

CommandOpenAPIMeta is the clicky-specific metadata attached to generated Cobra commands so the RPC/OpenAPI layer can expose entity semantics without guessing from paths or operationIds.

func GetCommandOpenAPIMeta

func GetCommandOpenAPIMeta(cmd *cobra.Command) *CommandOpenAPIMeta

type Config

type Config struct {
	// DefaultMethod is the default HTTP method for operations.
	DefaultMethod string
	// PathPrefix is the path prefix for REST APIs.
	PathPrefix string
	// AutoGeneratePaths controls whether REST paths are generated from the
	// command hierarchy.
	AutoGeneratePaths bool
	// DefaultTags are applied to all operations.
	DefaultTags []string
}

Config holds configuration for RPC conversion.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a sensible default configuration.

type ContextDataFunc

type ContextDataFunc = func(ctx context.Context, flags map[string]string, args []string) (any, error)

ContextDataFunc is the context-aware data closure stored for a command. Its signature matches rpc.ContextDataFunc structurally; the RPC converter converts it when wiring RPCOperation.ContextDataFunc. Defined here (the root package) to avoid a clicky -> clicky/rpc import cycle.

func GetContextDataFunc

func GetContextDataFunc(cmd *cobra.Command) ContextDataFunc

GetContextDataFunc returns the context-aware data function registered for a command, if any. Used by the RPC converter to wire ContextDataFunc.

type ContextFilter

type ContextFilter[ListOpts any] interface {
	OptionsWithContext(ctx context.Context, opts ListOpts) map[string]api.Textable
}

ContextFilter is an OPTIONAL extension a Filter may implement when its option set is resolved from request-scoped state (e.g. a per-request database handle) rather than a process global. When the lookup handler runs with a context (the RPC path feeds r.Context(), the CLI feeds cmd.Context()) it prefers OptionsWithContext over Options; filters that don't implement it fall back to the plain Options() path unchanged.

type ContextLookupFunc

type ContextLookupFunc = func(ctx context.Context, flags map[string]string, args []string) (any, error)

ContextLookupFunc is the context-aware variant of a filter lookup closure. When present, the RPC executor prefers it over LookupFunc and feeds the request's context.Context (r.Context() over HTTP, cmd.Context() on the CLI) so a filter's Options/OptionsWithQuery can resolve request-scoped state (e.g. the per-request database handle) instead of a process global.

func GetContextLookupFunc

func GetContextLookupFunc(cmd *cobra.Command) ContextLookupFunc

GetContextLookupFunc returns the context-aware lookup function registered for a command, if any. Used by the RPC converter to wire ContextLookupFunc.

type ContextSearchableFilter

type ContextSearchableFilter[ListOpts any] interface {
	OptionsWithQueryAndContext(ctx context.Context, opts ListOpts, query string, limit int) (options map[string]api.Textable, total int)
}

ContextSearchableFilter is the context-aware counterpart of SearchableFilter: a large/searchable filter whose head and server-side search both need request-scoped state. The lookup handler prefers it over OptionsWithQuery when a context is available.

type DataFunc

type DataFunc func(flags map[string]string, args []string) (any, error)

DataFunc is a function that returns structured data directly, bypassing stdout capture. Used by commands registered via AddCommand to provide data to the HTTP handler. (ContextDataFunc / ContextLookupFunc — the context-aware variants — are defined alongside the command registry in command.go.)

type DynamicEntityBuilder

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

DynamicEntityBuilder assembles a schema-driven entity and registers it through RegisterDynamicEntity. Filters referenced by x-clicky-filter must be registered (RegisterFilter / RegisterFilterSpec) before Register is called.

func NewDynamicEntity

func NewDynamicEntity(name string, schema []byte) *DynamicEntityBuilder

NewDynamicEntity starts building a dynamic entity from a JSON schema.

func (*DynamicEntityBuilder) Get

func (*DynamicEntityBuilder) List

func (*DynamicEntityBuilder) Register

func (b *DynamicEntityBuilder) Register()

Register parses the schema, generates the runtime types, wires the named filters, and registers the entity. It panics on an invalid schema or an unregistered filter reference — both are setup-time wiring errors.

type DynamicEntitySpec

type DynamicEntitySpec struct {
	Name    string
	Parent  string
	Aliases []string
	// Icon is an opaque UI icon name emitted on the surface (x-clicky.surfaces[].icon).
	Icon string
	// Title overrides the auto-generated surface title when non-empty.
	Title    string
	ListType reflect.Type // generated struct type: flag binding + OpenAPI params
	ItemType reflect.Type // generated item struct type for the response schema
	List     ContextDataFunc
	Get      ContextDataFunc
	Filters  []DynamicFilter
	// FilterRefs maps a filter key to the reusable named filter backing it, so
	// the OpenAPI layer can emit an x-clicky-lookup $ref (see EntityInfo.FilterRefs).
	FilterRefs map[string]string
}

DynamicEntitySpec is the type-erased description of a schema-driven entity, assembled by the entity package's NewDynamicEntity builder. It reuses the existing pipeline: ListType drives CLI flag binding and OpenAPI parameters, ItemType drives the response schema, and Filters feed the shared lookup engine.

type DynamicFilter

type DynamicFilter struct {
	Key        string
	Label      string
	Type       string
	Multi      bool
	Searchable bool
	// Options returns the head set (query == "") or search matches, plus the true
	// total behind the head. Required.
	Options func(ctx context.Context, flags map[string]string, query string, limit int) (map[string]api.Textable, int)
	// Selected labels the currently-selected value(s) of this filter's key.
	// Optional; nil means "no selection rendering".
	Selected func(ctx context.Context, flags map[string]string) map[string]api.Textable
}

DynamicFilter describes one filter on a dynamic (schema-driven) entity. The Options/Selected closures resolve from the request flag map — they are built by the entity package from a reusable named filter — so the type-agnostic filter logic stays out of this package while reusing the shared lookup builder.

type DynamicGetFunc

type DynamicGetFunc func(ctx context.Context, id string) (map[string]any, error)

DynamicGetFunc fetches a single dynamic-entity row by id.

type DynamicListFunc

type DynamicListFunc func(ctx context.Context, opts map[string]string) ([]map[string]any, error)

DynamicListFunc lists rows of a dynamic entity for the given flag map.

type Entity

type Entity[T EntityItem, ListOpts any, R any] struct {
	Name string
	// Parent, when set, nests the entity's command under a parent cobra command
	// with that name. The parent command is created lazily by GenerateCLI if it
	// does not already exist.
	Parent string
	// Aliases applied to the generated entity cobra command.
	Aliases []string
	List    func(opts ListOpts) ([]T, error)
	Get     func(id string) (R, error)
	// ListWithContext / GetWithContext / GetWithFlagsAndContext are context-aware
	// variants of List / Get / GetWithFlags. When set they take precedence over
	// their non-context counterparts and receive the request-scoped
	// context.Context (r.Context() over HTTP, cmd.Context() on the CLI). Use them
	// to resolve per-request state (e.g. a database/config bundle) instead of
	// reaching for process globals.
	ListWithContext        func(ctx context.Context, opts ListOpts) ([]T, error)
	ListPaged              func(opts ListOpts) (PagedResult[T], error)
	ListPagedWithContext   func(ctx context.Context, opts ListOpts) (PagedResult[T], error)
	GetWithContext         func(ctx context.Context, id string) (R, error)
	GetWithFlagsAndContext func(ctx context.Context, id string, flags map[string]string) (R, error)
	// GetFlags, when non-nil, is a zero-value struct value implementing
	// ActionFlags whose `flag:"..."` tagged fields are registered as
	// CLI flags on the generated `get` subcommand. The parsed values are
	// passed into GetWithFlags.
	//
	// GetWithFlags is mutually exclusive with Get: when both are set,
	// GetWithFlags wins. Set GetFlags to declare the flag schema and
	// GetWithFlags to receive the typed values.
	GetFlags     ActionFlags
	GetWithFlags func(id string, flags map[string]string) (R, error)
	Create       func(body map[string]any) (R, error)
	Update       func(id string, body map[string]any) (R, error)
	Delete       func(id string) error
	// CreateWithContext / UpdateWithContext / DeleteWithContext are context-aware
	// variants of Create / Update / Delete, mirroring the read-side seam above.
	// When set they take precedence and receive the request-scoped context, so
	// writes target the request's environment rather than a process global.
	CreateWithContext func(ctx context.Context, body map[string]any) (R, error)
	UpdateWithContext func(ctx context.Context, id string, body map[string]any) (R, error)
	DeleteWithContext func(ctx context.Context, id string) error
	Filters           []Filter[ListOpts]

	Actions     []EntityAction
	BulkActions []EntityBulkAction

	// Admin groups admin-only operations (inspect, configure, etc.)
	// that produce different views/columns from the main CRUD operations.
	// Generates subcommands under an "admin" subgroup and routes like /entity/admin/{verb}.
	Admin *Entity[T, ListOpts, R]

	// ValidArgs provides shell completion for the ID argument.
	ValidArgs func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)

	// ToolGroup names the group all of this entity's operations belong to. It is
	// inherited by every generated operation; an action may override it via
	// WithToolGroup. AI tool-preference layers use it to toggle related tools
	// together.
	ToolGroup string
}

Entity configures a CRUD resource. All function fields are optional. T is the type returned by List and must implement EntityItem. R is the type returned by Get/Create/Update.

type EntityAction

type EntityAction interface {
	// contains filtered or unexported methods
}

EntityAction is the type-erased registration surface for custom entity actions. Use Action or ActionWithFlags to construct values.

type EntityBuilder

type EntityBuilder[T EntityItem, ListOpts any, R any] struct {
	// contains filtered or unexported fields
}

EntityBuilder provides a fluent API for registering typed entities.

func NewEntity

func NewEntity[T EntityItem, ListOpts any, R any](name string) *EntityBuilder[T, ListOpts, R]

NewEntity starts a typed entity registration. The resulting entity is served identically on the CLI and over HTTP/RPC. See the package github.com/flanksource/clicky/entity for an authoring guide, the best practices for operations that behave well on both surfaces, and runnable examples.

func (*EntityBuilder[T, ListOpts, R]) Admin

func (b *EntityBuilder[T, ListOpts, R]) Admin(admin Entity[T, ListOpts, R]) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Aliases

func (b *EntityBuilder[T, ListOpts, R]) Aliases(aliases ...string) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Build

func (b *EntityBuilder[T, ListOpts, R]) Build() Entity[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Create

func (b *EntityBuilder[T, ListOpts, R]) Create(fn func(map[string]any) (R, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) CreateWithContext

func (b *EntityBuilder[T, ListOpts, R]) CreateWithContext(fn func(context.Context, map[string]any) (R, error)) *EntityBuilder[T, ListOpts, R]

CreateWithContext sets the context-aware create handler. The context carries request-scoped state (e.g. the originating *http.Request via rpc.RequestFromContext), letting handlers read the raw nested JSON body the executor would otherwise flatten to string flags.

func (*EntityBuilder[T, ListOpts, R]) Delete

func (b *EntityBuilder[T, ListOpts, R]) Delete(fn func(string) error) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) DeleteWithContext

func (b *EntityBuilder[T, ListOpts, R]) DeleteWithContext(fn func(context.Context, string) error) *EntityBuilder[T, ListOpts, R]

DeleteWithContext sets the context-aware delete handler. See CreateWithContext.

func (*EntityBuilder[T, ListOpts, R]) Filters

func (b *EntityBuilder[T, ListOpts, R]) Filters(filters ...Filter[ListOpts]) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Get

func (b *EntityBuilder[T, ListOpts, R]) Get(fn func(string) (R, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) GetWithContext

func (b *EntityBuilder[T, ListOpts, R]) GetWithContext(fn func(context.Context, string) (R, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) GetWithFlags

func (b *EntityBuilder[T, ListOpts, R]) GetWithFlags(flags ActionFlags, fn func(string, map[string]string) (R, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) List

func (b *EntityBuilder[T, ListOpts, R]) List(fn func(ListOpts) ([]T, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) ListPaged

func (b *EntityBuilder[T, ListOpts, R]) ListPaged(fn func(ListOpts) (PagedResult[T], error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) ListPagedWithContext

func (b *EntityBuilder[T, ListOpts, R]) ListPagedWithContext(fn func(context.Context, ListOpts) (PagedResult[T], error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) ListWithContext

func (b *EntityBuilder[T, ListOpts, R]) ListWithContext(fn func(context.Context, ListOpts) ([]T, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Parent

func (b *EntityBuilder[T, ListOpts, R]) Parent(parent string) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) Register

func (b *EntityBuilder[T, ListOpts, R]) Register()

func (*EntityBuilder[T, ListOpts, R]) ToolGroup

func (b *EntityBuilder[T, ListOpts, R]) ToolGroup(group string) *EntityBuilder[T, ListOpts, R]

ToolGroup names the group this entity's operations belong to. Every generated operation inherits it (an action can override it via WithToolGroup). AI tool-preference layers use the group to enable/disable related tools together.

func (*EntityBuilder[T, ListOpts, R]) Update

func (b *EntityBuilder[T, ListOpts, R]) Update(fn func(string, map[string]any) (R, error)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) UpdateWithContext

func (b *EntityBuilder[T, ListOpts, R]) UpdateWithContext(fn func(context.Context, string, map[string]any) (R, error)) *EntityBuilder[T, ListOpts, R]

UpdateWithContext sets the context-aware update handler. See CreateWithContext.

func (*EntityBuilder[T, ListOpts, R]) ValidArgs

func (b *EntityBuilder[T, ListOpts, R]) ValidArgs(fn func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective)) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) WithAction

func (b *EntityBuilder[T, ListOpts, R]) WithAction(action EntityAction) *EntityBuilder[T, ListOpts, R]

func (*EntityBuilder[T, ListOpts, R]) WithBulkAction

func (b *EntityBuilder[T, ListOpts, R]) WithBulkAction(action EntityBulkAction) *EntityBuilder[T, ListOpts, R]

type EntityBulkAction

type EntityBulkAction interface {
	// contains filtered or unexported methods
}

EntityBulkAction is the type-erased registration surface for custom bulk actions. Use BulkAction or BulkFilterAction to construct values.

type EntityInfo

type EntityInfo struct {
	Name    string
	Parent  string
	Aliases []string
	// Icon is an opaque UI icon name carried through to the OpenAPI surface
	// (x-clicky.surfaces[].icon). Empty for entities that declare no icon.
	Icon string
	// Title overrides the auto-generated surface title when non-empty.
	Title       string
	Type        reflect.Type
	ListType    reflect.Type
	Operations  []EntityOperation
	Actions     []ActionInfo
	BulkActions []BulkActionInfo
	ValidArgs   func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
	IsAdmin     bool
	// FilterRefs maps a list filter's flag key to the name of the reusable named
	// filter backing it (when the filter was attached via the entity package's
	// Use/As helper). The OpenAPI layer uses it to emit an x-clicky-lookup $ref
	// to the shared filter definition. Nil when no filters reference a named one.
	FilterRefs map[string]string
	// ToolGroup names the group all of this entity's operations belong to. It is
	// inherited by every generated operation (an action may override it) and is
	// used by AI tool-preference layers to toggle related tools together.
	ToolGroup string
}

EntityInfo is the type-erased representation stored in the registry.

func GetEntities

func GetEntities() []EntityInfo

GetEntities returns all registered entities.

func GetEntity

func GetEntity(name string) (EntityInfo, bool)

GetEntity returns the registered entity with the given name (or alias) and whether it was found.

type EntityItem

type EntityItem interface {
	GetID() string
	GetName() string
}

EntityItem is the interface that all entity types must implement.

type EntityOperation

type EntityOperation struct {
	Verb     string // "list", "get", "create", "update", "delete"
	Method   string // Optional explicit HTTP method for generated RPC/OpenAPI routes.
	DataFunc func(flags map[string]string, args []string) (any, error)
	// ContextDataFunc, when set, is preferred over DataFunc by both the CLI run
	// path (fed cmd.Context()) and the RPC executor (fed r.Context()). Lets the
	// operation resolve request-scoped state instead of process globals.
	ContextDataFunc ContextDataFunc
	// FlagsType, when non-nil, binds typed flags from the given struct type
	// onto the generated cobra command and collects their values into the
	// flag map passed to DataFunc. Used by actions that implement
	// ActionFlags; ignored for the built-in CRUD verbs.
	FlagsType  reflect.Type
	LookupFunc func(flags map[string]string, args []string) (any, error)
	// ContextLookupFunc, when set, is the context-aware filter lookup closure.
	// The RPC converter wires it onto RPCOperation.ContextLookupFunc and the
	// executor prefers it over LookupFunc, feeding the request context.
	ContextLookupFunc ContextLookupFunc
	BindCompletions   func(cmd *cobra.Command)
	ResponseType      reflect.Type
	ResponseArray     bool
	ResponsePaged     bool
	ResponseEntityID  bool
}

EntityOperation represents a single CRUD operation.

type ExecutableCommand

type ExecutableCommand interface {
	// Execute runs the command with the given options, capturing everything
	// written to os.Stdout / os.Stderr (including direct writes, not just the
	// cobra writers). It is safe for concurrent callers: implementations that
	// swap process-global file descriptors serialize internally.
	Execute(ctx context.Context, opts ExecuteOptions) (stdout, stderr string, err error)

	// Path is the full space-joined command path ("user create").
	Path() string
	// Name is the leaf command name ("create").
	Name() string
	// RootName is the top-level application command name ("xero-cli"), used for
	// the MCP tool title.
	RootName() string

	// Runnable reports whether the command has a Run/RunE (not a pure group).
	Runnable() bool
	// Hidden reports whether the command is hidden from help/listing.
	Hidden() bool

	// IsBoolFlag reports whether the named flag is a boolean flag. Used by the
	// CLI-string reconstruction to render "--flag" vs "--flag=value".
	IsBoolFlag(name string) bool
}

ExecutableCommand is the transport-neutral handle an RPCOperation holds onto in place of a concrete *cobra.Command. It encapsulates everything the execution path (RPC executor, MCP server) and the tool-surfacing layers (aichat) need, so those packages no longer reference cobra directly.

The cobra-specific machinery — the global os.Stdout/os.Stderr pipe swap, flag copying, state reset — lives entirely inside the CobraExecutableCommand implementation (rpc package).

type ExecuteOptions

type ExecuteOptions struct {
	// Args are the positional arguments.
	Args []string
	// Flags maps flag name -> string value. A flag absent on the command is
	// silently skipped (path params and other non-flag keys may appear here);
	// a present flag whose value fails to parse returns an error.
	Flags map[string]string
	// FormatOverrides are best-effort flag values applied before execution
	// (e.g. {"format":"markdown","no-color":"true"}). A flag absent on the
	// command is silently skipped. Used by the MCP server's format forcing.
	FormatOverrides map[string]string
}

ExecuteOptions controls how an ExecutableCommand runs one invocation. It is the transport-neutral subset of what the RPC executor and MCP server previously poked directly onto a *cobra.Command.

type Filter

type Filter[ListOpts any] interface {
	Key() string
	Label() string
	Lookup(opts *ListOpts) (map[string]api.Textable, error)
	Options(opts ListOpts) map[string]api.Textable
}

Filter resolves raw entity option values into the backend-specific shape and exposes UI metadata for the currently selected and available values.

func LiftFilters

func LiftFilters[Outer any, Inner any](
	filters []Filter[Inner],
	project func(*Outer) *Inner,
) []Filter[Outer]

LiftFilters adapts a Filter[Inner] slice so the same picker definitions work against an outer struct that embeds (or otherwise reaches) the inner filter struct. The project func extracts a *Inner from any *Outer the filter system hands us; lookup keys, labels, options, and selected-value reads/writes flow through unchanged. Use this to keep picker constructors scoped to the inner filter type while letting subcommand options structs — which also carry positional args, behaviour flags, etc. — satisfy Filterable[Outer].

type FilterContext

type FilterContext struct {
	// Context is the request context (HTTP request or CLI command). It may be
	// nil when a source is invoked outside a request (e.g. shell completion).
	Context context.Context
	// Key is the bound flag/field name this filter resolves on the current
	// entity (e.g. "owner" when a "users" filter is attached via .As("owner")).
	Key string
	// Params holds the current values of every flag/filter on the entity, so a
	// source can narrow its options against sibling selections (cascading).
	Params map[string]string
}

FilterContext carries the request-scoped state a FilterSource needs to resolve its options. It is the single representation both static and dynamic entities can produce: a static entity reflects its typed ListOpts into Params, a dynamic entity already holds the flag map.

func (FilterContext) Ctx

func (fc FilterContext) Ctx() context.Context

Ctx returns the context, defaulting to context.Background when none was set.

type FilterSource

type FilterSource interface {
	// Options returns the available options, optionally narrowed by query, and
	// the true total count behind the (possibly capped) returned set. An empty
	// query means "the head set". A non-positive limit means "no cap".
	Options(fc FilterContext, query string, limit int) (options map[string]api.Textable, total int, err error)
	// Resolve labels the currently-selected raw value(s) for display. A value
	// with no known label is echoed back as plain text.
	Resolve(fc FilterContext, values []string) (map[string]api.Textable, error)
}

FilterSource produces the options for a NamedFilter. Implementations are type-agnostic — they read FilterContext.Params rather than a typed struct — so one source serves any entity that references the filter.

func EntityOptions

func EntityOptions(entityName string) FilterSource

EntityOptions is a FilterSource that resolves its options from another registered entity: value = GetID(), label = PrettyShort()/GetName(). Search is an in-memory substring match over the source entity's list.

func FuncOptions

func FuncOptions(fn func(fc FilterContext, query string, limit int) (map[string]api.Textable, int, error)) FilterSource

FuncOptions is a FilterSource backed by a user-supplied function — the general escape hatch for DB-backed or computed options with server-side search. The function owns query matching and the limit cap.

func StaticOptions

func StaticOptions(options map[string]api.Textable) FilterSource

StaticOptions is a FilterSource backed by a fixed set of options, narrowed by case-insensitive substring match on the option key or its rendered label.

type FilterSourceSpec

type FilterSourceSpec struct {
	Kind string `json:"kind" yaml:"kind"`
	// Options is the value→label map for a static source.
	Options map[string]string `json:"options,omitempty" yaml:"options,omitempty"`
	// Entity is the source entity name for an entity source.
	Entity string `json:"entity,omitempty" yaml:"entity,omitempty"`
}

FilterSourceSpec is the declarative description of where a filter's options come from. "static" and "entity" are constructible from a spec; "func" is Go-only and only ever appears on the emit side (NamedFilter.Spec).

type FilterSpec

type FilterSpec struct {
	Name   string           `json:"name" yaml:"name"`
	Label  string           `json:"label,omitempty" yaml:"label,omitempty"`
	Type   string           `json:"type,omitempty" yaml:"type,omitempty"`
	Multi  bool             `json:"multi,omitempty" yaml:"multi,omitempty"`
	Source FilterSourceSpec `json:"source" yaml:"source"`
}

FilterSpec is the JSON/YAML-authorable form of a NamedFilter. It is also the shape emitted for OpenAPI (NamedFilter.Spec), so a Go-defined filter and a declaratively-defined one are indistinguishable to clients.

type Filterable

type Filterable[T any] interface {
	Filters() []Filter[T]
}

Filterable is implemented by AddNamedCommand options structs that want to expose typed filter lookups (dropdowns/typeaheads on the web UI, shell completions on the CLI) on the subcommand itself rather than inheriting only the parent entity's Filters slice. The same Filter[T] values that back an Entity's Filters slice work here; AddNamedCommand stores a LookupFunc in the lookupFuncRegistry and binds completions whenever an opts struct satisfies this interface.

Use this whenever a subcommand's flag surface diverges from its parent list view — typically when a positional argument pins one or more identifier filters and the subcommand only exposes the orthogonal (status, type, date range) lenses.

type Help

type Help interface {
	Help() api.Textable
}

Help is implemented by options structs that supply their own long-form command help. The generated command sets cmd.Long from Help().ANSI().

type MultiFilter

type MultiFilter []string

MultiFilter is a string-list flag whose values are interpreted with collections.MatchItems semantics: plain values include, !value excludes.

type Name

type Name interface {
	GetName() string
}

type NamedFilter

type NamedFilter struct {
	// Name is the unique registry key.
	Name string
	// Label is the human-facing control label. Defaults to Name when empty.
	Label string
	// Type is the UI control type: "select" (default), "multi-select", "date",
	// "from", or "to".
	Type string
	// Multi reports whether the control accepts multiple selections.
	Multi bool
	// Source resolves the filter's options.
	Source FilterSource
}

NamedFilter is a reusable filter definition registered under a unique name and attached to entities by reference. Authored in Go (RegisterFilter) or declaratively (RegisterFilterSpec), both normalize to this type.

func FilterFromSpec

func FilterFromSpec(s FilterSpec) (NamedFilter, error)

FilterFromSpec builds (but does not register) a NamedFilter from a declarative spec, validating the source kind.

func GetFilter

func GetFilter(name string) (NamedFilter, bool)

GetFilter returns the named filter and whether it was found.

func MustGetFilter

func MustGetFilter(name string) NamedFilter

MustGetFilter returns the named filter, panicking if it is not registered. Used at attach time (Use/.As), where an unknown reference is a wiring bug.

func (NamedFilter) Spec

func (f NamedFilter) Spec() FilterSpec

Spec emits the declarative form of a NamedFilter, used for OpenAPI generation and round-tripping. A func/custom source emits its kind without detail (its options are resolved server-side via the lookup endpoint).

type PageInfo

type PageInfo struct {
	Limit  int   `json:"limit"`
	Offset int   `json:"offset"`
	Total  int64 `json:"total"`
}

PageInfo carries the effective paging window for a list response.

type Paged

type Paged interface {
	PageMetadata() PageInfo
	PageRows() any
}

Paged is implemented by list results that carry total-count metadata.

type PagedResult

type PagedResult[T any] struct {
	Data []T      `json:"data"`
	Page PageInfo `json:"page"`
}

PagedResult is a typed list response with paging metadata.

func NewPagedResult

func NewPagedResult[T any](rows []T, limit, offset int, total int64) PagedResult[T]

NewPagedResult returns a paged result with a stable non-nil data array.

func (PagedResult[T]) PageMetadata

func (p PagedResult[T]) PageMetadata() PageInfo

func (PagedResult[T]) PageRows

func (p PagedResult[T]) PageRows() any

type Property

type Property struct {
	Type        string      `json:"type"`
	Description string      `json:"description"`
	Enum        []string    `json:"enum,omitempty"`
	Default     interface{} `json:"default,omitempty"`
}

Property represents a JSON schema property.

type RPCOperation

type RPCOperation struct {
	Name              string               `json:"name"`
	Description       string               `json:"description"`
	Parameters        []RPCParameter       `json:"parameters"`
	Schema            Schema               `json:"schema"`
	Command           ExecutableCommand    `json:"-"`                // Transport-neutral handle to the original command
	Path              string               `json:"path,omitempty"`   // For REST APIs
	Method            string               `json:"method,omitempty"` // HTTP method
	Tags              []string             `json:"tags,omitempty"`   // OpenAPI tags (auto-derived from the parent command)
	Group             string               `json:"group,omitempty"`  // tool-group this operation belongs to (clicky/tool-group)
	DataFunc          DataFunc             `json:"-"`                // Direct data provider, bypasses stdout capture
	ContextDataFunc   ContextDataFunc      `json:"-"`                // Context-aware data provider; preferred over DataFunc when set
	LookupFunc        DataFunc             `json:"-"`                // Direct filter metadata provider
	ContextLookupFunc ContextLookupFunc    `json:"-"`                // Context-aware filter metadata provider; preferred over LookupFunc when set
	Clicky            *ClickyOperationMeta `json:"-"`                // Entity semantics used for OpenAPI extensions
	ResponseType      reflect.Type         `json:"-"`                // Static response type for OpenAPI generation
	ResponseArray     bool                 `json:"-"`                // Response body is an array of ResponseType
	ResponsePaged     bool                 `json:"-"`                // Response body is a paged envelope around ResponseType rows
	ResponseEntityID  bool                 `json:"-"`                // Array item schema should include Clicky _id metadata
}

RPCOperation represents a generic RPC operation that can be converted to various formats (OpenAPI, MCP tools, …).

type RPCParameter

type RPCParameter struct {
	Name        string      `json:"name"`
	Type        string      `json:"type"`
	Description string      `json:"description"`
	Required    bool        `json:"required"`
	Default     interface{} `json:"default,omitempty"`
	In          string      `json:"in,omitempty"` // "query", "path", "header" for REST
}

RPCParameter represents a parameter in an RPC operation.

type RPCService

type RPCService struct {
	Name        string         `json:"name"`
	Version     string         `json:"version"`
	Description string         `json:"description"`
	Operations  []RPCOperation `json:"operations"`
}

RPCService represents a collection of RPC operations.

type ResponseOpenAPIMeta

type ResponseOpenAPIMeta struct {
	Type     reflect.Type
	Array    bool
	Paged    bool
	EntityID bool
}

ResponseOpenAPIMeta describes the static response type for a generated command. It is consumed by the RPC/OpenAPI layer without executing handlers.

func GetCommandResponseMeta

func GetCommandResponseMeta(cmd *cobra.Command) *ResponseOpenAPIMeta

GetCommandResponseMeta returns static response metadata attached to a command.

type Schema

type Schema struct {
	Type       string              `json:"type"`
	Properties map[string]Property `json:"properties"`
	Required   []string            `json:"required"`
}

Schema represents a JSON schema for operation input/output.

type SearchableFilter

type SearchableFilter[ListOpts any] interface {
	OptionsWithQuery(opts ListOpts, query string, limit int) (options map[string]api.Textable, total int)
}

SearchableFilter is an OPTIONAL extension a Filter may implement when its option set is too large to enumerate in one shot (e.g. a SQL DISTINCT column with thousands of values). The lookup handler type-asserts to it; filters that don't implement it keep the plain Options() behaviour unchanged.

OptionsWithQuery returns at most limit options. An empty query means "the head set": the first limit options plus total = the true distinct count, so the UI can show "… and N more". A non-empty query returns up to limit options matching it (server-side search), letting the UI surface values that sort beyond the head. total is only meaningful for the head (query == "") call.

type TypedFilter

type TypedFilter[ListOpts any] interface {
	LookupType() string
}

TypedFilter is an OPTIONAL extension a Filter may implement when the underlying flag type is intentionally broader than the UI control type. For example, date-math flags often remain strings at the CLI layer while the web lookup response should still advertise a from/to date-range control.

Jump to

Keyboard shortcuts

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