UNPKG

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

Version:

Web-specific implementation of the app-bridge ecosystem

151 lines 5.25 kB
// src/index.ts import { applyPatch } from "fast-json-patch"; function createWebBridge() { const stateByStore = /* @__PURE__ */ new Map(); const stores = /* @__PURE__ */ new Map(); const stateListeners = /* @__PURE__ */ new Map(); const storeListeners = /* @__PURE__ */ new Set(); const notifyStateListeners = (storeKey) => { const listeners = stateListeners.get(storeKey); if (listeners && stateByStore.has(storeKey)) { const state = stateByStore.get(storeKey); listeners.forEach((listener) => listener(state)); } }; const notifyStoreListeners = () => { storeListeners.forEach((listener) => listener()); }; if (typeof window !== "undefined" && window.ReactNativeWebView) { console.log("[Web Bridge] ReactNativeWebView detected. Adding message listener and sending BRIDGE_READY."); window.ReactNativeWebView.postMessage(JSON.stringify({ type: "BRIDGE_READY" })); const messageHandler = (event) => { try { const message = JSON.parse(event.data); if (message.type === "STATE_INIT") { if (message.data === null) { stateByStore.delete(message.storeKey); } else { stateByStore.set(message.storeKey, message.data); } notifyStateListeners(message.storeKey); notifyStoreListeners(); } else if (message.type === "STATE_UPDATE") { if (message.data === null) { stateByStore.delete(message.storeKey); notifyStateListeners(message.storeKey); notifyStoreListeners(); } else if (message.operations) { const currentState = stateByStore.get( message.storeKey ); if (currentState) { const result = applyPatch(currentState, message.operations); stateByStore.set( message.storeKey, result.newDocument ); notifyStateListeners(message.storeKey); } } } } catch (error) { console.error("[Web Bridge] Error handling message:", error); } }; window.addEventListener("message", messageHandler); } else { console.warn("[Web Bridge] ReactNativeWebView NOT detected."); } return { /** * Check if the bridge is supported * For web bridge, this checks if ReactNativeWebView is available */ isSupported: () => typeof window !== "undefined" && !!window.ReactNativeWebView, /** * Get a store by its key * Returns undefined if the store doesn't exist */ getStore: (storeKey) => { if (!stateByStore.has(storeKey)) return void 0; let store = stores.get(storeKey); if (!store) { const storeImpl = { getSnapshot: () => stateByStore.get(storeKey), subscribe: (listener) => { if (!stateListeners.has(storeKey)) { stateListeners.set(storeKey, /* @__PURE__ */ new Set()); } const listeners = stateListeners.get(storeKey); listeners.add(listener); if (stateByStore.has(storeKey)) { listener(stateByStore.get(storeKey)); } return () => { listeners.delete(listener); }; }, dispatch: async (event) => { console.log(`[Web Bridge] Dispatching event for store ${String(storeKey)}:`, event); if (!window.ReactNativeWebView) { console.warn( "[Web Bridge] Cannot dispatch events: ReactNativeWebView not available" ); return; } const message = { type: "EVENT", storeKey, event }; console.log("[Web Bridge] Sending message to native:", message); window.ReactNativeWebView.postMessage(JSON.stringify(message)); }, reset: () => { console.warn("[Web Bridge] Reset operation not supported in web bridge"); }, // Add 'on' method to satisfy the interface on: (eventType, _listener) => { console.warn(`[Web Bridge] store.on("${eventType}", ...) was called, but listeners added on the web side are not executed. Add listeners on the native side or via the store's 'on' config.`); return () => { }; } }; stores.set(storeKey, storeImpl); store = storeImpl; } return store; }, /** * Set or remove a store for a given key */ setStore: (key, store) => { if (store === void 0) { stores.delete(key); stateByStore.delete(key); } else { stores.set(key, store); const snapshot = store.getSnapshot(); if (snapshot !== void 0) { stateByStore.set(key, snapshot); } } notifyStoreListeners(); }, /** * Subscribe to store availability changes * Returns an unsubscribe function */ subscribe: (listener) => { storeListeners.add(listener); return () => { storeListeners.delete(listener); }; } }; } export { createWebBridge }; //# sourceMappingURL=index.mjs.map