fado

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package fado is the public Go API for embedding FadoDB.

Nothing is implemented yet. The intended surface is small: open a database, define classes, create and change documents, request transitions, query the document tables, and subscribe to changes. See docs/architecture.md.

Package fado is the Go API of FadoDB: open a database file; define schemas, classes and explicit tables; create, change, read and delete documents as commits with recalculation, typed history and an event log; attach files; filter documents with a formula and run read-only SQL over the views; redefine a class as a new version that touches no document, and migrate live documents on purpose (docs/architecture.md, "Go API").

The embedder supplies the principal on every call (ADR-0027), may give every mutating call an idempotency key and a business date (ADR-0026), and can run a change, or a draft class, through the commit pipeline without writing anything (ADR-0025). A document moves through its class's statechart with Transition, and Transitions says which moves are open (ADR-0040); a preview, a listing or a read may be asked at a moment the caller chooses with At, and a preview reports the moment it ran under (ADR-0042). The principal decides what a call may set and sees: the rights table and the per-transition rules are enforced on every call, a refusal being forbidden and a hidden variable absent (ADR-0041). Not here yet: rules and QUERY (Phase 4), the class-level access rules compiled to row predicates (Phase 5) and the server (Phase 6).

Example

The invoice of docs/architecture.md, as far as this build takes it: a class with defaults, objects, lines and auto formulas; a document created, changed, previewed and read back with its history.

package main

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/MojoHand70/fadodb/fado"
)

func main() {
	tmp, err := os.MkdirTemp("", "fado-example")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(tmp)
	db, err := fado.Open(filepath.Join(tmp, "example.fado"), fado.Options{
		Now: func() time.Time { return time.Date(2026, 11, 8, 12, 0, 0, 0, time.UTC) },
	})
	if err != nil {
		panic(err)
	}
	defer db.Close()
	ctx := context.Background()

	_, err = db.DefineClass(ctx, []byte(`{
	  "name": "Invoice",
	  "title": "\"INV-\" & number",
	  "variables": {
	    "number":   {"type": "text", "required": true},
	    "customer": {"type": "ref", "class": "Customer"},
	    "issued":   {"type": "date", "default": "TODAY()"},
	    "due":      {"type": "date", "default": "ADDDAYS(issued, 30)"},
	    "owner":    {"type": "text", "default": "USER()"},
	    "discount": {"type": "decimal", "scale": 2, "default": "0"},
	    "net":      {"type": "money", "scale": 2},
	    "shipping": {"type": "object", "schema": "Address"},
	    "lines":    {"type": "rows", "schema": {"sku": "text", "qty": "number", "price": "decimal(2)"}},
	    "total":     {"formula": "ROUND(net * (1 - discount), 2)"},
	    "ship_city": {"formula": "shipping.city"}
	  },
	  "states": {"initial": "draft", "list": ["draft", "approved", "paid"]}
	}`))
	if err != nil {
		panic(err)
	}

	alice := fado.Principal{ID: "alice", Groups: []string{"sales"}}
	values := fado.Values{
		"number":   "2026-0042",
		"customer": "Customer:42",
		"net":      "100.00 EUR",
		"shipping": fado.Values{"line1": "Długa 1", "city": "Gdańsk", "country": "PL"},
		"lines":    []fado.Values{{"sku": "A1", "qty": 2, "price": "9.50"}},
	}
	doc, err := db.Create(ctx, "Invoice", values, fado.As(alice), fado.Key("ksef-2026-0042"), fado.WithID("inv_42"))
	if err != nil {
		panic(err)
	}
	fmt.Println(doc.Title, doc.State, doc.Version, doc.Values["issued"], doc.Values["due"], doc.Values["owner"], doc.Values["total"], doc.Values["ship_city"])

	// The same command with the same key is the same commit, not a second
	// document.
	again, err := db.Create(ctx, "Invoice", values, fado.As(alice), fado.Key("ksef-2026-0042"), fado.WithID("inv_42"))
	fmt.Println("replayed:", err == nil && again.Version == doc.Version)

	// A change recalculates what depends on it and is a new version.
	doc, _ = db.Set(ctx, "Invoice", "inv_42", fado.Values{"discount": "0.10"}, fado.As(alice))
	fmt.Println(doc.Version, doc.Values["total"])

	// A preview computes and writes nothing.
	preview, _ := db.Preview(ctx, "Invoice", "inv_42", fado.Values{"discount": "0.50"}, "", fado.As(alice))
	current, _ := db.Get(ctx, "Invoice", "inv_42")
	fmt.Println("preview:", preview.Document.Values["total"], "stored:", current.Values["total"])

	// A refusal names the variable and a stable code.
	_, err = db.Set(ctx, "Invoice", "inv_42", fado.Values{"total": "1.00 EUR"})
	var e *fado.Error
	if errors.As(err, &e) {
		fmt.Println(e.Code, e.Variable)
	}

	// Every version is in the history, with who committed it.
	history, _ := db.History(ctx, "Invoice", "inv_42")
	for _, h := range history {
		fmt.Println("version", h.Version, h.Values["total"], h.ActorID, h.ValidTo == 0)
	}

	// A filter is a formula over the class's variables; SQL runs over the
	// views.
	open, _ := db.Query(ctx, fado.Filter{Class: "Invoice", Where: "state = \"draft\" AND total > 50.00", Fields: []string{"number", "total"}})
	fmt.Println("filtered:", len(open), open[0].Values["number"], open[0].Values["total"])
	rows, _ := db.SQL(ctx, `SELECT count(*) AS n, sum(total) AS cents FROM Invoice`, nil)
	fmt.Println("sql:", rows.Columns, rows.Rows[0])

	// A definition's problems come back at once, each with a place.
	_, err = db.DefineClass(ctx, []byte(`{"name": "Receipt", "variables": {"amount": {"type": "money", "default": "1"}, "paid": {"type": "bool", "formula": "amount"}}}`))
	var diags fado.Diagnostics
	if errors.As(err, &diags) {
		for _, d := range diags {
			fmt.Println(d.Path, d.Code)
		}
	}
}
Output:
INV-2026-0042 draft 1 2026-11-08 2026-12-08 alice 100.00 EUR Gdańsk
replayed: true
2 90.00 EUR
preview: 50.00 EUR stored: 90.00 EUR
read_only total
version 1 100.00 EUR alice false
version 2 90.00 EUR alice true
filtered: 1 2026-0042 90.00 EUR
sql: [n cents] [1 9000]
/variables/amount/default type_mismatch
/variables/paid/formula type_mismatch

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BlobStore

