Help us shape the future of Go! Take the 2026 Go Developer Survey

nodra

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 3 Imported by: 0

README

Nodra

Nodra is a concurrency-safe reactive state store for Go desktop applications. It provides Zustand-like ergonomics for MyGo apps while keeping state independent from the renderer and persistence backend.

简体中文 · 日本語 · 한국어 · Español

Install

go get github.com/yldm-tech/nodra@latest

The core package is github.com/yldm-tech/nodra. The optional inspector is github.com/yldm-tech/nodra/devtools.

Quick start

package main

import (
	"fmt"

	"github.com/yldm-tech/nodra"
)

type State struct{ Count int }

func main() {
	store := nodra.New(State{})
	defer store.Close()

	stop := store.Subscribe(func(s nodra.Snapshot[State]) {
		fmt.Println(s.Value.Count)
	})
	defer stop()

	_ = store.Update(func(s *State) { s.Count++ })
}

Core capabilities

  • Versioned Store[T] with synchronous subscriptions, bounded watches and read-only views.
  • Derived state, selectors, projections, field bindings and atomic transactions.
  • Undo/redo history with grouping, filtering and payload budgets.
  • JSON, versioned migration, encrypted, backup, journaled and conflict-checked persistence.
  • Debounced, cancelable and queued async actions with progress, retries and metrics.
  • Scope for ownership and shutdown ordering.
  • Optional MyGo invalidation helpers and a local authenticated DevTools inspector.

MyGo

stop := nodra.InvalidateSelector(store, window, func(s State) int {
	return s.Count
})
defer stop()

The integration targets MyGo v0.6.3. Keep temporary widget state in the view owner and commit only the edited field to the shared Store. See examples/mygo for a minimal native window.

Persistence

persistence := nodra.NewPersistence(store, nodra.FileStorage{Path: "state.json", Backup: true}, nodra.JSONCodec[State]{})
if err := persistence.Load(ctx); err != nil {
	// Handle a missing or invalid startup file before starting autosave.
}
writer, _ := persistence.AutoSaveAsync(ctx, nodra.AsyncOptions{Debounce: 100 * time.Millisecond})
defer writer.Close(shutdownCtx)

Use VersionedJSONCodec, AESGCMCodec, Journal, Durable and WithConflictDetection when the application needs migrations, encryption or stronger file recovery guarantees. Details are in docs/architecture.md.

Complete example

examples/workbench is the full Nodra + MyGo desktop project. It combines cloned state, local drafts, validated backup persistence, debounced and cancelable async search, async saves, scoped shutdown, native invalidation, English/Simplified Chinese localization and optional DevTools.

go run ./examples/workbench
go run ./examples/workbench -devtools

The smaller examples cover one integration at a time:

DevTools

inspector := devtools.New(devtools.Options{})
defer inspector.Close()
stop, err := devtools.Attach(inspector, "state", store.ReadOnly())
if err != nil {
	panic(err)
}
defer stop()
url, _ := inspector.Listen()
fmt.Println(url)

DevTools is read-only by default. AllowRestore explicitly enables typed JSON editing and snapshot restoration. See docs/devtools.md for redaction, limits, remote read-only mode and the JSON compatibility contract.

Documentation

Verification

go test ./...
go test -race ./...
go vet ./...
golangci-lint run ./...
go run golang.org/x/vuln/cmd/govulncheck@latest ./...

Nodra is one Go module: github.com/yldm-tech/nodra. The project requires Go 1.27.3 or newer and uses MyGo v0.6.3 for native integration.

Documentation

Overview

Package nodra is a concurrency-safe reactive state store for Go desktop applications.

The root package is the stable public API; implementation details are kept in internal/core. Start with New, Create or NewWithClone, then use Store.Get, Set, Update, UpdateErr, Patch, Reset, Transaction, Batch, BatchErr, Watch, Subscribe, SubscribeWith, SubscribeSnapshot, SubscribeMutations, Do, Action, TaskSnapshot, ActiveTasks and Close. ReadOnly exposes the observation-only surface. Derived values use Derive, DeriveProjection, Combine, Select, SubscribeSelector and SubscribeProjection. Persistence uses NewPersistence, JSONCodec, VersionedJSONCodec, FileStorage, CheckedFileStorage and AESGCMCodec. Async work uses NewAsyncAction, Task and Scope. MyGo redraws use Invalidate, InvalidateTarget, InvalidateSelector and InvalidateProjection.

