flags

package
v4.0.1 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 7 Imported by: 0

README

GWC | Feature Flags

The flags package provides typed browser-visible feature flag and experiment helpers.

Use it for evaluated, non-secret rollout decisions that are safe to transfer through SSR bootstrap, hydrated state, or browser storage. Keep secret policy, entitlement checks, and server-only rollout rules on the application server.

Public APIs

github.com/monstercameron/GoWebComponents/flags (package flags)
  • Functions: BuildSet, UseExperiment, UseFlag, UseRegistry
  • Types: Assignment, Experiment, ExperimentHandle, Flag, FlagHandle, Registry, Set, Variant

Example

Inside a component render:

registry := flags.UseRegistry(flags.BuildSet(
	map[string]flags.Flag{
		"new-nav": {Enabled: true, Value: "compact"},
	},
	map[string]flags.Experiment{
		"pricing-copy": {
			Enabled: true,
			Salt:    "2026-06",
			Variants: []flags.Variant{
				{Name: "control", Weight: 50},
				{Name: "direct", Weight: 50},
			},
		},
	},
))

newNav := flags.UseFlag("new-nav", false)
assignment := registry.Get().GetAssignment("pricing-copy", userID)

File Map

flags/
|-- doc.go
|-- flags.go
|-- flags_test.go
\-- README.md

Documentation

Overview

Package flags provides browser-visible feature flag and experiment helpers.

The package intentionally evaluates only public, non-secret decisions that are safe to transfer through SSR bootstrap, browser storage, or client state. Server-side policy, entitlement checks, and secret rollout rules remain application-owned.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Assignment

type Assignment struct {
	Experiment string
	Variant    string
	Value      string
	Enabled    bool
	Bucket     int
	Reason     string
}

Assignment describes an evaluated experiment outcome.

type Experiment

type Experiment struct {
	Enabled  bool
	Salt     string
	Variants []Variant
}

Experiment describes one deterministic weighted experiment.

type ExperimentHandle

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

ExperimentHandle exposes one hook-selected experiment assignment.

func UseExperiment

func UseExperiment(parseName string, parseSubject string) ExperimentHandle

UseExperiment subscribes the current component to one deterministic experiment assignment.

func (ExperimentHandle) Get

func (parseHandle ExperimentHandle) Get() Assignment

Get returns the current experiment assignment.

type Flag

type Flag struct {
	Enabled bool
	Value   string
	Reason  string
}

Flag describes one evaluated browser-visible feature flag.

type FlagHandle

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

FlagHandle exposes one hook-selected flag.

func UseFlag

func UseFlag(parseName string, parseFallback bool) FlagHandle

UseFlag subscribes the current component to one shared feature flag.

func (FlagHandle) Enabled

func (parseHandle FlagHandle) Enabled() bool

Enabled returns whether the current flag is enabled.

func (FlagHandle) Get

func (parseHandle FlagHandle) Get() Flag

Get returns the current flag value.

func (FlagHandle) Value

func (parseHandle FlagHandle) Value(parseFallback string) string

Value returns the current flag value or the fallback when blank.

type Registry

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

Registry exposes the current shared flag set.

func UseRegistry

func UseRegistry(parseInitial Set) Registry

UseRegistry subscribes the current component to the shared flag registry.

func (Registry) Get

func (parseRegistry Registry) Get() Set

Get returns the current registry set.

func (Registry) Set

func (parseRegistry Registry) Set(parseSet Set)

Set replaces the current registry set with an immutable copy.

func (Registry) Update

func (parseRegistry Registry) Update(parseUpdate func(Set) Set)

Update replaces the current registry set using the previous value.

type RemoteFetchFunc

type RemoteFetchFunc func(parseCtx context.Context) (Set, error)

RemoteFetchFunc is the caller-supplied transport hook that retrieves a fresh Set from any source (HTTP poll, SSE stream, file watch, etc.). The package is transport-agnostic; it calls this function and reacts to the result.

type RemoteProvider

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

RemoteProvider holds a live, mutex-guarded snapshot of a remotely managed flag Set, and notifies subscribers on every successful refresh. A remote payload that sets a flag's Enabled field to false acts as a kill switch: IsKilled returns true and all subscribers are notified without requiring a page reload.

func NewRemoteProvider

func NewRemoteProvider(parseInitial Set, parseFetch RemoteFetchFunc) *RemoteProvider

