diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js index 6e113556206f4..2181eb5fe700c 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js @@ -438,6 +438,50 @@ describe('ReactFlightDOMReply', () => { expect(response.obj).toBe(obj); }); + it('can return an opaque object through an async function', async () => { + function fn() { + return 'this is a client function'; + } + + const args = [fn]; + + const temporaryReferences = + ReactServerDOMClient.createTemporaryReferenceSet(); + const body = await ReactServerDOMClient.encodeReply(args, { + temporaryReferences, + }); + + const temporaryReferencesServer = + ReactServerDOMServer.createTemporaryReferenceSet(); + const serverPayload = await ReactServerDOMServer.decodeReply( + body, + webpackServerMap, + {temporaryReferences: temporaryReferencesServer}, + ); + + async function action(arg) { + return arg; + } + + const stream = await serverAct(() => + ReactServerDOMServer.renderToReadableStream( + { + result: action.apply(null, serverPayload), + }, + null, + {temporaryReferences: temporaryReferencesServer}, + ), + ); + const response = await ReactServerDOMClient.createFromReadableStream( + stream, + { + temporaryReferences, + }, + ); + + expect(await response.result).toBe(fn); + }); + it('should supports streaming ReadableStream with objects', async () => { let controller1; let controller2; diff --git a/packages/react-server/src/ReactFlightServerTemporaryReferences.js b/packages/react-server/src/ReactFlightServerTemporaryReferences.js index e368b2e800795..1ea89c81980c1 100644 --- a/packages/react-server/src/ReactFlightServerTemporaryReferences.js +++ b/packages/react-server/src/ReactFlightServerTemporaryReferences.js @@ -70,6 +70,13 @@ const proxyHandlers = { `Instead, you can export a Client Component wrapper ` + `that itself renders a Client Context Provider.`, ); + case 'then': + // Allow returning a temporary reference from an async function + // Unlike regular Client References, a Promise would never have been serialized as + // an opaque Temporary Reference, but instead would have been serialized as a + // Promise on the server and so doesn't hit this path. So we can assume this wasn't + // a Promise on the client. + return undefined; } throw new Error( // eslint-disable-next-line react-internal/safe-string-coercion