The public method index and ownership/error contracts are in docs/public-api.md. Go's current generic type-alias documentation omits methods from the Store, History, Persistence and AsyncAction entries, so this package comment intentionally keeps the discoverable method index here too. The v1 support matrix is in docs/support.md.

Index

Constants

View Source
const (
	LatestWins ActionPolicy = core.LatestWins
	QueueAll   ActionPolicy = core.QueueAll

	TaskQueued     TaskPhase = core.TaskQueued
	TaskRunning    TaskPhase = core.TaskRunning
	TaskSucceeded  TaskPhase = core.TaskSucceeded
	TaskCanceled   TaskPhase = core.TaskCanceled
	TaskSuperseded TaskPhase = core.TaskSuperseded
	TaskFailed     TaskPhase = core.TaskFailed

	MutationDirect      MutationType = core.MutationDirect
	MutationPatch       MutationType = core.MutationPatch
	MutationAction      MutationType = core.MutationAction
	MutationReset       MutationType = core.MutationReset
	MutationTransaction MutationType = core.MutationTransaction
)

Variables

View Source
var (
	ErrNoUndo              = core.ErrNoUndo
	ErrNoRedo              = core.ErrNoRedo
	ErrSuperseded          = core.ErrSuperseded
	ErrInvalidAction       = core.ErrInvalidAction
	ErrQueueFull           = core.ErrQueueFull
	ErrUnsupportedVersion  = core.ErrUnsupportedVersion
	ErrMissingMigration    = core.ErrMissingMigration
	ErrClosed              = core.ErrClosed
	ErrFileConflict        = core.ErrFileConflict
	ErrHistoryBatch        = core.ErrHistoryBatch
	ErrHistoryGroup        = core.ErrHistoryGroup
	ErrInvalidBinding      = core.ErrInvalidBinding
	ErrInvalidHistory      = core.ErrInvalidHistory
	ErrInvalidKey          = core.ErrInvalidKey
	ErrInvalidPersistence  = core.ErrInvalidPersistence
	ErrNoBackup            = core.ErrNoBackup
	ErrPersistenceRecovery = core.ErrPersistenceRecovery
)

Functions

func Atomic2 added in v0.9.0

func Atomic2[A, B any](first *Store[A], second *Store[B], fn func(*A, *B) error) error

func AtomicGroup added in v0.9.0

func AtomicGroup(ops ...AtomicOp) error

func Invalidate added in v0.2.0

func Invalidate[T any](source Observable[T], window *mygo.Window) func()

func InvalidateProjection added in v0.6.0

func InvalidateProjection[T, V any](source Reader[T], window *mygo.Window, project func(T) V) func()

func InvalidateProjectionWith added in v0.6.0

func InvalidateProjectionWith[T, V any](source Reader[T], window *mygo.Window, project func(T) V, options SelectorOptions[V]) func()

func InvalidateSelector added in v0.5.0

func InvalidateSelector[T, V any](source Observable[T], window *mygo.Window, selector func(T) V) func()

func InvalidateSelectorWith added in v0.5.0

func InvalidateSelectorWith[T, V any](source Observable[T], window *mygo.Window, selector func(T) V, options SelectorOptions[V]) func()

func InvalidateTarget added in v0.9.0

func InvalidateTarget[T any](source Observable[T], target InvalidationTarget) func()

func SubscribeProjection added in v0.6.0

func SubscribeProjection[T, V any](source Reader[T], project func(T) V, fn func(V)) func()

func SubscribeProjectionWith added in v0.6.0

func SubscribeProjectionWith[T, V any](source Reader[T], project func(T) V, fn func(current, previous V), options SelectorOptions[V]) func()

func SubscribeSelector

func SubscribeSelector[T, S any](source Observable[T], selector func(T) S, fn func(S)) func()

func SubscribeSelectorWith

