UNPKG

convex

Version:

Client for the Convex Cloud

75 lines (65 loc) 2.54 kB
import { ActionRequest, ActionResponse, RequestId } from "./protocol.js"; import { jsonToConvex } from "../../values/index.js"; import { createError, logToConsole } from "../logging.js"; type ActionStatus = { message: ActionRequest; onResult: (result: any) => void; onFailure: (reason: any) => void; }; export class ActionManager { private inflightActions: Map<RequestId, ActionStatus>; constructor() { this.inflightActions = new Map(); } request(message: ActionRequest): Promise<any> { const result = new Promise((resolve, reject) => { this.inflightActions.set(message.requestId, { message, onResult: resolve, onFailure: reject, }); }); return result; } /** * Update the state after receiving a action response. */ onResponse(response: ActionResponse) { const actionInfo = this.inflightActions.get(response.requestId); if (actionInfo === undefined) { // Got a response of a message that we don't know about. That shouldn't // really happen unless we get duplicate messages or something. return; } this.inflightActions.delete(response.requestId); const udfPath = actionInfo.message.udfPath; for (const line of response.logLines) { logToConsole("info", "action", udfPath, line); } if (response.success) { actionInfo.onResult(jsonToConvex(response.result)); } else { logToConsole("error", "action", udfPath, response.result); actionInfo.onFailure(createError("action", udfPath, response.result)); } } hasInflightActions(): boolean { return this.inflightActions.size > 0; } restart() { // Unlike mutations, actions are not idempotent. When we reconnect to the // backend, we don't know if it is safe to resend in-flight actions, so we // cancel them and consider them failed. // TODO(presley): If we make the server remember it has started executing a // function, we can resend here and remove browser to backend connectivity as // a source of transient errors. For example, if a function has never reached // the server, then we can safely execute it. If a function was executed // successfully but response didn't reach the client. Server can return // success on reconnect. for (const [actionId, actionInfo] of this.inflightActions) { this.inflightActions.delete(actionId); const udfPath = actionInfo.message.udfPath; actionInfo.onFailure(createError("action", udfPath, "Transient error")); } } }