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

nodra

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 24, 2026 License: MIT Imports: 10 Imported by: 0

README

nodra

Nodra is a production-oriented reactive state store for Go desktop applications. It is a state library for MyGo apps with Zustand-like ergonomics, and does not tie your state to a renderer or a persistence backend.

Install

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

The core API is in github.com/yldm-tech/nodra. The optional local inspector is in github.com/yldm-tech/nodra/devtools, so applications can omit its server and UI.

MyGo usage

Redraw a native MyGo window whenever its store publishes:

stop := nodra.Invalidate(store, window)

Call stop when the window closes; go run ./examples/mygo shows the whole app.

Core usage

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

tasks := nodra.New([]Task{})
stop := tasks.SubscribeWith(nodra.SubscribeOptions[[]Task]{Immediate: true}, func(snapshot nodra.Snapshot[[]Task]) {
	log.Printf("%d tasks", len(snapshot.Value))
})
defer stop()

tasks.Update(func(value *[]Task) {
	*value = append(*value, task)
})

What is included

  • Generic, concurrency-safe Store[T] with versioned snapshots
  • Opt-in NewWithClone / DefineWithClone for isolated mutable state and deep rollback
  • Set, Update, and UpdateErr with value rollback on returned errors and panics
  • Subscribe and SubscribeWith with immediate delivery and per-subscriber equality
  • SubscribeSelector and SubscribeSelectorWith for focused updates with previous values
  • Derive for memoized computed/getter values
  • Action / Do with action timing and error events
  • SubscribeMutations with direct, patch, reset and action metadata
  • Create, Define, State, Patch, and Reset aliases for familiar Pinia/Zustand workflows
  • Store IDs and independent named store instances
  • Action lifecycle hooks: before, after, and error
  • Nested Batch notification groups that publish one final snapshot
  • Transaction for one serialized, fallible mutation
  • Track history with bounded undo and redo
  • Persistence[T] with pluggable storage and codec interfaces
  • Ready-to-use FileStorage and JSONCodec[T] with atomic file replacement
  • VersionedJSONCodec[T] with sequential schema migrations and explicit legacy JSON support
  • AutoSaveWithErrors for observable persistence failures
  • AutoSaveAsync for debounced, coalesced writes with Flush, Close and an error stream
  • Context-bound, bounded Watch streams that close with the store
  • Invalidate to redraw a MyGo window when a store publishes
  • Optional DevTools with a local browser inspector, mutation/action timeline, lazy state trees, key/value search, bounded state diffs, snapshot export, JSON editing and opt-in state restoration

Design guarantees

  • Store locks are released before user listeners run, so listeners can safely read or update the same store.
  • Versions increment once per published update, not once per mutation inside a batch.
  • Closing a store is permanent; updates return nodra.ErrClosed and active watches terminate.
  • By default, values are not deep copied. Copy reference types before changing them, or use NewWithClone with a complete clone function. Cloned stores isolate constructor and Set inputs, update candidates, reads, each subscriber, mutation metadata and reset state.
  • Selectors and derived getters catch up with changes during initialization and ignore computations from older versions once a newer version has been processed. Callbacks still run on writer goroutines and may run concurrently.
  • Concurrent Save calls on one Persistence instance are serialized from state capture through storage write, so a delayed save cannot overwrite a newer save. Keep one persistence instance per destination; Load is intended for startup before concurrent writes.
  • Subscriptions discard obsolete snapshots once they have accepted a newer source version. Callbacks that already started may still overlap or finish out of order. Watch channels are bounded and retain the newest accepted version; slow consumers may skip intermediate snapshots.
  • History keeps source versions monotonic and commits stack changes with successful undo/redo writes. Failed writes leave stacks intact. Travel inside an open batch returns ErrHistoryBatch; finish the batch before undoing or redoing.

Isolated mutable state

When state contains maps, slices or pointers, opt in to cloning instead of maintaining copy-on-write manually:

store := nodra.NewWithClone(map[string]int{"count": 0}, func(v map[string]int) map[string]int {
    return maps.Clone(v)
})
err := store.Transaction(func(state *map[string]int) error {
    (*state)["count"]++
    return validate(*state) // an error discards the candidate, including map changes
})