func SubscribeSelectorWith[T, V any](source Observable[T], selector func(T) V, fn func(current, previous V), options SelectorOptions[V]) func()

Types

type AESGCMCodec added in v0.9.0

type AESGCMCodec[T any] = core.AESGCMCodec[T]

AESGCMCodec encrypts another codec's bytes with authenticated AES-GCM.

type ActionEvent

type ActionEvent = core.ActionEvent

type ActionHooks

type ActionHooks = core.ActionHooks

type ActionMetrics added in v0.9.0

type ActionMetrics = core.ActionMetrics

type ActionOptions added in v0.6.0

type ActionOptions[I any] = core.ActionOptions[I]

type ActionPolicy added in v0.6.0

type ActionPolicy = core.ActionPolicy

type ActionTrace added in v0.6.0

type ActionTrace = core.ActionTrace

type AsyncAction added in v0.6.0

type AsyncAction[T, I, R any] = core.AsyncAction[T, I, R]

func NewAsyncAction added in v0.6.0

func NewAsyncAction[T, I, R any](store *Store[T], name string, work func(context.Context, I, func(float64)) (R, error), apply func(*T, R) error) (*AsyncAction[T, I, R], error)

func NewAsyncActionWithOptions added in v0.6.0

func NewAsyncActionWithOptions[T, I, R any](store *Store[T], name string, work func(context.Context, I, func(float64)) (R, error), apply func(*T, R) error, options ActionOptions[I]) (*AsyncAction[T, I, R], error)

type AsyncOptions added in v0.3.0

type AsyncOptions = core.AsyncOptions

type AsyncPersistence added in v0.3.0

type AsyncPersistence[T any] = core.AsyncPersistence[T]

type AsyncState added in v0.6.0

type AsyncState = core.AsyncState

type AtomicOp added in v0.9.0

type AtomicOp = core.AtomicOp

func AtomicUpdate added in v0.9.0

func AtomicUpdate[T any](store *Store[T], fn func(*T) error) AtomicOp

type CheckedFileStorage added in v0.7.0

type CheckedFileStorage = core.CheckedFileStorage

type Codec

type Codec[T any] = core.Codec[T]

type Definition

type Definition[T any] = core.Definition[T]

func Define

func Define[T any](id string, initial func() T) *Definition[T]

func DefineWithClone added in v0.3.0

func DefineWithClone[T any](id string, initial func() T, clone func(T) T) *Definition[T]

type Derived

type Derived[T, V any] = core.Derived[T, V]

func Combine added in v0.7.0

func Combine[A, B, V any](first Observable[A], second Observable[B], project func(A, B) V, equals func(V, V) bool) *Derived[Pair[A, B], V]

func Derive

func Derive[T, V any](source Observable[T], selector func(T) V, equals func(V, V) bool) *Derived[T, V]

func DeriveProjection added in v0.6.0

func DeriveProjection[T, V any](source Reader[T], project func(T) V, equals func(V, V) bool) *Derived[T, V]

type Field added in v0.7.0

type Field[T, V any] = core.Field[T, V]

func BindField added in v0.7.0

func BindField[T, V any](store *Store[T], read func(T) V, write func(*T, V) error) (Field[T, V], error)

type FileConflictError added in v0.7.0

type FileConflictError = core.FileConflictError

type FileFingerprint added in v0.7.0

type FileFingerprint = core.FileFingerprint

type FileStorage

type FileStorage = core.FileStorage

type History

type History[T any] = core.History[T]

func Track

func Track[T any](store *Store[T], limit int) *History[T]

func TrackWith added in v0.6.0

func TrackWith[T any](store *Store[T], options HistoryOptions[T]) *History[T]

type HistoryOptions added in v0.6.0

type HistoryOptions[T any] = core.HistoryOptions[T]

type HistoryState added in v0.7.0

type HistoryState = core.HistoryState

type InvalidationTarget added in v0.9.0

type InvalidationTarget = core.InvalidationTarget

type JSONCodec

type JSONCodec[T any] = core.JSONCodec[T]

type JSONMigration added in v0.3.0

type JSONMigration = core.JSONMigration

type KeyProvider added in v0.9.0

type KeyProvider = core.KeyProvider

type LoadError added in v0.6.0

