import * as Effect from "../Effect.ts"; import * as Layer from "../Layer.ts"; import * as Schema from "../Schema.ts"; import type * as Event from "./Event.ts"; import type * as EventGroup from "./EventGroup.ts"; import { Entry } from "./EventJournal.ts"; import * as EventLog from "./EventLog.ts"; import type { StoreId } from "./EventLogMessage.ts"; /** * Required fields that can be part of a declared row key. * * @stability unstable * @category utility types * @since 4.0.6 */ export type RequiredField = { [K in keyof Fields]: Fields[K] extends { readonly "~type.optionality": "optional"; } ? never : K; }[keyof Fields] & string; /** * Values of the declared key fields of a row. * * @stability unstable * @category utility types * @since 4.0.6 */ export type KeyValues = { readonly [K in Names]: Schema.Schema.Type; }; /** * Required fields whose values are all strings, all numbers or all * `Uint8Array`s, so their JSON encodings form keys without `derive`. * * @stability unstable * @category utility types * @since 4.0.6 */ export type DefaultKeyField = { [K in RequiredField]: [Schema.Schema.Type] extends [string] ? K : [Schema.Schema.Type] extends [number] ? K : [Schema.Schema.Type] extends [Uint8Array] ? K : never; }[RequiredField]; /** * A row key declared from one or more fields. * * **Details** * * The primary key is the result of `derive`, which must be deterministic and * unique per row. Without it, the primary key is the JSON tuple of the * fields' JSON encodings, in declaration order, which needs every field to be * a {@link DefaultKeyField}. Other fields can collide in JSON, such as `null` * and `undefined`, so they require `derive`. * * @stability unstable * @category models * @since 4.0.6 */ export type KeyDeclaration> = { readonly fields: readonly [Names, ...Array]; } & ([Names] extends [DefaultKeyField] ? { readonly derive?: ((fields: KeyValues) => string) | undefined; } : { readonly derive: (fields: KeyValues) => string; }); /** * Primary key type of a row: the key value for a string-typed single-field * key, and `string` otherwise. * * @stability unstable * @category utility types * @since 4.0.6 */ export type PrimaryKey = Schema.Schema.Type extends string ? Schema.Schema.Type : string; /** * Optional non-key fields of a `patch` payload. * * @stability unstable * @category utility types * @since 4.0.6 */ export type PatchFields = { readonly [K in Exclude]: Schema.optionalKey; }; /** * Schema of a row event's payload, derived from the row schema. * * **Details** * * - `set` carries the full row, validated by the row schema. * - `patch` carries the row key and the fields to write. An absent field is * not written. The key itself cannot be patched. * - `remove` carries the row key. * * @stability unstable * @category models * @since 4.0.6 */ export type PayloadSchema = Schema.Union; }>, Schema.TaggedStruct<"patch", { readonly key: KeySchema; readonly fields: Schema.Struct>; }>, Schema.TaggedStruct<"remove", { readonly key: KeySchema; }> ]>; /** * Decoded payload of a row event. * * @stability unstable * @category models * @since 4.0.6 */ export type Payload = Schema.Schema.Type>; /** * A row event to write, as returned by the `set`, `patch` and `remove` * constructors of a row. * * @stability unstable * @category models * @since 4.0.6 */ export type Write = { readonly event: Tag; readonly payload: Payload; }; /** * A row type that syncs through an `EventLog`. * * **Details** * * Includes `EventGroup.add` options, the row schema, its key fields and * constructors for the events to write. The row key is the event primary key; * success is `void`. * * `Key` is the union of the key fields. `KeySchema` is the schema of the key * in `patch` and `remove` payloads and callbacks: the field's schema for a * single-field key, and a struct of the key fields for a declared key. * * @stability unstable * @category models * @since 4.0.6 */ export interface EventRow { readonly tag: Tag; readonly keyFields: ReadonlyArray; readonly schema: Schema.Struct; readonly primaryKey: (payload: Payload) => string; readonly payload: PayloadSchema; readonly success: typeof Schema.Void; readonly error: Error; /** * Computes the primary key of a row from its key fields. */ readonly keyOf: (fields: KeyValues) => PrimaryKey; /** * Replaces the whole row, creating it if missing. */ readonly set: (row: Schema.Struct["Type"]) => Write; /** * Writes fields of an existing row. * * **Details** * * A patch on a missing row does nothing. To clear a value, use a nullable * field and write `null`. * * Absent fields stay unwritten, even with decoding defaults. Values whose * encoding omits the key, such as `Option.none()` for * `Schema.OptionFromOptionalKey`, fail encoding: replicas cannot distinguish * them from unwritten fields. */ readonly patch: (key: Schema.Schema.Type, fields: Schema.Struct>["Type"]) => Write; /** * Deletes the row. */ readonly remove: (key: Schema.Schema.Type) => Write; } /** * Defines a row type as a single event. * * **Details** * * `key` names a required field, or declares required fields with an * optional `derive` function. `patch`, `remove` and the handler callbacks take * the key as typed values: the field's value for a single field, and a struct * of the fields for a declaration. Key fields cannot be patched. To move a * row, remove it and set it again. * * A single-field key must be a {@link DefaultKeyField}. Declare other fields * with a `derive` function. * * `keyOf` computes the primary key that conflicts are grouped by: the value * of a string single-field key, the result of `derive`, or else the JSON * tuple of the key fields' JSON encodings. `-0` and `0` are the same key. * * Last-writer-wins applies to top-level fields. Nested values are atomic, so * concurrent updates within one nested value do not merge. * * `error` is the error schema of the event. Handler callbacks can fail with it. * * **Gotchas** * * Do not compact row events with `EventLog.groupCompaction` or * `EventLog.groupBatchCompaction`. Compaction runs before stored conflicts * are read, so merging row ops loses or misapplies writes. Use * {@link compaction}, which only drops entries that newer ones shadow. * * For multi-row operations, use `EventLog.writeMany` with one op per row. * * @stability unstable * @category constructors * @since 4.0.6 */ export declare const make: = never, const Declared extends RequiredField = never, Error extends Schema.Top = typeof Schema.Never>(tag: Tag, options: { readonly key: Key | KeyDeclaration; readonly schema: Schema.Struct; readonly error?: Error | undefined; }) => EventRow] extends [never] ? NoInfer : NoInfer, Error, [NoInfer] extends [never] ? Fields[NoInfer] : Schema.Struct<{ readonly [K in NoInfer]: Fields[K]; }>>; /** * Builds the handler for a row event, to pass to `handlers.handle`. * * **Details** * * Resolves newer conflicts in id order and calls at most one callback with * typed values and key, never conflicts. * * Newer writes take precedence per top-level field. Empty patches and entries * with nothing left to write skip the callback but are still journaled and synced. * A late `set` retains newer patches, including when the row is missing. * * Decodes stored entry bytes so local writes and replay pass the same values * to callbacks, stripping keys outside the schema at any depth. Requires the * payload schema's decoding services, plus any services the callbacks require. * Callbacks may fail with the event's error type; success is always `void`. * * **Gotchas** * * Convergence requires this callback contract: * * - `set` replaces the whole row, creating it if it is missing. * - `patch` updates an existing row and does nothing if the row is missing. * Upserting can resurrect removed rows on only some replicas. * - `remove` is idempotent. * * @stability unstable * @category handlers * @since 4.0.6 */ export declare const handler: (row: EventRow, callbacks: { readonly set: (row: Schema.Struct["Type"], key: Schema.Schema.Type) => Effect.Effect; readonly patch: (key: Schema.Schema.Type, fields: Schema.Struct>["Type"]) => Effect.Effect; readonly remove: (key: Schema.Schema.Type) => Effect.Effect; }) => (options: { readonly storeId: StoreId; readonly payload: Payload; readonly entry: Entry; readonly conflicts: ReadonlyArray<{ readonly entry: Entry; readonly payload: Payload; }>; }) => Effect.Effect>; /** * Registers a compactor for one row event of a group. * * **Details** * * Drops entries superseded within the same remote batch and row by a newer * `set` or `remove`. A `patch` is also dropped when newer patches together * overwrite all its fields. Kept entries retain their ids and stored conflicts. * * A `set` or `remove` followed only by patches is kept: without it, the * patches would apply to a different row, or to none. Entries that fail to * decode are kept. * * Leaves other events' compactors unchanged. Register one compactor per row * event. * * @stability unstable * @category compaction * @since 4.0.6 */ export declare const compaction: , Fields extends Schema.Struct.Fields, Key extends keyof Fields & string, Error extends Schema.Top, KeySchema extends Schema.Constraint>(group: EventGroup.EventGroup, row: EventRow) => Layer.Layer>; type DecodingServices = PayloadSchema["DecodingServices"]; export {}; //# sourceMappingURL=EventRow.d.ts.map