Clone all mutable nested references too. The function must support concurrent calls and must not reenter the store. Each isolation boundary costs a copy; keep using New for immutable state or large data already managed with copy-on-write. Named factories support the same policy through DefineWithClone.

Collection selectors

Selectors accept slices, maps and values containing interfaces. By default dynamically comparable values use == (including pointer identity); other values use reflect.DeepEqual. Nil and empty collections differ with the default comparison. Supply Equals for domain-specific equality or faster comparisons of large collections:

stop := nodra.SubscribeSelectorWith(store,
    func(s State) []string { return s.Tags },
    func(current, previous []string) { window.Invalidate() },
    nodra.SelectorOptions[[]string]{Equals: slices.Equal[[]string]},
)
defer stop()

For maps use maps.Equal[map[string]int, map[string]int] or a custom comparator. Comparators must be pure and concurrency-safe; a supplied comparator replaces the default entirely. Selected reference values follow the source store's ownership policy: treat them as read-only and copy before changing them. SubscribeSelector offers the same default equality without previous values or options.

Versioned persistence

VersionedJSONCodec[T] wraps state in {"schemaVersion": N, "state": ...}. Migrations[n] transforms state JSON from version n to n+1. With AllowUnversioned: true, existing raw JSON enters the chain at version zero:

codec := nodra.VersionedJSONCodec[Settings]{
    Version: 2,
    AllowUnversioned: true,
    Migrations: map[int]nodra.JSONMigration{
        0: migrateLegacyToV1,
        1: migrateV1ToV2,
    },
}
persistence := nodra.NewPersistence(store, nodra.FileStorage{Path: path}, codec)
err := persistence.Load(ctx) // completes migrations before changing the store

Missing migrations, invalid migration output and future schemas return errors. A failed load leaves the store and file untouched. Load before starting autosave; successful migration is written by a subsequent Save or autosave.

Write-behind persistence

Move encoding and disk I/O off the goroutine updating the store:

writer, err := persistence.AutoSaveAsync(ctx, nodra.AsyncOptions{
    Debounce: 100 * time.Millisecond,
})
// Handle err before using writer.
// Updates now coalesce on one worker; Flush bypasses the debounce delay.
err = writer.Flush(flushCtx)
// At application exit, stop updates and drain before canceling ctx or closing store.
err = writer.Close(shutdownCtx)

The writer schedules the current state once at startup. Flush waits for the version visible at the call; canceling one flush wait leaves the worker running. Failed writes stay pending and retry on the next update or flush. Errors() is a bounded stream for background failures; Flush and Close return errors directly. Close stops observing, attempts the final flush, and stops even on failure. Retry a failed shutdown write with persistence.Save and a fresh context. Parent cancellation and store closure stop the worker without a guaranteed drain.

examples/persistent combines cloning, legacy migration, error handling and graceful shutdown:

go run ./examples/persistent -file /path/to/state.json

Repository layout

*.go                           store, history, persistence, MyGo invalidation, and tests (package nodra)
devtools/                      optional inspector, local HTTP API and embedded browser UI
examples/                      standalone and native MyGo examples
docs/architecture.md           lifecycle and threading details

The repository is one Go module, github.com/yldm-tech/nodra, with a single go.mod at the root.

DevTools

import "github.com/yldm-tech/nodra/devtools"

inspector := devtools.New(devtools.Options{}) // read-only by default
defer inspector.Close()
stop, err := devtools.Attach(inspector, "preferences", prefsStore)
// Handle err before using stop; call stop when this store is no longer in scope.
url, err := inspector.Listen() // random loopback port; open the full session URL

The browser shows multiple stores, live state, bounded mutation/action history, action durations and errors, before/after diffs, filtering and JSON export. Set AllowRestore: true explicitly to enable typed JSON editing and restoring a retained snapshot. Restoration calls Set and emits new events; it does not reverse external action effects. Redact filters state before capture and disables restoration. No external assets, services or frontend build step are required.

Try the interactive example and see the DevTools guide for lifecycle, redaction and browser tests:

go run ./examples/devtools -restore

Nodra DevTools inspecting a state change

Verification

go test -race ./...
go vet ./...

Run the native MyGo example with go run ./examples/mygo.

