@open-game-system/app-bridge-native
Version:
React Native specific implementation of the app-bridge ecosystem
281 lines (280 loc) • 8.79 kB
JavaScript
;
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