/** * Stores event-log entries and replication state. * * `EventJournal` records committed entries, exposes them for replay, publishes * local changes, and tracks the remote metadata needed to exchange entries with * other journals. Higher-level event-log schemas and handlers use this service * to rebuild projections, sync offline clients, import remote changes, and * coordinate writes per store. This module also defines journal errors, entry * and remote identifiers, schemas, and in-memory or IndexedDB-backed journal * layers. * * @stability unstable * @since 4.0.0 */ import * as Arr from "../Array.ts"; import type { Brand } from "../Brand.ts"; import * as Context from "../Context.ts"; import * as DateTime from "../DateTime.ts"; import * as Effect from "../Effect.ts"; import * as SchemaBinary from "../encoding/SchemaBinary.ts"; import * as Layer from "../Layer.ts"; import type { Option } from "../Option.ts"; import * as Order from "../Order.ts"; import * as PubSub from "../PubSub.ts"; import * as Schema from "../Schema.ts"; import type * as SchemaAST from "../SchemaAST.ts"; import type { Scope } from "../Scope.ts"; import type { StoreId } from "./EventLogMessage.ts"; declare const EventJournal_base: Context.ServiceClass, EventJournalError>; /** * Read the stored entries with the given ids. The result is aligned with * `ids`, with `undefined` for ids that are not in the journal. */ readonly entriesById: (ids: ReadonlyArray) => Effect.Effect, EventJournalError>; /** * Write an event to the journal, performing an effect before committing the * event. */ readonly write: (options: { readonly event: string; readonly primaryKey: string; readonly payload: Uint8Array; readonly effect: (entry: Entry) => Effect.Effect; }) => Effect.Effect; /** * Write several events to the journal, performing an effect before * committing them. * * **Details** * * `effect` runs once with every entry, in input order and with ascending * ids. The entries are committed together and then published to `changes` * only after it succeeds. If it fails, nothing is committed or published. */ readonly writeMany: (options: { readonly entries: Arr.NonEmptyReadonlyArray<{ readonly event: string; readonly primaryKey: string; readonly payload: Uint8Array; }>; readonly effect: (entries: Arr.NonEmptyReadonlyArray) => Effect.Effect; }) => Effect.Effect; /** * Write events from a remote source to the journal. * * **Details** * * `effect` runs once with every new entry after compaction, sorted by * `Entry.Order`. Each entry is paired with the stored entries for the same * event and primary key whose id is strictly greater, in id order. Entries * in the batch never conflict with each other: applying them in id order * gives the same result as delivering them one at a time. A compaction * replacement written by `EventLog` takes the place and conflicts of the * source entry it is anchored to, not of its own id. * * All conflicts are read before `effect` runs and the batch is committed * after it succeeds, with no journal I/O while it runs. It is not called * when the batch has no new entries. If `effect` fails, no entry from the * batch is committed. */ readonly writeFromRemote: (options: { readonly remoteId: RemoteId; readonly entries: ReadonlyArray; readonly compact?: ((uncommitted: ReadonlyArray) => Effect.Effect, EventJournalError>) | undefined; readonly effect: (entries: ReadonlyArray<{ readonly entry: Entry; readonly conflicts: ReadonlyArray; }>) => Effect.Effect; }) => Effect.Effect<{ readonly duplicateEntries: ReadonlyArray; }, EventJournalError>; /** * Run an effect with the uncommitted entries for a remote source. * * The effect is not run when there are no uncommitted entries, in which case * `Option.none()` is returned. Otherwise, its result is wrapped in * `Option.some()`. */ readonly withRemoteUncommited: (remoteId: RemoteId, f: (entries: Arr.NonEmptyReadonlyArray) => Effect.Effect) => Effect.Effect, EventJournalError | E, R>; /** * Records upload acknowledgements, advancing `nextRemoteSequence` only across * contiguous sequences from the current cursor or `firstSequence`. */ readonly writeRemoteAcks: (options: { readonly remoteId: RemoteId; readonly acks: ReadonlyArray; readonly firstSequence?: number | undefined; }) => Effect.Effect; /** * Retrieve the first unused sequence number for a remote source. */ readonly nextRemoteSequence: (remoteId: RemoteId) => Effect.Effect; /** * The entries added to the local journal. */ readonly changes: Effect.Effect, never, Scope>; /** * Remove all data */ readonly destroy: Effect.Effect; /** * Run an effect with a lock on the journal. */ readonly withLock: (storeId: StoreId) => (effect: Effect.Effect) => Effect.Effect; }>; /** * Context service for storing and replaying event journal entries. * * **Details** * * The service writes local entries, imports entries from remote journals, exposes * a stream of local changes, and provides per-store locking. * * Entries written by `EventLog` carry payloads encoded with their event's * `payloadSchemaBinary`; see {@link Entry} for how to decode them. * * @stability unstable * @category services * @since 4.0.0 */ export declare class EventJournal extends EventJournal_base { } declare const TypeId: "effect/eventlog/EventJournal/EventJournalError"; declare const EventJournalError_base: new = {}>(args: import("../Types.ts").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("../Cause.ts").YieldableError & { readonly _tag: "EventJournalError"; } & Readonly; /** * Error raised by event journal operations. * * **Details** * * The error records the journal method that failed and the underlying cause. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class EventJournalError extends EventJournalError_base<{ readonly method: string; readonly cause: unknown; }> { /** * Marks this value as an event journal error for runtime guards. * * @stability unstable * @since 4.0.0 */ readonly [TypeId]: "effect/eventlog/EventJournal/EventJournalError"; } /** * Brand identifier used for `RemoteId` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export type RemoteIdTypeId = "effect/eventlog/EventJournal/RemoteId"; /** * Runtime brand identifier used for `RemoteId` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export declare const RemoteIdTypeId: RemoteIdTypeId; /** * Branded byte identifier for a remote event journal source. * * @stability unstable * @category models * @since 4.0.0 */ export type RemoteId = Uint8Array & Brand; /** * Schema for branded remote event journal identifiers. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const RemoteId: Schema.brand; /** * Generates a new random `RemoteId`. * * **When to use** * * Use when generating a fresh event-log remote id internally and the UUID bytes * are trusted to satisfy the brand. * * **Gotchas** * * This is unsafe because the generated UUID bytes are cast to the brand without * schema validation. * * @stability unstable * @category unsafe * @since 4.0.0 */ export declare const makeRemoteIdUnsafe: () => RemoteId; /** * Runtime brand identifier used for `EntryId` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export declare const EntryIdTypeId: EntryIdTypeId; /** * Brand identifier used for `EntryId` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export type EntryIdTypeId = "effect/eventlog/EventJournal/EntryId"; /** * Branded byte identifier for an event journal entry. * * @stability unstable * @category models * @since 4.0.0 */ export type EntryId = Uint8Array & Brand; /** * Schema for branded event journal entry identifiers. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const EntryId: Schema.brand, Uint8Array>, "effect/eventlog/EventJournal/EntryId">; /** * Provides an Ordering instance for entry identifiers based on their raw UUID bytes. * * @stability unstable * @category ordering * @since 4.0.0 */ export declare const EntryIdOrder: Order.Order; /** * Generates a UUID v7 `EntryId`, optionally using the supplied millisecond * timestamp. * * **When to use** * * Use when generating an event-log entry id internally and the UUID v7 bytes * are trusted to satisfy the brand. * * **Details** * * Ids taken from the clock are strictly ascending within the current process. * Ids created in the same millisecond carry an incrementing 12-bit counter. If * the clock stalls or moves backwards, ids keep the last millisecond, and * after 4096 ids in one millisecond they move to the next one. * * The journals generate local ids from this sequence, or from a copy of it * advanced past the latest id in their store, so a local id sorts after every * entry committed to that store, even when the local clock is behind. * * **Gotchas** * * This is unsafe because the generated UUID bytes are cast to the brand without * schema validation. * * Throws a `RangeError` once the sequence passes the last UUID v7 millisecond. * Every later call throws too, because any id it could return would sort before * one already generated. * * Ids created with an explicit `msecs` are not part of the sequence, and clamp * to the UUID v7 range. * * @stability unstable * @category unsafe * @since 4.0.0 */ export declare const makeEntryIdUnsafe: (options?: { msecs?: number; }) => EntryId; /** * Extracts the millisecond timestamp encoded in a UUID v7 `EntryId`. * * @stability unstable * @category getters * @since 4.0.0 */ export declare const entryIdMillis: (entryId: EntryId) => number; declare const Entry_base: Schema.Class, Uint8Array>, "effect/eventlog/EventJournal/EntryId">; readonly event: Schema.String; readonly primaryKey: Schema.String; readonly payload: Schema.Uint8Array; }>, {}>; /** * Schema for a committed event journal entry. * * **Details** * * An entry records its ID, event tag, primary key, and SchemaBinary-encoded * payload, with helpers for array SchemaBinary encoding and creation timestamps. * * **Gotchas** * * `EventLog` encodes `payload` with the `payloadSchemaBinary` of the event whose * tag is `entry.event`, so decode it with that event's codec. A codec with an * incompatible wire layout, such as a union of a group's tagged payload schemas, * cannot read it. Entries from remote journals can carry tags the local schema * does not define. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare class Entry extends Entry_base { #private; /** * SchemaBinary codec for arrays of committed event journal entries. * * @stability unstable * @since 4.0.0 */ static arraySchemaBinary: Schema.$Array>; /** * Encodes arrays of committed entries with the SchemaBinary entry codec. * * @stability unstable * @since 4.0.0 */ static encodeArray: (input: unknown, options?: SchemaAST.ParseOptions) => Effect.Effect[], Schema.SchemaError, never>; /** * Decodes arrays of committed entries with the SchemaBinary entry codec. * * @stability unstable * @since 4.0.0 */ static decodeArray: (input: unknown, options?: SchemaAST.ParseOptions) => Effect.Effect, Schema.SchemaError>; /** * Ordering for committed entries by their event journal entry id. * * @stability unstable * @since 4.0.0 */ static Order: Order.Order; /** * String representation of the entry id. * * @stability unstable * @since 4.0.0 */ get idString(): string; /** * Creation timestamp encoded in the UUID v7 entry id, in epoch milliseconds. * * @stability unstable * @since 4.0.0 */ get createdAtMillis(): number; /** * Creation timestamp encoded in the UUID v7 entry id, as a UTC date-time. * * @stability unstable * @since 4.0.0 */ get createdAt(): DateTime.Utc; } /** * A local entry's remote-assigned sequence. * * @stability unstable * @category models * @since 4.0.6 */ export interface RemoteAck { readonly entryId: EntryId; readonly sequence: number; } declare const RemoteEntry_base: Schema.Class, {}>; /** * Schema for an event journal entry received from a remote source. * * **Details** * * It pairs the remote sequence number with the journal entry payload. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare class RemoteEntry extends RemoteEntry_base { } /** * Creates an in-memory `EventJournal` service. * * **Gotchas** * * Entries, remote tracking state, and locks live only in the current process and * are lost when the service is discarded. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeMemory: Effect.Effect; /** * Layer that provides an in-memory `EventJournal`. * * **Gotchas** * * All journal data is stored in process memory and is not persisted across layer * lifetimes. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const layerMemory: Layer.Layer; /** * Creates an `EventJournal` backed by IndexedDB. * * **Details** * * The journal stores entries and remote replication metadata in the configured * browser database, publishes local changes, and requires `Scope` so the database * connection can be closed when the scope ends. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeIndexedDb: (options?: { readonly database?: string; }) => Effect.Effect; /** * Provides `EventJournal` using the IndexedDB-backed implementation created by * `makeIndexedDb`. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const layerIndexedDb: (options?: { readonly database?: string; }) => Layer.Layer; export {}; //# sourceMappingURL=EventJournal.d.ts.map