CI runs race tests and vet on Linux, macOS and Windows. It also runs golangci-lint, two 10-second fuzz targets, benchmark smoke tests, cross builds for Linux and Windows, and checks that the module path is github.com/yldm-tech/nodra, that there is no other go.mod, go.work or replace directive, and that go mod tidy leaves no diff.

Fuzzing and benchmarks
go test -run '^$' -fuzz=FuzzVersionedJSONDecode -fuzztime=30s -parallel=2 .
go test -run '^$' -fuzz=FuzzHistoryStateMachine -fuzztime=30s -parallel=2 .
go test -run '^$' -bench=. -benchmem -count=5 .

Fuzzing checks accepted versioned/legacy JSON round trips and compares bounded undo/redo/batch operations with an independent state-machine model. Benchmarks report store updates with 0/1/100 listeners, full cloning of a 1,000-entry map, and selection of 1,000-element slices with default versus custom equality. Run repeated measurements on the same Go version, hardware and load before comparing changes. The CI -benchtime=1x run only checks execution, with no performance thresholds.

An illustrative local run on Apple M4 Pro, darwin/arm64 measured approximately 73 ns per update without listeners, 101 ns with one listener and 1.51 µs with 100 listeners; cloning the map cost 6.81 µs. Slice selection including the projection clone took 6.57 µs with default equality and 1.13 µs with slices.Equal. These are machine-specific measurements, not API guarantees.

Releases

The module is released by release-please with plain vX.Y.Z tags and one root CHANGELOG.md.

  1. Every push to main updates the release PR when there are releasable commits since the last tag. Merging that PR tags the release and publishes a GitHub release.
  2. After a release, post-release.yml checks that the tag resolves through proxy.golang.org.

Commit messages follow Conventional Commits:

  • feat: ... adds a feature (minor bump)
  • fix: ... fixes a bug (patch bump)
  • feat!: ... or a BREAKING CHANGE: footer marks a breaking change (minor bump while the version is below 1.0.0)
  • docs, test, refactor, ci, build and chore do not trigger a release

Use a scope where it helps, e.g. fix(persist): ....

Release PRs are pushed with the workflow's GITHUB_TOKEN. GitHub holds their test runs at "approval required", so the release workflow approves those runs itself (.github/scripts/start-pr-ci.sh). Setting a RELEASE_PLEASE_TOKEN repository secret (a fine-grained PAT or GitHub App token with contents and pull request write access) makes them trigger CI directly instead.

Pinia/Zustand-style example

store := nodra.Create(Counter{Count: 0})
increment := store.Action("increment", func(state *Counter) error {
    state.Count++
    return nil
})
double := nodra.Derive(store, func(state Counter) int {
    return state.Count * 2
}, func(a, b int) bool { return a == b })

_ = increment()
fmt.Println(store.State().Count, double.Get())

See docs/architecture.md for the full concept mapping.

Named stores

counter := nodra.Define("counter", func() Counter {
    return Counter{Count: 0}
})
store := counter.New()

Each call to New creates an independent instance with the same initial-state factory. The ID is available to mutation and action observers for logging and devtools integrations.

Documentation

Overview

Package nodra is a state library for MyGo apps: concurrency-safe reactive stores whose snapshots drive native MyGo views. Invalidate redraws a window when its store publishes; the Store itself does not depend on a running MyGo app, so services and tests can use it as well.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrNoUndo = errors.New("nodra: nothing to undo")
	ErrNoRedo = errors.New("nodra: nothing to redo")
)

ErrNoUndo and ErrNoRedo report an empty history stack.

View Source
var (
	ErrUnsupportedVersion = errors.New("nodra: unsupported schema version")
	ErrMissingMigration   = errors.New("nodra: missing schema migration")
)
View Source
var ErrClosed = errors.New("nodra: store is closed")
View Source
var ErrHistoryBatch = errors.New("nodra: cannot undo or redo inside an open batch")

ErrHistoryBatch reports travel without an independent publication boundary.

View Source
var ErrInvalidPersistence = errors.New("nodra: invalid persistence configuration")

Functions

func Invalidate added in v0.2.0

func Invalidate[T any](store *Store[T], window *mygo.Window) (stop func())

