46 lines
2.6 KiB
JavaScript
46 lines
2.6 KiB
JavaScript
"use strict";
|
|||
|
|
const { PROVIDERS } = require("./subscription-store.cjs");
|
||
|
|
const { publicError } = require("./subscriptions.cjs");
|
||
|
|
function attachSubscriptionChild(child, controller) {
|
||
|
|
let disposed = false;
|
||
|
|
const send = (packet) => { if (!disposed && child.connected) { try { child.send(packet, () => {}); } catch { /* child exited */ } } };
|
||
|
|
const listener = async (message) => {
|
||
|
|
if (disposed || !child.connected || message?.channel !== "lh:subscriptions" || typeof message.id !== "string") return;
|
||
|
|
const { id, op } = message;
|
||
|
|
try {
|
||
|
|
if (!controller || !["snapshot", "lease"].includes(op)) throw new Error();
|
||
|
|
if (op === "lease" && (typeof message.background !== "boolean" || !validScope(message.scope))) throw new Error();
|
||
|
|
const value = op === "snapshot" ? await controller.snapshot() : await controller.lease(message.scope, message.background);
|
||
|
|
send({ channel: "lh:subscriptions", id, op, ok: true, value });
|
||
|
|
} catch (error) { send({ channel: "lh:subscriptions", id, op, ok: false, error: publicError(error) }); }
|
||
|
|
};
|
||
|
|
child.on("message", listener);
|
||
|
|
return {
|
||
|
|
send(snapshot) { send({ channel: "lh:subscriptions", op: "changed", snapshot }); },
|
||
|
|
dispose() { disposed = true; child.removeListener("message", listener); },
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function validScope(scope) {
|
||
|
|
return scope && PROVIDERS.includes(scope.providerId) && typeof scope.connectionId === "string" && !!scope.connectionId && typeof scope.selectedModelId === "string" && !!scope.selectedModelId && [scope.epoch, scope.authorityRevision].every((n) => Number.isSafeInteger(n) && n >= 0);
|
||
|
|
}
|
||
|
|
/** Caller authenticates the exact trusted main-frame origin before dispatch. */
|
||
|
|
async function subscriptionControl(command, controller) {
|
||
|
|
try {
|
||
|
|
if (!controller || !command || typeof command !== "object") throw new Error();
|
||
|
|
if (command.op === "status") return await controller.status();
|
||
|
|
if (!PROVIDERS.includes(command.providerId)) throw new Error();
|
||
|
|
const id = command.providerId;
|
||
|
|
switch (command.op) {
|
||
|
|
case "login": return await controller.login(id, command.enabled);
|
||
|
|
case "cancel": return controller.cancel(id);
|
||
|
|
case "respond": return await controller.respond(id, command.attemptId, command.promptId, command.value);
|
||
|
|
case "open": return await controller.open(id, command.attemptId, command.actionId);
|
||
|
|
case "retry": return await controller.retry(id);
|
||
|
|
case "disconnect": return await controller.disconnect(id);
|
||
|
|
case "consent": return await controller.consent(id, command.enabled);
|
||
|
|
default: throw new Error();
|
||
|
|
}
|
||
|
|
} catch (error) { throw new Error(publicError(error)); }
|
||
|
|
}
|
||
|
|
module.exports = { attachSubscriptionChild, subscriptionControl, validScope };
|