// Copyright 2015-present 650 Industries. All rights reserved. // swiftlint:disable line_length /** * This file contains the browser scripts that are injected into the WebView. * @generated by buildBrowserScripts.ts */ internal let INSTALL_GLOBALS_SCRIPT: String = """ // browserScripts/InstallGlobals/Deferred.ts class Deferred { promise; resolveCallback; rejectCallback; constructor() { this.promise = new Promise((resolve, reject) => { this.resolveCallback = resolve; this.rejectCallback = reject; }); } resolve(value) { this.resolveCallback(value); } reject(reason) { this.rejectCallback(reason); } getPromise() { return this.promise; } } // browserScripts/InstallGlobals/EventEmitterProxy.ts class EventEmitterProxy { moduleName; listeners; constructor(moduleName) { this.moduleName = moduleName; } addListener = (eventName, listener) => { if (!this.listeners) { this.listeners = new Map; } if (!this.listeners.has(eventName)) { this.listeners.set(eventName, new Set); } this.listeners.get(eventName)?.add(listener); const nativeListenerId = window.ExpoDomWebView.nextEventListenerId++; listener.$$nativeListenerId = nativeListenerId; const source = ` globalThis.expo.$$DomWebViewEventListenerMap ||= {}; globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'] ||= new Map(); const listener = (...args) => { const serializeArgs = args.map((arg) => JSON.stringify(arg)).join(','); const script = 'window.ExpoDomWebView.eventEmitterProxy.${this.moduleName}.emit("${eventName}", ' + serializeArgs + ')'; globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script); }; globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].set(${nativeListenerId}, listener); globalThis.expo.modules.${this.moduleName}.addListener('${eventName}', listener); `; window.ExpoDomWebView.eval(source); return { remove: () => { this.removeListener(eventName, listener); } }; }; removeListener = (eventName, listener) => { const nativeListenerId = listener.$$nativeListenerId; if (nativeListenerId != null) { const source = `(function() { const nativeListener = globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].get(${nativeListenerId}); if (nativeListener != null) { globalThis.expo.modules.${this.moduleName}.removeListener('${eventName}', nativeListener); globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].delete(${nativeListenerId}); } })(); true; `; window.ExpoDomWebView.eval(source); } this.listeners?.get(eventName)?.delete(listener); }; removeAllListeners = (eventName) => { const source = ` globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].clear(); globalThis.expo.modules.${this.moduleName}.removeAllListeners('${eventName}'); `; window.ExpoDomWebView.eval(source); this.listeners?.get(eventName)?.clear(); }; emit = (eventName, ...args) => { const listeners = new Set(this.listeners?.get(eventName)); listeners.forEach((listener) => { try { listener(...args); } catch (error) { console.error(error); } }); }; } // browserScripts/InstallGlobals/utils.ts function serializeArgs(args) { return args.map((arg) => { if (typeof arg === "object" && arg.sharedObjectId != null) { return `globalThis.expo.sharedObjectRegistry.get(${arg.sharedObjectId})`; } return JSON.stringify(arg); }).join(","); } // browserScripts/InstallGlobals/proxies.ts function createSharedObjectProxy(sharedObjectId) { return new Proxy({}, { get: (target, prop) => { const name = String(prop); if (name === "sharedObjectId") { return sharedObjectId; } return function(...args) { const serializedArgs = serializeArgs(args); const source = `globalThis.expo.sharedObjectRegistry.get(${sharedObjectId})?.${name}?.call(globalThis.expo.sharedObjectRegistry.get(${sharedObjectId}),${serializedArgs})`; return window.ExpoDomWebView.eval(source); }; } }); } function createConstructorProxy(moduleName, property, propertyName) { return new Proxy(function() { }, { construct(target, args) { const serializedArgs = serializeArgs(args); const sharedObjectId = window.ExpoDomWebView.nextSharedObjectId++; const sharedObjectProxy = createSharedObjectProxy(sharedObjectId); window.ExpoDomWebView.sharedObjectFinalizationRegistry.register(sharedObjectProxy, sharedObjectId); const source = `globalThis.expo.sharedObjectRegistry ||= new Map(); globalThis.expo.sharedObjectRegistry.set(${sharedObjectId}, new ${property}(${serializedArgs}));`; window.ExpoDomWebView.eval(source); return sharedObjectProxy; } }); } function createPropertyProxy(propertyTypeCache, moduleName, propertyName) { const property = `globalThis.expo.modules.${moduleName}.${propertyName}`; let propertyType = propertyTypeCache[propertyName]; if (!propertyType) { const typeCheck = `${property}?.prototype?.__expo_shared_object_id__ != null ? 'sharedObject' : typeof ${property}`; propertyType = window.ExpoDomWebView.eval(typeCheck); propertyTypeCache[propertyName] = propertyType; } if (propertyType === "sharedObject") { return createConstructorProxy(moduleName, property, propertyName); } if (propertyType === "function") { return function(...args) { const serializedArgs = serializeArgs(args); const source = `${property}(${serializedArgs})`; return window.ExpoDomWebView.eval(source); }; } return window.ExpoDomWebView.eval(property); } function createExpoModuleProxy(moduleName) { const propertyTypeCache = {}; return new Proxy({}, { get: (target, prop) => { const name = String(prop); if (["addListener", "removeListener", "removeAllListeners"].includes(name)) { return window.ExpoDomWebView.eventEmitterProxy[moduleName][name]; } return createPropertyProxy(propertyTypeCache, moduleName, name); } }); } // browserScripts/InstallGlobals/ExpoDomWebView.ts class ExpoDomWebView { nextDeferredId; nextSharedObjectId; nextEventListenerId; deferredMap; sharedObjectFinalizationRegistry; expoModulesProxy; eventEmitterProxy; constructor() { this.nextDeferredId = 0; this.nextSharedObjectId = 0; this.nextEventListenerId = 0; this.deferredMap = new Map; this.sharedObjectFinalizationRegistry = new FinalizationRegistry((sharedObjectId) => { this.eval(`globalThis.expo.sharedObjectRegistry.delete(${sharedObjectId})`); }); const expoModules = {}; const eventEmitterProxy = {}; this.eval("Object.keys(globalThis.expo.modules)").forEach((name) => { expoModules[name] = createExpoModuleProxy(name); eventEmitterProxy[name] = new EventEmitterProxy(name); }); this.expoModulesProxy = expoModules; this.eventEmitterProxy = eventEmitterProxy; } eval(source) { const { deferredId, deferred } = this.createDeferred(); const args = JSON.stringify({ source, deferredId }); const result = JSON.parse(window.ExpoDomWebViewBridge.eval(args)); if (result.isPromise) { return deferred.getPromise(); } this.removeDeferred(deferredId); return result.value; } createDeferred() { const deferred = new Deferred; const deferredId = this.nextDeferredId; this.deferredMap.set(deferredId, deferred); this.nextDeferredId += 1; return { deferredId, deferred }; } resolveDeferred(deferredId, value) { const deferred = this.deferredMap.get(deferredId); if (deferred) { deferred.resolve(value); this.deferredMap.delete(deferredId); } } rejectDeferred(deferredId, reason) { const deferred = this.deferredMap.get(deferredId); if (deferred) { deferred.reject(reason); this.deferredMap.delete(deferredId); } } removeDeferred(deferredId) { this.deferredMap.delete(deferredId); } } // browserScripts/InstallGlobals/index.ts window.ExpoDomWebView = new ExpoDomWebView; """ internal let NATIVE_EVAL_WRAPPER_SCRIPT: String = """ // browserScripts/NativeEvalWrapper/index.ts (function() { const result = "%%SOURCE%%"; if (result instanceof Promise) { result.then((resolved) => { const resolvedString = JSON.stringify(resolved); const script = 'window.ExpoDomWebView.resolveDeferred("%%DEFERRED_ID%%", ' + resolvedString + ")"; globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script); }).catch((error) => { const errorString = JSON.stringify(error); const script = 'window.ExpoDomWebView.rejectDeferred("%%DEFERRED_ID%%", ' + errorString + ")"; globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script); }); return JSON.stringify({ isPromise: true, value: null }); } else { return JSON.stringify({ isPromise: false, value: result }); } })(); """ // swiftlint:enable line_length