Skip to content

Tx

The transaction of one Session commit, available as a service inside Session.commit(body).

Everything a body stages is written in one atomic storage batch, or not at all. Table reads are allowed only before the transaction’s first table write (ReadAfterWrite); document access is not a table read.

import { Session, type State, Tx } from "@effective-harness/core"
import * as Effect from "effect/Effect"
declare const Todos: State.ConversationDefinition<{ readonly items: ReadonlyArray<string> }>
const program = Session.commit(
Effect.gen(function* () {
const conversation = yield* Tx.createConversation({ ownership: { kind: "ownerless" } })
yield* Tx.appendEntry(conversation.id, { kind: "app.note", data: "hello" })
yield* Tx.update(Todos.of(conversation.id), (todos) => ({ items: [...todos.items, "write docs"] }))
})
)

Since v0.0.0


Failures of table reads.

Signature

type ReadError = StorageFailure | ReadAfterWrite | TransactionSettled

Source

Since v0.0.0

Where a new task lives and whether the scheduler treats it as a boundary.

Signature

export interface TaskOptions {
readonly ownership: TaskOwnership
/** Excluded from idle waits, conversation aborts and cascades. Conversation-owned tasks only. */
readonly background?: boolean
}

Source

Since v0.0.0

Signature

declare class Tx

Source

Since v0.0.0

Signature

export interface TxShape {
// Table reads
readonly conversation: (id: ConversationId) => Effect.Effect<Option.Option<ConversationRecord>, ReadError>
readonly entry: (id: EntryId) => Effect.Effect<Option.Option<EntryRecord>, ReadError>
readonly task: (id: TaskId) => Effect.Effect<Option.Option<TaskRecord>, ReadError>
readonly scanConversations: (
query: ConversationQuery,
limit: number,
cursor?: Cursor
) => Effect.Effect<Page<ConversationRecord>, ReadError>
readonly scanEntries: (
query: EntryQuery,
limit: number,
cursor?: Cursor
) => Effect.Effect<Page<EntryRecord>, ReadError>
readonly latestHeadMarker: (
conversationId: ConversationId
) => Effect.Effect<Option.Option<EntryRecord & { readonly head: EntryId }>, ReadError>
readonly scanTasks: (query: TaskQuery, limit: number, cursor?: Cursor) => Effect.Effect<Page<TaskRecord>, ReadError>
readonly submission: (id: SubmissionId) => Effect.Effect<Option.Option<SubmissionRecord>, ReadError>
/** The submission with a conversation-scoped request ID. */
readonly submissionByRequest: (
conversationId: ConversationId,
requestId: string
) => Effect.Effect<Option.Option<SubmissionRecord>, ReadError>
// Table writes
readonly createConversation: (options: {
readonly ownership: ConversationOwnership
}) => Effect.Effect<ConversationRecord, WriteError>
/**
* Create a conversation that continues from one visible entry of another. It sees the parent's transcript through
* that entry; each conversation state follows its fork policy (`asOf`: the value at the entry's commit, `current`:
* the committed value now, `initial`: not copied). Task state and tasks are never copied.
*/
readonly forkConversation: (
parentConversationId: ConversationId,
at: EntryId,
options: { readonly ownership: ConversationOwnership }
) => Effect.Effect<ConversationRecord, WriteError>
readonly appendEntry: (conversationId: ConversationId, draft: EntryDraft) => Effect.Effect<EntryRecord, WriteError>
/** Create a raw submission record; no admission rules apply. Hosts use `Conversation.submit`. */
readonly createSubmission: (create: SubmissionCreate) => Effect.Effect<SubmissionRecord, WriteError>
/**
* Settle a queued or placed submission; only a placed input can be answered. A settled one stays unchanged. Resolved
* at assembly against the transaction's latest candidate, so it is not a table read.
*/
readonly settleSubmission: (
id: SubmissionId,
settlement: SubmissionSettlement
) => Effect.Effect<void, TransactionSettled>
/** Record a queued submission's placement at `entry`: an input becomes `placed`, a write `done`. */
readonly placeSubmission: (id: SubmissionId, entry: EntryId) => Effect.Effect<void, TransactionSettled>
/**
* Create a pending task. A child task lives in its owner's conversation. New owned work needs a live owner in the
* commit's final state: not `completing`, terminal or abort-marked.
*/
readonly createTask: <I, C extends Task.Checkpoint, R>(
task: Task.Task<I, C, R>,
input: I,
options: TaskOptions
) => Effect.Effect<TaskId, WriteError>
// Documents
readonly doc: <A>(ref: State.Ref<A>) => Effect.Effect<A, WriteError>
readonly update: <A>(ref: State.Ref<A>, f: (value: A) => A) => Effect.Effect<A, WriteError>
readonly set: <A>(ref: State.Ref<A>, value: A) => Effect.Effect<void, WriteError>
readonly retire: <A>(ref: State.Ref<A>) => Effect.Effect<void, WriteError>
}

