import type { Scope } from "../scope.ts"; import type { State, StateStore } from "../state.ts"; import { withExponentialBackoff } from "../util/retry.ts"; /** * Wraps a local or remote state store implementation * and handles serialization/deserialization. */ export abstract class StateStoreProxy implements StateStore { private dispatch?: Promise; constructor(readonly scope: Scope) {} abstract provision(): Promise; private async run( method: TMethod, params: StateStoreProxy.API[TMethod]["params"], ): Promise { this.dispatch ??= this.provision(); return this.dispatch.then(async (dispatch) => { const result = await withExponentialBackoff( () => dispatch(method, params), () => true, ); return result; }); } init(): Promise { return this.run("init", []); } deinit(): Promise { return this.run("deinit", []); } list(): Promise { return this.run("list", []); } listScopes(): Promise { return this.run("listScopes", []); } count(): Promise { return this.run("count", []); } async get(key: string): Promise { const result = await this.run("get", [key]); if (result === undefined) { return undefined; } return await this.deserialize(result); } async getBatch(ids: string[]): Promise> { return this.deserializeMany(await this.run("getBatch", [ids])); } async all(): Promise> { return this.deserializeMany(await this.run("all", [])); } async set(key: string, value: State): Promise { return this.run("set", [key, await this.serialize(value)]); } async delete(key: string): Promise { return this.run("delete", [key]); } private async deserialize(state: State): Promise { const { deserialize } = await import("../serde.ts"); // dynamic import to avoid circular dependency return await deserialize(this.scope, state); } private async serialize(state: State) { const { serialize } = await import("../serde.ts"); return await serialize(this.scope, state); } private async deserializeMany(states: Record) { return Object.fromEntries( await Promise.all( Object.entries(states).map(async ([key, value]) => [ key, await this.deserialize(value), ]), ), ); } } export declare namespace StateStoreProxy { export type API = { [K in keyof Required]: NonNullable extends ( ...args: infer Args ) => Promise ? { method: K; params: Args; result: Return; } : never; }; export type Method = keyof API; export type Dispatch = ( method: TMethod, params: API[TMethod]["params"], ) => Promise; export type Request = { method: TMethod; params: API[TMethod]["params"]; context: TContext; }; export type SuccessResponse = { success: true; status: number; result: API[TMethod]["result"]; }; export type ErrorResponse = { success: false; status: number; error: string; }; export type Response = | SuccessResponse | ErrorResponse; }