From 4707842f6a7cf3508fd319e9d429cde24ac30e7f Mon Sep 17 00:00:00 2001 From: sebmarkbage Date: Wed, 3 May 2023 22:43:04 +0000 Subject: [PATCH] [Flight] Progressively Enhanced Server Actions (#26774) This automatically exposes `$$FORM_ACTIONS` on Server References coming from Flight. So that when they're used in a form action, we can encode the ID for the server reference as a hidden field or as part of the name of a button. If the Server Action is a bound function it can have complex data associated with it. In this case this additional data is encoded as additional form fields. To process a POST on the server there's now a `decodeAction` helper that can take one of these progressive posts from FormData and give you a function that is prebound with the correct closure and FormData so that you can just invoke it. I updated the fixture which now has a "Server State" that gets automatically refreshed. This also lets us visualize form fields. There's no "Action State" here for showing error messages that are not thrown, that's still up to user space. DiffTrain build for [aef7ce5547c9489dc48e31f69b002cd17206e0cb](https://github.com/facebook/react/commit/aef7ce5547c9489dc48e31f69b002cd17206e0cb) --- compiled/facebook-www/REVISION | 2 +- .../ReactFlightDOMRelayClient-dev.classic.js | 783 +++++++++++++++++- .../ReactFlightDOMRelayClient-dev.modern.js | 783 +++++++++++++++++- .../ReactFlightDOMRelayClient-prod.classic.js | 155 +++- .../ReactFlightDOMRelayClient-prod.modern.js | 155 +++- 5 files changed, 1867 insertions(+), 11 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 12ee8d30c47b8..9f9959d882f5f 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -c10010a6a00911fe99452bc561dd47c3e15f4eb8 +aef7ce5547c9489dc48e31f69b002cd17206e0cb diff --git a/compiled/facebook-www/ReactFlightDOMRelayClient-dev.classic.js b/compiled/facebook-www/ReactFlightDOMRelayClient-dev.classic.js index 25d582c6ebd76..d84eceb8a2e14 100644 --- a/compiled/facebook-www/ReactFlightDOMRelayClient-dev.classic.js +++ b/compiled/facebook-www/ReactFlightDOMRelayClient-dev.classic.js @@ -121,17 +121,794 @@ function dispatchHint(code, model) { } } -var knownServerReferences = new WeakMap(); +// This refers to a WWW module. +var warningWWW = require("warning"); +function error(format) { + { + { + for ( + var _len2 = arguments.length, + args = new Array(_len2 > 1 ? _len2 - 1 : 0), + _key2 = 1; + _key2 < _len2; + _key2++ + ) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning("error", format, args); + } + } +} + +function printWarning(level, format, args) { + { + var React = require("react"); + + var ReactSharedInternals = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; // Defensive in case this is fired before React is initialized. + + if (ReactSharedInternals != null) { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== "") { + format += "%s"; + args.push(stack); + } + } // TODO: don't ignore level and pass it down somewhere too. + + args.unshift(format); + args.unshift(false); + warningWWW.apply(null, args); + } +} // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. var REACT_ELEMENT_TYPE = Symbol.for("react.element"); +var REACT_PROVIDER_TYPE = Symbol.for("react.provider"); +var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"); +var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"); +var REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"); +var REACT_MEMO_TYPE = Symbol.for("react.memo"); var REACT_LAZY_TYPE = Symbol.for("react.lazy"); var REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( "react.default_value" ); +var MAYBE_ITERATOR_SYMBOL = Symbol.iterator; +var FAUX_ITERATOR_SYMBOL = "@@iterator"; +function getIteratorFn(maybeIterable) { + if (maybeIterable === null || typeof maybeIterable !== "object") { + return null; + } + + var maybeIterator = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable[FAUX_ITERATOR_SYMBOL]; + + if (typeof maybeIterator === "function") { + return maybeIterator; + } + + return null; +} + +// in case they error. + +var jsxPropsParents = new WeakMap(); +var jsxChildrenParents = new WeakMap(); + +function isObjectPrototype(object) { + if (!object) { + return false; + } + + var ObjectPrototype = Object.prototype; + + if (object === ObjectPrototype) { + return true; + } // It might be an object from a different Realm which is + // still just a plain simple object. + + if (Object.getPrototypeOf(object)) { + return false; + } + + var names = Object.getOwnPropertyNames(object); + + for (var i = 0; i < names.length; i++) { + if (!(names[i] in ObjectPrototype)) { + return false; + } + } + + return true; +} + +function isSimpleObject(object) { + if (!isObjectPrototype(Object.getPrototypeOf(object))) { + return false; + } + + var names = Object.getOwnPropertyNames(object); + + for (var i = 0; i < names.length; i++) { + var descriptor = Object.getOwnPropertyDescriptor(object, names[i]); + + if (!descriptor) { + return false; + } + + if (!descriptor.enumerable) { + if ( + (names[i] === "key" || names[i] === "ref") && + typeof descriptor.get === "function" + ) { + // React adds key and ref getters to props objects to issue warnings. + // Those getters will not be transferred to the client, but that's ok, + // so we'll special case them. + continue; + } + + return false; + } + } + + return true; +} +function objectName(object) { + // $FlowFixMe[method-unbinding] + var name = Object.prototype.toString.call(object); + return name.replace(/^\[object (.*)\]$/, function (m, p0) { + return p0; + }); +} + +function describeKeyForErrorMessage(key) { + var encodedKey = JSON.stringify(key); + return '"' + key + '"' === encodedKey ? key : encodedKey; +} + +function describeValueForErrorMessage(value) { + switch (typeof value) { + case "string": { + return JSON.stringify( + value.length <= 10 ? value : value.slice(0, 10) + "..." + ); + } + + case "object": { + if (isArray(value)) { + return "[...]"; + } + + var name = objectName(value); + + if (name === "Object") { + return "{...}"; + } + + return name; + } + + case "function": + return "function"; + + default: + // eslint-disable-next-line react-internal/safe-string-coercion + return String(value); + } +} + +function describeElementType(type) { + if (typeof type === "string") { + return type; + } + + switch (type) { + case REACT_SUSPENSE_TYPE: + return "Suspense"; + + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + } + + if (typeof type === "object") { + switch (type.$$typeof) { + case REACT_FORWARD_REF_TYPE: + return describeElementType(type.render); + + case REACT_MEMO_TYPE: + return describeElementType(type.type); + + case REACT_LAZY_TYPE: { + var lazyComponent = type; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + + try { + // Lazy may contain any component type so we recursively resolve it. + return describeElementType(init(payload)); + } catch (x) {} + } + } + } + + return ""; +} + +function describeObjectForErrorMessage(objectOrArray, expandedName) { + var objKind = objectName(objectOrArray); + + if (objKind !== "Object" && objKind !== "Array") { + return objKind; + } + + var str = ""; + var start = -1; + var length = 0; + + if (isArray(objectOrArray)) { + if (jsxChildrenParents.has(objectOrArray)) { + // Print JSX Children + var type = jsxChildrenParents.get(objectOrArray); + str = "<" + describeElementType(type) + ">"; + var array = objectOrArray; + + for (var i = 0; i < array.length; i++) { + var value = array[i]; + var substr = void 0; + + if (typeof value === "string") { + substr = value; + } else if (typeof value === "object" && value !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + substr = "{" + describeObjectForErrorMessage(value) + "}"; + } else { + substr = "{" + describeValueForErrorMessage(value) + "}"; + } + + if ("" + i === expandedName) { + start = str.length; + length = substr.length; + str += substr; + } else if (substr.length < 15 && str.length + substr.length < 40) { + str += substr; + } else { + str += "{...}"; + } + } + + str += ""; + } else { + // Print Array + str = "["; + var _array = objectOrArray; + + for (var _i = 0; _i < _array.length; _i++) { + if (_i > 0) { + str += ", "; + } + + var _value = _array[_i]; + + var _substr = void 0; + + if (typeof _value === "object" && _value !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr = describeObjectForErrorMessage(_value); + } else { + _substr = describeValueForErrorMessage(_value); + } + + if ("" + _i === expandedName) { + start = str.length; + length = _substr.length; + str += _substr; + } else if (_substr.length < 10 && str.length + _substr.length < 40) { + str += _substr; + } else { + str += "..."; + } + } + + str += "]"; + } + } else { + if (objectOrArray.$$typeof === REACT_ELEMENT_TYPE) { + str = "<" + describeElementType(objectOrArray.type) + "/>"; + } else if (jsxPropsParents.has(objectOrArray)) { + // Print JSX + var _type = jsxPropsParents.get(objectOrArray); + + str = "<" + (describeElementType(_type) || "..."); + var object = objectOrArray; + var names = Object.keys(object); + + for (var _i2 = 0; _i2 < names.length; _i2++) { + str += " "; + var name = names[_i2]; + str += describeKeyForErrorMessage(name) + "="; + var _value2 = object[name]; + + var _substr2 = void 0; + + if ( + name === expandedName && + typeof _value2 === "object" && + _value2 !== null + ) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr2 = describeObjectForErrorMessage(_value2); + } else { + _substr2 = describeValueForErrorMessage(_value2); + } + + if (typeof _value2 !== "string") { + _substr2 = "{" + _substr2 + "}"; + } + + if (name === expandedName) { + start = str.length; + length = _substr2.length; + str += _substr2; + } else if (_substr2.length < 10 && str.length + _substr2.length < 40) { + str += _substr2; + } else { + str += "..."; + } + } + + str += ">"; + } else { + // Print Object + str = "{"; + var _object = objectOrArray; + + var _names = Object.keys(_object); + + for (var _i3 = 0; _i3 < _names.length; _i3++) { + if (_i3 > 0) { + str += ", "; + } + + var _name = _names[_i3]; + str += describeKeyForErrorMessage(_name) + ": "; + var _value3 = _object[_name]; + + var _substr3 = void 0; + + if (typeof _value3 === "object" && _value3 !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr3 = describeObjectForErrorMessage(_value3); + } else { + _substr3 = describeValueForErrorMessage(_value3); + } + + if (_name === expandedName) { + start = str.length; + length = _substr3.length; + str += _substr3; + } else if (_substr3.length < 10 && str.length + _substr3.length < 40) { + str += _substr3; + } else { + str += "..."; + } + } + + str += "}"; + } + } + + if (expandedName === undefined) { + return str; + } + + if (start > -1 && length > 0) { + var highlight = " ".repeat(start) + "^".repeat(length); + return "\n " + str + "\n " + highlight; + } + + return "\n " + str; +} + +var knownServerReferences = new WeakMap(); // Serializable values +// Thenable +// function serializeByValueID(id: number): string { +// return '$' + id.toString(16); +// } + +function serializePromiseID(id) { + return "$@" + id.toString(16); +} + +function serializeServerReferenceID(id) { + return "$F" + id.toString(16); +} + +function serializeSymbolReference(name) { + return "$S" + name; +} + +function serializeFormDataReference(id) { + // Why K? F is "Function". D is "Date". What else? + return "$K" + id.toString(16); +} + +function serializeNumber(number) { + if (Number.isFinite(number)) { + if (number === 0 && 1 / number === -Infinity) { + return "$-0"; + } else { + return number; + } + } else { + if (number === Infinity) { + return "$Infinity"; + } else if (number === -Infinity) { + return "$-Infinity"; + } else { + return "$NaN"; + } + } +} + +function serializeUndefined() { + return "$undefined"; +} + +function serializeDateFromDateJSON(dateJSON) { + // JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString. + // We need only tack on a $D prefix. + return "$D" + dateJSON; +} + +function serializeBigInt(n) { + return "$n" + n.toString(10); +} + +function escapeStringValue(value) { + if (value[0] === "$") { + // We need to escape $ prefixed strings since we use those to encode + // references to IDs and as special symbol values. + return "$" + value; + } else { + return value; + } +} + +function processReply(root, formFieldPrefix, resolve, reject) { + var nextPartId = 1; + var pendingParts = 0; + var formData = null; + + function resolveToJSON(key, value) { + var parent = this; // Make sure that `parent[key]` wasn't JSONified before `value` was passed to us + + { + // $FlowFixMe[incompatible-use] + var originalValue = parent[key]; + + if ( + typeof originalValue === "object" && + originalValue !== value && + !(originalValue instanceof Date) + ) { + if (objectName(originalValue) !== "Object") { + error( + "Only plain objects can be passed to Server Functions from the Client. " + + "%s objects are not supported.%s", + objectName(originalValue), + describeObjectForErrorMessage(parent, key) + ); + } else { + error( + "Only plain objects can be passed to Server Functions from the Client. " + + "Objects with toJSON methods are not supported. Convert it manually " + + "to a simple value before passing it to props.%s", + describeObjectForErrorMessage(parent, key) + ); + } + } + } + + if (value === null) { + return null; + } + + if (typeof value === "object") { + // $FlowFixMe[method-unbinding] + if (typeof value.then === "function") { + // We assume that any object with a .then property is a "Thenable" type, + // or a Promise type. Either of which can be represented by a Promise. + if (formData === null) { + // Upgrade to use FormData to allow us to stream this value. + formData = new FormData(); + } + + pendingParts++; + var promiseId = nextPartId++; + var thenable = value; + thenable.then( + function (partValue) { + var partJSON = JSON.stringify(partValue, resolveToJSON); // $FlowFixMe[incompatible-type] We know it's not null because we assigned it above. + + var data = formData; // eslint-disable-next-line react-internal/safe-string-coercion + + data.append(formFieldPrefix + promiseId, partJSON); + pendingParts--; + + if (pendingParts === 0) { + resolve(data); + } + }, + function (reason) { + // In the future we could consider serializing this as an error + // that throws on the server instead. + reject(reason); + } + ); + return serializePromiseID(promiseId); + } // TODO: Should we the Object.prototype.toString.call() to test for cross-realm objects? + + if (value instanceof FormData) { + if (formData === null) { + // Upgrade to use FormData to allow us to use rich objects as its values. + formData = new FormData(); + } + + var data = formData; + var refId = nextPartId++; // Copy all the form fields with a prefix for this reference. + // These must come first in the form order because we assume that all the + // fields are available before this is referenced. + + var prefix = formFieldPrefix + refId + "_"; // $FlowFixMe[prop-missing]: FormData has forEach. + + value.forEach(function (originalValue, originalKey) { + data.append(prefix + originalKey, originalValue); + }); + return serializeFormDataReference(refId); + } + + if (!isArray(value)) { + var iteratorFn = getIteratorFn(value); + + if (iteratorFn) { + return Array.from(value); + } + } + + { + if (value !== null && !isArray(value)) { + // Verify that this is a simple plain object. + if (value.$$typeof === REACT_ELEMENT_TYPE) { + error( + "React Element cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (value.$$typeof === REACT_LAZY_TYPE) { + error( + "React Lazy cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (value.$$typeof === REACT_PROVIDER_TYPE) { + error( + "React Context Providers cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (objectName(value) !== "Object") { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "%s objects are not supported.%s", + objectName(value), + describeObjectForErrorMessage(parent, key) + ); + } else if (!isSimpleObject(value)) { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "Classes or other objects with methods are not supported.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(value); + + if (symbols.length > 0) { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "Objects with symbol properties like %s are not supported.%s", + symbols[0].description, + describeObjectForErrorMessage(parent, key) + ); + } + } + } + } // $FlowFixMe[incompatible-return] + + return value; + } + + if (typeof value === "string") { + // TODO: Maybe too clever. If we support URL there's no similar trick. + if (value[value.length - 1] === "Z") { + // Possibly a Date, whose toJSON automatically calls toISOString + // $FlowFixMe[incompatible-use] + var _originalValue = parent[key]; // $FlowFixMe[method-unbinding] + + if (_originalValue instanceof Date) { + return serializeDateFromDateJSON(value); + } + } + + return escapeStringValue(value); + } + + if (typeof value === "boolean") { + return value; + } + + if (typeof value === "number") { + return serializeNumber(value); + } + + if (typeof value === "undefined") { + return serializeUndefined(); + } + + if (typeof value === "function") { + var metaData = knownServerReferences.get(value); + + if (metaData !== undefined) { + var metaDataJSON = JSON.stringify(metaData, resolveToJSON); + + if (formData === null) { + // Upgrade to use FormData to allow us to stream this value. + formData = new FormData(); + } // The reference to this function came from the same client so we can pass it back. + + var _refId = nextPartId++; // eslint-disable-next-line react-internal/safe-string-coercion + + formData.set(formFieldPrefix + _refId, metaDataJSON); + return serializeServerReferenceID(_refId); + } + + throw new Error( + "Client Functions cannot be passed directly to Server Functions. " + + "Only Functions passed from the Server can be passed back again." + ); + } + + if (typeof value === "symbol") { + // $FlowFixMe[incompatible-type] `description` might be undefined + var name = value.description; + + if (Symbol.for(name) !== value) { + throw new Error( + "Only global symbols received from Symbol.for(...) can be passed to Server Functions. " + + ("The symbol Symbol.for(" + // $FlowFixMe[incompatible-type] `description` might be undefined + value.description + + ") cannot be found among global symbols.") + ); + } + + return serializeSymbolReference(name); + } + + if (typeof value === "bigint") { + return serializeBigInt(value); + } + + throw new Error( + "Type " + + typeof value + + " is not supported as an argument to a Server Function." + ); + } // $FlowFixMe[incompatible-type] it's not going to be undefined because we'll encode it. + + var json = JSON.stringify(root, resolveToJSON); + + if (formData === null) { + // If it's a simple data structure, we just use plain JSON. + resolve(json); + } else { + // Otherwise, we use FormData to let us stream in the result. + formData.set(formFieldPrefix + "0", json); + + if (pendingParts === 0) { + // $FlowFixMe[incompatible-call] this has already been refined. + resolve(formData); + } + } +} +var boundCache = new WeakMap(); + +function encodeFormData(reference) { + var resolve, reject; // We need to have a handle on the thenable so that we can synchronously set + // its status from processReply, when it can complete synchronously. + + var thenable = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + processReply( + reference, + "", + function (body) { + if (typeof body === "string") { + var data = new FormData(); + data.append("0", body); + body = data; + } + + var fulfilled = thenable; + fulfilled.status = "fulfilled"; + fulfilled.value = body; + resolve(body); + }, + function (e) { + var rejected = thenable; + rejected.status = "rejected"; + rejected.reason = e; + reject(e); + } + ); + return thenable; +} + +function encodeFormAction(identifierPrefix) { + var reference = knownServerReferences.get(this); + + if (!reference) { + throw new Error( + "Tried to encode a Server Action from a different instance than the encoder is from. " + + "This is a bug in React." + ); + } + + var data = null; + var name; + var boundPromise = reference.bound; + + if (boundPromise !== null) { + var thenable = boundCache.get(reference); + + if (!thenable) { + thenable = encodeFormData(reference); + boundCache.set(reference, thenable); + } + + if (thenable.status === "rejected") { + throw thenable.reason; + } else if (thenable.status !== "fulfilled") { + throw thenable; + } + + var encodedFormData = thenable.value; // This is hacky but we need the identifier prefix to be added to + // all fields but the suspense cache would break since we might get + // a new identifier each time. So we just append it at the end instead. + + var prefixedData = new FormData(); // $FlowFixMe[prop-missing] + + encodedFormData.forEach(function (value, key) { + prefixedData.append("$ACTION_" + identifierPrefix + ":" + key, value); + }); + data = prefixedData; // We encode the name of the prefix containing the data. + + name = "$ACTION_REF_" + identifierPrefix; + } else { + // This is the simple case so we can just encode the ID. + name = "$ACTION_ID_" + reference.id; + } + + return { + name: name, + method: "POST", + encType: "multipart/form-data", + data: data + }; +} var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; @@ -536,8 +1313,10 @@ function createServerReferenceProxy(response, metaData) { return Promise.resolve(p).then(function (bound) { return callServer(metaData.id, bound.concat(args)); }); - }; + }; // Expose encoder for use by SSR. + // TODO: Only expose this in SSR builds and not the browser client. + proxy.$$FORM_ACTION = encodeFormAction; knownServerReferences.set(proxy, metaData); return proxy; } diff --git a/compiled/facebook-www/ReactFlightDOMRelayClient-dev.modern.js b/compiled/facebook-www/ReactFlightDOMRelayClient-dev.modern.js index 25d582c6ebd76..d84eceb8a2e14 100644 --- a/compiled/facebook-www/ReactFlightDOMRelayClient-dev.modern.js +++ b/compiled/facebook-www/ReactFlightDOMRelayClient-dev.modern.js @@ -121,17 +121,794 @@ function dispatchHint(code, model) { } } -var knownServerReferences = new WeakMap(); +// This refers to a WWW module. +var warningWWW = require("warning"); +function error(format) { + { + { + for ( + var _len2 = arguments.length, + args = new Array(_len2 > 1 ? _len2 - 1 : 0), + _key2 = 1; + _key2 < _len2; + _key2++ + ) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning("error", format, args); + } + } +} + +function printWarning(level, format, args) { + { + var React = require("react"); + + var ReactSharedInternals = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; // Defensive in case this is fired before React is initialized. + + if (ReactSharedInternals != null) { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== "") { + format += "%s"; + args.push(stack); + } + } // TODO: don't ignore level and pass it down somewhere too. + + args.unshift(format); + args.unshift(false); + warningWWW.apply(null, args); + } +} // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. var REACT_ELEMENT_TYPE = Symbol.for("react.element"); +var REACT_PROVIDER_TYPE = Symbol.for("react.provider"); +var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"); +var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"); +var REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"); +var REACT_MEMO_TYPE = Symbol.for("react.memo"); var REACT_LAZY_TYPE = Symbol.for("react.lazy"); var REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( "react.default_value" ); +var MAYBE_ITERATOR_SYMBOL = Symbol.iterator; +var FAUX_ITERATOR_SYMBOL = "@@iterator"; +function getIteratorFn(maybeIterable) { + if (maybeIterable === null || typeof maybeIterable !== "object") { + return null; + } + + var maybeIterator = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable[FAUX_ITERATOR_SYMBOL]; + + if (typeof maybeIterator === "function") { + return maybeIterator; + } + + return null; +} + +// in case they error. + +var jsxPropsParents = new WeakMap(); +var jsxChildrenParents = new WeakMap(); + +function isObjectPrototype(object) { + if (!object) { + return false; + } + + var ObjectPrototype = Object.prototype; + + if (object === ObjectPrototype) { + return true; + } // It might be an object from a different Realm which is + // still just a plain simple object. + + if (Object.getPrototypeOf(object)) { + return false; + } + + var names = Object.getOwnPropertyNames(object); + + for (var i = 0; i < names.length; i++) { + if (!(names[i] in ObjectPrototype)) { + return false; + } + } + + return true; +} + +function isSimpleObject(object) { + if (!isObjectPrototype(Object.getPrototypeOf(object))) { + return false; + } + + var names = Object.getOwnPropertyNames(object); + + for (var i = 0; i < names.length; i++) { + var descriptor = Object.getOwnPropertyDescriptor(object, names[i]); + + if (!descriptor) { + return false; + } + + if (!descriptor.enumerable) { + if ( + (names[i] === "key" || names[i] === "ref") && + typeof descriptor.get === "function" + ) { + // React adds key and ref getters to props objects to issue warnings. + // Those getters will not be transferred to the client, but that's ok, + // so we'll special case them. + continue; + } + + return false; + } + } + + return true; +} +function objectName(object) { + // $FlowFixMe[method-unbinding] + var name = Object.prototype.toString.call(object); + return name.replace(/^\[object (.*)\]$/, function (m, p0) { + return p0; + }); +} + +function describeKeyForErrorMessage(key) { + var encodedKey = JSON.stringify(key); + return '"' + key + '"' === encodedKey ? key : encodedKey; +} + +function describeValueForErrorMessage(value) { + switch (typeof value) { + case "string": { + return JSON.stringify( + value.length <= 10 ? value : value.slice(0, 10) + "..." + ); + } + + case "object": { + if (isArray(value)) { + return "[...]"; + } + + var name = objectName(value); + + if (name === "Object") { + return "{...}"; + } + + return name; + } + + case "function": + return "function"; + + default: + // eslint-disable-next-line react-internal/safe-string-coercion + return String(value); + } +} + +function describeElementType(type) { + if (typeof type === "string") { + return type; + } + + switch (type) { + case REACT_SUSPENSE_TYPE: + return "Suspense"; + + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + } + + if (typeof type === "object") { + switch (type.$$typeof) { + case REACT_FORWARD_REF_TYPE: + return describeElementType(type.render); + + case REACT_MEMO_TYPE: + return describeElementType(type.type); + + case REACT_LAZY_TYPE: { + var lazyComponent = type; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + + try { + // Lazy may contain any component type so we recursively resolve it. + return describeElementType(init(payload)); + } catch (x) {} + } + } + } + + return ""; +} + +function describeObjectForErrorMessage(objectOrArray, expandedName) { + var objKind = objectName(objectOrArray); + + if (objKind !== "Object" && objKind !== "Array") { + return objKind; + } + + var str = ""; + var start = -1; + var length = 0; + + if (isArray(objectOrArray)) { + if (jsxChildrenParents.has(objectOrArray)) { + // Print JSX Children + var type = jsxChildrenParents.get(objectOrArray); + str = "<" + describeElementType(type) + ">"; + var array = objectOrArray; + + for (var i = 0; i < array.length; i++) { + var value = array[i]; + var substr = void 0; + + if (typeof value === "string") { + substr = value; + } else if (typeof value === "object" && value !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + substr = "{" + describeObjectForErrorMessage(value) + "}"; + } else { + substr = "{" + describeValueForErrorMessage(value) + "}"; + } + + if ("" + i === expandedName) { + start = str.length; + length = substr.length; + str += substr; + } else if (substr.length < 15 && str.length + substr.length < 40) { + str += substr; + } else { + str += "{...}"; + } + } + + str += ""; + } else { + // Print Array + str = "["; + var _array = objectOrArray; + + for (var _i = 0; _i < _array.length; _i++) { + if (_i > 0) { + str += ", "; + } + + var _value = _array[_i]; + + var _substr = void 0; + + if (typeof _value === "object" && _value !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr = describeObjectForErrorMessage(_value); + } else { + _substr = describeValueForErrorMessage(_value); + } + + if ("" + _i === expandedName) { + start = str.length; + length = _substr.length; + str += _substr; + } else if (_substr.length < 10 && str.length + _substr.length < 40) { + str += _substr; + } else { + str += "..."; + } + } + + str += "]"; + } + } else { + if (objectOrArray.$$typeof === REACT_ELEMENT_TYPE) { + str = "<" + describeElementType(objectOrArray.type) + "/>"; + } else if (jsxPropsParents.has(objectOrArray)) { + // Print JSX + var _type = jsxPropsParents.get(objectOrArray); + + str = "<" + (describeElementType(_type) || "..."); + var object = objectOrArray; + var names = Object.keys(object); + + for (var _i2 = 0; _i2 < names.length; _i2++) { + str += " "; + var name = names[_i2]; + str += describeKeyForErrorMessage(name) + "="; + var _value2 = object[name]; + + var _substr2 = void 0; + + if ( + name === expandedName && + typeof _value2 === "object" && + _value2 !== null + ) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr2 = describeObjectForErrorMessage(_value2); + } else { + _substr2 = describeValueForErrorMessage(_value2); + } + + if (typeof _value2 !== "string") { + _substr2 = "{" + _substr2 + "}"; + } + + if (name === expandedName) { + start = str.length; + length = _substr2.length; + str += _substr2; + } else if (_substr2.length < 10 && str.length + _substr2.length < 40) { + str += _substr2; + } else { + str += "..."; + } + } + + str += ">"; + } else { + // Print Object + str = "{"; + var _object = objectOrArray; + + var _names = Object.keys(_object); + + for (var _i3 = 0; _i3 < _names.length; _i3++) { + if (_i3 > 0) { + str += ", "; + } + + var _name = _names[_i3]; + str += describeKeyForErrorMessage(_name) + ": "; + var _value3 = _object[_name]; + + var _substr3 = void 0; + + if (typeof _value3 === "object" && _value3 !== null) { + // $FlowFixMe[incompatible-call] found when upgrading Flow + _substr3 = describeObjectForErrorMessage(_value3); + } else { + _substr3 = describeValueForErrorMessage(_value3); + } + + if (_name === expandedName) { + start = str.length; + length = _substr3.length; + str += _substr3; + } else if (_substr3.length < 10 && str.length + _substr3.length < 40) { + str += _substr3; + } else { + str += "..."; + } + } + + str += "}"; + } + } + + if (expandedName === undefined) { + return str; + } + + if (start > -1 && length > 0) { + var highlight = " ".repeat(start) + "^".repeat(length); + return "\n " + str + "\n " + highlight; + } + + return "\n " + str; +} + +var knownServerReferences = new WeakMap(); // Serializable values +// Thenable +// function serializeByValueID(id: number): string { +// return '$' + id.toString(16); +// } + +function serializePromiseID(id) { + return "$@" + id.toString(16); +} + +function serializeServerReferenceID(id) { + return "$F" + id.toString(16); +} + +function serializeSymbolReference(name) { + return "$S" + name; +} + +function serializeFormDataReference(id) { + // Why K? F is "Function". D is "Date". What else? + return "$K" + id.toString(16); +} + +function serializeNumber(number) { + if (Number.isFinite(number)) { + if (number === 0 && 1 / number === -Infinity) { + return "$-0"; + } else { + return number; + } + } else { + if (number === Infinity) { + return "$Infinity"; + } else if (number === -Infinity) { + return "$-Infinity"; + } else { + return "$NaN"; + } + } +} + +function serializeUndefined() { + return "$undefined"; +} + +function serializeDateFromDateJSON(dateJSON) { + // JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString. + // We need only tack on a $D prefix. + return "$D" + dateJSON; +} + +function serializeBigInt(n) { + return "$n" + n.toString(10); +} + +function escapeStringValue(value) { + if (value[0] === "$") { + // We need to escape $ prefixed strings since we use those to encode + // references to IDs and as special symbol values. + return "$" + value; + } else { + return value; + } +} + +function processReply(root, formFieldPrefix, resolve, reject) { + var nextPartId = 1; + var pendingParts = 0; + var formData = null; + + function resolveToJSON(key, value) { + var parent = this; // Make sure that `parent[key]` wasn't JSONified before `value` was passed to us + + { + // $FlowFixMe[incompatible-use] + var originalValue = parent[key]; + + if ( + typeof originalValue === "object" && + originalValue !== value && + !(originalValue instanceof Date) + ) { + if (objectName(originalValue) !== "Object") { + error( + "Only plain objects can be passed to Server Functions from the Client. " + + "%s objects are not supported.%s", + objectName(originalValue), + describeObjectForErrorMessage(parent, key) + ); + } else { + error( + "Only plain objects can be passed to Server Functions from the Client. " + + "Objects with toJSON methods are not supported. Convert it manually " + + "to a simple value before passing it to props.%s", + describeObjectForErrorMessage(parent, key) + ); + } + } + } + + if (value === null) { + return null; + } + + if (typeof value === "object") { + // $FlowFixMe[method-unbinding] + if (typeof value.then === "function") { + // We assume that any object with a .then property is a "Thenable" type, + // or a Promise type. Either of which can be represented by a Promise. + if (formData === null) { + // Upgrade to use FormData to allow us to stream this value. + formData = new FormData(); + } + + pendingParts++; + var promiseId = nextPartId++; + var thenable = value; + thenable.then( + function (partValue) { + var partJSON = JSON.stringify(partValue, resolveToJSON); // $FlowFixMe[incompatible-type] We know it's not null because we assigned it above. + + var data = formData; // eslint-disable-next-line react-internal/safe-string-coercion + + data.append(formFieldPrefix + promiseId, partJSON); + pendingParts--; + + if (pendingParts === 0) { + resolve(data); + } + }, + function (reason) { + // In the future we could consider serializing this as an error + // that throws on the server instead. + reject(reason); + } + ); + return serializePromiseID(promiseId); + } // TODO: Should we the Object.prototype.toString.call() to test for cross-realm objects? + + if (value instanceof FormData) { + if (formData === null) { + // Upgrade to use FormData to allow us to use rich objects as its values. + formData = new FormData(); + } + + var data = formData; + var refId = nextPartId++; // Copy all the form fields with a prefix for this reference. + // These must come first in the form order because we assume that all the + // fields are available before this is referenced. + + var prefix = formFieldPrefix + refId + "_"; // $FlowFixMe[prop-missing]: FormData has forEach. + + value.forEach(function (originalValue, originalKey) { + data.append(prefix + originalKey, originalValue); + }); + return serializeFormDataReference(refId); + } + + if (!isArray(value)) { + var iteratorFn = getIteratorFn(value); + + if (iteratorFn) { + return Array.from(value); + } + } + + { + if (value !== null && !isArray(value)) { + // Verify that this is a simple plain object. + if (value.$$typeof === REACT_ELEMENT_TYPE) { + error( + "React Element cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (value.$$typeof === REACT_LAZY_TYPE) { + error( + "React Lazy cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (value.$$typeof === REACT_PROVIDER_TYPE) { + error( + "React Context Providers cannot be passed to Server Functions from the Client.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (objectName(value) !== "Object") { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "%s objects are not supported.%s", + objectName(value), + describeObjectForErrorMessage(parent, key) + ); + } else if (!isSimpleObject(value)) { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "Classes or other objects with methods are not supported.%s", + describeObjectForErrorMessage(parent, key) + ); + } else if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(value); + + if (symbols.length > 0) { + error( + "Only plain objects can be passed to Client Components from Server Components. " + + "Objects with symbol properties like %s are not supported.%s", + symbols[0].description, + describeObjectForErrorMessage(parent, key) + ); + } + } + } + } // $FlowFixMe[incompatible-return] + + return value; + } + + if (typeof value === "string") { + // TODO: Maybe too clever. If we support URL there's no similar trick. + if (value[value.length - 1] === "Z") { + // Possibly a Date, whose toJSON automatically calls toISOString + // $FlowFixMe[incompatible-use] + var _originalValue = parent[key]; // $FlowFixMe[method-unbinding] + + if (_originalValue instanceof Date) { + return serializeDateFromDateJSON(value); + } + } + + return escapeStringValue(value); + } + + if (typeof value === "boolean") { + return value; + } + + if (typeof value === "number") { + return serializeNumber(value); + } + + if (typeof value === "undefined") { + return serializeUndefined(); + } + + if (typeof value === "function") { + var metaData = knownServerReferences.get(value); + + if (metaData !== undefined) { + var metaDataJSON = JSON.stringify(metaData, resolveToJSON); + + if (formData === null) { + // Upgrade to use FormData to allow us to stream this value. + formData = new FormData(); + } // The reference to this function came from the same client so we can pass it back. + + var _refId = nextPartId++; // eslint-disable-next-line react-internal/safe-string-coercion + + formData.set(formFieldPrefix + _refId, metaDataJSON); + return serializeServerReferenceID(_refId); + } + + throw new Error( + "Client Functions cannot be passed directly to Server Functions. " + + "Only Functions passed from the Server can be passed back again." + ); + } + + if (typeof value === "symbol") { + // $FlowFixMe[incompatible-type] `description` might be undefined + var name = value.description; + + if (Symbol.for(name) !== value) { + throw new Error( + "Only global symbols received from Symbol.for(...) can be passed to Server Functions. " + + ("The symbol Symbol.for(" + // $FlowFixMe[incompatible-type] `description` might be undefined + value.description + + ") cannot be found among global symbols.") + ); + } + + return serializeSymbolReference(name); + } + + if (typeof value === "bigint") { + return serializeBigInt(value); + } + + throw new Error( + "Type " + + typeof value + + " is not supported as an argument to a Server Function." + ); + } // $FlowFixMe[incompatible-type] it's not going to be undefined because we'll encode it. + + var json = JSON.stringify(root, resolveToJSON); + + if (formData === null) { + // If it's a simple data structure, we just use plain JSON. + resolve(json); + } else { + // Otherwise, we use FormData to let us stream in the result. + formData.set(formFieldPrefix + "0", json); + + if (pendingParts === 0) { + // $FlowFixMe[incompatible-call] this has already been refined. + resolve(formData); + } + } +} +var boundCache = new WeakMap(); + +function encodeFormData(reference) { + var resolve, reject; // We need to have a handle on the thenable so that we can synchronously set + // its status from processReply, when it can complete synchronously. + + var thenable = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + processReply( + reference, + "", + function (body) { + if (typeof body === "string") { + var data = new FormData(); + data.append("0", body); + body = data; + } + + var fulfilled = thenable; + fulfilled.status = "fulfilled"; + fulfilled.value = body; + resolve(body); + }, + function (e) { + var rejected = thenable; + rejected.status = "rejected"; + rejected.reason = e; + reject(e); + } + ); + return thenable; +} + +function encodeFormAction(identifierPrefix) { + var reference = knownServerReferences.get(this); + + if (!reference) { + throw new Error( + "Tried to encode a Server Action from a different instance than the encoder is from. " + + "This is a bug in React." + ); + } + + var data = null; + var name; + var boundPromise = reference.bound; + + if (boundPromise !== null) { + var thenable = boundCache.get(reference); + + if (!thenable) { + thenable = encodeFormData(reference); + boundCache.set(reference, thenable); + } + + if (thenable.status === "rejected") { + throw thenable.reason; + } else if (thenable.status !== "fulfilled") { + throw thenable; + } + + var encodedFormData = thenable.value; // This is hacky but we need the identifier prefix to be added to + // all fields but the suspense cache would break since we might get + // a new identifier each time. So we just append it at the end instead. + + var prefixedData = new FormData(); // $FlowFixMe[prop-missing] + + encodedFormData.forEach(function (value, key) { + prefixedData.append("$ACTION_" + identifierPrefix + ":" + key, value); + }); + data = prefixedData; // We encode the name of the prefix containing the data. + + name = "$ACTION_REF_" + identifierPrefix; + } else { + // This is the simple case so we can just encode the ID. + name = "$ACTION_ID_" + reference.id; + } + + return { + name: name, + method: "POST", + encType: "multipart/form-data", + data: data + }; +} var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; @@ -536,8 +1313,10 @@ function createServerReferenceProxy(response, metaData) { return Promise.resolve(p).then(function (bound) { return callServer(metaData.id, bound.concat(args)); }); - }; + }; // Expose encoder for use by SSR. + // TODO: Only expose this in SSR builds and not the browser client. + proxy.$$FORM_ACTION = encodeFormAction; knownServerReferences.set(proxy, metaData); return proxy; } diff --git a/compiled/facebook-www/ReactFlightDOMRelayClient-prod.classic.js b/compiled/facebook-www/ReactFlightDOMRelayClient-prod.classic.js index 14b683c01ff64..e83f145a5d7f1 100644 --- a/compiled/facebook-www/ReactFlightDOMRelayClient-prod.classic.js +++ b/compiled/facebook-www/ReactFlightDOMRelayClient-prod.classic.js @@ -72,14 +72,162 @@ function parseModelRecursively(response, parentObj, key, value) { var dummy = {}, ReactDOMCurrentDispatcher = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Dispatcher, - knownServerReferences = new WeakMap(), REACT_ELEMENT_TYPE = Symbol.for("react.element"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( "react.default_value" ), - ContextRegistry = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ContextRegistry; + MAYBE_ITERATOR_SYMBOL = Symbol.iterator; +function getIteratorFn(maybeIterable) { + if (null === maybeIterable || "object" !== typeof maybeIterable) return null; + maybeIterable = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable["@@iterator"]; + return "function" === typeof maybeIterable ? maybeIterable : null; +} +var knownServerReferences = new WeakMap(); +function serializeNumber(number) { + return Number.isFinite(number) + ? 0 === number && -Infinity === 1 / number + ? "$-0" + : number + : Infinity === number + ? "$Infinity" + : -Infinity === number + ? "$-Infinity" + : "$NaN"; +} +function processReply(root, formFieldPrefix, resolve, reject) { + function resolveToJSON(key, value) { + if (null === value) return null; + if ("object" === typeof value) { + if ("function" === typeof value.then) { + null === formData && (formData = new FormData()); + pendingParts++; + var promiseId = nextPartId++; + value.then( + function (partValue) { + partValue = JSON.stringify(partValue, resolveToJSON); + var data = formData; + data.append(formFieldPrefix + promiseId, partValue); + pendingParts--; + 0 === pendingParts && resolve(data); + }, + function (reason) { + reject(reason); + } + ); + return "$@" + promiseId.toString(16); + } + if (value instanceof FormData) { + null === formData && (formData = new FormData()); + var data = formData; + key = nextPartId++; + var prefix = formFieldPrefix + key + "_"; + value.forEach(function (originalValue, originalKey) { + data.append(prefix + originalKey, originalValue); + }); + return "$K" + key.toString(16); + } + return !isArrayImpl(value) && getIteratorFn(value) + ? Array.from(value) + : value; + } + if ("string" === typeof value) { + if ("Z" === value[value.length - 1] && this[key] instanceof Date) + return "$D" + value; + value = "$" === value[0] ? "$" + value : value; + return value; + } + if ("boolean" === typeof value) return value; + if ("number" === typeof value) return serializeNumber(value); + if ("undefined" === typeof value) return "$undefined"; + if ("function" === typeof value) { + value = knownServerReferences.get(value); + if (void 0 !== value) + return ( + (value = JSON.stringify(value, resolveToJSON)), + null === formData && (formData = new FormData()), + (key = nextPartId++), + formData.set(formFieldPrefix + key, value), + "$F" + key.toString(16) + ); + throw Error(formatProdErrorMessage(469)); + } + if ("symbol" === typeof value) { + key = value.description; + if (Symbol.for(key) !== value) + throw Error(formatProdErrorMessage(470, value.description)); + return "$S" + key; + } + if ("bigint" === typeof value) return "$n" + value.toString(10); + throw Error(formatProdErrorMessage(472, typeof value)); + } + var nextPartId = 1, + pendingParts = 0, + formData = null; + root = JSON.stringify(root, resolveToJSON); + null === formData + ? resolve(root) + : (formData.set(formFieldPrefix + "0", root), + 0 === pendingParts && resolve(formData)); +} +var boundCache = new WeakMap(); +function encodeFormData(reference) { + var resolve, + reject, + thenable = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + processReply( + reference, + "", + function (body) { + if ("string" === typeof body) { + var data = new FormData(); + data.append("0", body); + body = data; + } + thenable.status = "fulfilled"; + thenable.value = body; + resolve(body); + }, + function (e) { + thenable.status = "rejected"; + thenable.reason = e; + reject(e); + } + ); + return thenable; +} +function encodeFormAction(identifierPrefix) { + var reference = knownServerReferences.get(this); + if (!reference) throw Error(formatProdErrorMessage(481)); + var data = null; + if (null !== reference.bound) { + data = boundCache.get(reference); + data || + ((data = encodeFormData(reference)), boundCache.set(reference, data)); + if ("rejected" === data.status) throw data.reason; + if ("fulfilled" !== data.status) throw data; + reference = data.value; + var prefixedData = new FormData(); + reference.forEach(function (value, key) { + prefixedData.append("$ACTION_" + identifierPrefix + ":" + key, value); + }); + data = prefixedData; + reference = "$ACTION_REF_" + identifierPrefix; + } else reference = "$ACTION_ID_" + reference.id; + return { + name: reference, + method: "POST", + encType: "multipart/form-data", + data: data + }; +} +var ContextRegistry = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ContextRegistry; function Chunk(status, value, reason, response) { this.status = status; this.value = value; @@ -243,6 +391,7 @@ function createServerReferenceProxy(response, metaData) { : callServer(metaData.id, args); } var callServer = response._callServer; + proxy.$$FORM_ACTION = encodeFormAction; knownServerReferences.set(proxy, metaData); return proxy; } diff --git a/compiled/facebook-www/ReactFlightDOMRelayClient-prod.modern.js b/compiled/facebook-www/ReactFlightDOMRelayClient-prod.modern.js index 14b683c01ff64..e83f145a5d7f1 100644 --- a/compiled/facebook-www/ReactFlightDOMRelayClient-prod.modern.js +++ b/compiled/facebook-www/ReactFlightDOMRelayClient-prod.modern.js @@ -72,14 +72,162 @@ function parseModelRecursively(response, parentObj, key, value) { var dummy = {}, ReactDOMCurrentDispatcher = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Dispatcher, - knownServerReferences = new WeakMap(), REACT_ELEMENT_TYPE = Symbol.for("react.element"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( "react.default_value" ), - ContextRegistry = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ContextRegistry; + MAYBE_ITERATOR_SYMBOL = Symbol.iterator; +function getIteratorFn(maybeIterable) { + if (null === maybeIterable || "object" !== typeof maybeIterable) return null; + maybeIterable = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable["@@iterator"]; + return "function" === typeof maybeIterable ? maybeIterable : null; +} +var knownServerReferences = new WeakMap(); +function serializeNumber(number) { + return Number.isFinite(number) + ? 0 === number && -Infinity === 1 / number + ? "$-0" + : number + : Infinity === number + ? "$Infinity" + : -Infinity === number + ? "$-Infinity" + : "$NaN"; +} +function processReply(root, formFieldPrefix, resolve, reject) { + function resolveToJSON(key, value) { + if (null === value) return null; + if ("object" === typeof value) { + if ("function" === typeof value.then) { + null === formData && (formData = new FormData()); + pendingParts++; + var promiseId = nextPartId++; + value.then( + function (partValue) { + partValue = JSON.stringify(partValue, resolveToJSON); + var data = formData; + data.append(formFieldPrefix + promiseId, partValue); + pendingParts--; + 0 === pendingParts && resolve(data); + }, + function (reason) { + reject(reason); + } + ); + return "$@" + promiseId.toString(16); + } + if (value instanceof FormData) { + null === formData && (formData = new FormData()); + var data = formData; + key = nextPartId++; + var prefix = formFieldPrefix + key + "_"; + value.forEach(function (originalValue, originalKey) { + data.append(prefix + originalKey, originalValue); + }); + return "$K" + key.toString(16); + } + return !isArrayImpl(value) && getIteratorFn(value) + ? Array.from(value) + : value; + } + if ("string" === typeof value) { + if ("Z" === value[value.length - 1] && this[key] instanceof Date) + return "$D" + value; + value = "$" === value[0] ? "$" + value : value; + return value; + } + if ("boolean" === typeof value) return value; + if ("number" === typeof value) return serializeNumber(value); + if ("undefined" === typeof value) return "$undefined"; + if ("function" === typeof value) { + value = knownServerReferences.get(value); + if (void 0 !== value) + return ( + (value = JSON.stringify(value, resolveToJSON)), + null === formData && (formData = new FormData()), + (key = nextPartId++), + formData.set(formFieldPrefix + key, value), + "$F" + key.toString(16) + ); + throw Error(formatProdErrorMessage(469)); + } + if ("symbol" === typeof value) { + key = value.description; + if (Symbol.for(key) !== value) + throw Error(formatProdErrorMessage(470, value.description)); + return "$S" + key; + } + if ("bigint" === typeof value) return "$n" + value.toString(10); + throw Error(formatProdErrorMessage(472, typeof value)); + } + var nextPartId = 1, + pendingParts = 0, + formData = null; + root = JSON.stringify(root, resolveToJSON); + null === formData + ? resolve(root) + : (formData.set(formFieldPrefix + "0", root), + 0 === pendingParts && resolve(formData)); +} +var boundCache = new WeakMap(); +function encodeFormData(reference) { + var resolve, + reject, + thenable = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + processReply( + reference, + "", + function (body) { + if ("string" === typeof body) { + var data = new FormData(); + data.append("0", body); + body = data; + } + thenable.status = "fulfilled"; + thenable.value = body; + resolve(body); + }, + function (e) { + thenable.status = "rejected"; + thenable.reason = e; + reject(e); + } + ); + return thenable; +} +function encodeFormAction(identifierPrefix) { + var reference = knownServerReferences.get(this); + if (!reference) throw Error(formatProdErrorMessage(481)); + var data = null; + if (null !== reference.bound) { + data = boundCache.get(reference); + data || + ((data = encodeFormData(reference)), boundCache.set(reference, data)); + if ("rejected" === data.status) throw data.reason; + if ("fulfilled" !== data.status) throw data; + reference = data.value; + var prefixedData = new FormData(); + reference.forEach(function (value, key) { + prefixedData.append("$ACTION_" + identifierPrefix + ":" + key, value); + }); + data = prefixedData; + reference = "$ACTION_REF_" + identifierPrefix; + } else reference = "$ACTION_ID_" + reference.id; + return { + name: reference, + method: "POST", + encType: "multipart/form-data", + data: data + }; +} +var ContextRegistry = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ContextRegistry; function Chunk(status, value, reason, response) { this.status = status; this.value = value; @@ -243,6 +391,7 @@ function createServerReferenceProxy(response, metaData) { : callServer(metaData.id, args); } var callServer = response._callServer; + proxy.$$FORM_ACTION = encodeFormAction; knownServerReferences.set(proxy, metaData); return proxy; }