Bifrost/tools/epiphany-operator-ledger.mjs
2026-07-19 13:12:04 +01:00

20 lines
4 KiB
JavaScript

import { createRequire } from "node:module";
import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { buildOperatorDefinitions } from "./epiphany-operator-command-documents.mjs";
export class DurableOperatorLedger {
constructor(cache,definitions){this.cache=cache;this.definitions=definitions;}
static async open(path,{cultcacheRuntime}={}){const runtime=cultcacheRuntime??loadCultCacheRuntime(),definitions=buildOperatorDefinitions(runtime.defineDocumentType),cache=runtime.CultCache.builder().withDocumentType(definitions.request).withDocumentType(definitions.admission).withDocumentType(definitions.sealedResult).withDocumentType(definitions.terminal).withGenericStore(new runtime.SingleFileMessagePackBackingStore(path)).build();await cache.pullAllBackingStores();return new DurableOperatorLedger(cache,definitions);}
async admit(id,request,admission){if(id!==request.requestId||id!==admission.admissionId||request.commandId!==admission.packet.commandId)throw new Error("operator admission ledger key is not bound to request and admission identity");const currentRequest=this.cache.get(this.definitions.request,id),currentAdmission=this.cache.get(this.definitions.admission,id);if(currentRequest&&stable(currentRequest)!==stable(request)||currentAdmission&&stable(currentAdmission)!==stable(admission))throw new Error("operator request identity collision");if(!currentRequest)await this.cache.put(this.definitions.request,id,request);if(!currentAdmission)await this.cache.put(this.definitions.admission,id,admission);}
async complete(id,result,providerIdentityId,receipt){const admission=this.cache.get(this.definitions.admission,id),request=this.cache.get(this.definitions.request,id);if(!admission||!request)throw new Error("operator result has no durable admitted request");if(!receipt||receipt.commandId!==result.commandId||receipt.providerIdentityId!==providerIdentityId||stable(receipt.result)!==stable(result)||receipt.commandId!==admission.packet.commandId||receipt.packetSha256!==admission.packetSha256||receipt.targetRuntimeId!==admission.packet.targetRuntimeId||request.commandId!==receipt.commandId)throw new Error("operator completion is not bound to its exact durable admission");const current=this.cache.get(this.definitions.sealedResult,result.commandId);if(current&&stable(current)!==stable(receipt))throw new Error("operator result identity collision");if(!current)await this.cache.put(this.definitions.sealedResult,result.commandId,receipt);}
async terminalize(value){const current=this.cache.get(this.definitions.terminal,value.requestId);if(current&&stable(current)!==stable(value))throw new Error("operator terminal disposition identity collision");if(!current)await this.cache.put(this.definitions.terminal,value.requestId,value);}
getAdmission(id){return this.cache.get(this.definitions.admission,id);}
getRequest(id){return this.cache.get(this.definitions.request,id);}
getCompletion(commandId){return this.cache.get(this.definitions.sealedResult,commandId);}
getTerminal(requestId){return this.cache.get(this.definitions.terminal,requestId);}
isExactCompletion(requestId,commandId){const request=this.getRequest(requestId),admission=this.getAdmission(requestId),completion=this.getCompletion(commandId);return Boolean(request&&admission&&completion&&request.commandId===commandId&&admission.packet.commandId===commandId&&completion.commandId===commandId&&completion.packetSha256===admission.packetSha256&&completion.targetRuntimeId===admission.packet.targetRuntimeId);}
}
function loadCultCacheRuntime(){const root=resolve(import.meta.dirname,".."),projects=resolve(root,".."),cult=resolve(process.env.VOIDBOT_CULTLIB_ROOT||resolve(projects,"CultLib")),pkg=resolve(cult,"packages","cultcache-ts","package.json");if(!existsSync(pkg))throw new Error(`CultCache TypeScript runtime is unavailable at ${pkg}.`);const requireCult=createRequire(pkg);return requireCult("cultcache-ts");}
function stable(v){return JSON.stringify(v,(_k,x)=>x instanceof Uint8Array?Buffer.from(x).toString("hex"):x);}