type LoadError = core.LoadError

type MemoryKeyProvider added in v0.9.0

type MemoryKeyProvider = core.MemoryKeyProvider

func NewMemoryKeyProvider added in v0.9.0

func NewMemoryKeyProvider(id string, key []byte) (*MemoryKeyProvider, error)

type MetricsSink added in v0.9.0

type MetricsSink = core.MetricsSink

type Mutation

type Mutation[T any] = core.Mutation[T]

type MutationType

type MutationType = core.MutationType

type Observable added in v0.7.0

type Observable[T any] = core.Observable[T]

type Pair added in v0.7.0

type Pair[A, B any] = core.Pair[A, B]

type PanicError added in v0.6.0

type PanicError = core.PanicError

type Persistence

type Persistence[T any] = core.Persistence[T]

Persistence connects a Store to a byte Storage and Codec.

func NewPersistence

func NewPersistence[T any](store *Store[T], storage Storage, codec Codec[T]) *Persistence[T]

func NewPersistenceWithOptions added in v0.6.0

func NewPersistenceWithOptions[T any](store *Store[T], storage Storage, codec Codec[T], options PersistenceOptions[T]) *Persistence[T]

type PersistenceOptions added in v0.6.0

type PersistenceOptions[T any] = core.PersistenceOptions[T]

type PersistenceState added in v0.7.0

type PersistenceState = core.PersistenceState

type ReadOnly added in v0.6.0

type ReadOnly[T any] = core.ReadOnly[T]

type Reader added in v0.6.0

type Reader[T any] = core.Reader[T]

type RecoveryError added in v0.9.0

type RecoveryError = core.RecoveryError

type RetryOptions added in v0.9.0

type RetryOptions = core.RetryOptions

type Scope added in v0.6.0

type Scope = core.Scope

func NewScope added in v0.6.0

func NewScope() *Scope

type SelectorOptions

type SelectorOptions[V any] = core.SelectorOptions[V]

type Snapshot

type Snapshot[T any] = core.Snapshot[T]

func Select added in v0.6.0

func Select[T, V any](source Reader[T], project func(T) V) Snapshot[V]

type Storage

type Storage = core.Storage

type Store

type Store[T any] = core.Store[T]

Store owns versioned state and synchronously publishes snapshots to its observers. See Store methods in docs/public-api.md when the Go documentation renderer omits methods for this root-package alias.

func Create

func Create[T any](initial T) *Store[T]

func New

func New[T any](initial T) *Store[T]

func NewWithClone added in v0.3.0

func NewWithClone[T any](initial T, clone func(T) T) *Store[T]

type SubscribeOptions

type SubscribeOptions[T any] = core.SubscribeOptions[T]

type Task added in v0.6.0

type Task = core.Task

type TaskMetrics added in v0.7.0

type TaskMetrics = core.TaskMetrics

type TaskPhase added in v0.6.0

type TaskPhase = core.TaskPhase

type TaskSnapshot added in v0.6.0

type TaskSnapshot = core.TaskSnapshot

type VersionedJSONCodec added in v0.3.0

type VersionedJSONCodec[T any] = core.VersionedJSONCodec[T]

Directories

Path Synopsis
Package devtools provides an opt-in local inspector for Nodra stores.
Package devtools provides an opt-in local inspector for Nodra stores.
examples
basic command
devtools command
Command devtools runs a counter with a live local inspector.
Command devtools runs a counter with a live local inspector.
mygo command
Example mygo demonstrates two native windows sharing business state with independent redraw subscriptions.
Example mygo demonstrates two native windows sharing business state with independent redraw subscriptions.
persistent command
Command persistent demonstrates cloned state, schema migration and write-behind persistence.
Command persistent demonstrates cloned state, schema migration and write-behind persistence.
workbench command
Command workbench searches real local file names and edits persisted settings.
Command workbench searches real local file names and edits persisted settings.
internal
core
Package nodra is a state library for MyGo apps: concurrency-safe reactive stores whose snapshots drive native MyGo views.
Package nodra is a state library for MyGo apps: concurrency-safe reactive stores whose snapshots drive native MyGo views.

Jump to

Keyboard shortcuts

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