type BlobStore = store.BlobStore

BlobStore holds the bytes of attachments by content hash (ADR-0033); an embedder may supply its own in Options.Blobs.

type Class

type Class = model.Class

Class is a compiled class definition.

type DB

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

DB is an open database.

func Open

func Open(path string, o Options) (*DB, error)

Open opens the database file at path, creating it when it does not exist.

func (*DB) CheckBlobs

func (db *DB) CheckBlobs(ctx context.Context) (mismatched []string, checked int, err error)

CheckBlobs recomputes the hash of every held blob and returns the ones whose bytes do not match, and how many it checked.

func (*DB) Class

func (db *DB) Class(name string) (*Class, bool)

Class returns a compiled class by name.

func (*DB) Classes

func (db *DB) Classes() []string

Classes lists the class names in definition order.

func (*DB) Close

func (db *DB) Close() error

Close closes the database.

func (*DB) Count

func (db *DB) Count(ctx context.Context, f Filter, opts ...Option) (int64, error)

Count counts the documents a filter's where matches.

func (*DB) Create

func (db *DB) Create(ctx context.Context, class string, values Values, opts ...Option) (*Document, error)

Create makes a document with the values given, the defaults of the inputs not given and every auto variable computed, as version 1.

func (*DB) CreateMany

func (db *DB) CreateMany(ctx context.Context, class string, docs []Values, opts ...Option) ([]*Document, error)

CreateMany makes documents in one transaction, one commit each, all or none: the bulk load path (ADR-0035). A batch takes no key and no id.

func (*DB) DefineClass