Source

Since v0.0.0

Failures of staging writes.

Signature

type WriteError =
| StorageFailure
| TransactionSettled
| ConversationNotFound
| EntryNotVisible
| TaskNotFound
| OwnerNotLive
| TaskRejected
| StateError

Source

Since v0.0.0

Append an immutable entry.

Signature

declare const appendEntry: (conversationId: ConversationId, draft: EntryDraft) => any

Source

Since v0.0.0

Signature

declare const conversation: (id: ConversationId) => any

Source

Since v0.0.0

Create a conversation with explicitly selected ownership.

Signature

declare const createConversation: (options: { readonly ownership: ConversationOwnership }) => any

Source

Since v0.0.0

Signature

declare const createSubmission: (create: SubmissionCreate) => any

Source

Since v0.0.0

Create a pending task; see TxShape.createTask.

Signature

declare const createTask: <I, C extends Task.Checkpoint, R>(
task: Task.Task<I, C, R>,
input: I,
options: TaskOptions
) => any

Source

Since v0.0.0

The staged value, creating the document with its initial value when absent.

Signature

declare const doc: <A>(ref: State.Ref<A>) => any

Source

Since v0.0.0

Signature

declare const entry: (id: EntryId) => any

Source

Since v0.0.0

Fork a conversation at one visible entry; see TxShape.forkConversation.

Signature

declare const forkConversation: (
parentConversationId: ConversationId,
at: EntryId,
options: { readonly ownership: ConversationOwnership }
) => any

Source

Since v0.0.0

The newest visible entry of the conversation that carries a head.

Signature

declare const latestHeadMarker: (conversationId: ConversationId) => any

Source

Since v0.0.0

Signature

declare const placeSubmission: (id: SubmissionId, entry: EntryId) => any

Source

Since v0.0.0

Retire the current incarnation; a later access in the same transaction creates a new one.

Signature

declare const retire: <A>(ref: State.Ref<A>) => any

Source

Since v0.0.0

Signature

declare const scanConversations: (query: ConversationQuery, limit: number, cursor?: Cursor) => any

Source

Since v0.0.0

Newest-first.

Signature

declare const scanEntries: (query: EntryQuery, limit: number, cursor?: Cursor) => any

Source

Since v0.0.0

Signature

declare const scanTasks: (query: TaskQuery, limit: number, cursor?: Cursor) => any

Source

Since v0.0.0

Replace the staged value, creating the document when absent.

Signature

declare const set: <A>(ref: State.Ref<A>, value: A) => any

Source

Since v0.0.0

Signature

declare const settleSubmission: (id: SubmissionId, settlement: SubmissionSettlement) => any

Source

Since v0.0.0

Signature

declare const submission: (id: SubmissionId) => any

Source

Since v0.0.0

Signature

declare const submissionByRequest: (conversationId: ConversationId, requestId: string) => any

Source

Since v0.0.0

Signature

declare const task: (id: TaskId) => any

Source

Since v0.0.0

Replace the staged value with f(value), creating the document when absent; returns the new value.

Signature

declare const update: <A>(ref: State.Ref<A>, f: (value: A) => A) => any

Source

Since v0.0.0