UNPKG

@statewalker/webrun-devtools

Version:

DevTools Extension for the StateWalker WebRun framework

130 lines (120 loc) 3.81 kB
import { newRegistry } from "@statewalker/utils"; import { callPort, listenPort } from "@statewalker/webrun-ports"; import { newConnectionHandler } from "./connectExtensionToPage.js"; import { newExtensionApi } from "./api/index.js"; import { METHOD_DONE, METHOD_INIT, METHOD_ADD_LISTENER, METHOD_REMOVE_LISTENER, METHOD_NOTIFY_LISTENER, } from "../libs/constants.js"; import { loadApiKey } from "../libs/apiKeysStore.js"; function getMethods(obj, index = {}, prefix = "") { for (const key in obj) { if (typeof obj[key] === "object") { getMethods(obj[key], index, `${prefix}${key}.`); } else { index[`${prefix}${key}`] = obj[key]; } } return index; } async function validateApiKey(key) { const apiKey = await loadApiKey(); return key === apiKey; } newConnectionHandler({ onConnect: (port) => { const [register, cleanup] = newRegistry(); port.start(); const api = newExtensionApi({ port }); const methods = getMethods(api); async function initializeConnection(apiKey, listeners) { if (!(await validateApiKey(apiKey))) { const message = "The page is not authorized to establish connection with this extension."; throw new Error(message); } else { for (let [listenerId, eventMethodName] of listeners) { await addListener(listenerId, eventMethodName); } initialized = true; } return { methods: Object.keys(methods), }; } async function addListener(listenerId, eventMethodName) { const fn = methods[eventMethodName]; if (typeof fn === "function") { listeners[listenerId] = fn(async (...callParams) => { await callPort(port, { method: METHOD_NOTIFY_LISTENER, args: [listenerId, ...callParams], }); }); return listenerId; } else { throw new Error( `Unknown event listener. Name: "${eventMethodName}"; ListenerId: "${listenerId}".` ); } } async function removeListener(listenerId) { if (listeners[listenerId]) { await listeners[listenerId](); delete listeners[listenerId]; } } let listeners = {}; let initialized = false; register(() => { for (let remove of Object.values(listeners)) { remove(); } listeners = {}; }); register( listenPort(port, async (data) => { if (!data) { throw new Error("No data"); } const { method, args } = data; if (method === METHOD_INIT) { const { apiKey, listeners = [] } = args[0] || {}; return await initializeConnection(apiKey, listeners); } if (!initialized) { throw new Error(`The API was not initialized. Method: "${method}"`); } if (method === METHOD_DONE) { cleanup(); } else if (method === METHOD_ADD_LISTENER) { const [listenerId, eventMethodName] = args; return await addListener(listenerId, eventMethodName); } else if (method === METHOD_REMOVE_LISTENER) { const [listenerId] = args; return await removeListener(listenerId); } else { const fn = methods[method]; if (typeof fn === "function") { return await fn(...args); } else { throw new Error(`Unknown method "${method}"}`); } } }) ); return cleanup; }, }); // chrome.tabs.onUpdated.addListener(async function (tabId, changeInfo) { // console.log('[chrome.tabs.onUpdated]', changeInfo) // if (changeInfo.status !== 'complete') return; // const apiKey = await loadApiKey(); // const cleanup = connectExtensionToPage({ // tabId, // apiKey // }); // });