go/docs/configuration.md
Snider 89d189dd95 docs: add human-friendly documentation for Core Go framework
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:02:37 +00:00

178 lines
5 KiB
Markdown

---
title: Configuration Options
description: WithService, WithName, WithApp, WithAssets, and WithServiceLock options.
---
# Configuration Options
The `Core` is configured through **options** -- functions with the signature `func(*Core) error`. These are passed to `core.New()` and applied in order during initialisation.
```go
type Option func(*Core) error
```
## Available Options
### WithService
```go
func WithService(factory func(*Core) (any, error)) Option
```
Registers a service using a factory function. The service name is **auto-discovered** from the Go package path of the returned type (the last path segment, lowercased).
```go
// If the returned type is from package "myapp/services/calculator",
// the service name becomes "calculator".
core.New(
core.WithService(calculator.NewService),
)
```
`WithService` also performs two automatic behaviours:
1. **Name discovery** -- uses `reflect` to extract the package name from the returned type.
2. **IPC handler discovery** -- if the service has a `HandleIPCEvents(c *Core, msg Message) error` method, it is registered as an action handler automatically.
If the factory returns an error or `nil`, `New()` fails with an error.
If the returned type has no package path (e.g. a primitive or anonymous type), `New()` fails with a descriptive error.
### WithName
```go
func WithName(name string, factory func(*Core) (any, error)) Option
```
Registers a service with an **explicit name**. Use this when the auto-discovered name would be wrong (e.g. anonymous functions, or when you want a different name).
```go
core.New(
core.WithName("greet", func(c *core.Core) (any, error) {
return &Greeter{}, nil
}),
)
```
Unlike `WithService`, `WithName` does **not** auto-discover IPC handlers. If your service needs to handle actions, register the handler manually:
```go
core.WithName("greet", func(c *core.Core) (any, error) {
svc := &Greeter{}
c.RegisterAction(svc.HandleIPCEvents)
return svc, nil
}),
```
### WithApp
```go
func WithApp(app any) Option
```
Injects a GUI runtime (e.g. a Wails App instance) into the Core. The app is stored in the `Core.App` field and can be accessed globally via `core.App()` after `SetInstance` is called.
```go
core.New(
core.WithApp(wailsApp),
)
```
This is primarily used for desktop applications where services need access to the windowing runtime.
### WithAssets
```go
func WithAssets(fs embed.FS) Option
```
Registers the application's embedded assets filesystem. Retrieve it later with `c.Assets()`.
```go
//go:embed frontend/dist
var assets embed.FS
core.New(
core.WithAssets(assets),
)
```
### WithServiceLock
```go
func WithServiceLock() Option
```
Prevents any services from being registered after `New()` returns. Any call to `RegisterService` after initialisation will return an error.
```go
c, err := core.New(
core.WithService(myService),
core.WithServiceLock(), // no more services can be added
)
// c.RegisterService("late", &svc) -> error
```
This is a safety measure to ensure all services are declared upfront, preventing accidental late-binding that could cause ordering or lifecycle issues.
**How it works:** The lock is recorded during option processing but only **applied** after all options have been processed. This means options that register services (like `WithService`) can appear in any order relative to `WithServiceLock`.
## Option Ordering
Options are applied in the order they are passed to `New()`. This means:
- Services registered earlier are available to later factories (via `c.Service()`).
- `WithServiceLock()` can appear at any position -- it only takes effect after all options have been processed.
- `WithApp` and `WithAssets` can appear at any position.
```go
core.New(
core.WithServiceLock(), // recorded, not yet applied
core.WithService(factory1), // succeeds (lock not yet active)
core.WithService(factory2), // succeeds
// After New() returns, the lock is applied
)
```
## Global Instance
For applications that need global access to the Core (typically GUI runtimes), there is a global instance mechanism:
```go
// Set the global instance (typically during app startup)
core.SetInstance(c)
// Retrieve it (panics if not set)
app := core.App()
// Non-panicking access
c := core.GetInstance()
if c == nil {
// not set
}
// Clear it (useful in tests)
core.ClearInstance()
```
These functions are thread-safe.
## Features
The `Core` struct includes a `Features` field for simple feature flagging:
```go
c.Features.Flags = []string{"experimental-ui", "beta-api"}
if c.Features.IsEnabled("experimental-ui") {
// enable experimental UI
}
```
Feature flags are string-matched (case-sensitive). This is a lightweight mechanism -- for complex feature management, register a dedicated service.
## Related Pages
- [Services](services.md) -- service registration and retrieval
- [Lifecycle](lifecycle.md) -- startup/shutdown after configuration
- [Getting Started](getting-started.md) -- end-to-end example