diff --git a/fixtures/flight/src/App.js b/fixtures/flight/src/App.js
index b73162847d..935f77fa96 100644
--- a/fixtures/flight/src/App.js
+++ b/fixtures/flight/src/App.js
@@ -120,10 +120,69 @@ async function ServerComponent({noCache}) {
return await fetchThirdParty(noCache);
}
+let veryDeepObject = [
+ {
+ bar: {
+ baz: {
+ a: {},
+ },
+ },
+ },
+ {
+ bar: {
+ baz: {
+ a: {},
+ },
+ },
+ },
+ {
+ bar: {
+ baz: {
+ a: {},
+ },
+ },
+ },
+ {
+ bar: {
+ baz: {
+ a: {
+ b: {
+ c: {
+ d: {
+ e: {
+ f: {
+ g: {
+ h: {
+ i: {
+ j: {
+ k: {
+ l: {
+ m: {
+ yay: 'You reached the end',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+];
+
export default async function App({prerender, noCache}) {
const res = await fetch('http://localhost:3001/todos');
const todos = await res.json();
+ console.log('Expand me:', veryDeepObject);
+
const dedupedChild = ;
const message = getServerState();
return (
diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js
index bf02c74571..37e7657115 100644
--- a/packages/react-client/src/ReactFlightClient.js
+++ b/packages/react-client/src/ReactFlightClient.js
@@ -1774,6 +1774,40 @@ function applyConstructor(
return undefined;
}
+function defineLazyGetter(
+ response: Response,
+ chunk: SomeChunk,
+ parentObject: Object,
+ key: string,
+): any {
+ // We don't immediately initialize it even if it's resolved.
+ // Instead, we wait for the getter to get accessed.
+ Object.defineProperty(parentObject, key, {
+ get: function () {
+ if (chunk.status === RESOLVED_MODEL) {
+ // If it was now resolved, then we initialize it. This may then discover
+ // a new set of lazy references that are then asked for eagerly in case
+ // we get that deep.
+ initializeModelChunk(chunk);
+ }
+ switch (chunk.status) {
+ case INITIALIZED: {
+ return chunk.value;
+ }
+ case ERRORED:
+ throw chunk.reason;
+ }
+ // Otherwise, we didn't have enough time to load the object before it was
+ // accessed or the connection closed. So we just log that it was omitted.
+ // TODO: We should ideally throw here to indicate a difference.
+ return OMITTED_PROP_ERROR;
+ },
+ enumerable: true,
+ configurable: false,
+ });
+ return null;
+}
+
function extractIterator(response: Response, model: Array): Iterator {
// $FlowFixMe[incompatible-use]: This uses raw Symbols because we're extracting from a native array.
return model[Symbol.iterator]();
@@ -2014,8 +2048,19 @@ function parseModelString(
if (value.length > 2) {
const debugChannel = response._debugChannel;
if (debugChannel) {
- const ref = value.slice(2);
- debugChannel('R:' + ref); // Release this reference immediately
+ const ref = value.slice(2); // We assume this doesn't have a path just id.
+ const id = parseInt(ref, 16);
+ if (!response._chunks.has(id)) {
+ // We haven't seen this id before. Query the server to start sending it.
+ debugChannel('Q:' + ref);
+ }
+ // Start waiting. This now creates a pending chunk if it doesn't already exist.
+ const chunk = getChunk(response, id);
+ if (chunk.status === INITIALIZED) {
+ // We already loaded this before. We can just use the real value.
+ return chunk.value;
+ }
+ return defineLazyGetter(response, chunk, parentObject, key);
}
}
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index e6e172729f..750fe609ed 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -4820,10 +4820,15 @@ function emitConsoleChunk(
const payload = [methodName, stackTrace, owner, env];
// $FlowFixMe[method-unbinding]
payload.push.apply(payload, args);
- let json = serializeDebugModel(request, 500, payload);
+ const objectLimit = request.deferredDebugObjects === null ? 500 : 10;
+ let json = serializeDebugModel(
+ request,
+ objectLimit + stackTrace.length,
+ payload,
+ );
if (json[0] !== '[') {
// This looks like an error. Try a simpler object.
- json = serializeDebugModel(request, 500, [
+ json = serializeDebugModel(request, 10 + stackTrace.length, [
methodName,
stackTrace,
owner,
@@ -5760,6 +5765,8 @@ export function resolveDebugMessage(request: Request, message: string): void {
if (retainedValue !== undefined) {
// If we still have this object, and haven't emitted it before, emit it on the stream.
const counter = {objectLimit: 10};
+ deferredDebugObjects.retained.delete(id);
+ deferredDebugObjects.existing.delete(retainedValue);
emitOutlinedDebugModelChunk(request, id, counter, retainedValue);
enqueueFlush(request);
}