@farcaster/frame-sdk 0.0.0-canary-20250508150817 → 0.0.0-canary-20250508220359

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import { type TransferHandler, type WireValue } from '@farcaster/frame-host';
2
+ export declare const proxiedFunctionsForReactNative: Map<string, Function>;
3
+ export declare const reactNativeFunctionSerializerTransferHandler: TransferHandler<Function, {
4
+ __isRNProxiedFunction: true;
5
+ id: string;
6
+ type: 'function';
7
+ }>;
8
+ export declare function _callWebViewProxiedFunction(functionId: string, wireArgs: WireValue[]): Promise<WireValue>;
@@ -0,0 +1,67 @@
1
+ import { fromWireValue, proxyMarker, toWireValue, } from '@farcaster/frame-host';
2
+ import { isInReactNativeWebViewEnvironment } from './rn';
3
+ // Since Comlink's proxyTransferHandler doesn't work on React Native, we need to
4
+ // manually serialize and deserialize functions.
5
+ // We associate each function with a unique ID and store it in a map so the host
6
+ // receives a function stub that when called passes the ID and arguments to the
7
+ // WebView which then looks up the function by ID and calls it.
8
+ export const proxiedFunctionsForReactNative = new Map();
9
+ let nextFunctionId = 0;
10
+ function generateFunctionId() {
11
+ return `rn_webview_fn_proxy_${nextFunctionId++}`;
12
+ }
13
+ const comlinkThrownValueMarker = Symbol.for('Comlink.thrown');
14
+ export const reactNativeFunctionSerializerTransferHandler = {
15
+ canHandle: (value) => {
16
+ if (isInReactNativeWebViewEnvironment() && typeof value === 'function') {
17
+ // In RN WebView, we force this handler for all functions to ensure ID-based proxying,
18
+ // as standard MessageChannel-based proxying won't work.
19
+ // This overrides Comlink's default proxyTransferHandler if the function was pre-marked.
20
+ return true;
21
+ }
22
+ return false;
23
+ },
24
+ serialize: (funcToProxy) => {
25
+ const functionId = generateFunctionId();
26
+ proxiedFunctionsForReactNative.set(functionId, funcToProxy);
27
+ console.debug(`[WebView RN Comlink] Serializing function (name: '${funcToProxy.name || 'anonymous'}', hasProxyMarker: ${!!funcToProxy[proxyMarker]}) to ID: ${functionId}`);
28
+ return [
29
+ { __isRNProxiedFunction: true, id: functionId, type: 'function' },
30
+ [],
31
+ ];
32
+ },
33
+ deserialize: (serializedValue) => {
34
+ const func = proxiedFunctionsForReactNative.get(serializedValue.id);
35
+ if (!func) {
36
+ console.error(`[WebView RN Comlink] Proxied function with ID ${serializedValue.id} not found during deserialization on WebView.`);
37
+ throw new Error(`Proxied function with ID ${serializedValue.id} not found on WebView.`);
38
+ }
39
+ console.debug(`[WebView RN Comlink] Deserializing ID ${serializedValue.id} back to function on WebView.`);
40
+ return func;
41
+ },
42
+ };
43
+ export async function _callWebViewProxiedFunction(functionId, wireArgs) {
44
+ const func = proxiedFunctionsForReactNative.get(functionId);
45
+ if (!func) {
46
+ console.error(`[WebView RN Comlink] Proxied function with ID ${functionId} not found for execution.`);
47
+ const error = new Error(`Proxied function with ID ${functionId} not found on WebView.`);
48
+ const valueWithMarker = { value: error, [comlinkThrownValueMarker]: 0 };
49
+ const [wireError] = toWireValue(valueWithMarker);
50
+ return wireError;
51
+ }
52
+ const args = wireArgs.map((arg) => fromWireValue(arg));
53
+ try {
54
+ const result = await func(...args);
55
+ const [wireResult, transferables] = toWireValue(result);
56
+ if (transferables.length > 0) {
57
+ console.warn('[WebView RN Comlink] Transferables returned from _callWebViewProxiedFunction are ignored.');
58
+ }
59
+ return wireResult;
60
+ }
61
+ catch (error) {
62
+ console.error(`[WebView RN Comlink] Error executing proxied function ID ${functionId}:`, error);
63
+ const valueWithMarker = { value: error, [comlinkThrownValueMarker]: 0 };
64
+ const [wireError] = toWireValue(valueWithMarker);
65
+ return wireError;
66
+ }
67
+ }
package/dist/rn.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @returns true if the environment is a React Native WebView, false otherwise.
3
+ */
4
+ export declare function isInReactNativeWebViewEnvironment(): boolean;
5
+ /**
6
+ * Initializes the React Native SDK.
7
+ * This function should be called before the SDK is used.
8
+ *
9
+ * This is necessary because Complink function proxing doesn't work on React Native
10
+ * so we have our custom implementation we need to expose to the host.
11
+ */
12
+ export declare const initializeReactNativeSDK: () => void;
package/dist/rn.js ADDED
@@ -0,0 +1,86 @@
1
+ import { expose, transferHandlers } from 'comlink';
2
+ import { _callWebViewProxiedFunction, proxiedFunctionsForReactNative, reactNativeFunctionSerializerTransferHandler, } from './rn-func-proxy';
3
+ const comlinkListeners = new Map();
4
+ /**
5
+ * @returns true if the environment is a React Native WebView, false otherwise.
6
+ */
7
+ export function isInReactNativeWebViewEnvironment() {
8
+ return typeof window !== 'undefined' && !!window.ReactNativeWebView;
9
+ }
10
+ /**
11
+ * Initializes the React Native SDK.
12
+ * This function should be called before the SDK is used.
13
+ *
14
+ * This is necessary because Complink function proxing doesn't work on React Native
15
+ * so we have our custom implementation we need to expose to the host.
16
+ */
17
+ export const initializeReactNativeSDK = () => {
18
+ if (isInReactNativeWebViewEnvironment()) {
19
+ if (!transferHandlers.has('rn_function')) {
20
+ transferHandlers.set('rn_function', reactNativeFunctionSerializerTransferHandler);
21
+ console.debug('[WebView RN Comlink] Registered reactNativeFunctionSerializerTransferHandler.');
22
+ }
23
+ const sdkApiToExpose = {
24
+ _callWebViewProxiedFunction,
25
+ };
26
+ if (window.ReactNativeWebView) {
27
+ const endpointForHostViaRN = {
28
+ postMessage: (message, _transfer) => {
29
+ if (window.ReactNativeWebView) {
30
+ console.debug('[SDK->Host Comlink] postMessage', message);
31
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
32
+ }
33
+ else {
34
+ console.error('[SDK->Host Comlink] ReactNativeWebView became undefined.');
35
+ }
36
+ },
37
+ addEventListener: (type, listener) => {
38
+ if (type === 'message' && typeof listener === 'function') {
39
+ const adaptedListener = (event) => {
40
+ const me = event;
41
+ const comlinkMessageData = me.data;
42
+ if (!comlinkMessageData) {
43
+ console.warn('[SDK Comlink Endpoint] Received event with no data from host');
44
+ return;
45
+ }
46
+ // Comlink's listener expects a MessageEvent-like object with a `data` property.
47
+ // Cast to `any` to satisfy the EventListenerOrEventListenerObject type constraint,
48
+ // while providing the structure Comlink needs.
49
+ listener({ data: comlinkMessageData });
50
+ };
51
+ comlinkListeners.set(listener, adaptedListener);
52
+ document.addEventListener('FarcasterFrameCallback', adaptedListener);
53
+ }
54
+ },
55
+ removeEventListener: (type, listener) => {
56
+ if (type === 'message' && typeof listener === 'function') {
57
+ const adaptedListener = comlinkListeners.get(listener);
58
+ if (adaptedListener) {
59
+ console.debug('[SDK->Host Comlink] removeEventListener', listener);
60
+ document.removeEventListener('FarcasterFrameCallback', adaptedListener);
61
+ comlinkListeners.delete(listener);
62
+ }
63
+ }
64
+ },
65
+ };
66
+ try {
67
+ expose(sdkApiToExpose, endpointForHostViaRN);
68
+ console.debug('[WebView RN Comlink] Exposed SDK API for RN Host:', Object.keys(sdkApiToExpose));
69
+ }
70
+ catch (e) {
71
+ // Comlink throws if an endpoint is already exposed.
72
+ console.warn('[WebView RN Comlink] Error exposing SDK API (might be already exposed): ', e);
73
+ }
74
+ window.addEventListener('unload', () => {
75
+ proxiedFunctionsForReactNative.clear();
76
+ comlinkListeners.forEach((adaptedListener, _originalListener) => {
77
+ document.removeEventListener('FarcasterFrameCallback', adaptedListener);
78
+ });
79
+ comlinkListeners.clear();
80
+ });
81
+ }
82
+ else {
83
+ console.warn('[WebView RN Comlink] ReactNativeWebView not found, cannot expose SDK API to host.');
84
+ }
85
+ }
86
+ };
package/dist/sdk.js CHANGED
@@ -3,6 +3,11 @@ import { proxy } from 'comlink';
3
3
  import { EventEmitter } from 'eventemitter3';
4
4
  import { frameHost } from './frameHost';
5
5
  import { provider } from './provider';
6
+ import { initializeReactNativeSDK, isInReactNativeWebViewEnvironment, } from './rn';
7
+ // We need to ensure the React Native specific parts of the SDK are initialized
8
+ // before the SDK is used. Otherwise certain functionality, such as proxied function
9
+ // calls, will not work.
10
+ initializeReactNativeSDK();
6
11
  export function createEmitter() {
7
12
  const emitter = new EventEmitter();
8
13
  return {
@@ -110,7 +115,12 @@ export const sdk = {
110
115
  ethProvider: provider,
111
116
  },
112
117
  setShareStateProvider: (fn) => {
113
- frameHost.setShareStateProvider.bind(frameHost)(proxy(fn));
118
+ if (isInReactNativeWebViewEnvironment()) {
119
+ frameHost.setShareStateProvider.bind(frameHost)(fn);
120
+ }
121
+ else {
122
+ frameHost.setShareStateProvider.bind(frameHost)(proxy(fn));
123
+ }
114
124
  },
115
125
  };
116
126
  // Required to pass SSR