extension

package module
v1.6.3 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package extension adapts Grove as a Forge extension.

Index

Constants

View Source
const ExtensionDescription = "Polyglot Go ORM with native query syntax per database"

ExtensionDescription is the human-readable description.

View Source
const ExtensionName = "grove"

ExtensionName is the name registered with Forge.

View Source
const ExtensionVersion = "0.1.0"

ExtensionVersion is the semantic version.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Driver name: "postgres", "sqlite", "mysql", "mongodb", "turso", "clickhouse".
	Driver string `json:"driver" mapstructure:"driver" yaml:"driver"`

	// DSN is the data source name / connection string.
	DSN string `json:"dsn" mapstructure:"dsn" yaml:"dsn"`

	// Databases defines additional named database connections.
	// When set, each entry creates a separate grove.DB registered in DI.
	Databases []DatabaseConfig `json:"databases" mapstructure:"databases" yaml:"databases"`

	// Default is the name of the default database when using multi-DB.
	// If empty, the first entry in Databases is the default.
	Default string `json:"default" mapstructure:"default" yaml:"default"`

	// DisableRoutes skips CRDT sync route registration.
	DisableRoutes bool `json:"disable_routes" mapstructure:"disable_routes" yaml:"disable_routes"`

	// DisableMigrate disables automatic migration execution.
	DisableMigrate bool `json:"disable_migrate" mapstructure:"disable_migrate" yaml:"disable_migrate"`

	// BasePath is the URL prefix for CRDT sync routes (default: "/sync").
	BasePath string `json:"base_path" mapstructure:"base_path" yaml:"base_path"`

	// LockTimeout caps how long migrations wait for the database migration
	// lock. 0 uses migrate.DefaultLockTimeout. Negative means wait until the
	// context deadline (maps to migrate's 0).
	LockTimeout time.Duration `json:"lock_timeout" mapstructure:"lock_timeout" yaml:"lock_timeout"`

	// CentralMigrations opts this extension into the shared MigrationRegistry
	// instead of running its own per-extension migration pass. When true, the
	// extension contributes its groups to the registry during Register and
	// Migrate/Rollback become no-ops (the registry's RunAll trigger owns
	// execution). Default false — existing apps are unaffected.
	CentralMigrations bool `json:"central_migrations" mapstructure:"central_migrations" yaml:"central_migrations"`

	// RequireConfig requires config to be present in YAML files.
	// If true and no config is found, Register returns an error.
	RequireConfig bool `json:"-" yaml:"-"`
}

Config holds the Grove extension configuration. Fields can be set programmatically via ExtOption functions or loaded from YAML configuration files (under "extensions.grove" or "grove" keys).

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default configuration. Driver and DSN are empty — callers must provide them via WithDriver(), WithDSN(), or YAML configuration.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that the configuration is usable.

type DBManager

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

DBManager manages multiple named grove.DB instances. It provides named access, a default database, and bulk close.

func NewDBManager

func NewDBManager() *DBManager

NewDBManager creates an empty DBManager.

func (*DBManager) Add

func (m *DBManager) Add(name string, db *grove.DB)

Add registers a named database. The first database added becomes the default unless SetDefault is called explicitly.

func (*DBManager) All

func (m *DBManager) All() map[string]*grove.DB

All returns a shallow copy of the name-to-DB map.

func (*DBManager) Close

func (m *DBManager) Close() error

Close closes all registered databases and returns the first error encountered.

func (*DBManager) Default

func (m *DBManager) Default() (*grove.DB, error)

Default returns the default database.

func (*DBManager) DefaultName

func (m *DBManager) DefaultName() string

DefaultName returns the name of the default database.

func (*DBManager) Get

func (m *DBManager) Get(name string) (*grove.DB, error)

Get returns the database registered under name.

func (*DBManager) Len

func (m *DBManager) Len() int

Len returns the number of registered databases.

func (*DBManager) SetDefault

func (m *DBManager) SetDefault(name string) error

SetDefault sets the default database by name. Returns an error if the name is not registered.

type DatabaseConfig

type DatabaseConfig struct {
	// Name is the unique identifier for this database.
	Name string `json:"name" mapstructure:"name" yaml:"name"`

	// Driver name: "postgres", "sqlite", "mysql", "mongodb", "turso", "clickhouse".
	Driver string `json:"driver" mapstructure:"driver" yaml:"driver"`

	// DSN is the data source name / connection string.
	DSN string `json:"dsn" mapstructure:"dsn" yaml:"dsn"`
}

DatabaseConfig defines a single named database connection.

type DriverFactory

type DriverFactory func(ctx context.Context, dsn string) (grove.GroveDriver, error)

DriverFactory builds a fully-opened grove driver from a DSN. Used by WithDriverFactory to let callers override the registry-based factory for a given driver name (e.g. inject MongoDB pool tuning, postgres pool sizing, etc.) without baking driver-specific imports into the extension package.

type ExtOption

type ExtOption func(*Extension)

ExtOption is a functional option for the Forge extension.

func WithBasePath