func (db *DB) DefineClass(ctx context.Context, definition []byte) (*Class, error)

DefineClass compiles a class definition in JSON, creates its tables and views and stores it. The error is Diagnostics when the definition is refused.

func (*DB) DefineSchema

func (db *DB) DefineSchema(ctx context.Context, definition []byte) (*Schema, error)

DefineSchema compiles a named schema in JSON and stores it.

func (*DB) DefineTable

func (db *DB) DefineTable(ctx context.Context, definition []byte) (*Table, error)

DefineTable compiles an explicit table definition in JSON, creates the table and its view and stores it; formulas read it through LOOKUP.

func (*DB) Delete

func (db *DB) Delete(ctx context.Context, class, id string, opts ...Option) error

Delete removes a document's current row and appends the event; its history stays.

func (*DB) Events

func (db *DB) Events(ctx context.Context, class, id string, opts ...Option) ([]*Event, error)

Events reads the events that touched a document, oldest first.

func (*DB) Get

func (db *DB) Get(ctx context.Context, class, id string, opts ...Option) (*Document, error)

Get reads the current version of a document.

func (*DB) History

func (db *DB) History(ctx context.Context, class, id string, opts ...Option) ([]*HistoryRow, error)

History reads every committed version of a document, oldest first.

func (*DB) ImportRows

func (db *DB) ImportRows(ctx context.Context, table string, rows []Values, opts ...Option) (int, error)

ImportRows writes rows into a table, each a map of column to value, replacing a row whose key exists; with Replace, the table is emptied first. It returns the number of rows written.

func (*DB) Migrate

func (db *DB) Migrate(ctx context.Context, class string, opts ...Option) (int, error)

Migrate brings every live document of a class that an older version computed to the current version, as commits in batches (ADR-0039). It returns how many documents it moved; terminal documents stay.

func (*DB) OpenBlob

func (db *DB) OpenBlob(ctx context.Context, hash string, opts ...Option) (io.ReadCloser, int64, error)

OpenBlob opens a blob by hash alone, for an administrator.

func (*DB) OpenFile

func (db *DB) OpenFile(ctx context.Context, class, id, hash string, opts ...Option) (io.ReadCloser, *FileInfo, error)

OpenFile opens a file a document holds, by the document and the hash, at the current version or the one AtVersion names.

func (*DB) Preview

func (db *DB) Preview(ctx context.Context, class, id string, values Values, transition string, opts ...Option) (*Preview, error)

Preview runs a create (empty id), a set or a transition through the commit pipeline and writes nothing (ADR-0025): the document as it would be, the states the command would visit and the transitions open from the last of them.

func (*DB) PreviewDraft added in v0.3.0

func (db *DB) PreviewDraft(ctx context.Context, definition []byte, id string, values Values, transition string, opts ...Option) (*Preview, error)

PreviewDraft compiles a class definition for this call alone and runs a create (empty id) or a set of an existing document of the stored class of the same name through the commit pipeline under it, writing and storing nothing (ADR-0025): a designer tries a draft on real documents, terminal ones included.

func (*DB) PutBlob

func (db *DB) PutBlob(ctx context.Context, name, mediaType string, r io.Reader, opts ...Option) (Values, error)

PutBlob stores the bytes of a file and returns the File fact to commit in an object<File> variable or a rows<File> element (ADR-0033).

func (*DB) Query

func (db *DB) Query(ctx context.Context, f Filter, opts ...Option) ([]*Document, error)

Query reads the documents of a class that match a filter, in its order, at most its limit, with the fields it names.

func (*DB) SQL

func (db *DB) SQL(ctx context.Context, statement string, args []any, opts ...Option) (*Rows, error)

SQL runs one read-only SELECT over the views with positional parameters; at most 10,000 rows come back.

func (*DB) Set

func (db *DB) Set(ctx context.Context, class, id string, values Values, opts ...Option) (*Document, error)

Set assigns values to a document and recalculates what depends on them, as a new version; a set that changes nothing commits nothing.

func (*DB) Table

func (db *DB) Table(name string) (*Table, bool)

Table returns a compiled table definition by name.

func (*DB) TableRows

