UNPKG

react-native-webview-comlink

Version:

Add JavaScript interface for react-native-webview, based on Comlink

131 lines (130 loc) 4.93 kB
import { fromWireValue, toWireValue, } from './message'; import { ArrayMap, FakeWeakRef } from './utils'; export class MessageHub { constructor(name, endpoint, logger) { this.endpoint = endpoint; this.logger = logger; this.localFunctionByID = {}; this.localFunctionIDByFunction = typeof Map !== 'undefined' ? new Map() : new ArrayMap(); this.resolveByRequestID = {}; this.remoteFunctionRefByID = {}; this.tag = `RNWC|${name}|`; if (typeof FinalizationRegistry !== 'undefined') { this.remoteFunctionRegistry = new FinalizationRegistry((cleanup) => { cleanup(); }); } } sendMessage(msg) { this.endpoint.postMessage(this.tag + JSON.stringify(msg)); } notifyRelease(msg) { this.sendMessage(msg); } requestResponse(msg) { return new Promise((resolve) => { this.resolveByRequestID[msg.rid] = resolve; this.sendMessage(msg); }); } registerLocalFunction(id, vid, localFunction) { if (this.localFunctionByID.hasOwnProperty(id)) { // update only the vid if the function is already registered this.localFunctionByID[id].vid = vid; return; } this.localFunctionByID[id] = { localFunction, vid, }; this.localFunctionIDByFunction.set(localFunction, id); } tryGetLocalFuntionID(localFunction) { return this.localFunctionIDByFunction.get(localFunction); } registerRemoteFunction(id, remoteFunction) { this.remoteFunctionRefByID[id] = typeof WeakRef !== 'undefined' ? new WeakRef(remoteFunction) : new FakeWeakRef(remoteFunction); if (this.remoteFunctionRegistry) { this.remoteFunctionRegistry.register(remoteFunction, remoteFunction.cleanup, remoteFunction); } } unregisterRemoteFunction(id) { const remoteFunction = this.tryGetRemoteFunction(id); if (!remoteFunction) { return; } delete this.remoteFunctionRefByID[id]; if (this.remoteFunctionRegistry) { this.remoteFunctionRegistry.unregister(remoteFunction); } } tryGetRemoteFunction(id) { var _a; return (_a = this.remoteFunctionRefByID[id]) === null || _a === void 0 ? void 0 : _a.deref(); } canHandleMessage(msg) { return msg === null || msg === void 0 ? void 0 : msg.startsWith(this.tag); } handleMessage(msg) { var _a; const data = JSON.parse(msg.substr(this.tag.length)); switch (data.type) { case "REQ" /* REQUEST */: { const { id, args, rid } = data; Promise.resolve() .then(() => { var _a; const localFunction = (_a = this.localFunctionByID[id]) === null || _a === void 0 ? void 0 : _a.localFunction; if (!localFunction) { throw new Error(`failed to invoke function, ${id} is missing`); } return localFunction.apply(null, args.map((arg) => fromWireValue(arg, this))); }) .then((value) => { this.sendMessage({ type: "RSP" /* RESPONSE */, ret: toWireValue(value, this), rid, }); }) .catch((error) => { const wireValue = toWireValue(error, this); wireValue.type = "T" /* THROW */; this.sendMessage({ type: "RSP" /* RESPONSE */, ret: wireValue, rid, }); }); break; } case "RSP" /* RESPONSE */: { const { rid, ret } = data; const resolve = this.resolveByRequestID[rid]; if (!resolve) { return; } delete this.resolveByRequestID[rid]; resolve(ret); break; } case "RLS" /* RELEASE */: { const { id, vid } = data; if (vid === ((_a = this.localFunctionByID[id]) === null || _a === void 0 ? void 0 : _a.vid)) { this.logger(`release local function: ${id}`); this.localFunctionIDByFunction.delete(this.localFunctionByID[id].localFunction); delete this.localFunctionByID[id]; } else { this.logger(`not release local function: ${id}, ${vid}`); } break; } default: break; } } }