UNPKG

@open-game-system/app-bridge-native

Version:

React Native specific implementation of the app-bridge ecosystem

281 lines (280 loc) 8.79 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { createNativeBridge: () => createNativeBridge, createStore: () => createStore }); module.exports = __toCommonJS(src_exports); var import_immer = require("immer"); var import_fast_json_patch = require("fast-json-patch"); var createStore = (config) => { let currentState = config.initialState; const stateListeners = /* @__PURE__ */ new Set(); const eventListeners = /* @__PURE__ */ new Map(); let storeInstance; const notifyStateListeners = () => { stateListeners.forEach((listener) => listener(currentState)); }; const notifyEventListeners = (eventType, event) => { const listeners = eventListeners.get(eventType); if (listeners) { listeners.forEach((listener) => { try { const result = listener(event, storeInstance); if (result instanceof Promise) { result.catch((error) => { console.error(`[Native Store] Unhandled promise rejection in async event listener for type "${eventType}":`, error); }); } } catch (error) { console.error(`[Native Store] Error in event listener for type "${eventType}":`, error); } }); } }; storeInstance = { getSnapshot: () => currentState, dispatch: (event) => { let stateChanged = false; if (config.producer) { const nextState = (0, import_immer.produce)(currentState, (draft) => { config.producer(draft, event); }); if (nextState !== currentState) { currentState = nextState; stateChanged = true; } } if (stateChanged) { notifyStateListeners(); } notifyEventListeners(event.type, event); }, subscribe: (listener) => { stateListeners.add(listener); listener(currentState); return () => { stateListeners.delete(listener); }; }, on: (eventType, listener) => { const eventTypeStr = eventType; if (!eventListeners.has(eventTypeStr)) { eventListeners.set(eventTypeStr, /* @__PURE__ */ new Set()); } const listeners = eventListeners.get(eventTypeStr); const typedListener = listener; listeners.add(typedListener); return () => { listeners.delete(typedListener); if (listeners.size === 0) { eventListeners.delete(eventTypeStr); } }; }, reset: () => { currentState = config.initialState; notifyStateListeners(); } }; if (config.on) { for (const eventType in config.on) { if (Object.prototype.hasOwnProperty.call(config.on, eventType)) { const listener = config.on[eventType]; if (listener) { storeInstance.on(eventType, listener); } } } } return storeInstance; }; function createNativeBridge() { const stores = /* @__PURE__ */ new Map(); const webViews = /* @__PURE__ */ new Set(); const readyWebViews = /* @__PURE__ */ new Set(); const readyStateListeners = /* @__PURE__ */ new Map(); const storeListeners = /* @__PURE__ */ new Set(); const notifyStoreListeners = () => { storeListeners.forEach((listener) => listener()); }; const notifyReadyStateListeners = (webView, isReady) => { const listeners = readyStateListeners.get(webView); if (listeners) { listeners.forEach((listener) => listener(isReady)); } }; const broadcastToWebViews = (message) => { const messageString = JSON.stringify(message); webViews.forEach((webView) => { if (webView.postMessage) { webView.postMessage(messageString); } else { console.warn("[Native Bridge] WebView instance lacks postMessage method."); } }); }; const processWebViewMessage = (data, sourceWebView) => { let parsedData; try { parsedData = JSON.parse(data); } catch (e) { console.warn("[Native Bridge] Failed to parse message:", data, e); return; } if (!parsedData || typeof parsedData !== "object" || !("type" in parsedData)) { console.warn("[Native Bridge] Invalid message format:", parsedData); return; } switch (parsedData.type) { case "BRIDGE_READY": { const targetWebViews = sourceWebView ? [sourceWebView] : Array.from(webViews); targetWebViews.forEach((webView) => { if (!webView) return; readyWebViews.add(webView); notifyReadyStateListeners(webView, true); stores.forEach((store, key) => { const initMessage = JSON.stringify({ type: "STATE_INIT", storeKey: key, data: store.getSnapshot() }); if (webView.postMessage) webView.postMessage(initMessage); }); }); break; } case "EVENT": { const { storeKey, event } = parsedData; const store = stores.get(storeKey); if (store) { store.dispatch(event); } break; } } }; return { isSupported: () => true, getStore: (key) => { return stores.get(key); }, setStore: (key, store) => { if (store === void 0) { stores.delete(key); } else { let prevState = store.getSnapshot(); stores.set(key, store); const initMessage = { type: "STATE_INIT", storeKey: key, data: store.getSnapshot() }; broadcastToWebViews(initMessage); store.subscribe((currentState) => { const operations = (0, import_fast_json_patch.compare)(prevState, currentState); if (operations.length > 0) { broadcastToWebViews({ type: "STATE_UPDATE", storeKey: key, operations }); } prevState = currentState; }); } notifyStoreListeners(); }, subscribe: (listener) => { storeListeners.add(listener); return () => { storeListeners.delete(listener); }; }, handleWebMessage: (message) => { const messageData = typeof message === "string" ? message : message.nativeEvent.data; processWebViewMessage(messageData, void 0); }, registerWebView: (webView) => { if (!webView) return () => { }; webViews.add(webView); stores.forEach((store, key) => { const initMessage = JSON.stringify({ type: "STATE_INIT", storeKey: key, data: store.getSnapshot() }); if (webView.postMessage) webView.postMessage(initMessage); }); return () => { webViews.delete(webView); readyWebViews.delete(webView); readyStateListeners.delete(webView); }; }, unregisterWebView: (webView) => { if (!webView) return; webViews.delete(webView); readyWebViews.delete(webView); readyStateListeners.delete(webView); }, subscribeToReadyState: (webView, callback) => { if (!webView) { callback(false); return () => { }; } let listeners = readyStateListeners.get(webView); if (!listeners) { listeners = /* @__PURE__ */ new Set(); readyStateListeners.set(webView, listeners); } listeners.add(callback); callback(readyWebViews.has(webView)); return () => { const currentListeners = readyStateListeners.get(webView); if (currentListeners) { currentListeners.delete(callback); if (currentListeners.size === 0) { readyStateListeners.delete(webView); } } }; }, getReadyState: (webView) => { if (!webView) return false; return readyWebViews.has(webView); } }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createNativeBridge, createStore }); //# sourceMappingURL=index.js.map