func WithBasePath(path string) ExtOption

WithBasePath sets the URL prefix for CRDT sync routes.

func WithCRDT

func WithCRDT(plugin *crdt.Plugin, scope ...hook.Scope) ExtOption

WithCRDT enables the CRDT plugin and registers the sync controller. The plugin's hooks are automatically added to the Grove DB, and the SyncController is registered with the Forge router for sync endpoints.

In multi-DB mode, CRDT hooks are applied to the default database unless WithCRDTDatabase is also used.

Example:

ext := extension.New(
    extension.WithDriver(pgdb),
    extension.WithCRDT(crdtPlugin, hook.Scope{Tables: []string{"documents"}}),
)

func WithCRDTDatabase

func WithCRDTDatabase(dbName string) ExtOption

WithCRDTDatabase attaches the CRDT plugin to a specific named database instead of the default. Requires WithCRDT and multi-DB mode.

func WithCentralMigrations

func WithCentralMigrations() ExtOption

WithCentralMigrations opts this extension into central migration mode. Instead of running its own migration pass, the extension contributes its groups to the shared MigrationRegistry. A single RunAll trigger (registered by the first contributing extension) runs them all in one ordered pass.

This option is OPT-IN and defaults off. It should remain off until forge CentralMigrator (Phase 3) support is in place. On forge ≤ 1.7.1 it is effectively unsafe: the central migration trigger fires on PhaseAfterRegister, which on that version runs after all extensions have started — schema is NOT guaranteed before Start, making this worse than the default per-extension path for any extension that seeds or queries during Start. Additionally, central rollback is not available until the forge CentralMigrator split-phase lands; calling Rollback while in central mode returns an explicit error.

func WithDSN

func WithDSN(driver, dsn string) ExtOption

WithDSN sets the driver name and DSN for the extension. The driver will be created from the registry during Register(). If WithDriver() is also set, it takes precedence.

func WithDatabase

func WithDatabase(name string, drv grove.GroveDriver) ExtOption

WithDatabase adds a named database with a pre-configured driver. Multiple calls create multiple named databases.

Example:

ext := extension.New(
    extension.WithDatabase("primary", pgDriver),
    extension.WithDatabase("analytics", chDriver),
    extension.WithDefaultDatabase("primary"),
)

func WithDatabaseDSN

func WithDatabaseDSN(name, driver, dsn string) ExtOption

WithDatabaseDSN adds a named database using a driver name and DSN. The driver will be created from the registry during Register().

Example:

ext := extension.New(
    extension.WithDatabaseDSN("primary", "postgres", "postgres://localhost/app"),
    extension.WithDatabaseDSN("analytics", "clickhouse", "clickhouse://localhost/analytics"),
)

func WithDefaultDatabase

func WithDefaultDatabase(name string) ExtOption

WithDefaultDatabase sets which named database is the default. The default is used for backward-compatible DB() access and unnamed DI injection.

func WithDisableMigrate

func WithDisableMigrate() ExtOption

WithDisableMigrate disables automatic migration execution.

func WithDisableRoutes

func WithDisableRoutes() ExtOption

WithDisableRoutes disables CRDT sync route registration.

func WithDriver

func WithDriver(drv grove.GroveDriver) ExtOption

WithDriver sets a pre-configured database driver for the extension. When set, this takes precedence over YAML driver/dsn configuration.

func WithDriverFactory

func WithDriverFactory(name string, factory DriverFactory) ExtOption

WithDriverFactory overrides the driver-construction step for one specific driver name. When the extension resolves a driver via YAML config or WithDSN / WithDatabaseDSN, it consults the override map first; only unknown names fall through to the global registry.

Use this to inject driver-specific tuning (mongo pool size, postgres pool sizing, etc.) without dragging the driver package into grove/extension. Multiple calls merge; later registrations win on the same driver name.