NewRemoteProvider returns a RemoteProvider seeded with parseInitial as the last-known-good Set. parseFetch supplies updates; it is called once per Refresh and once per Poll interval.

func (*RemoteProvider) Age

func (parseP *RemoteProvider) Age(parseNow time.Time) time.Duration

Age returns the time elapsed since the last successful Refresh, measured relative to parseNow. Inject the same clock you pass to setNow when testing staleness.

func (*RemoteProvider) Current

func (parseP *RemoteProvider) Current() Set

Current returns the current last-known-good Set. It is always valid; even after a failed Refresh it holds the last successfully fetched value.

func (*RemoteProvider) IsKilled

func (parseP *RemoteProvider) IsKilled(parseFeature string) bool

IsKilled reports whether parseFeature is actively killed. A flag is considered killed when the current Set contains it AND its Enabled field is false. A missing flag is not killed — it is simply absent.

Kill-switch pattern: deploy a remote payload with the flag present and Enabled set to false. Components that gate a subtree on IsKilled and react via Subscribe will disable themselves immediately without a reload. Re-enabling the flag (Enabled true) in a subsequent payload un-kills it.

func (*RemoteProvider) IsStale

func (parseP *RemoteProvider) IsStale(parseMaxAge time.Duration, parseNow time.Time) bool

IsStale reports whether the last successful Refresh is older than parseMaxAge as of parseNow.

func (*RemoteProvider) LastError

func (parseP *RemoteProvider) LastError() error

LastError returns the error from the most recent failed Refresh, or nil if the last Refresh succeeded.

func (*RemoteProvider) Poll

func (parseP *RemoteProvider) Poll(
	parseCtx context.Context,
	parseInterval time.Duration,
	parseSleep func(context.Context, time.Duration) error,
) error

Poll calls Refresh on every parseInterval tick until parseCtx is cancelled. parseSleep is called between iterations; inject a no-op in tests so they run instantly. Errors from individual Refresh calls are recorded in LastError but do not stop the loop (outage resilience). Poll returns parseCtx.Err() when the context is done.

func (*RemoteProvider) Refresh

func (parseP *RemoteProvider) Refresh(parseCtx context.Context) error

Refresh calls the injected fetch function once. On success it replaces the current Set, records the update time, clears the last error, and notifies all subscribers. On failure it preserves the previous Set and last-known-good time, stores the error, and returns it. A failed refresh never clobbers good values.

func (*RemoteProvider) Subscribe

func (parseP *RemoteProvider) Subscribe(parseHandler func(Set)) (parseUnsubscribe func())

Subscribe registers parseHandler to be called after every successful Refresh. It returns an unsubscribe function; calling it removes the handler and is leak-free (the internal registry returns to baseline size). A panicking handler does not interrupt delivery to the remaining ones.

type Set

type Set struct {
	Flags       map[string]Flag
	Experiments map[string]Experiment
}

Set stores browser-visible flags and experiments.

func BuildSet

func BuildSet(parseFlags map[string]Flag, parseExperiments map[string]Experiment) Set

BuildSet builds an immutable copy of flags and experiments.

func ParseRemoteSet

func ParseRemoteSet(parseRaw []byte) (Set, error)

ParseRemoteSet decodes parseRaw from the canonical remote payload shape:

{"flags": {"name": {"enabled": true, "value": "...", "reason": "..."}},
 "experiments": {"name": {"enabled": true, "salt": "...", "variants": [...]}}}

A malformed payload returns a descriptive error; the caller should keep the last-known-good Set rather than replacing it. ParseRemoteSet never panics; it is safe to call from any goroutine.

func (Set) GetAssignment

func (parseSet Set) GetAssignment(parseName string, parseSubject string) Assignment

GetAssignment returns a deterministic weighted experiment assignment.

func (Set) GetEnabled

func (parseSet Set) GetEnabled(parseName string, parseFallback bool) bool

GetEnabled returns whether a named flag is enabled.

func (Set) GetFlag

func (parseSet Set) GetFlag(parseName string, parseFallback Flag) Flag

GetFlag returns a named flag or the fallback when it is absent.

func (Set) GetValue

func (parseSet Set) GetValue(parseName string, parseFallback string) string

GetValue returns a named flag value or the fallback when absent.

type Variant

type Variant struct {
	Name   string
	Value  string
	Weight int
}

Variant describes one weighted experiment outcome.

Jump to

Keyboard shortcuts

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