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 (CustomSerializer) ¶
Example_customSerializer shows how to inject a custom serializer using built-in factories.
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"github.com/aretw0/loam"
"github.com/aretw0/loam/pkg/adapters/fs"
)
func main() {
// Setup
tmpDir, err := os.MkdirTemp("", "loam-custom-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// Initialize with Strict JSON Serializer provided by the FS adapter
// This replaces the default JSON serializer with one that preserves large integers.
repo, err := loam.Init(filepath.Join(tmpDir, "vault"),
loam.WithAutoInit(true),
loam.WithSerializer(".json", fs.NewJSONSerializer(true)),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Save a document with a large number that might lose precision in float64
jsonContent := `{"big_id": 9223372036854775807, "content": "Large Int"}`
err = os.WriteFile(filepath.Join(tmpDir, "vault", "strict.json"), []byte(jsonContent), 0644)
if err != nil {
log.Fatal(err)
}
// Read it back via Loam
doc, err := repo.Get(ctx, "strict.json")
if err != nil {
log.Fatal(err)
}
// Check type of big_id
val := doc.Metadata["big_id"]
fmt.Printf("Type: %T\n", val)
fmt.Printf("Value: %v\n", val)
}
Output: Type: json.Number Value: 9223372036854775807
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 WithEventBuffer(size int) Option
- func WithForceTemp(force bool) Option
- func WithLogger(logger *slog.Logger) Option
- func WithMustExist(must bool) Option
- func WithRepository(repo core.Repository) Option
- func WithSerializer(ext string, s any) Option
- func WithSystemDir(name string) Option
- func WithVersioning(enabled bool) 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 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 WithMustExist ¶ added in v0.5.1
WithMustExist ensures the vault directory must already exist.
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 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).
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/typed-watch
command
|
|
|
features/csv_smart_json
command
|
|
|
features/csv_smart_json/typed
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. |