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.
Use the Blob constructor + append with filename instead of File
constructor. Node.js doesn't expose a global File constructor but does
support it in this form.
Queue fields until we get the 'end' event from the previous file. We
rely on previous files being available by the time a field is resolved.
However, since the 'end' event in Readable is fired after two
micro-tasks, these are not resolved in order.
I use a queue of the fields while we're still waiting on files to
finish. This still doesn't resolve files and fields in order relative to
each other but that doesn't matter for our usage.
Builds on top of https://github.com/facebook/react/pull/26661
This lets you pass FormData objects through the Flight Reply
serialization. It does that by prefixing each entry with the ID of the
reference and then the decoding side creates a new FormData object
containing only those fields (without the prefix).
Ideally this should be more generic. E.g. you should be able to pass
Blobs, Streams and Typed Arrays by reference inside plain objects too.
You should also be able to send Blobs and FormData in the regular Flight
serialization too so that they can go both directions. They should be
symmetrical. We'll get around to adding more of those features in the
Flight protocol as we go.
---------
Co-authored-by: Sophie Alpert <git@sophiebits.com>
This adds `encodeReply` to the Flight Client and `decodeReply` to the
Flight Server.
Basically, it's a reverse Flight. It serializes values passed from the
client to the server. I call this a "Reply". The tradeoffs and
implementation details are a bit different so it requires its own
implementation but is basically a clone of the Flight Server/Client but
in reverse. Either through callServer or ServerContext.
The goal of this project is to provide the equivalent serialization as
passing props through RSC to client. Except React Elements and
Components and such. So that you can pass a value to the client and back
and it should have the same serialization constraints so when we add
features in one direction we should mostly add it in the other.
Browser support for streaming request bodies are currently very limited
in that only Chrome supports it. So this doesn't produce a
ReadableStream. Instead `encodeReply` produces either a JSON string or
FormData. It uses a JSON string if it's a simple enough payload. For
advanced features it uses FormData. This will also let the browser
stream things like File objects (even though they're not yet supported
since it follows the same rules as the other Flight).
On the server side, you can either consume this by blocking on
generating a FormData object or you can stream in the
`multipart/form-data`. Even if the client isn't streaming data, the
network does. On Node.js busboy seems to be the canonical library for
this, so I exposed a `decodeReplyFromBusboy` in the Node build. However,
if there's ever a web-standard way to stream form data, or if a library
wins in that space we can support it. We can also just build a multipart
parser that takes a ReadableStream built-in.
On the server, server references passed as arguments are loaded from
Node or Webpack just like the client or SSR does. This means that you
can create higher order functions on the client or server. This can be
tokenized when done from a server components but this is a security
implication as it might be tempting to think that these are not fungible
but you can swap one function for another on the client. So you have to
basically treat an incoming argument as insecure, even if it's a
function.
I'm not too happy with the naming parity:
Encode `server.renderToReadableStream` Decode: `client.createFromFetch`
Decode `client.encodeReply` Decode: `server.decodeReply`
This is mainly an implementation details of frameworks but it's annoying
nonetheless. This comes from that `renderToReadableStream` does do some
"rendering" by unwrapping server components etc. The `create` part comes
from the parity with Fizz/Fiber where you `render` on the server and
`create` a root on the client.
Open to bike-shedding this some more.
---------
Co-authored-by: Josh Story <josh.c.story@gmail.com>
This is just moving some stuff around and renaming things.
This tuple is opaque to the Flight implementation and we should probably
encode it separately as a single string instead of a model object.
The term "Metadata" isn't the same as when used for ClientReferences so
it's not really the right term anyway.
I also made it optional since a bound function with no arguments bound
is technically different than a raw instance of that function (it's a
clone).
I also renamed the type ReactModel to ReactClientValue. This is the
generic serializable type for something that can pass through the
serializable boundary from server to client. There will be another one
for client to server.
I also filled in missing classes and ensure the serializable sub-types
are explicit. E.g. Array and Thenable.
This setting is an incremental path to the next Flow version enforcing
type annotations on most functions (except some inline callbacks).
Used
```
node_modules/.bin/flow codemod annotate-functions-and-classes --write .
```
to add a majority of the types with some hand cleanup when for large
inferred objects that should just be `Fiber` or weird constructs
including `any`.
Suppressed the remaining issues.
Builds on #25918
* Facebook -> Meta in copyright
rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g'
* Manual tweaks
This update range includes:
- `types_first` ([blog](https://flow.org/en/docs/lang/types-first/), all exports need annotated types) is default. I disabled this for now to make that change incremental.
- Generics that escape the scope they are defined in are an error. I fixed some with explicit type annotations and some are suppressed that I didn't easily figure out.
Add aborting to the Flight Server. This encodes the reason as an "error"
row that gets thrown client side. These are still exposed in prod which
is a follow up we'll still have to do to encode them as digests instead.
The error is encoded once and then referenced by each row that needs to
be updated.
* Implements useId hook for Flight server.
The approach for ids for Flight is different from Fizz/Client where there is a need for determinancy. Flight rendered elements will not be rendered on the client and as such the ids generated in a request only need to be unique. However since FLight does support refetching subtrees it is possible a client will need to patch up a part of the tree rather than replacing the entire thing so it is not safe to use a simple incrementing counter. To solve for this we allow the caller to specify a prefix. On an initial fetch it is likely this will be empty but on refetches or subtrees we expect to have a client `useId` provide the prefix since it will guaranteed be unique for that subtree and thus for the entire tree. It is also possible that we will automatically provide prefixes based on a client/Fizz useId on refetches
in addition to the core change I also modified the structure of options for renderToReadableStream where `onError`, `context`, and the new `identifierPrefix` are properties of an Options object argument to avoid the clumsiness of a growing list of optional function arguments.
* defend against useId call outside of rendering
* switch to S from F for Server Component ids
* default to empty string identifier prefix
* Add a test demonstrating that there is no warning when double rendering on the client a server component that used useId
* lints and gates
This type isn't exported so it's technically not public.
This object mimics a ReadableStream.
Currently this is safe to destructure and call separately but I'm not sure
that's even guaranteed. It should probably be treated as a class in docs.
* Flight side of server context
* 1 more test
* rm unused function
* flow+prettier
* flow again =)
* duplicate ReactServerContext across packages
* store default value when lazily initializing server context
* .
* better comment
* derp... missing import
* rm optional chaining
* missed feature flag
* React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ??
* add warning if non ServerContext passed into useServerContext
* pass context in as array of arrays
* make importServerContext nott pollute the global context state
* merge main
* remove useServerContext
* dont rely on object getters in ReactServerContext and disallow JSX
* add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry
* gate test case as experimental
* feedback
* remove unions
* Lint
* fix oopsies (tests/lint/mismatching arguments/signatures
* lint again
* replace-fork
* remove extraneous change
* rebase
* 1 more test
* rm unused function
* flow+prettier
* flow again =)
* duplicate ReactServerContext across packages
* store default value when lazily initializing server context
* .
* better comment
* derp... missing import
* rm optional chaining
* missed feature flag
* React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ??
* add warning if non ServerContext passed into useServerContext
* pass context in as array of arrays
* make importServerContext nott pollute the global context state
* merge main
* remove useServerContext
* dont rely on object getters in ReactServerContext and disallow JSX
* add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry
* gate test case as experimental
* feedback
* remove unions
* Lint
* fix oopsies (tests/lint/mismatching arguments/signatures
* lint again
* replace-fork
* remove extraneous change
* rebase
* reinline
* rebase
* add back changes lost due to rebase being hard
* emit chunk for provider
* remove case for React provider type
* update type for SomeChunk
* enable flag with experimental
* add missing types
* fix flow type
* missing type
* t: any
* revert extraneous type change
* better type
* better type
* feedback
* change import to type import
* test?
* test?
* remove react-dom
* remove react-native-renderer from react-server-native-relay/package.json
* gate change in FiberNewContext, getComponentNameFromType, use switch statement in FlightServer
* getComponentNameFromTpe: server context type gated and use displayName if available
* fallthrough
* lint....
* POP
* lint
* Rename pipeToNodeWritable to renderToNodePipe
* Add startWriting API to Flight
We don't really need it in this case because there's way less reason to
delay the stream in Flight.
* Pass the destination to startWriting instead of renderToNode
* Rename startWriting to pipe
This mirrors the ReadableStream API in Node
* Error codes
* Rename to renderToPipeableStream
This mimics the renderToReadableStream API for the browser.
* Pass in Destination lazily in startFlowing instead of createRequest
* Delay fatal errors until we have a destination to forward them to
* Flow can now be inferred by whether there's a destination set
We can drop the destination when we're not flowing since there's nothing to
write to.
Fatal errors now close once flowing starts back up again.
* Defer fatal errors in Flight too
* Remove reentrant check from Fizz/Flight
* Make startFlowing explicit in Flight
This is already an explicit call in Fizz. This moves flowing to be explicit.
That way we can avoid calling it in start() for web streams and therefore
avoid the reentrant call.
* Add regression test
This test doesn't actually error due to the streams polyfill not behaving
like Chrome but rather according to spec.
* Update the Web Streams polyfill
Not that we need this but just in case there are differences that are fixed.
* Add onError option to Flight Server
The callback is called any time an error is generated in a server component.
This allows it to be logged on a server if needed. It'll still be rethrown
on the client so it can be logged there too but in case it never reaches
the client, here's a way to make sure it doesn't get lost.
* Add fatal error handling
* Move files
* Update paths
* Rename import variables
* Rename /server to /writer
This is mainly because "React Server Server" is weird so we need another
dimension.
* Use "react-server" convention to enforce that writer is only loaded in a server