Documentation
¶
Overview ¶
Package loam is the Composition Root for the Loam application.
It connects the core business logic (Domain Layer) with the infrastructure adapters (Persistence Layer) using the Hexagonal Architecture pattern.
Philosophy:
Loam is an "Embedded Transactional Engine" for content & metadata. It treats a collection of documents as a transactional database, abstracting the underlying storage mechanism. While the default implementation uses the File System and Git, Loam's core is agnostic, allowing for future adapters (e.g., S3, SQLite).
Features:
- **Hexagonal Architecture**: Core domain is isolated from persistence details.
- **Transactional Safe**: Atomic operations regardless of the underlying storage.
- **Metadata First**: Native support for structured metadata indexing (Frontmatter, JSON fields, etc).
- **Typed Retrieval**: Generic wrapper (`OpenTypedRepository[T]`) for type-safe document access.
- **Default Adapter (FS + Git)**: Out-of-the-box support for local Markdown files with Git versioning.
- **Extensible**: Designed to support other backends (SQL, S3, NoSQL) via `core.Repository`.
Usage:
// Initialize service with functional options
svc, err := loam.New("./vault",
loam.WithAutoInit(true),
loam.WithLogger(logger),
)
// Save a document with a change reason (semantics)
ctx := context.WithValue(context.Background(), core.ChangeReasonKey, "initial check-in")
err := svc.SaveDocument(ctx, "my-note", "content", nil)
Example (Basic) ¶
Example_basic demonstrates how to initialize a Vault, save a note, and read it back.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/aretw0/loam"
"github.com/aretw0/loam/pkg/core"
)
func main() {
// Create a temporary directory for the example
tmpDir, err := os.MkdirTemp("", "loam-example-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Initialize the Loam service (Vault) targeting the temporary directory.
// WithAutoInit(true) ensures the underlying storage (git repo) is initialized.
vault, err := loam.New(tmpDir, loam.WithAutoInit(true))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// 1. Save a Document
err = vault.SaveDocument(ctx, "hello-world", "This is my first note in Loam.", core.Metadata{
"tags": []string{"example"},
"author": "Gopher",
})
if err != nil {
log.Fatal(err)
}
// 2. Read it back
doc, err := vault.GetDocument(ctx, "hello-world")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found document: %s\n", doc.ID)
}
Output: Found document: hello-world
Example (CsvNestedData) ¶
Example_csvNestedData demonstrates Loam's "Smart CSV" capability, which automatically handles nested structures (like maps or slices) by serializing them as JSON within the CSV column.
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"github.com/aretw0/loam"
)
func main() {
// Setup: Temporary repository
tmpDir, err := os.MkdirTemp("", "loam-csv-example-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
repo, err := loam.Init(filepath.Join(tmpDir, "vault"), loam.WithAutoInit(true))
if err != nil {
log.Fatal(err)
}
type Metrics struct {
Host string `json:"host"`
Tags map[string]string `json:"tags"` // Nested Map
Load []int `json:"load"` // Nested Slice
}
metricsRepo := loam.NewTypedRepository[Metrics](repo)
ctx := context.Background()
// 1. Save complex data to CSV
err = metricsRepo.Save(ctx, &loam.DocumentModel[Metrics]{
ID: "metrics/server-01.csv", // .csv extension triggers CSV adapter
Data: Metrics{
Host: "server-01",
Tags: map[string]string{"env": "prod", "region": "us-east"},
Load: []int{10, 20, 15},
},
})
if err != nil {
log.Fatal(err)
}
// 2. Read it back
// Loam automatically parses the JSON strings inside the CSV back into Maps and Slices.
doc, err := metricsRepo.Get(ctx, "metrics/server-01.csv")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Host: %s\n", doc.Data.Host)
fmt.Printf("Tag Region: %s\n", doc.Data.Tags["region"])
fmt.Printf("Load: %v\n", doc.Data.Load)
}
Output: Host: server-01 Tag Region: us-east Load: [10 20 15]
Example (StrictMode) ¶
Example_strictMode demonstrates how to enable global strict mode for type fidelity. This ensures that large integers (int64) are not lost as float64 during parsing across ALL supported formats (JSON, YAML, Markdown).
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"github.com/aretw0/loam"
)
func main() {
// Setup
tmpDir, err := os.MkdirTemp("", "loam-strict-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Initialize with Global Strict Mode
// This applies strict parsing (json.Number) to all serializers.
repo, err := loam.Init(filepath.Join(tmpDir, "vault"),
loam.WithAutoInit(true),
loam.WithStrict(true),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// 1. JSON Example (Large Int)
jsonContent := `{"big_id": 9223372036854775807, "type": "json"}`
_ = os.WriteFile(filepath.Join(tmpDir, "vault", "strict.json"), []byte(jsonContent), 0644)
// 2. YAML Example (Large Int)
yamlContent := "big_id: 9223372036854775807\ntype: yaml"
_ = os.WriteFile(filepath.Join(tmpDir, "vault", "strict.yaml"), []byte(yamlContent), 0644)
// Read back and verify types
for _, file := range []string{"strict.json", "strict.yaml"} {
doc, err := repo.Get(ctx, file)
if err != nil {
log.Fatal(err)
}
val := doc.Metadata["big_id"]
fmt.Printf("[%s] Type: %T\n", doc.Metadata["type"], val)
}
}
Output: [json] Type: json.Number [yaml] Type: json.Number
Index ¶
- Constants
- Variables
- func AppendFooter(msg string) string
- func FindVaultRoot(startDir string) (string, error)
- func FormatChangeReason(ctype, scope, subject, body string) string
- func Init(path string, opts ...Option) (core.Repository, error)
- func IsDevRun() bool
- func New(path string, opts ...Option) (*core.Service, error)
- func NewTypedRepository[T any](repo core.Repository) *typed.Repository[T]
- func NewTypedService[T any](svc *core.Service) *typed.Service[T]
- func OpenTypedRepository[T any](path string, opts ...Option) (*typed.Repository[T], error)
- func OpenTypedService[T any](path string, opts ...Option) (*typed.Service[T], error)
- func ResolveVaultPath(userPath string, forceTemp bool) string
- func Sync(path string, opts ...Option) error
- type DocumentModel
- type Option
- func WithAdapter(name string) Option
- func WithAutoInit(auto bool) Option
- func WithContentExtraction(enabled bool) Option
- func WithDevSafety(enabled bool) Option
- func WithEventBuffer(size int) Option
- func WithForceTemp(force bool) Option
- func WithLogger(logger *slog.Logger) Option
- func WithMarkdownBodyKey(key string) Option
- func WithMustExist(must bool) Option
- func WithReadOnly(enabled bool) Option
- func WithRepository(repo core.Repository) Option
- func WithSerializer(ext string, s any) Option
- func WithStrict(strict bool) Option
- func WithSystemDir(name string) Option
- func WithVersioning(enabled bool) Option
- func WithWatcherErrorHandler(fn func(error)) Option
- type TypedRepository
- type TypedService
Examples ¶
Constants ¶
const ( CommitTypeFeat = platform.CommitTypeFeat CommitTypeFix = platform.CommitTypeFix CommitTypeDocs = platform.CommitTypeDocs CommitTypeStyle = platform.CommitTypeStyle CommitTypeRefactor = platform.CommitTypeRefactor CommitTypePerf = platform.CommitTypePerf CommitTypeTest = platform.CommitTypeTest CommitTypeChore = platform.CommitTypeChore )
Variables ¶
var Version string
Functions ¶
func AppendFooter ¶ added in v0.5.1
AppendFooter appends the Loam footer to an arbitrary message.
func FindVaultRoot ¶ added in v0.8.1
FindVaultRoot recursively looks upwards for a vault root indicator.
func FormatChangeReason ¶ added in v0.5.1
FormatChangeReason builds a Conventional Commit message.
func Init ¶ added in v0.5.1
func Init(path string, opts ...Option) (core.Repository, error)
Init initializes a repository explicitly.
func IsDevRun ¶ added in v0.5.1
func IsDevRun() bool
IsDevRun checks if the current process is running via `go run` or `go test`.
func NewTypedRepository ¶ added in v0.8.1
func NewTypedRepository[T any](repo core.Repository) *typed.Repository[T]
NewTypedRepository creates a type-safe wrapper around an existing repository.
Example ¶
ExampleNewTypedRepository demonstrates how to use the Generic Typed Wrapper for type safety.
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"github.com/aretw0/loam"
)
func main() {
// Setup: Temporary repository
tmpDir, err := os.MkdirTemp("", "loam-typed-example-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Use loam.Init to get the Repository directly
repo, err := loam.Init(filepath.Join(tmpDir, "vault"), loam.WithAutoInit(true))
if err != nil {
log.Fatal(err)
}
// Define your Domain Model
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
// Wrap the repository
userRepo := loam.NewTypedRepository[User](repo)
ctx := context.Background()
// Save a typed document
err = userRepo.Save(ctx, &loam.DocumentModel[User]{
ID: "users/alice",
Content: "Alice's Profile",
Data: User{
Name: "Alice",
Email: "alice@example.com",
},
})
if err != nil {
log.Fatal(err)
}
// Retrieve it back
doc, err := userRepo.Get(ctx, "users/alice")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User Name: %s\n", doc.Data.Name)
}
Output: User Name: Alice
func NewTypedService ¶ added in v0.8.1
NewTypedService creates a type-safe wrapper around an existing service.
func OpenTypedRepository ¶ added in v0.8.1
OpenTypedRepository simplifies creating a TypedRepository from a path.
func OpenTypedService ¶ added in v0.8.1
OpenTypedService simplifies creating a TypedService from a path.
func ResolveVaultPath ¶ added in v0.5.1
ResolveVaultPath determines the actual path for the vault based on safety rules.
Types ¶
type DocumentModel ¶ added in v0.6.0
type DocumentModel[T any] = typed.DocumentModel[T]
DocumentModel is a public alias for the typed document model.
type Option ¶ added in v0.5.1
Option defines a functional option for configuring Loam.
func WithAdapter ¶ added in v0.5.1
WithAdapter allows specifying the storage adapter to use by name.
func WithAutoInit ¶ added in v0.5.1
WithAutoInit enables automatic initialization of the vault (creates directory and git init).
func WithContentExtraction ¶ added in v0.10.8
WithContentExtraction controls whether JSON/YAML/CSV content fields are extracted into Document.Content. When disabled, the file payload is preserved 1:1 in Metadata.
func WithDevSafety ¶ added in v0.10.6
WithDevSafety controls the "Sandbox" safety mechanism (go run temp dir).
func WithEventBuffer ¶ added in v0.9.0
func WithForceTemp ¶ added in v0.5.1
WithForceTemp forces the use of a temporary directory (useful for testing).
func WithLogger ¶ added in v0.5.1
WithLogger sets the logger for the service.
func WithMarkdownBodyKey ¶ added in v0.10.8
WithMarkdownBodyKey sets the metadata key used to store Markdown body when content extraction is disabled. Defaults to "body".
func WithMustExist ¶ added in v0.5.1
WithMustExist ensures the vault directory must already exist.
func WithReadOnly ¶ added in v0.10.6
WithReadOnly enables read-only mode (bypasses safety lock, prevents writes).
func WithRepository ¶ added in v0.5.1
func WithRepository(repo core.Repository) Option
WithRepository allows injecting a custom storage adapter.
func WithSerializer ¶ added in v0.10.2
WithSerializer registers a custom serializer for a specific extension. The serializer must implement the adapter's Serializer interface.
func WithStrict ¶ added in v0.10.4
WithStrict enables strict mode for all default serializers. When enabled, numbers in JSON/YAML/Markdown will be parsed as json.Number (string based) to preserve precision of large integers.
func WithSystemDir ¶ added in v0.6.0
WithSystemDir allows specifying the hidden directory name (e.g. ".loam").
func WithVersioning ¶ added in v0.5.1
WithVersioning enables or disables version control (e.g. Git).
func WithWatcherErrorHandler ¶ added in v0.10.5
WithWatcherErrorHandler registers a callback to handle errors occurring during the Watch loop.
type TypedRepository ¶ added in v0.6.0
type TypedRepository[T any] = typed.Repository[T]
TypedRepository is a public alias for the typed repository.
type TypedService ¶ added in v0.8.1
TypedService is a public alias for the typed service.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
loam
command
|
|
|
examples
|
|
|
demos/readonly
command
|
|
|
demos/typed-watch
command
|
|
|
features/config-loading
command
|
|
|
features/csv_smart_json
command
|
|
|
features/csv_smart_json/typed
command
|
|
|
features/observability
command
|
|
|
limitations/strict_yaml_fidelity
command
|
|
|
internal
|
|
|
pkg
|
|
|
core
Document is the central entity of the domain.
|
Document is the central entity of the domain. |