@grammyjs/conversations
Version:
Conversational interfaces for grammY
213 lines (212 loc) • 8.88 kB
TypeScript
import { type Checkpoint, type ReplayState } from "./state.js";
export { type Checkpoint, type ReplayState } from "./state.js";
/**
* Controls for a replay. This is the object that a {@link ReplayEngine} passes
* to the replay function when executing it.
*/
export interface ReplayControls {
/**
* Interrupts the current replay and record this event in the replay logs.
* The replay will finish with an {@link Interrupted} result.
*
* Use {@link ReplayEngine.supply} to supply the result of this interrupt to
* the underlying replay state. When replaying the modified state, this call
* to `interrupt` will resolve with the supplied value.
*
* You also need to pass a key that identifies this type of interrupt. It is
* stored in the replay state and will be collated with the key that is
* passed to `interrupt` during the repeated call. If the two keys do not
* match, this means that a bad replay was detected and an error will be
* thrown. You should discard the replay state and restart the replay from
* scratch.
*
* @param key A key to collate interrupts across replays
*/
interrupt(key: string): Promise<unknown>;
/**
* Cancels the replay. This tells the replay engine that a supplied
* interrupt value should be rejected. The replay will finish with a
* {@link Canceled} result.
*
* A message object can be passed to `cancel`. This can be used to
* communicate to the caller why the interrupt value was rejected.
*
* @param message A message specifiying the reason for the cancelation
*/
cancel(message?: unknown): Promise<never>;
/**
* Performs an action.
*
* Actions are a way to signal to the replay engine that a particular piece
* of code should not be run repeatedly. The result of the action will be
* stored in the underlying replay log. During a subsequent replay, the
* action will not be repeated. Instead, the return is taken from the replay
* log.
*
* You also need to pass a key that identifies this type of action. It is
* stored in the replay state and will be collated with the key that is
* passed to `action` during the repeated call. If the two keys do not
* match, this means that a bad replay was detected and an error will be
* thrown. You should discard the replay state and restart the replay from
* scratch.
*
* @param fn The action to perform
* @param key A key to collate actions across replays
*/
action<R = unknown>(fn: () => R | Promise<R>, key: string): Promise<R>;
/**
* Creates a checkpoint at the current position of the replay. This can be
* passed to {@link ReplayEngine.reset} in order to restart a replay from an
* arbitrary position.
*/
checkpoint(): Checkpoint;
}
/** A function to be replayed by a {@link ReplayEngine} */
export type Builder = (controls: ReplayControls) => void | Promise<void>;
/** The result of a replay performed by a {@link ReplayEngine} */
export type ReplayResult = Returned | Thrown | Interrupted | Canceled;
/**
* This result is returned by a {@link ReplayEngine} when the builder function
* completes normally by returning.
*/
export interface Returned {
/**
* Type of the replay result, indicates that the replay has completed
* normally because the builder function has returned.
*/
type: "returned";
/** The return value of the builder function */
returnValue: unknown;
}
/**
* This result is returned by a {@link ReplayEngine} when the builder function
* throws an error.
*/
export interface Thrown {
/**
* Type of the replay result, indicates that the replay has completed
* because the builder function has thrown an error.
*/
type: "thrown";
/** The error thrown by the builder function */
error: unknown;
}
/**
* This result is returned by a {@link ReplayEngine} when the builder function
* interrupts itself by calling {@link ReplayControls.interrupt}.
*/
export interface Interrupted {
/**
* Type of the replay result, indicates that the replay has completed
* because the builder function has interrupted itself.
*/
type: "interrupted";
/** The replay state left behind by the replay engine */
state: ReplayState;
/** The list of concurrent interrupts that were performed */
interrupts: number[];
}
/**
* This result is returned by a {@link ReplayEngine} when the builder function
* cancels itself by calling {@link ReplayControls.cancel}.
*/
export interface Canceled {
/**
* Type of the replay result, indicates that the replay has completed
* because the builder function has canceled itself.
*/
type: "canceled";
/** The message passed to the last concurrent cancel call */
message?: unknown;
}
/**
* A replay engine takes control of the event loop of the JavaScript runtime and
* lets you execute a JavaScript function in abnormal ways. The function
* execution can be halted, resumed, aborted, and reversed. This lets you run a
* function partially and persist the state of execution in a database. Later,
* function execution can be resumed from where it was left off.
*
* Replay engines are the fundamental building block of the conversations
* plugin. In a sense, everything else is just a number of wrapper layers to
* make working with replay engines more convenient, and to integrate the power
* of replay engines into your bot's middleware system.
*
* Using a standalone replay engine is straightforward.
*
* 1. Create an instance of this class and pass a normal JavaScript function to
* the constructor. The function receives a {@link ReplayControls} object as
* its only parameter.
* 2. Call {@link ReplayEngine.play} to begin a new execution. It returns a
* {@link ReplayResult} object.
* 3. Use the {@link ReplayState} you obtained inside the result object and
* resume execution by calling {@link ReplayEngine.replay}.
*
* The `ReplayEngine` class furthermore provides you with static helper methods
* to supply values to interrupts, and to reset the replay state to a previously
* created checkpoint.
*/
export declare class ReplayEngine {
private readonly builder;
/**
* Constructs a new replay engine from a builder function. The function
* receives a single parameter that can be used to control the replay.
*
* @param builder A builder function to be executed and replayed
*/
constructor(builder: Builder);
/**
* Begins a new execution of the builder function. This starts based on
* fresh state. The execution is independent from any previously created
* executions.
*
* A {@link ReplayResult} object is returned to communicate the outcome of
* the execution.
*/
play(): Promise<ReplayResult>;
/**
* Resumes execution based on a previously created replay state. This is the
* most important method of this class.
*
* A {@link ReplayResult} object is returned to communicate the outcome of
* the execution.
*
* @param state A previously created replay state
*/
replay(state: ReplayState): Promise<ReplayResult>;
/**
* Creates a new replay state with a single unresolved interrupt. This state
* can be used as a starting point to replay arbitrary builder functions.
*
* You need to pass the collation key for the aforementioned first
* interrupt. This must be the same value that the builder function will
* pass to its first interrupt.
*
* @param key The builder functions first collation key
*/
static open(key: string): readonly [ReplayState, number];
/**
* Mutates a given replay state by supplying a value for a given interrupt.
* The next time the state is replayed, the targeted interrupt will return
* this value.
*
* The interrupt value has to be one of the interrupts of a previously
* received {@link Interrupted} result.
*
* In addition to mutating the replay state, a checkpoint is created and
* returned. This checkpoint may be used to reset the replay state to its
* previous value. This will undo this and all following mutations.
*
* @param state A replay state to mutate
* @param interrupt An interrupt to resolve
* @param value The value to supply
*/
static supply(state: ReplayState, interrupt: number, value: unknown): Checkpoint;
/**
* Resets a given replay state to a previously received checkpoint by
* mutating the replay state.
*
* @param state The state to mutate
* @param checkpoint The checkpoint to which to return
*/
static reset(state: ReplayState, checkpoint: Checkpoint): void;
}