Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsinfuego authored Oct 22, 2024
2 parents e34a73f + ba146c7 commit 58364f1
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 98 deletions.
111 changes: 62 additions & 49 deletions packages/vite/src/rsc/rscRequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,61 +99,14 @@ export function createRscRequestHandler(
console.log('rscId', rscId)
console.log('rsaId', rsaId)

// TODO (RSC): When is this ever '_'? Can we remove that extra check?
if (rscId && rscId !== '_') {
res.setHeader('Content-Type', 'text/x-component')
} else {
rscId = undefined
}

if (rsaId) {
// TODO (RSC): For React Server Actions we need to limit the request
// size somehow
// https://nextjs.org/docs/app/api-reference/functions/server-actions#size-limitation
if (req.headers['content-type']?.startsWith('multipart/form-data')) {
console.log('RSA: multipart/form-data')
const bb = busboy({ headers: req.headers })
// TODO (RSC): The generic here could be typed better
const reply = decodeReplyFromBusboy<unknown[]>(bb)

req.pipe(bb)
args = await reply

// TODO (RSC): Loop over args (to not only look at args[0])
if (args[0] instanceof FormData) {
const serializedFormData: Record<string, any> = {}

for (const [key, value] of args[0]) {
// Several form fields can share the same name. This should be
// represented as an array of the values of all those fields
if (serializedFormData[key] !== undefined) {
if (!Array.isArray(serializedFormData[key])) {
serializedFormData[key] = [serializedFormData[key]]
}

serializedFormData[key].push(value)
} else {
serializedFormData[key] = value
}
}

args[0] = {
__formData__: true,
state: serializedFormData,
}
}
} else {
console.log('RSA: regular body')
let body = ''

for await (const chunk of req) {
body += chunk
}

if (body) {
args = await decodeReply(body)
}
}
}
args = await handleRsa(rsaId, req, args)
}

console.log('rscRequestHandler: args', args)
Expand Down Expand Up @@ -204,3 +157,63 @@ export function createRscRequestHandler(
}
}
}

async function handleRsa(
rsaId: string | undefined,
req: ExpressRequest,
args: unknown[],
) {
if (!rsaId) {
return args
}

// TODO (RSC): For React Server Actions we need to limit the request
// size somehow
// https://nextjs.org/docs/app/api-reference/functions/server-actions#size-limitation
if (req.headers['content-type']?.startsWith('multipart/form-data')) {
console.log('RSA: multipart/form-data')
const bb = busboy({ headers: req.headers })
// TODO (RSC): The generic here could be typed better
const reply = decodeReplyFromBusboy<unknown[]>(bb)

req.pipe(bb)
args = await reply

// TODO (RSC): Loop over args (to not only look at args[0])
if (args[0] instanceof FormData) {
const serializedFormData: Record<string, any> = {}

for (const [key, value] of args[0]) {
// Several form fields can share the same name. This should be
// represented as an array of the values of all those fields
if (serializedFormData[key] !== undefined) {
if (!Array.isArray(serializedFormData[key])) {
serializedFormData[key] = [serializedFormData[key]]
}

serializedFormData[key].push(value)
} else {
serializedFormData[key] = value
}
}

args[0] = {
__formData__: true,
state: serializedFormData,
}
}
} else {
console.log('RSA: regular body')
let body = ''

for await (const chunk of req) {
body += chunk
}

if (body) {
args = await decodeReply(body)
}
}

return args
}
96 changes: 48 additions & 48 deletions packages/vite/src/rsc/rscStudioHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,64 +52,63 @@ const processRenderRscStream = async (
}

const postFlightToStudio = (payload: string, metadata: Record<string, any>) => {
if (shouldSendToStudio()) {
const base64Payload = Buffer.from(payload).toString('base64')
const encodedMetadata = Buffer.from(JSON.stringify(metadata)).toString(
'base64',
)
const jsonBody = JSON.stringify({
flight: {
encodedPayload: base64Payload,
encoding: 'base64',
encodedMetadata,
},
})
if (!shouldSendToStudio()) {
return
}

// Options to configure the HTTP POST request
// TODO (RSC): Get these from the toml and Studio config
const options = {
hostname: 'localhost',
port: getStudioPort(),
path: '/.redwood/functions/rsc-flight',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(jsonBody),
},
}
const base64Payload = Buffer.from(payload).toString('base64')
const encodedMetadata = Buffer.from(JSON.stringify(metadata)).toString(
'base64',
)
const jsonBody = JSON.stringify({
flight: {
encodedPayload: base64Payload,
encoding: 'base64',
encodedMetadata,
},
})

const req = http.request(options, (res) => {
res.setEncoding('utf8')
})
// Options to configure the HTTP POST request
// TODO (RSC): Get these from the toml and Studio config
const options = {
hostname: 'localhost',
port: getStudioPort(),
path: '/.redwood/functions/rsc-flight',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(jsonBody),
},
}

req.on('error', (e: Error) => {
console.error(
`An error occurred sending the Flight Payload to Studio: ${e.message}`,
)
})
const req = http.request(options, (res) => {
res.setEncoding('utf8')
})

req.write(jsonBody)
req.end()
}
req.on('error', (e: Error) => {
console.error('An error occurred sending the Flight Payload to Studio')
console.error(e)
})

req.write(jsonBody)
req.end()
}

const createStudioFlightHandler = (
readable: ReadableStream,
metadata: Record<string, any>,
) => {
if (shouldSendToStudio()) {
processRenderRscStream(readable)
.then((payload) => {
console.debug('Sending RSC Rendered stream to Studio')
postFlightToStudio(payload, metadata)
console.debug('Sent RSC Rendered stream to Studio', payload, metadata)
})
.catch((error) => {
console.error('An error occurred getting RSC Rendered steam:', error)
})
} else {
console.debug('Studio is not enabled')
}
processRenderRscStream(readable)
.then((payload) => {
console.debug('Sending RSC Rendered stream to Studio')
postFlightToStudio(payload, metadata)
console.debug('Sent RSC Rendered stream to Studio')
console.debug('payload to Studio:', payload)
console.debug('metadata to Studio:', metadata)
})
.catch((error) => {
console.error('An error occurred getting RSC Rendered steam:', error)
})
}

interface StudioRenderInput extends Omit<RenderInput, 'serverState'> {
Expand All @@ -123,6 +122,7 @@ export const sendRscFlightToStudio = async (input: StudioRenderInput) => {
console.debug('Studio is not enabled')
return
}

const { rscId, rsaId, args, basePath, req, handleError } = input

try {
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/runFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ function createWebSocketServer() {
// Event listener for incoming messages. The `data` is a Buffer
ws.on('message', (data) => {
const message = data.toString()
console.log('Received message:', message)
console.log('runFeServer.ts: Received message:')
console.log(message.slice(0, 120) + '...')

// Broadcast the message to all connected clients
wsServer.clients.forEach((client) => {
Expand Down

0 comments on commit 58364f1

Please sign in to comment.