UNPKG

@steambrew/client

Version:
187 lines (186 loc) 6.77 kB
/** * Make reusable IPC call declarations * * frontend: * ```typescript * const method = callable<[{ arg1: string }]>("methodName"); // declare the method * method({ arg1: 'value' }); // call the method * ``` * * backend: * ```python * def methodName(arg1: str): * pass * ``` */ const callable = (_route) => (..._params) => Promise.resolve(undefined); const m_private_context = undefined; export const pluginSelf = m_private_context; const CDP_PROXY_BINDING = '__millennium_cdp_proxy__'; const CDP_EXTENSION_BINDING = '__millennium_extension_route__'; const CDP_EXT_RESP = 'MILLENNIUM_CHROME_DEV_TOOLS_PROTOCOL_DO_NOT_USE_OR_OVERRIDE_ONMESSAGE'; const BindPluginSettings = () => undefined; const pluginConfig = { get: async () => undefined, set: async () => { }, delete: async () => { }, getAll: async () => ({}) }; const usePluginConfig = (() => [undefined, async () => { }]); const subscribePluginConfig = () => () => { }; let _nextId = 0; const _pending = new Map(); const _eventDispatchers = new Set(); let _busInitialized = false; function initializeCDPBus() { if (_busInitialized) return; _busInitialized = true; window.__millennium_cdp_resolve__ = (callbackId, result) => { const pending = _pending.get(callbackId); if (pending) { _pending.delete(callbackId); pending.resolve(result ?? {}); } }; window.__millennium_cdp_reject__ = (callbackId, error) => { const pending = _pending.get(callbackId); if (pending) { _pending.delete(callbackId); pending.reject(new Error(`CDP Error: ${error}`)); } }; window.__millennium_cdp_event__ = (data) => { for (const cb of _eventDispatchers) { try { cb(data); } catch (_) { } } }; window[CDP_EXT_RESP] = { __handleCDPResponse: (response) => { if (response.id !== undefined) { const pending = _pending.get(response.id); if (pending) { _pending.delete(response.id); if (response.error) { pending.reject(new Error(`CDP Error: ${response.error.message}`)); } else { pending.resolve(response.result ?? {}); } } return; } if (response.method !== undefined) { for (const cb of _eventDispatchers) { try { cb(response); } catch (_) { } } } }, }; } export class MillenniumChromeDevToolsProtocol { constructor(pluginName) { this._pluginName = pluginName; this.eventListeners = new Map(); const eventListeners = this.eventListeners; this._eventDispatcher = (data) => { if (data.method === undefined) return; const params = data.sessionId ? { ...data.params, sessionId: data.sessionId } : data.params; const listeners = eventListeners.get(data.method); if (listeners) { for (const listener of listeners) { try { listener(params); } catch (_) { } } } }; initializeCDPBus(); _eventDispatchers.add(this._eventDispatcher); } on(event, listener) { const isFirst = !this.eventListeners.has(event) || this.eventListeners.get(event).size === 0; if (!this.eventListeners.has(event)) { this.eventListeners.set(event, new Set()); } this.eventListeners.get(event).add(listener); if (isFirst) { try { window[CDP_PROXY_BINDING](JSON.stringify({ action: 'subscribe', pluginName: this._pluginName, event })); } catch (_) { } } return () => this.off(event, listener); } off(event, listener) { const listeners = this.eventListeners.get(event); if (listeners) { listeners.delete(listener); if (listeners.size === 0) { this.eventListeners.delete(event); try { window[CDP_PROXY_BINDING](JSON.stringify({ action: 'unsubscribe', pluginName: this._pluginName, event })); } catch (_) { } } } } send(method, params = {}, sessionId) { if (method.startsWith('Extensions.')) { return this._sendViaExtensionRoute(method, params, sessionId); } return new Promise((resolve, reject) => { const callbackId = _nextId++; _pending.set(callbackId, { resolve, reject }); const payload = { action: 'cdp_call', pluginName: this._pluginName, callbackId, method }; if (params && Object.keys(params).length > 0) { payload.params = params; } if (sessionId) { payload.sessionId = sessionId; } try { window[CDP_PROXY_BINDING](JSON.stringify(payload)); } catch (error) { _pending.delete(callbackId); reject(error); } }); } _sendViaExtensionRoute(method, params, sessionId) { return new Promise((resolve, reject) => { const id = _nextId++; _pending.set(id, { resolve, reject }); const payload = { id, method }; if (params && Object.keys(params).length > 0) { payload.params = params; } if (sessionId) { payload.sessionId = sessionId; } try { window[CDP_EXTENSION_BINDING](JSON.stringify(payload)); } catch (error) { _pending.delete(id); reject(error); } }); } } /* backwards compat with old callers (without requiring recompile with new @steambrew/ttc version). falls back to Millenniums internal CDP. */ class MillenniumChromeDevToolsProtocolShared extends MillenniumChromeDevToolsProtocol { constructor() { super('__millennium_internal__'); } send(method, params = {}, sessionId) { return this._sendViaExtensionRoute(method, params, sessionId); } } const ChromeDevToolsProtocol = new MillenniumChromeDevToolsProtocolShared(); const Millennium = window.Millennium; export { BindPluginSettings, callable, ChromeDevToolsProtocol, Millennium, pluginConfig, subscribePluginConfig, usePluginConfig };