From 86893d48318e20af39230321517e87f6f9a6a83d Mon Sep 17 00:00:00 2001 From: sebmarkbage Date: Wed, 8 May 2024 01:58:17 +0000 Subject: [PATCH] [Fizz] Fallback to client replaying actions if we're trying to serialize a Blob (#28987) This follows the same principle as in #28611. We cannot serialize Blobs of a form data into HTML because you can't initialize a file input to some value. However the serialization of state in an Action can contain blobs. In this case we do error but outside the try/catch that recovers to error to client replaying instead of MPA mode. This errors earlier to ensure that this works. Testing this is a bit annoying because JSDOM doesn't have any of the Blob methods but the Blob needs to be compatible with FormData and the FormData needs to be compatible with `
` nodes in these tests. So I polyfilled those in JSDOM with some hacks. A possible future enhancement would be to encode these blobs in a base64 mode instead and have some way to receive them on the server. It's just a matter of layering this. I think the RSC layer's `FORM_DATA` implementation can pass some flag to encode as base64 and then have decodeAction include some way to parse them. That way this case would work in MPA mode too. DiffTrain build for [6bac4f2f31378cd58dffe6181e00639366a6081a](https://github.com/facebook/react/commit/6bac4f2f31378cd58dffe6181e00639366a6081a) --- compiled/facebook-www/REVISION | 2 +- .../ReactDOMServer-dev.classic.js | 31 ++++++++--- .../facebook-www/ReactDOMServer-dev.modern.js | 31 ++++++++--- .../ReactDOMServer-prod.classic.js | 54 +++++++++++-------- .../ReactDOMServer-prod.modern.js | 54 +++++++++++-------- .../ReactDOMServerStreaming-dev.modern.js | 29 +++++++--- .../ReactDOMServerStreaming-prod.modern.js | 18 +++++-- 7 files changed, 147 insertions(+), 72 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 4eb3ea66224f9..272b4a9a4fd96 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -5d29478716cefbf8290dfdd19129156c2ac75973 +6bac4f2f31378cd58dffe6181e00639366a6081a diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index f4e58b2ca311a..2775b030c1368 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require('react'); var ReactDOM = require('react-dom'); -var ReactVersion = '19.0.0-www-classic-f3d84129'; +var ReactVersion = '19.0.0-www-classic-b547cfd9'; // This refers to a WWW module. var warningWWW = require('warning'); @@ -2572,11 +2572,7 @@ var startHiddenInputChunk = stringToPrecomputedChunk('"); } +function validateAdditionalFormField(value) { + if ("string" !== typeof value) throw Error(formatProdErrorMessage(480)); +} function getCustomFormFields(resumableState, formAction) { if ("function" === typeof formAction.$$FORM_ACTION) { var id = resumableState.nextFormID++; resumableState = resumableState.idPrefix + id; try { - return formAction.$$FORM_ACTION(resumableState); + var customFields = formAction.$$FORM_ACTION(resumableState); + if (customFields) { + var formData = customFields.data; + null != formData && formData.forEach(validateAdditionalFormField); + } + return customFields; } catch (x) { if ("object" === typeof x && null !== x && "function" === typeof x.then) throw x; @@ -2627,16 +2635,16 @@ function createRenderState(resumableState, generateStaticMarkup) { "\x3c/script>" ); bootstrapScriptContent = idPrefix + "P:"; - var JSCompiler_object_inline_segmentPrefix_1631 = idPrefix + "S:"; + var JSCompiler_object_inline_segmentPrefix_1633 = idPrefix + "S:"; idPrefix += "B:"; - var JSCompiler_object_inline_preconnects_1645 = new Set(), - JSCompiler_object_inline_fontPreloads_1646 = new Set(), - JSCompiler_object_inline_highImagePreloads_1647 = new Set(), - JSCompiler_object_inline_styles_1648 = new Map(), - JSCompiler_object_inline_bootstrapScripts_1649 = new Set(), - JSCompiler_object_inline_scripts_1650 = new Set(), - JSCompiler_object_inline_bulkPreloads_1651 = new Set(), - JSCompiler_object_inline_preloads_1652 = { + var JSCompiler_object_inline_preconnects_1647 = new Set(), + JSCompiler_object_inline_fontPreloads_1648 = new Set(), + JSCompiler_object_inline_highImagePreloads_1649 = new Set(), + JSCompiler_object_inline_styles_1650 = new Map(), + JSCompiler_object_inline_bootstrapScripts_1651 = new Set(), + JSCompiler_object_inline_scripts_1652 = new Set(), + JSCompiler_object_inline_bulkPreloads_1653 = new Set(), + JSCompiler_object_inline_preloads_1654 = { images: new Map(), stylesheets: new Map(), scripts: new Map(), @@ -2673,7 +2681,7 @@ function createRenderState(resumableState, generateStaticMarkup) { scriptConfig.moduleScriptResources[href] = null; scriptConfig = []; pushLinkImpl(scriptConfig, props); - JSCompiler_object_inline_bootstrapScripts_1649.add(scriptConfig); + JSCompiler_object_inline_bootstrapScripts_1651.add(scriptConfig); bootstrapChunks.push('