func (db *DB) TableRows(ctx context.Context, table string) ([]Values, error)

TableRows reads every row of a table in key order.

func (*DB) Tables

func (db *DB) Tables() []string

Tables lists the table names in definition order.

func (*DB) Transition added in v0.3.0

func (db *DB) Transition(ctx context.Context, class, id, name string, values Values, opts ...Option) (*Document, error)

Transition moves a document along the named transition of its class, with the assignments in values applied in the same commit (ADR-0040); values may be nil. Every eventless transition that then fires is a further commit in the same transaction. A transition that cannot happen is refused with unknown_transition, wrong_state, guard_false, too_many_transitions or terminal, before anything is written.

func (*DB) Transitions added in v0.3.0

func (db *DB) Transitions(ctx context.Context, class, id string, opts ...Option) ([]TransitionInfo, error)

Transitions lists the named transitions leaving the document's state, each with whether the principal may take it now (ADR-0040). It writes nothing.

func (*DB) ValidateClass

func (db *DB) ValidateClass(definition []byte) Diagnostics

ValidateClass reports what DefineClass would refuse, storing nothing.

func (*DB) Version

func (db *DB) Version(ctx context.Context, class, id string, version int64, opts ...Option) (*HistoryRow, error)

Version reads one committed version of a document.

type Diagnostic

type Diagnostic = model.Diagnostic

Diagnostic is one finding: a code, the JSON pointer of the element, the position in a formula, a message and a hint.

type Diagnostics

type Diagnostics = model.Diagnostics

Diagnostics are the findings of a class or schema definition; the error DefineClass, DefineSchema and ValidateClass return.

type Document

type Document = store.Document

Document is a document at one version.

type Error

type Error = store.Error

Error is a refusal with a stable code, the variable it is about, a message and a hint.

type Event

type Event = store.Event

Event is one row of the event log.

type FileInfo

type FileInfo = store.FileInfo

FileInfo is the fact of a file as a document holds it.

type Filter

type Filter = store.Filter

Filter is a query over a class: a where formula, an order, a limit, an offset and the fields to return (ADR-0038).

type HistoryRow

type HistoryRow = store.HistoryRow

HistoryRow is one committed version with its validity and actor.

type Option

type Option = store.Option

Option configures one call: As, Key, BusinessDate, WithID.

func As

func As(p Principal) Option

As runs the call as the principal.

func At added in v0.3.0

func At(t time.Time) Option

At runs a call that reads or previews at the moment (ADR-0042): NOW() is t and TODAY() its UTC day in every formula the call evaluates, and a preview reports it as Now. A zero t is the database's clock. A command that writes refuses it with not_allowed: a change is previewed at a moment, or made in a copy of the database opened with Options.Now set.

func AtVersion

func AtVersion(version int64) Option

AtVersion makes OpenFile read a committed version instead of the current one.

func BusinessDate

func BusinessDate(day time.Time) Option

BusinessDate gives the commit's event its business date (ADR-0026), a calendar day.

func Key

func Key(k string) Option

Key gives a mutating call its idempotency key (ADR-0026).

func Replace

func Replace() Option

Replace makes ImportRows empty the table before writing the rows.

func WithID

func WithID(id string) Option

WithID gives a new document the caller's id.

type Options

type Options = store.Options

Options configure Open.

type Preview

type Preview = store.Preview

Preview is what a commit would do (ADR-0025): the document, the errors per variable, the states visited and the transitions open.

type Principal

type Principal = store.Principal

Principal is who a call runs for (ADR-0027).

type Rows

type Rows = store.Rows

Rows is the result of a SQL statement: columns and rows.

type Schema

type Schema = model.Schema

Schema is a resolved named schema (ADR-0031).

type Table

type Table = model.Table

Table is a compiled explicit table definition (ADR-0016).

type TransitionInfo added in v0.3.0

type TransitionInfo = store.TransitionInfo

TransitionInfo is one named transition leaving a document's state and whether it may be taken now (ADR-0040).

type Values

type Values = store.Values

Values are variable values by name in the wire shape (ADR-0018).

Jump to

Keyboard shortcuts

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