@grammyjs/conversations
Version:
Conversational interfaces for grammY
132 lines (131 loc) • 4.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.create = create;
exports.inspect = inspect;
exports.mutate = mutate;
exports.cursor = cursor;
const resolve_js_1 = require("./resolve.js");
/**
* Creates and returns an empty {@link ReplayState} object.
*
* The returned replay state can be inspected via {@link inspect}, mutated via
* {@link mutate}, and replayed via {@link cursor}.
*/
function create() {
return { send: [], receive: [] };
}
/**
* Provides inspections tools for a given replay state.
*
* @param state The replay state to inspect
*/
function inspect(state) {
function opCount() {
return state.send.length;
}
function doneCount() {
return state.receive.length;
}
function payload(op) {
if (op < 0)
throw new Error(`Op ${op} is invalid`);
if (op >= state.send.length)
throw new Error(`No op ${op} in state`);
return state.send[op].payload;
}
function checkpoint() {
return [opCount(), doneCount()];
}
return { opCount, doneCount, payload, checkpoint };
}
/**
* Provides tools to mutate a given replay state.
*
* @param state The replay state to mutate
*/
function mutate(state) {
function op(payload) {
const index = state.send.length;
state.send.push({ payload });
return index;
}
function done(op, result) {
if (op < 0)
throw new Error(`Op ${op} is invalid`);
if (op >= state.send.length)
throw new Error(`No op ${op} in state`);
state.receive.push({ send: op, returnValue: result });
}
function reset([op, done]) {
if (op < 0 || done < 0)
throw new Error("Invalid checkpoint");
state.send.splice(op);
state.receive.splice(done);
}
return { op, done, reset };
}
/**
* Provides tools to iterate a given replay state.
*
* @param state The replay state to iterate
*/
function cursor(state) {
let changes = (0, resolve_js_1.resolver)();
function notify() {
changes.resolve();
changes = (0, resolve_js_1.resolver)();
}
let send = 0; // 0 <= send <= state.send.length
let receive = 0; // 0 <= receive <= state.receive.length
function op(payload) {
if (send < state.send.length) {
// replay existing data (do nothing)
const expected = state.send[send].payload;
if (expected !== payload) {
throw new Error(`Bad replay, expected op '${expected}'`);
}
}
else { // send === state.send.length
// log new data
state.send.push({ payload });
}
const index = send++;
notify();
return index;
}
async function done(op, result) {
if (op < 0)
throw new Error(`Op ${op} is invalid`);
if (op >= state.send.length)
throw new Error(`No op ${op} in state`);
let data;
if (receive < state.receive.length) {
// replay existing data (do nothing)
while (state.receive[receive].send !== op) {
// make sure we resolve only when it is our turn
await changes.promise;
if (receive === state.receive.length) {
// It will never be our turn, because the replay completed
// and we are still here. We will have to call `result`.
return await done(op, result);
}
} // state.receive[receive].send === op
data = state.receive[receive].returnValue;
}
else { // receive === state.receive.length
data = await result();
state.receive.push({ send: op, returnValue: data });
}
receive++;
notify();
return data;
}
async function perform(action, payload) {
const index = op(payload);
return await done(index, () => action(index));
}
function checkpoint() {
return [send, receive];
}
return { perform, op, done, checkpoint };
}