capri/repository
Types
pub type Decoder(t) {
Decoder(decode.Decoder(t))
CustomDecoder(
function: fn(dynamic.Dynamic) -> #(
t,
List(decode.DecodeError),
),
)
}
Constructors
-
Decoder(decode.Decoder(t)) -
CustomDecoder( function: fn(dynamic.Dynamic) -> #(t, List(decode.DecodeError)), )
Registration and opening failures. Decode errors retain the exact child path that contained the invalid payload.
pub type Error {
InvalidPrefix
InvalidRepositoryId
InvalidRepositoryVersion(version: Int)
InvalidMigration(from_version: Int, to_version: Int)
AlreadyCurrent(from_version: Int, to_version: Int)
InvalidMigrationOrder(expected: Int, actual: Int)
ReservedPrefix(path: path.Path)
MalformedMetadata(path: path.Path, payload: dynamic.Dynamic)
RepositoryAlreadyRegistered(path: path.Path)
InvalidChildKey(prefix: path.Path, key: dynamic.Dynamic)
RepositoryIdMismatch(
path: path.Path,
expected: String,
actual: String,
)
NewerRepositoryVersion(
path: path.Path,
registered: Int,
requested: Int,
)
MigrationRequired(
path: path.Path,
from_version: Int,
to_version: Int,
)
MissingRepositoryMetadata(path: path.Path)
MigrationVersionMismatch(
path: path.Path,
expected: Int,
actual: Int,
)
MissingMigration(
path: path.Path,
from_version: Int,
to_version: Int,
)
TargetVersionMismatch(expected: Int, actual: Int)
DataLossNotProven(
path: path.Path,
from_version: Int,
to_version: Int,
)
DataLossDetected(
path: path.Path,
from_version: Int,
to_version: Int,
)
DescendantsPresent(path: path.Path)
MissingChildPayload(path: path.Path)
UnexpectedChildStoredProcedure(path: path.Path)
DuplicateKey(path: path.Path)
RecordDecodeFailed(
path: path.Path,
errors: List(decode.DecodeError),
)
Conflict(path: path.Path)
KhepriError(khepri_error.Error)
}
Constructors
-
InvalidPrefix -
InvalidRepositoryId -
InvalidRepositoryVersion(version: Int) -
InvalidMigration(from_version: Int, to_version: Int) -
AlreadyCurrent(from_version: Int, to_version: Int) -
InvalidMigrationOrder(expected: Int, actual: Int) -
ReservedPrefix(path: path.Path) -
MalformedMetadata(path: path.Path, payload: dynamic.Dynamic) -
RepositoryAlreadyRegistered(path: path.Path) -
InvalidChildKey(prefix: path.Path, key: dynamic.Dynamic) -
RepositoryIdMismatch( path: path.Path, expected: String, actual: String, ) -
NewerRepositoryVersion( path: path.Path, registered: Int, requested: Int, ) -
MigrationRequired( path: path.Path, from_version: Int, to_version: Int, ) -
MissingRepositoryMetadata(path: path.Path) -
MigrationVersionMismatch( path: path.Path, expected: Int, actual: Int, ) -
MissingMigration( path: path.Path, from_version: Int, to_version: Int, ) -
TargetVersionMismatch(expected: Int, actual: Int) -
DataLossNotProven( path: path.Path, from_version: Int, to_version: Int, ) -
DataLossDetected( path: path.Path, from_version: Int, to_version: Int, ) -
DescendantsPresent(path: path.Path) -
MissingChildPayload(path: path.Path) -
UnexpectedChildStoredProcedure(path: path.Path) -
DuplicateKey(path: path.Path) -
RecordDecodeFailed( path: path.Path, errors: List(decode.DecodeError), ) -
Conflict(path: path.Path) -
KhepriError(khepri_error.Error)
An executable migration plan for one repository prefix. Only plan can create
this capability, so safe execution cannot be replaced by an arbitrary
caller-provided function.
pub opaque type Migration
Errors returned by the small repository operation surface.
pub type OperationError {
StoreError(khepri_error.Error)
InvalidData(path: path.Path, errors: List(decode.DecodeError))
AlreadyExists(path: path.Path)
OperationConflict(path: path.Path)
CorruptRepository(reason: Corruption)
}
Constructors
-
StoreError(khepri_error.Error) -
InvalidData(path: path.Path, errors: List(decode.DecodeError)) -
AlreadyExists(path: path.Path) -
OperationConflict(path: path.Path) -
CorruptRepository(reason: Corruption)
A non-root exact path carrying the repository capability for its children.
pub opaque type Prefix(record)
A versioned decoder and migration chain bound to a Khepri path prefix.
pub opaque type Repository(record)
One repository-wide transformation in an ordered migration chain.
Steps receive all direct children, allowing records to be renamed, moved, split, merged, or transformed together.
pub opaque type Step
Values
pub fn all(
store: lifecycle.StoreHandle,
prefix: Prefix(record),
) -> yielder.Yielder(Result(Child(record), OperationError))
Lazily stream every decoded direct child beneath a repository prefix.
Each internal page is read from a separate Khepri snapshot. Concurrent inserts or deletes may therefore appear or disappear between pages.
pub fn bind(
store: lifecycle.StoreHandle,
path: path.Path,
repository: Repository(record),
) -> Result(Prefix(record), Error)
Bind a repository definition to a path.
If the path has not yet been registered, it is registered. If it has already been registered, it is opened.
Registration races are resolved by reopening the repository after another caller successfully registers the same prefix.
pub fn child(
prefix: Prefix(record),
key: path.Key,
) -> TypedPath(record)
Return the repository-bound path of a direct child.
pub fn delete(
store: lifecycle.StoreHandle,
record_path: TypedPath(record),
) -> Result(option.Option(record), OperationError)
Atomically delete and return a repository-bound record.
Missing records return None. Returning the deleted value makes single-use
resources safe without exposing a separate destructive transaction API.
pub fn get(
store: lifecycle.StoreHandle,
record_path: TypedPath(record),
) -> Result(option.Option(record), OperationError)
Read and decode a repository-bound payload.
pub fn insert(
store: lifecycle.StoreHandle,
record_path: TypedPath(record),
value: record,
) -> Result(Nil, OperationError)
Create a record only when its exact node is absent.
pub fn migrate(migrations: List(Migration)) -> Result(Nil, Error)
Apply repository migrations in order, rejecting any step that cannot prove it is lossless for every stored payload.
pub fn migrate_all(
store: lifecycle.StoreHandle,
prefix_path: path.Path,
repository: Repository(record),
) -> Result(Nil, Error)
Migrate to current for a given repository. Already current is a success condition. Fetches all migration steps passed to a repository, plans them, and then runs them. Safe to run at application startup.
pub fn migration(
from_version: Int,
to_version: Int,
forward: fn(List(Entry(dynamic.Dynamic))) -> List(
Entry(dynamic.Dynamic),
),
reverse: fn(List(Entry(dynamic.Dynamic))) -> List(
Entry(dynamic.Dynamic),
),
) -> Result(Step, Error)
Define a reversible repository-wide migration step. Safe migrations prove that reversing the complete transformed dataset reproduces the exact original keys and payloads.
pub fn new(
id: String,
version: Int,
decoder: Decoder(record),
migrations: List(Step),
) -> Result(Repository(record), Error)
pub fn open(
store: lifecycle.StoreHandle,
path: path.Path,
repository: Repository(record),
) -> Result(Prefix(record), Error)
Open an existing repository-bound prefix. Metadata is read and verified; child payloads are not decoded, rewritten, or canonicalized.
pub fn put(
store: lifecycle.StoreHandle,
record_path: TypedPath(record),
value: record,
) -> Result(Nil, OperationError)
Encode, decode-check, and persist a value at a repository-bound path.
pub fn register(
store: lifecycle.StoreHandle,
path: path.Path,
repository: Repository(record),
) -> Result(Prefix(record), Error)
Initialize repository metadata at an unregistered prefix. Existing direct
child payloads are decoded and canonicalized before the atomic commit. Use
open for an already-registered repository.
pub fn run_decoder(
data: dynamic.Dynamic,
decoder: Decoder(anything),
) -> Result(anything, List(decode.DecodeError))
pub fn unsafe_migrate(
migrations: List(Migration),
) -> Result(Nil, Error)
Apply repository migrations in order while permitting transformations that discard or irreversibly alter stored data.
pub fn unsafe_migration(
from_version: Int,
to_version: Int,
forward: fn(List(Entry(dynamic.Dynamic))) -> List(
Entry(dynamic.Dynamic),
),
) -> Result(Step, Error)
Define a forward-only repository-wide migration step. It can only be applied
through unsafe_migrate.