UNPKG

@resonatehq/gcp

Version:

Resonate FaaS handler for Google Cloud Functions (TypeScript)

96 lines (95 loc) 4.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Resonate = void 0; const sdk_1 = require("@resonatehq/sdk"); const encryptor_1 = require("@resonatehq/sdk/dist/src/encryptor"); class Resonate { constructor({ verbose = false, encryptor = undefined, } = {}) { this.registry = new sdk_1.Registry(); this.verbose = verbose; this.encryptor = encryptor ?? new encryptor_1.NoopEncryptor(); } register(nameOrFunc, funcOrOptions, maybeOptions = {}) { const { version = 1 } = (typeof funcOrOptions === "object" ? funcOrOptions : maybeOptions) ?? {}; const func = typeof nameOrFunc === "function" ? nameOrFunc : funcOrOptions; const name = typeof nameOrFunc === "string" ? nameOrFunc : func.name; this.registry.add(func, name, version); } handlerHttp() { return async (req, res) => { try { if (req.method !== "POST") { return res .status(405) .json({ error: "Method not allowed. Use POST." }); } const proto = req.get("x-forwarded-proto") || req.protocol; const host = req.get("host"); if (!proto || !host) { return res.status(400).json({ error: "Missing required headers: x-forwarded-proto or host.", }); } const url = `${proto}://${host}${req.originalUrl || ""}`; if (!req.body) { return res.status(400).json({ error: "Request body missing." }); } const body = req.body; if (!body || !(body.type === "invoke" || body.type === "resume") || !body.task) { return res.status(400).json({ error: 'Request body must contain "type" and "task" for Resonate invocation.', }); } const encoder = new sdk_1.JsonEncoder(); const network = new sdk_1.HttpNetwork({ headers: {}, timeout: 60 * 1000, // 60s url: body.href.base, verbose: this.verbose, }); const resonateInner = new sdk_1.ResonateInner({ anycastNoPreference: url, anycastPreference: url, clock: new sdk_1.WallClock(), dependencies: new Map(), handler: new sdk_1.Handler(network, encoder, this.encryptor), heartbeat: new sdk_1.NoopHeartbeat(), network, pid: `pid-${Math.random().toString(36).substring(7)}`, registry: this.registry, ttl: 30 * 1000, // 30s unicast: url, verbose: this.verbose, }); const task = { kind: "unclaimed", task: body.task }; resonateInner.process(task, (error, status) => { if (error || !status) { return res.status(500).json({ error: "Task processing failed", details: { error, status }, }); } if (status.kind === "completed") { return res.status(200).json({ status: "completed", result: status.promise.value, requestUrl: url, }); } return res.status(200).json({ status: "suspended", requestUrl: url, }); }); } catch (error) { return res.status(500).json({ error: `Handler failed: ${error}`, }); } }; } } exports.Resonate = Resonate;