Invalidate requests a new frame of window's native Content whenever store publishes a snapshot, so a MyGo view that reads the store during its render shows the new state. It returns the function that stops the subscription; with a nil store or window it subscribes to nothing and returns a no-op.

func SubscribeSelector

func SubscribeSelector[T any, S any](store *Store[T], selector func(T) S, fn func(S)) func()

func SubscribeSelectorWith

func SubscribeSelectorWith[T any, V any](store *Store[T], selector func(T) V, fn func(current, previous V), options SelectorOptions[V]) func()

SubscribeSelectorWith calls fn with current and previous selected values. It catches up after registration and ignores obsolete computations. Callbacks run on writer goroutines and may overlap, as with Store.Subscribe.

Types

type ActionEvent

type ActionEvent struct {
	StoreID  string
	Name     string
	Started  time.Time
	Finished time.Time
	Duration time.Duration
	Err      error
}

ActionEvent describes one completed action.

type ActionHooks

type ActionHooks struct {
	Before func(ActionEvent)
	After  func(ActionEvent)
	Error  func(ActionEvent)
}

ActionHooks observes action lifecycle events. Before runs before the mutation; After runs only on success; Error runs only on failure.

type AsyncOptions added in v0.3.0

type AsyncOptions struct {
	Debounce time.Duration
}

AsyncOptions configures write-behind persistence. Debounce is a trailing-edge delay for bursts of updates; zero starts immediately. Flush bypasses the delay.

type AsyncPersistence added in v0.3.0

type AsyncPersistence[T any] struct {
	// contains filtered or unexported fields
}

AsyncPersistence owns one worker and a bounded error stream. It coalesces updates without blocking writer goroutines on encoding or storage. Failed writes remain pending; the next update or Flush retries them without spinning. Values must be immutable or the store must use NewWithClone.

func (*AsyncPersistence[T]) Close added in v0.3.0

func (a *AsyncPersistence[T]) Close(ctx context.Context) error

Close stops observing updates, flushes the final observed state, then stops the worker even if the flush fails. It is idempotent; concurrent callers wait on the same result with their own context. Do not mutate the store concurrently with Close if those changes must be included. Retry failed shutdown writes with Persistence.Save; a closed async writer cannot be restarted.

func (*AsyncPersistence[T]) Errors added in v0.3.0

func (a *AsyncPersistence[T]) Errors() <-chan error

Errors reports failed write attempts. The bounded stream may drop errors if unread; Flush and Close return their write error directly. It closes on exit.

func (*AsyncPersistence[T]) Flush added in v0.3.0

func (a *AsyncPersistence[T]) Flush(ctx context.Context) error

Flush waits until the version visible when called has been saved, bypassing debounce and retrying a previous failure. Canceling this wait does not cancel the shared worker. Later updates may be included in the same write.

type Codec

type Codec[T any] interface {
	Encode(T) ([]byte, error)
	Decode([]byte) (T, error)
}

Codec serializes and restores one state value.

type Definition

type Definition[T any] struct {
	// contains filtered or unexported fields
}

Definition is a reusable named store factory, analogous to Pinia's defineStore and Zustand's reusable vanilla store creator.

func Define

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

Define creates a store definition. Each New call gets independent state.

func DefineWithClone added in v0.3.0

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

DefineWithClone creates a named factory with NewWithClone's isolation policy.

func (*Definition[T]) ID

func (d *Definition[T]) ID() string

func (*Definition[T]) New

func (d *Definition[T]) New() *Store[T]

type Derived

type Derived[T any, V any] struct {
	// contains filtered or unexported fields
}

Derived is a memoized getter derived from a store. It recomputes when the source publishes and only notifies listeners when the selected value changes.

func Derive

func Derive[T any, V any](store *Store[T], selector func(T) V, equals func(V, V) bool) *Derived[T, V]

func (*Derived[T, V]) Close

func (d *Derived[T, V]) Close() error

func (*Derived[T, V]) Get

func (d *Derived[T, V]) Get() V

func (*Derived[T, V]) Subscribe

func (d *Derived[T, V]) Subscribe(fn func(V)) func()

type FileStorage

type FileStorage struct {
	Path string
	Perm os.FileMode
}

FileStorage stores encoded state at Path. Save writes a temporary file in the same directory and renames it into place, so readers see a complete file.