Example (twinos tuning the mongo driver's pool):

ext := extension.New(
    extension.WithDSN("mongo", "mongodb://localhost:27017/twinos"),
    extension.WithDriverFactory("mongo", func(ctx context.Context, dsn string) (grove.GroveDriver, error) {
        m := mongodriver.New()
        if err := m.Open(ctx, dsn,
            mongodriver.WithMaxPoolSize(200),
            mongodriver.WithMinPoolSize(10),
            mongodriver.WithMaxConnecting(4),
        ); err != nil {
            return nil, err
        }
        return m, nil
    }),
)

func WithHook

func WithHook(h any, scope ...hook.Scope) ExtOption

WithHook adds a lifecycle hook to the Grove DB.

func WithHookFor

func WithHookFor(dbName string, h any, scope ...hook.Scope) ExtOption

WithHookFor adds a hook scoped to a specific named database.

func WithLockTimeout

func WithLockTimeout(d time.Duration) ExtOption

WithLockTimeout sets how long migrations wait for the migration lock. 0 uses migrate.DefaultLockTimeout. Negative means wait until the context deadline.

func WithMigrations

func WithMigrations(groups ...*migrate.Group) ExtOption

WithMigrations adds migration groups to the extension.

func WithMigrationsFor

func WithMigrationsFor(dbName string, groups ...*migrate.Group) ExtOption

WithMigrationsFor adds migration groups for a specific named database.

func WithRequireConfig

func WithRequireConfig(require bool) ExtOption

WithRequireConfig requires config to be present in YAML files. If true and no config is found, Register returns an error.

func WithSyncController

func WithSyncController(opts ...crdt.SyncControllerOption) ExtOption

WithSyncController overrides the default sync controller options. These options configure the sync endpoints (poll interval, keep-alive, hooks).

Example:

ext := extension.New(
    extension.WithDriver(pgdb),
    extension.WithCRDT(crdtPlugin),
    extension.WithSyncController(
        crdt.WithStreamPollInterval(2 * time.Second),
        crdt.WithStreamKeepAlive(30 * time.Second),
    ),
)

func WithSyncer

func WithSyncer(syncer *crdt.Syncer) ExtOption

WithSyncer configures the CRDT background syncer. Requires WithCRDT. The syncer is started in the extension's Start method and runs until the context is cancelled.

Example:

ext := extension.New(
    extension.WithDriver(pgdb),
    extension.WithCRDT(crdtPlugin),
    extension.WithSyncer(syncer),
)

type Extension

type Extension struct {
	*forge.BaseExtension
	// contains filtered or unexported fields
}

Extension adapts Grove as a Forge extension.

func New

func New(opts ...ExtOption) *Extension

New creates a Grove Forge extension with the given options.

func (*Extension) DB

func (e *Extension) DB() *grove.DB

DB returns the default Grove DB instance (nil until Register is called).

func (*Extension) Health

func (e *Extension) Health(_ context.Context) error

Health implements forge.Extension.

func (*Extension) Init

func (e *Extension) Init(_ forge.App) error

Init builds the DB and registers hooks. Can be called standalone outside Forge.

func (*Extension) Manager

func (e *Extension) Manager() *DBManager

Manager returns the DBManager for multi-DB mode. Returns nil in single-DB mode.

func (*Extension) Migrate

func (e *Extension) Migrate(ctx context.Context) (*forge.MigrationResult, error)

Migrate runs all pending migrations forward. In single-DB mode, runs migrations from e.groups. In multi-DB mode, runs migrations for each named database from e.dbMigrations.

func (*Extension) MigrationGroups

func (e *Extension) MigrationGroups() []*migrate.Group

MigrationGroups returns all registered migration groups (single-DB mode).

func (*Extension) MigrationStatus

func (e *Extension) MigrationStatus(ctx context.Context) ([]*forge.MigrationGroupInfo, error)

MigrationStatus returns the current state of all migrations grouped by their owning module/extension.

func (*Extension) Register

func (e *Extension) Register(fapp forge.App) error

Register implements forge.Extension.

func (*Extension) Rollback

func (e *Extension) Rollback(ctx context.Context) (*forge.MigrationResult, error)

Rollback rolls back the last batch of applied migrations. In single-DB mode, rolls back from e.groups. In multi-DB mode, rolls back for each named database from e.dbMigrations.

func (*Extension) Start

func (e *Extension) Start(ctx context.Context) error

Start implements forge.Extension.

func (*Extension) Stop

func (e *Extension) Stop(_ context.Context) error

Stop gracefully shuts down all Grove databases and CRDT subsystems.

type MigrationRegistry

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

MigrationRegistry collects migration groups contributed by every extension and runs them as a single ordered pass per database. Putting every group into one orchestrator lets grove's topological sort resolve cross-extension DependsOn declarations (which fail when each extension runs its own orchestrator), and collapses N per-extension lock cycles into one acquire/release per database.

func NewMigrationRegistry

func NewMigrationRegistry(opts ...RegistryOption) *MigrationRegistry

NewMigrationRegistry creates an empty registry.

func (*MigrationRegistry) Contribute

func (r *MigrationRegistry) Contribute(dbKey string, drv grove.GroveDriver, groups ...*migrate.Group)

Contribute adds migration groups for a database. dbKey "" is the default database. The first driver contributed for a dbKey wins; later contributions for the same dbKey append their groups (callers must contribute the same driver instance for a given database).

func (*MigrationRegistry) RollbackAll

func (r *MigrationRegistry) RollbackAll(ctx context.Context) (*forge.MigrationResult, error)

RollbackAll rolls back the last batch per database, databases in reverse order.

func (*MigrationRegistry) RunAll

RunAll runs one ordered migration pass per contributed database.

func (*MigrationRegistry) StatusAll

StatusAll returns migration status across all contributed databases.

type RegistryOption

type RegistryOption func(*MigrationRegistry)

RegistryOption configures a MigrationRegistry.

func WithRegistryLockTimeout

func WithRegistryLockTimeout(d time.Duration) RegistryOption

WithRegistryLockTimeout sets the lock-wait budget for every orchestrator the registry runs. 0 uses migrate.DefaultLockTimeout.

Jump to

Keyboard shortcuts

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