1- import fs from 'node:fs/promises' ;
2- import path from 'node:path' ;
31import {
42 type MiddlewareHandler ,
53 defineServerConfig ,
64} from '@modern-js/server-runtime' ;
5+ import { loadServerAction } from 'react-server-dom-rspack/server.node' ;
76
87const shouldProxyRemoteAsset = ( pathname : string ) => {
98 if ( pathname . startsWith ( '/static/js/async/' ) ) {
@@ -27,60 +26,55 @@ const REMOTE_COUNTER_SOURCE_MODULE = './src/components/RemoteClientCounter.tsx';
2726const createRemoteNestedMixedAliasChunk = ( ) =>
2827 `\n;(globalThis["chunk_rscHost"] = globalThis["chunk_rscHost"] || []).push([["__federation_expose_RemoteNestedMixed_alias"],{"${ REMOTE_COUNTER_ALIAS_MODULE } ":function(module,__unused,__webpack_require__){module.exports=__webpack_require__("${ REMOTE_COUNTER_SOURCE_MODULE } ");}}]);` ;
2928
30- const REMOTE_ACTION_ID_TO_PROXY_EXPORT = {
31- '606c30f35d74d843171a8a71358eda595991e4ee16270e9f052af3faef57a19999' :
32- 'proxyIncrementRemoteCount' ,
33- '40e41a2ee9d9de373b364dcf2a0201701057c8502037bf9ef2cd26bb2a1259dabd' :
34- 'proxyRemoteActionEcho' ,
35- '408da81ddb8214f8cb98a83552cb70c4d17b27b6fd36d972cac89e7030a4874fd4' :
36- 'proxyNestedRemoteAction' ,
37- '40fd3fd0c01e4d21630b7f6f902c1ddc49d3b05418ca1da003c4fdc6c6272e0bf2' :
38- 'proxyDefaultRemoteAction' ,
39- } as const ;
40- const ACTION_EXPOSE_PATHS = new Set ( [
41- '/static/js/async/__federation_expose_actions.js' ,
42- '/static/js/async/__federation_expose_nestedActions.js' ,
43- '/static/js/async/__federation_expose_defaultAction.js' ,
44- ] ) ;
45-
46- let cachedProxyActionIds :
47- | Partial <
48- Record <
49- ( typeof REMOTE_ACTION_ID_TO_PROXY_EXPORT ) [ keyof typeof REMOTE_ACTION_ID_TO_PROXY_EXPORT ] ,
50- string
51- >
52- >
53- | undefined ;
54-
55- const getProxyActionIds = async ( ) => {
56- if ( cachedProxyActionIds ) {
57- return cachedProxyActionIds ;
29+ const proxyRemoteRscAction : MiddlewareHandler = async ( c , next ) => {
30+ const request = c . req . raw ;
31+ if ( request . method !== 'POST' ) {
32+ await next ( ) ;
33+ return ;
5834 }
5935
60- const serverBundlePath = path . resolve (
61- __dirname ,
62- '../dist/bundles/server-component-root.js' ,
63- ) ;
64- const serverBundleCode = await fs . readFile ( serverBundlePath , 'utf-8' ) ;
65-
66- const actionIdMap : Partial <
67- Record <
68- ( typeof REMOTE_ACTION_ID_TO_PROXY_EXPORT ) [ keyof typeof REMOTE_ACTION_ID_TO_PROXY_EXPORT ] ,
69- string
70- >
71- > = { } ;
72- Object . values ( REMOTE_ACTION_ID_TO_PROXY_EXPORT ) . forEach ( exportName => {
73- const pattern = new RegExp (
74- `registerServerReference\\\\(${ exportName } ,\\\\s*"([^"]+)"` ,
75- ) ;
76- const match = serverBundleCode . match ( pattern ) ;
77- if ( match ?. [ 1 ] ) {
78- actionIdMap [ exportName ] = match [ 1 ] ;
36+ const actionId = request . headers . get ( 'x-rsc-action' ) ;
37+ if ( ! actionId ) {
38+ await next ( ) ;
39+ return ;
40+ }
41+
42+ try {
43+ const localAction = loadServerAction ( actionId ) ;
44+ if ( typeof localAction === 'function' ) {
45+ await next ( ) ;
46+ return ;
7947 }
80- } ) ;
81- cachedProxyActionIds = actionIdMap ;
48+ } catch ( _error ) {
49+ // Unknown host action ID: forward to remote server below.
50+ }
51+
52+ const remotePort = process . env . RSC_MF_REMOTE_PORT ;
53+ if ( ! remotePort ) {
54+ await next ( ) ;
55+ return ;
56+ }
8257
83- return actionIdMap ;
58+ const reqUrl = new URL ( c . req . url ) ;
59+ const remoteUrl = `http://127.0.0.1:${ remotePort } ${ reqUrl . pathname } ${ reqUrl . search } ` ;
60+ const headers = new Headers ( request . headers ) ;
61+ headers . delete ( 'host' ) ;
62+ const body = await request . arrayBuffer ( ) ;
63+ const upstream = await fetch ( remoteUrl , {
64+ method : 'POST' ,
65+ headers,
66+ body,
67+ } ) . catch ( ( ) => undefined ) ;
68+
69+ if ( ! upstream ) {
70+ await next ( ) ;
71+ return ;
72+ }
73+
74+ c . res = new Response ( await upstream . arrayBuffer ( ) , {
75+ status : upstream . status ,
76+ headers : upstream . headers ,
77+ } ) ;
8478} ;
8579
8680const proxyRemoteFederationAsset : MiddlewareHandler = async ( c , next ) => {
@@ -106,28 +100,11 @@ const proxyRemoteFederationAsset: MiddlewareHandler = async (c, next) => {
106100 return ;
107101 }
108102
109- const shouldPatchActionChunk = ACTION_EXPOSE_PATHS . has ( pathname ) ;
110103 const shouldPatchNestedMixed =
111104 pathname === '/static/js/async/__federation_expose_RemoteNestedMixed.js' ;
112- if ( shouldPatchActionChunk || shouldPatchNestedMixed ) {
105+ if ( shouldPatchNestedMixed ) {
113106 let chunkText = await upstream . text ( ) ;
114-
115- if ( shouldPatchActionChunk ) {
116- const proxyActionIds = await getProxyActionIds ( ) . catch ( ( ) => ( { } ) ) ;
117- Object . entries ( REMOTE_ACTION_ID_TO_PROXY_EXPORT ) . forEach (
118- ( [ remoteActionId , proxyExport ] ) => {
119- const proxyActionId = proxyActionIds [ proxyExport ] ;
120- if ( ! proxyActionId ) {
121- return ;
122- }
123- chunkText = chunkText . split ( remoteActionId ) . join ( proxyActionId ) ;
124- } ,
125- ) ;
126- }
127-
128- if ( shouldPatchNestedMixed ) {
129- chunkText = `${ chunkText } ${ createRemoteNestedMixedAliasChunk ( ) } ` ;
130- }
107+ chunkText = `${ chunkText } ${ createRemoteNestedMixedAliasChunk ( ) } ` ;
131108
132109 c . res = new Response ( chunkText , {
133110 status : upstream . status ,
@@ -146,6 +123,10 @@ const proxyRemoteFederationAsset: MiddlewareHandler = async (c, next) => {
146123
147124export default defineServerConfig ( {
148125 middlewares : [
126+ {
127+ name : 'proxy-remote-rsc-action' ,
128+ handler : proxyRemoteRscAction ,
129+ } ,
149130 {
150131 name : 'proxy-remote-federation-asset' ,
151132 handler : proxyRemoteFederationAsset ,
0 commit comments