func (FileStorage) Load

func (f FileStorage) Load(ctx context.Context) ([]byte, error)

func (FileStorage) Save

func (f FileStorage) Save(ctx context.Context, data []byte) (err error)

type History

type History[T any] struct {
	// contains filtered or unexported fields
}

History records successful store snapshots and provides bounded undo/redo.

func Track

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

Track starts recording changes to store. limit is the maximum number of undo entries; values below one disable history recording.

func (*History[T]) CanRedo

func (h *History[T]) CanRedo() bool

func (*History[T]) CanUndo

func (h *History[T]) CanUndo() bool

func (*History[T]) Close

func (h *History[T]) Close() error

Close stops recording future changes. It does not close the underlying store.

func (*History[T]) Redo

func (h *History[T]) Redo() error

Redo restores the next state.

func (*History[T]) Undo

func (h *History[T]) Undo() error

Undo restores the previous state.

type JSONCodec

type JSONCodec[T any] struct{}

JSONCodec encodes a store value using encoding/json.

func (JSONCodec[T]) Decode

func (JSONCodec[T]) Decode(data []byte) (T, error)

func (JSONCodec[T]) Encode

func (JSONCodec[T]) Encode(value T) ([]byte, error)

type JSONMigration added in v0.3.0

type JSONMigration func(json.RawMessage) (json.RawMessage, error)

JSONMigration converts one schema version to the next. Inputs and outputs are state JSON, without the version envelope. Return an error to abort Load.

type Mutation

type Mutation[T any] struct {
	Type     MutationType
	Name     string
	Previous T
	Value    T
	Version  uint64
	At       time.Time
}

Mutation contains Pinia-like subscription metadata.

type MutationType

type MutationType string

MutationType describes why a store published a snapshot.

const (
	MutationDirect MutationType = "direct"
	MutationPatch  MutationType = "patch"
	MutationAction MutationType = "action"
	MutationReset  MutationType = "reset"
)

type Persistence

type Persistence[T any] struct {
	// contains filtered or unexported fields
}

Persistence connects a store to a Storage and Codec. It is explicit by default: call Load at startup and Save after successful application actions. Save calls on the same instance are serialized, including state capture and encoding. Separate instances sharing a storage destination are not coordinated.

func NewPersistence

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

func (*Persistence[T]) AutoSave

func (p *Persistence[T]) AutoSave(ctx context.Context) func()

AutoSave subscribes to updates and saves synchronously after each update. The returned function stops autosaving. Use Batch to coalesce writes.

func (*Persistence[T]) AutoSaveAsync added in v0.3.0

func (p *Persistence[T]) AutoSaveAsync(ctx context.Context, options AsyncOptions) (*AsyncPersistence[T], error)

AutoSaveAsync subscribes and schedules the current state once, then all subsequent versions. Use Close before closing the store to drain writes. Parent cancellation and store closure stop the worker without a guaranteed drain. Storage.Save must respect context cancellation to bound shutdown time.

func (*Persistence[T]) AutoSaveWithErrors

func (p *Persistence[T]) AutoSaveWithErrors(ctx context.Context) (<-chan error, func())

AutoSaveWithErrors subscribes to updates and reports persistence failures on a buffered channel. The channel closes when ctx is canceled or stop is called. Stop releases the background watcher but does not wait for in-flight saves.

func (*Persistence[T]) Load

func (p *Persistence[T]) Load(ctx context.Context) error

func (*Persistence[T]) Save

func (p *Persistence[T]) Save(ctx context.Context) error

type SelectorOptions

type SelectorOptions[V any] struct {
	Immediate bool
	Equals    func(previous, next V) bool
}

SelectorOptions configures a selector subscription. With no Equals function, comparable values use == (including pointer identity); other values use reflect.DeepEqual. A custom comparator can use slices.Equal or maps.Equal.

type Snapshot

type Snapshot[T any] struct {
	Value   T
	Version uint64
}

type Storage

type Storage interface {
	Load(context.Context) ([]byte, error)
	Save(context.Context, []byte) error
}

Storage is the minimal persistence backend used by Persistence. Files, key-value databases and encrypted stores can implement it.

type Store

type Store[T any] struct {
	// contains filtered or unexported fields
}

func Create

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

Create is the Pinia/Zustand-style constructor name for a typed store.

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]

NewWithClone isolates owned state, snapshots, subscriber values and rollback with clone. Clone must copy all mutable references, be safe for concurrent calls, and must not reenter the store. A nil clone preserves New's behavior.

func (*Store[T]) Action

func (s *Store[T]) Action(name string, fn func(*T) error) func() error

Action returns a reusable named action function, matching Pinia action definitions while keeping Go's explicit function types.

func (*Store[T]) Batch

func (s *Store[T]) Batch(fn func()) error

func (*Store[T]) Close

func (s *Store[T]) Close() error

func (*Store[T]) Do

func (s *Store[T]) Do(name string, fn func(*T) error) error

Do runs a named mutation and emits an action event after it completes.

func (*Store[T]) Get

func (s *Store[T]) Get() T

func (*Store[T]) ID

func (s *Store[T]) ID() string

func (*Store[T]) Patch

func (s *Store[T]) Patch(fn func(*T)) error

func (*Store[T]) Reset

func (s *Store[T]) Reset() error

func (*Store[T]) Set

func (s *Store[T]) Set(value T) error

func (*Store[T]) Snapshot

func (s *Store[T]) Snapshot() Snapshot[T]

func (*Store[T]) State

func (s *Store[T]) State() T

func (*Store[T]) Subscribe

func (s *Store[T]) Subscribe(fn func(Snapshot[T])) func()

func (*Store[T]) SubscribeActionHooks

func (s *Store[T]) SubscribeActionHooks(hooks ActionHooks) func()

SubscribeActionHooks registers lifecycle hooks and returns an unsubscribe function.

func (*Store[T]) SubscribeActions

func (s *Store[T]) SubscribeActions(fn func(ActionEvent)) func()

func (*Store[T]) SubscribeMutations

func (s *Store[T]) SubscribeMutations(fn func(Mutation[T])) func()

func (*Store[T]) SubscribeWith

func (s *Store[T]) SubscribeWith(options SubscribeOptions[T], fn func(Snapshot[T])) func()

func (*Store[T]) Transaction

func (s *Store[T]) Transaction(fn func(*T) error) error

func (*Store[T]) Update

func (s *Store[T]) Update(fn func(*T)) error
Example
package main

import (
	"fmt"

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

func main() {
	store := nodra.New(0)
	store.Subscribe(func(snapshot nodra.Snapshot[int]) {
		fmt.Println(snapshot.Value, snapshot.Version)
	})
	if err := store.Update(func(value *int) { *value = 42 }); err != nil {
		fmt.Println(err)
	}
}
Output:
42 1

func (*Store[T]) UpdateErr

func (s *Store[T]) UpdateErr(fn func(*T) error) error

func (*Store[T]) Version

func (s *Store[T]) Version() uint64

func (*Store[T]) Watch

func (s *Store[T]) Watch(ctx context.Context, buffer int) <-chan Snapshot[T]

type SubscribeOptions

type SubscribeOptions[T any] struct {
	Immediate bool
	Equals    func(previous, next T) bool
}

type VersionedJSONCodec added in v0.3.0

type VersionedJSONCodec[T any] struct {
	Version          int
	Migrations       map[int]JSONMigration
	AllowUnversioned bool
}

VersionedJSONCodec stores {"schemaVersion": N, "state": ...}. Migrations[n] upgrades version n to n+1, sequentially. AllowUnversioned accepts legacy raw JSON as version zero. Version must be positive; future versions are rejected. Decode never writes storage: persist a successful upgrade explicitly with Save.

func (VersionedJSONCodec[T]) Decode added in v0.3.0

func (c VersionedJSONCodec[T]) Decode(data []byte) (T, error)

func (VersionedJSONCodec[T]) Encode added in v0.3.0

func (c VersionedJSONCodec[T]) Encode(value T) ([]byte, error)

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 Nodra driving a native MyGo view.
Example mygo demonstrates Nodra driving a native MyGo view.
persistent command
Command persistent demonstrates cloned state, schema migration and write-behind persistence.
Command persistent demonstrates cloned state, schema migration and write-behind persistence.

Jump to

Keyboard shortcuts

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