From 665de2ed283205fccecda649ae2d66f62983f15f Mon Sep 17 00:00:00 2001 From: Joseph Savona <6425824+josephsavona@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:14:09 -0700 Subject: [PATCH 1/3] [compiler] Improve name hints for outlined functions (#34434) The previous PR added name hints for anonymous functions, but didn't handle the case of outlined functions. Here we do some cleanup around function `id` and name hints: * Make `HIRFunction.id` a ValidatedIdentifierName, which involved some cleanup of the validation helpers * Add `HIRFunction.nameHint: string` as a place to store the generated name hints which are not valid identifiers * Update Codegen to always use the `id` as the actual function name, and only use nameHint as part of generating the object+property wrapper for debug purposes. This ensures we don't conflate synthesized hints with real function names. Then, we also update OutlineFunctions to use the function name _or_ the nameHint as the input to generating a unique identifier. This isn't quite as nice as the object form since we lose our formatting, but it's a simple step that gives more context to the developer than `_temp` does. Switching to output the object+property lookup form for outlined functions is a bit more involved, let's do that in a follow-up. --- .../src/Entrypoint/Pipeline.ts | 18 ++--- .../src/HIR/BuildHIR.ts | 23 ++++-- .../src/HIR/HIR.ts | 79 +++++++++++-------- .../src/HIR/PrintHIR.ts | 3 + .../src/Optimization/LowerContextAccess.ts | 2 + .../src/Optimization/OutlineFunctions.ts | 4 +- .../src/Optimization/OutlineJsx.ts | 1 + .../ReactiveScopes/BuildReactiveFunction.ts | 1 + .../ReactiveScopes/CodegenReactiveFunction.ts | 17 ++-- .../src/Transform/NameAnonymousFunctions.ts | 3 +- ...name-anonymous-functions-outline.expect.md | 52 ++++++++++++ .../name-anonymous-functions-outline.js | 14 ++++ 12 files changed, 154 insertions(+), 63 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 7f8640122b942..e0b0536f28cc6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -325,6 +325,15 @@ function runWithEnvironment( outlineJSX(hir); } + if (env.config.enableNameAnonymousFunctions) { + nameAnonymousFunctions(hir); + log({ + kind: 'hir', + name: 'NameAnonymousFunctions', + value: hir, + }); + } + if (env.config.enableFunctionOutlining) { outlineFunctions(hir, fbtOperands); log({kind: 'hir', name: 'OutlineFunctions', value: hir}); @@ -415,15 +424,6 @@ function runWithEnvironment( }); } - if (env.config.enableNameAnonymousFunctions) { - nameAnonymousFunctions(hir); - log({ - kind: 'hir', - name: 'NameAnonymougFunctions', - value: hir, - }); - } - const reactiveFunction = buildReactiveFunction(hir); log({ kind: 'reactive', diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 4d0461fe105b0..702eaf0f692a2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -47,6 +47,7 @@ import { makePropertyLiteral, makeType, promoteTemporary, + validateIdentifierName, } from './HIR'; import HIRBuilder, {Bindings, createTemporaryPlace} from './HIRBuilder'; import {BuiltInArrayId} from './ObjectShape'; @@ -213,6 +214,16 @@ export function lower( ); } + let validatedId: HIRFunction['id'] = null; + if (id != null) { + const idResult = validateIdentifierName(id); + if (idResult.isErr()) { + builder.errors.merge(idResult.unwrapErr()); + } else { + validatedId = idResult.unwrap().value; + } + } + if (builder.errors.hasAnyErrors()) { return Err(builder.errors); } @@ -234,7 +245,8 @@ export function lower( ); return Ok({ - id, + id: validatedId, + nameHint: null, params, fnType: bindings == null ? env.fnType : 'Other', returnTypeAnnotation: null, // TODO: extract the actual return type node if present @@ -3563,19 +3575,14 @@ function lowerFunctionToValue( ): InstructionValue { const exprNode = expr.node; const exprLoc = exprNode.loc ?? GeneratedSource; - let name: string | null = null; - if (expr.isFunctionExpression()) { - name = expr.get('id')?.node?.name ?? null; - } else if (expr.isFunctionDeclaration()) { - name = expr.get('id')?.node?.name ?? null; - } const loweredFunc = lowerFunction(builder, expr); if (!loweredFunc) { return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc}; } return { kind: 'FunctionExpression', - name, + name: loweredFunc.func.id, + nameHint: null, type: expr.node.type, loc: exprLoc, loweredFunc, diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index fa502f821d0b7..4d2d4ed80d69c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -7,7 +7,11 @@ import {BindingKind} from '@babel/traverse'; import * as t from '@babel/types'; -import {CompilerError} from '../CompilerError'; +import { + CompilerDiagnostic, + CompilerError, + ErrorCategory, +} from '../CompilerError'; import {assertExhaustive} from '../Utils/utils'; import {Environment, ReactFunctionType} from './Environment'; import type {HookKind} from './ObjectShape'; @@ -54,7 +58,8 @@ export type SourceLocation = t.SourceLocation | typeof GeneratedSource; */ export type ReactiveFunction = { loc: SourceLocation; - id: string | null; + id: ValidIdentifierName | null; + nameHint: string | null; params: Array; generator: boolean; async: boolean; @@ -276,7 +281,8 @@ export type ReactiveTryTerminal = { // A function lowered to HIR form, ie where its body is lowered to an HIR control-flow graph export type HIRFunction = { loc: SourceLocation; - id: string | null; + id: ValidIdentifierName | null; + nameHint: string | null; fnType: ReactFunctionType; env: Environment; params: Array; @@ -1124,7 +1130,8 @@ export type JsxAttribute = export type FunctionExpression = { kind: 'FunctionExpression'; - name: string | null; + name: ValidIdentifierName | null; + nameHint: string | null; loweredFunc: LoweredFunction; type: | 'ArrowFunctionExpression' @@ -1301,11 +1308,41 @@ export function forkTemporaryIdentifier( export function validateIdentifierName( name: string, -): Result { - if (isReservedWord(name) || !t.isValidIdentifier(name)) { - return Err(null); +): Result { + if (isReservedWord(name)) { + const error = new CompilerError(); + error.pushDiagnostic( + CompilerDiagnostic.create({ + category: ErrorCategory.Syntax, + reason: 'Expected a non-reserved identifier name', + description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`, + suggestions: null, + }).withDetails({ + kind: 'error', + loc: GeneratedSource, + message: 'reserved word', + }), + ); + return Err(error); + } else if (!t.isValidIdentifier(name)) { + const error = new CompilerError(); + error.pushDiagnostic( + CompilerDiagnostic.create({ + category: ErrorCategory.Syntax, + reason: `Expected a valid identifier name`, + description: `\`${name}\` is not a valid JavaScript identifier`, + suggestions: null, + }).withDetails({ + kind: 'error', + loc: GeneratedSource, + message: 'reserved word', + }), + ); } - return Ok(makeIdentifierName(name).value); + return Ok({ + kind: 'named', + value: name as ValidIdentifierName, + }); } /** @@ -1314,31 +1351,7 @@ export function validateIdentifierName( * original source code. */ export function makeIdentifierName(name: string): ValidatedIdentifier { - if (isReservedWord(name)) { - CompilerError.throwInvalidJS({ - reason: 'Expected a non-reserved identifier name', - loc: GeneratedSource, - description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`, - suggestions: null, - }); - } else { - CompilerError.invariant(t.isValidIdentifier(name), { - reason: `Expected a valid identifier name`, - description: `\`${name}\` is not a valid JavaScript identifier`, - details: [ - { - kind: 'error', - loc: GeneratedSource, - message: null, - }, - ], - suggestions: null, - }); - } - return { - kind: 'named', - value: name as ValidIdentifierName, - }; + return validateIdentifierName(name).unwrap(); } /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts index 197e486e8af3b..71fb4c43b33e8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -56,6 +56,9 @@ export function printFunction(fn: HIRFunction): string { } else { definition += '<>'; } + if (fn.nameHint != null) { + definition += ` ${fn.nameHint}`; + } if (fn.params.length !== 0) { definition += '(' + diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index 62845934c16f4..50f00427205bb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -249,6 +249,7 @@ function emitSelectorFn(env: Environment, keys: Array): Instruction { const fn: HIRFunction = { loc: GeneratedSource, id: null, + nameHint: null, fnType: 'Other', env, params: [obj], @@ -275,6 +276,7 @@ function emitSelectorFn(env: Environment, keys: Array): Instruction { value: { kind: 'FunctionExpression', name: null, + nameHint: null, loweredFunc: { func: fn, }, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts index 0e6d1fd59201f..6ab0a811ff5d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts @@ -31,7 +31,9 @@ export function outlineFunctions( ) { const loweredFunc = value.loweredFunc.func; - const id = fn.env.generateGloballyUniqueIdentifierName(loweredFunc.id); + const id = fn.env.generateGloballyUniqueIdentifierName( + loweredFunc.id ?? loweredFunc.nameHint, + ); loweredFunc.id = id.value; fn.env.outlineFunction(loweredFunc, null); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts index 6232456e620c3..5c98997953d0e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts @@ -364,6 +364,7 @@ function emitOutlinedFn( const fn: HIRFunction = { loc: GeneratedSource, id: null, + nameHint: null, fnType: 'Other', env, params: [propsObj], diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts index b400bb349876a..2e8584050eb7a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts @@ -44,6 +44,7 @@ export function buildReactiveFunction(fn: HIRFunction): ReactiveFunction { return { loc: fn.loc, id: fn.id, + nameHint: fn.nameHint, params: fn.params, generator: fn.generator, async: fn.async, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 28d8afd84b4fe..c497253a22fb5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -43,7 +43,6 @@ import { ValidIdentifierName, getHookKind, makeIdentifierName, - validateIdentifierName, } from '../HIR/HIR'; import {printIdentifier, printInstruction, printPlace} from '../HIR/PrintHIR'; import {eachPatternOperand} from '../HIR/visitors'; @@ -62,6 +61,7 @@ export const EARLY_RETURN_SENTINEL = 'react.early_return_sentinel'; export type CodegenFunction = { type: 'CodegenFunction'; id: t.Identifier | null; + nameHint: string | null; params: t.FunctionDeclaration['params']; body: t.BlockStatement; generator: boolean; @@ -384,6 +384,7 @@ function codegenReactiveFunction( type: 'CodegenFunction', loc: fn.loc, id: fn.id !== null ? t.identifier(fn.id) : null, + nameHint: fn.nameHint, params, body, generator: fn.generator, @@ -2328,10 +2329,6 @@ function codegenInstructionValue( reactiveFunction, ).unwrap(); - const validatedName = - instrValue.name != null - ? validateIdentifierName(instrValue.name) - : Err(null); if (instrValue.type === 'ArrowFunctionExpression') { let body: t.BlockStatement | t.Expression = fn.body; if (body.body.length === 1 && loweredFunc.directives.length == 0) { @@ -2343,9 +2340,7 @@ function codegenInstructionValue( value = t.arrowFunctionExpression(fn.params, body, fn.async); } else { value = t.functionExpression( - validatedName - .map(name => t.identifier(name)) - .unwrapOr(null), + instrValue.name != null ? t.identifier(instrValue.name) : null, fn.params, fn.body, fn.generator, @@ -2354,10 +2349,10 @@ function codegenInstructionValue( } if ( cx.env.config.enableNameAnonymousFunctions && - validatedName.isErr() && - instrValue.name != null + instrValue.name == null && + instrValue.nameHint != null ) { - const name = instrValue.name; + const name = instrValue.nameHint; value = t.memberExpression( t.objectExpression([t.objectProperty(t.stringLiteral(name), value)]), t.stringLiteral(name), diff --git a/compiler/packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions.ts index 5fed3af0f733f..cf6d443b907ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions.ts @@ -26,7 +26,8 @@ export function nameAnonymousFunctions(fn: HIRFunction): void { * nesting depth. */ const name = `${prefix}${node.generatedName}]`; - node.fn.name = name; + node.fn.nameHint = name; + node.fn.loweredFunc.func.nameHint = name; } /** * Whether or not we generated a name for the function at this node, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.expect.md new file mode 100644 index 0000000000000..a1267c6f8295b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.expect.md @@ -0,0 +1,52 @@ + +## Input + +```javascript +// @enableNameAnonymousFunctions +import {Stringify} from 'shared-runtime'; + +function Component(props) { + const onClick = () => { + console.log('hello!'); + }; + return
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enableNameAnonymousFunctions +import { Stringify } from "shared-runtime"; + +function Component(props) { + const $ = _c(1); + const onClick = _ComponentOnClick; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +function _ComponentOnClick() { + console.log("hello!"); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; + +``` + +### Eval output +(kind: ok)
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.js new file mode 100644 index 0000000000000..0906cb928a8c3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/name-anonymous-functions-outline.js @@ -0,0 +1,14 @@ +// @enableNameAnonymousFunctions +import {Stringify} from 'shared-runtime'; + +function Component(props) { + const onClick = () => { + console.log('hello!'); + }; + return
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], +}; From 969a9790ad58cc27eea63df9e5cf992b66bf1fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 9 Sep 2025 16:46:11 -0400 Subject: [PATCH 2/3] [Flight] Track I/O Entry for the RSC Stream itself (#34425) One thing that can suspend is the downloading of the RSC stream itself. This tracks an I/O entry for each Promise (`SomeChunk`) that represents the request to the RSC stream. As the value we use the `Response` for `createFromFetch` (or the `ReadableStream` for `createFromReadableStream`). The start time is when you called those. Since we're not awaiting the whole stream, each I/O entry represents the part of the stream up until it got unblocked. However, in a production environment with TLS packets and buffering in practice the chunks received by the client isn't exactly at the boundary of each row. It's a bit longer into larger chunks. From testing, it seems like multiples of 16kb or 64kb uncompressed are common. To simulate a production environment we group into roughly 64kb chunks if they happen in rapid sequence. Note that this might be too small to give a good idea because of the throttle many boundaries might be skipped anyway so this might show too many. The React DevTools will see each I/O entry as separate but dedupe if an outer boundary already depends on the same chunk. This deduping makes it so that small boundaries that are blocked on the same chunk, don't get treated as having unique suspenders. If you have a boundary with large content, then that content will likely be in a separate chunk which is not in the parent and then it gets marked as. This is all just an approximation. The goal of this is just to highlight that very large boundaries will very likely suspend even if they don't suspend on any I/O on the server. In practice, these boundaries can float around a lot and it's really any Suspense boundary that might suspend but some are more likely than others which this is meant to highlight. It also just lets you inspect how many bytes needs to be transferred before you can show a particular part of the content, to give you an idea that it's not just I/O on the server that might suspend. If you don't use the debug channel it can be misleading since the data in development mode stream will have a lot more data in it which leads to more chunking. Similarly to "client references" these I/O infos don't have an "env" since it's the client that has the I/O and so those are excluded from flushing in the Server performance tracks. Note that currently the same Response can appear many times in the same Instance of SuspenseNode in DevTools when there are multiple chunks. In a follow up I'll show only the last one per Response at any given level. Note that when a separate debugChannel is used it has its own I/O entry that's on the `_debugInfo` for the debug chunks in that channel. However, if everything works correctly these should never leak into the DevTools UI since they should never be propagated from a debug chunk to the values waited by the runtime. This is easy to break though. --- fixtures/flight/src/App.js | 7 + fixtures/flight/src/LargeContent.js | 1115 +++++++++++++++++ .../react-client/src/ReactFlightClient.js | 375 +++++- .../src/__tests__/ReactFlight-test.js | 40 +- .../react-markup/src/ReactMarkupServer.js | 24 +- .../src/ReactNoopFlightClient.js | 2 +- .../src/client/ReactFlightDOMClientBrowser.js | 17 +- .../src/client/ReactFlightDOMClientNode.js | 2 +- .../src/client/ReactFlightDOMClientBrowser.js | 17 +- .../src/client/ReactFlightDOMClientEdge.js | 15 +- .../src/client/ReactFlightDOMClientNode.js | 2 +- .../src/client/ReactFlightDOMClientBrowser.js | 17 +- .../src/client/ReactFlightDOMClientEdge.js | 15 +- .../src/client/ReactFlightDOMClientNode.js | 2 +- .../src/__tests__/ReactFlightDOMEdge-test.js | 29 +- .../src/client/ReactFlightDOMClientBrowser.js | 17 +- .../src/client/ReactFlightDOMClientEdge.js | 15 +- .../src/client/ReactFlightDOMClientNode.js | 2 +- .../ReactFlightAsyncDebugInfo-test.js | 730 ++++++----- 19 files changed, 2038 insertions(+), 405 deletions(-) create mode 100644 fixtures/flight/src/LargeContent.js diff --git a/fixtures/flight/src/App.js b/fixtures/flight/src/App.js index e6366f4bd0ea4..df2c8922b8424 100644 --- a/fixtures/flight/src/App.js +++ b/fixtures/flight/src/App.js @@ -21,6 +21,8 @@ import {Note} from './cjs/Note.js'; import {GenerateImage} from './GenerateImage.js'; +import LargeContent from './LargeContent.js'; + import {like, greet, increment} from './actions.js'; import {getServerState} from './ServerState.js'; @@ -233,6 +235,11 @@ export default async function App({prerender, noCache}) { {dedupedChild} {Promise.resolve([dedupedChild])} + {prerender ? null : ( // TODO: prerender is broken for large content for some reason. + + + + )} diff --git a/fixtures/flight/src/LargeContent.js b/fixtures/flight/src/LargeContent.js new file mode 100644 index 0000000000000..cacdda3df725e --- /dev/null +++ b/fixtures/flight/src/LargeContent.js @@ -0,0 +1,1115 @@ +import * as React from 'react'; + +export default async function LargeContent() { + return ( +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras auctor + dapibus nunc, at feugiat sem tempor id. Pellentesque euismod a libero in + dignissim. Curabitur consequat urna blandit eros pellentesque sagittis. + Quisque sed odio elit. Vivamus semper, ipsum eget congue ornare, neque + nibh suscipit mauris, in luctus ligula urna nec ligula. Interdum et + malesuada fames ac ante ipsum primis in faucibus. Vivamus in ligula sit + amet ligula gravida accumsan. Suspendisse potenti. Suspendisse vulputate + auctor ligula nec scelerisque. Praesent tempus diam nec porta malesuada. + Cras pellentesque euismod sapien, ut luctus risus egestas id. Nam sed + neque vehicula, tempor lacus non, maximus felis. Curabitur lobortis + efficitur massa, sed volutpat elit vulputate eget. Nullam posuere risus + vel purus bibendum placerat at nec libero. +

+

+ Pellentesque eget laoreet nulla, in dictum ex. Maecenas odio nulla, + laoreet sed dapibus eget, fermentum ut ipsum. Nam nec gravida massa, non + scelerisque augue. Donec vulputate nibh at sem convallis, quis imperdiet + arcu vestibulum. Duis eget nibh sapien. Praesent nulla arcu, mattis et + velit sit amet, viverra facilisis leo. Integer gravida nisi urna, in + fermentum purus sagittis a. Integer a convallis dui. Nunc gravida erat + nec nunc porttitor, eu pharetra nibh varius. Vestibulum ante ipsum + primis in faucibus orci luctus et ultrices posuere cubilia curae; Cras + quis lobortis sapien, et accumsan mi. Sed in interdum libero. Vivamus + congue, est mollis elementum porta, lectus nulla maximus urna, sed + cursus ex est sit amet lorem. Morbi et porta urna, sit amet varius + magna. Maecenas tincidunt lorem nec dolor venenatis elementum. +

+

+ Vivamus dapibus mi urna. Aliquam rutrum accumsan nisi, vitae convallis + risus rhoncus a. Phasellus ultrices rutrum leo. Phasellus eget diam et + turpis porttitor tincidunt eget et massa. Aliquam quis pellentesque + orci, eu auctor urna. Maecenas hendrerit nisi vestibulum ligula blandit, + id vestibulum erat facilisis. Phasellus ac pulvinar nibh. Fusce + suscipit, lectus vel viverra blandit, risus erat congue urna, in cursus + tortor lorem nec eros. Mauris dapibus, justo ac gravida mattis, nunc + massa euismod elit, vel eleifend ipsum nisi vitae massa. Cras id + volutpat magna, in semper quam. +

+

+ Ut id vehicula turpis. Nullam ac iaculis sem, pulvinar elementum odio. + Aliquam venenatis feugiat risus ut lacinia. Pellentesque habitant morbi + tristique senectus et netus et malesuada fames ac turpis egestas. Cras + quis blandit tellus. Ut vitae imperdiet dolor. Duis rutrum a tortor in + porta. Nulla sed euismod leo. Etiam urna nisi, eleifend tristique velit + sit amet, bibendum eleifend turpis. Pellentesque tellus purus, faucibus + quis pellentesque ac, finibus sed mi. Aenean eget lobortis neque. +

+

+ Sed fermentum pulvinar dolor, a euismod felis pulvinar sed. Aliquam et + vestibulum eros. Pellentesque mattis efficitur erat. Quisque scelerisque + sem venenatis eros bibendum porta. Fusce a elit et ante rutrum gravida. + Sed ex orci, venenatis non elementum id, faucibus et diam. Nullam + posuere viverra tempus. Duis faucibus metus urna, sit amet bibendum + lorem fermentum ut. Quisque laoreet ultrices purus. +

+

+ Praesent placerat laoreet augue, eu mollis mi vulputate in. Sed commodo + est at sapien laoreet pulvinar. Interdum et malesuada fames ac ante + ipsum primis in faucibus. Sed libero velit, porttitor vitae lectus sit + amet, accumsan rhoncus mi. Donec tincidunt purus odio. Aliquam ac + molestie nisi, sed hendrerit sem. Nulla facilisi. Sed tempor odio eu + nisl facilisis, vel porttitor augue auctor. Fusce in ex euismod, + dignissim diam quis, lacinia purus. Interdum et malesuada fames ac ante + ipsum primis in faucibus. Integer non nibh odio. Ut ut ligula felis. + Curabitur tempor lacus a risus condimentum, eu accumsan turpis sagittis. + Nulla facilisi. Quisque at congue nunc, at suscipit neque. +

+

+ Integer feugiat ligula quis nunc ultrices, eget sodales metus elementum. + Proin ex justo, mollis a pulvinar ullamcorper, fermentum sit amet + tellus. Fusce id euismod nisi. Cras mattis placerat purus nec venenatis. + Aenean sed dui faucibus, egestas ipsum at, tempus purus. Proin facilisis + tellus quis odio ultrices, a tincidunt elit congue. Nam tempus sapien + vel quam sodales tempus. Ut blandit pellentesque lorem vel imperdiet. + Cras nisi turpis, volutpat a mauris nec, faucibus ultrices tortor. + Praesent a ornare augue. Ut eget neque sed felis imperdiet vestibulum et + ut metus. Maecenas vel dolor ante. Vivamus posuere risus interdum lacus + pulvinar, eget tincidunt velit tempus. Sed viverra erat tellus, nec + iaculis dolor tincidunt et. Sed rhoncus mauris nibh, non lacinia lorem + vehicula nec. +

+

+ Maecenas ultrices malesuada urna quis maximus. Proin vel dictum ipsum. + Duis lacinia turpis vel molestie pharetra. Nullam iaculis porta rhoncus. + Maecenas ullamcorper massa non velit sagittis suscipit. Quisque lobortis + blandit diam, vehicula cursus quam faucibus in. Duis eget mauris vitae + augue convallis interdum. Ut ac iaculis ex, quis maximus justo. Ut sed + fermentum tellus. Pellentesque facilisis turpis nisi, id molestie tortor + aliquam dignissim. Aenean luctus quam at arcu viverra venenatis. Proin + quis pellentesque nisi. Donec iaculis nunc id ornare pharetra. Donec id + ligula mollis, hendrerit ipsum in, vulputate justo. Praesent in + condimentum sem. Pellentesque malesuada velit ullamcorper dui pharetra + placerat. +

+

+ In lacus nibh, finibus sed nulla ut, dictum feugiat magna. Aliquam + semper turpis non placerat scelerisque. Maecenas lobortis, est vitae + aliquam maximus, metus tortor porta dolor, vitae ullamcorper elit enim + vel tellus. Vestibulum volutpat posuere luctus. Nullam eros leo, aliquam + et ullamcorper sed, viverra in erat. Nam mauris metus, imperdiet non + convallis et, viverra et tortor. Nulla quis pretium tellus. In + scelerisque finibus mi convallis euismod. Pellentesque ante tellus, + vestibulum non egestas in, volutpat eu arcu. Donec malesuada risus nisi, + eget consequat massa rhoncus ut. Maecenas eget magna a metus consectetur + interdum. Sed lorem est, eleifend sed mattis in, feugiat ac magna. + Pellentesque iaculis nunc sed metus laoreet, ac feugiat est maximus. + Aenean velit magna, dapibus ut scelerisque ac, sollicitudin sed justo. + Phasellus venenatis metus sit amet dapibus imperdiet. +

+

+ Nullam nec volutpat purus. Duis elementum ex risus, id consectetur + lectus commodo molestie. Duis tristique nisl non convallis tristique. + Nam eget lectus tempor, porta orci ut, fermentum nibh. Morbi egestas + semper tempor. Nunc est lacus, aliquet id pellentesque vel, tincidunt id + eros. Suspendisse vitae nibh vitae ligula faucibus tristique eu eu + lorem. Donec nec turpis consequat, laoreet sapien ut, condimentum orci. + Mauris imperdiet feugiat consectetur. Nam vel ultrices arcu. Donec quis + pharetra augue, in egestas quam. Ut vitae ligula bibendum, suscipit + risus non, malesuada tellus. Praesent quis dui nec odio rutrum tempor + vitae nec augue. +

+

+ Nam tristique erat vitae congue venenatis. Suspendisse sed mattis nisi. + Aliquam erat volutpat. Quisque vitae odio eu sapien hendrerit pretium. + Etiam sollicitudin orci purus, a bibendum erat tincidunt at. Nunc + ultricies ac arcu eget consectetur. Cras augue libero, hendrerit et + placerat in, imperdiet sed lectus. Pellentesque varius enim laoreet est + finibus, vitae vulputate leo venenatis. Nam tincidunt metus eget neque + tristique pretium. +

+

+ Ut ultrices dapibus ornare. Aliquam et est eu nisl mattis fermentum. + Aliquam luctus est sed lobortis aliquam. Donec vel pretium purus. Donec + augue augue, euismod viverra orci a, lacinia maximus risus. Nam sagittis + nibh eget urna feugiat, et venenatis erat facilisis. Donec fermentum non + nisi ut pretium. Aenean sodales fermentum lacus, vitae fermentum elit + efficitur hendrerit. Proin dictum pretium lacus, vestibulum efficitur + orci hendrerit ac. Maecenas non elementum diam, vel venenatis eros. + Donec finibus luctus arcu, porttitor condimentum tortor tincidunt vel. +

+

+ Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus + viverra massa diam, in dignissim nunc dapibus eu. Phasellus tincidunt + fermentum libero vitae eleifend. Nullam vel pellentesque libero, et + sagittis turpis. Quisque euismod quis diam quis ornare. Cras id quam + elementum, suscipit augue id, sodales eros. Vivamus egestas, quam vitae + sagittis pellentesque, justo purus aliquam purus, vel bibendum turpis + nisi quis lectus. Ut ac libero vitae mauris placerat luctus et eget + velit. Nullam semper eros elementum, efficitur lacus eget, consequat + magna. Ut eu tincidunt urna, vel congue sapien. Vestibulum ante ipsum + primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec + odio velit, rutrum sit amet porta et, imperdiet a augue. +

+

+ Nunc nec odio nec ipsum convallis mattis. Nulla ut aliquet quam. Morbi + enim tortor, dignissim sit amet volutpat non, consequat in neque. + Maecenas vitae enim blandit, dictum lorem eget, placerat turpis. + Suspendisse potenti. Integer dapibus efficitur nunc non dignissim. Proin + rhoncus laoreet imperdiet. Aenean nec iaculis est. Praesent placerat + nibh ac augue blandit pulvinar. Suspendisse vitae ipsum eget sapien + aliquet fermentum in a urna. +

+

+ Quisque porta odio eu blandit vehicula. Maecenas sapien libero, sodales + quis quam ut, pharetra finibus libero. Etiam vel neque ipsum. Aliquam + quis dui mi. Vestibulum vitae massa nulla. Ut est nibh, elementum vitae + nisi fermentum, viverra consectetur risus. Phasellus cursus egestas + felis quis interdum. Nam non ipsum et mauris dignissim vehicula nec nec + est. Duis quis venenatis mi. Pellentesque blandit ullamcorper felis, + commodo ultricies ligula faucibus vel. Maecenas scelerisque neque a erat + consequat vehicula. Ut finibus tempus facilisis. In sodales lacus at + magna tincidunt, in maximus neque convallis. In at lorem eu purus + volutpat aliquam at vitae enim. Sed sit amet mauris vitae tortor finibus + scelerisque ut et eros. +

+

+ Integer in fermentum massa, a maximus purus. Donec rhoncus, lectus sit + amet consequat vestibulum, neque ante cursus urna, eleifend varius felis + ante sed enim. In dui mi, euismod ac mollis id, porttitor vel leo. + Pellentesque habitant morbi tristique senectus et netus et malesuada + fames ac turpis egestas. Nunc nisl sapien, convallis bibendum vehicula + non, convallis ut sem. Nullam non molestie massa. Phasellus eu volutpat + mi. Pellentesque porta ex sed turpis commodo, sit amet aliquet ex + sagittis. Vestibulum molestie ante a magna eleifend imperdiet. In + hendrerit nibh sed eleifend facilisis. Mauris malesuada cursus tortor + non fringilla. Sed a nibh sed sem accumsan finibus. Vivamus egestas + mauris porta, ultricies felis at, sodales arcu. Ut eu lacinia eros. + Aliquam consequat laoreet ante, in interdum eros lacinia ac. Nunc luctus + nisl eget nulla vehicula, a fermentum diam porta. +

+

+ Proin vel tellus placerat, lacinia arcu pretium, gravida ligula. + Vestibulum pharetra diam non ultrices finibus. Quisque facilisis + imperdiet lorem, ullamcorper molestie odio laoreet in. Praesent tellus + quam, laoreet et orci quis, consequat ullamcorper magna. Nam lobortis id + ligula ut sodales. Quisque venenatis, lacus non accumsan fermentum, + magna mauris dignissim mi, vel finibus nulla leo id nibh. Integer + condimentum nulla et diam scelerisque mollis. Interdum et malesuada + fames ac ante ipsum primis in faucibus. +

+

+ Fusce gravida ultrices hendrerit. Mauris aliquet, risus in feugiat + dictum, sem ligula mattis odio, consequat consectetur mi tellus non + nulla. Duis quis rhoncus ex, at vehicula urna. Quisque vitae dolor + tincidunt, egestas mauris id, tristique mauris. Mauris hendrerit iaculis + urna. Mauris molestie dictum massa, non porttitor arcu commodo id. + Praesent enim erat, malesuada dictum orci id, gravida consequat metus. + Duis vel magna ultricies, ultrices mi eu, euismod lectus. Quisque non + mauris ut urna efficitur vestibulum eu eget sem. Integer bibendum + bibendum arcu, at placerat felis luctus faucibus. +

+

+ Quisque porttitor euismod justo, in mattis tortor gravida et. Aliquam + dapibus enim ut purus blandit consectetur. Quisque erat est, pretium + eget mattis in, finibus at urna. Nulla pulvinar euismod magna eu + sollicitudin. Sed varius sapien quis dolor vehicula rhoncus. Etiam eget + arcu vel mi placerat scelerisque. Proin sed viverra est. Nullam sit amet + porta urna. In hac habitasse platea dictumst. Etiam eget ullamcorper + ipsum. Aenean nisi magna, efficitur sed maximus nec, finibus ut massa. +

+

+ Nullam vitae condimentum odio. In tempus velit vel lacus molestie + vestibulum at congue lacus. Vestibulum sed ante elit. Aliquam suscipit + est sed metus mollis, ut gravida dolor scelerisque. Mauris placerat + risus turpis, nec varius neque malesuada non. Fusce ultrices facilisis + tempus. Praesent consectetur aliquam nulla. Quisque a luctus leo. Nam + odio elit, consectetur quis turpis ut, vestibulum laoreet leo. Sed vitae + purus quis metus dignissim viverra. Proin lobortis tempor tincidunt. + Phasellus posuere tristique nisl, et laoreet mauris sodales quis. Donec + finibus leo arcu, a ultrices mi viverra at. Aenean lacinia ex quis + pretium vehicula. In hac habitasse platea dictumst. +

+

+ Morbi gravida sit amet leo aliquam mattis. Aliquam vestibulum, est ac + rutrum sollicitudin, dui dolor mollis turpis, finibus euismod purus est + id purus. Proin eleifend leo consequat augue suscipit accumsan. Sed nec + dolor id sem tincidunt tempor. Morbi neque nisl, malesuada nec placerat + eget, finibus viverra quam. In id leo gravida, lobortis libero quis, + bibendum diam. Aliquam et fermentum augue, et pretium lacus. +

+

+ Phasellus quis massa in mi suscipit auctor eget in ante. Phasellus a + efficitur lectus. Nam porta eros a est aliquet, interdum luctus tortor + accumsan. Nam ac mollis est. Duis vitae sagittis magna. Pellentesque + molestie vehicula tortor. Curabitur in semper sapien. Phasellus vitae + bibendum ligula, in bibendum leo. Phasellus pellentesque mi sed ante + tristique, et condimentum enim fringilla. Nulla facilisi. +

+

+ Phasellus porta risus eu lobortis dapibus. Nulla ut leo sem. Vivamus nec + nisi erat. Proin vel dolor auctor, tristique purus at, congue nisi. In + sit amet fermentum tortor. Nullam volutpat, massa sit amet tempor + sollicitudin, orci risus aliquet elit, et consequat ante nisl vel odio. + Sed auctor mollis justo, vel gravida lacus aliquam vel. Sed elit tellus, + blandit nec dictum a, tempus quis libero. +

+

+ Mauris molestie in elit ac faucibus. Praesent a velit diam. Praesent ut + dapibus arcu, ut molestie quam. Cras eget pretium sem. Sed quis lacus + hendrerit, auctor nisi eget, venenatis dui. Cras id nisl aliquam, + elementum lacus ac, sodales purus. Cras venenatis quam eu leo mattis, + feugiat venenatis turpis rutrum. Vestibulum non leo lectus. Aenean at + leo pellentesque ex efficitur sollicitudin. Sed nec nunc eu elit + hendrerit ultrices. Nullam porta tincidunt vestibulum. Aliquam convallis + nisl eu ipsum posuere, vitae aliquam diam tempor. +

+

+ Cras vitae auctor risus. Morbi aliquam aliquet massa, et varius sem + interdum eu. Praesent purus ligula, rhoncus sed consequat id, rutrum ut + elit. Aenean in est eu velit fermentum consequat. Sed vulputate placerat + augue, vel cursus purus placerat ut. Maecenas mauris velit, tristique + fringilla fermentum eu, hendrerit in dui. Fusce vestibulum ligula non + sem tempus, pellentesque vulputate nibh iaculis. Curabitur eleifend vel + ante non malesuada. Mauris a dolor nec leo sagittis convallis nec non + velit. Maecenas accumsan est at ligula malesuada, eu pulvinar ligula + consequat. Suspendisse potenti. Nullam nec urna id metus efficitur + posuere et in dui. Maecenas varius, eros vitae sollicitudin pulvinar, + sem nisi rhoncus arcu, id feugiat nunc tellus id velit. Vestibulum sit + amet dictum lectus. Praesent eget massa pulvinar, mollis leo at, tempor + erat. +

+

+ Duis ut mi ac ex fringilla molestie. Sed ultricies rutrum purus. Nam + varius placerat varius. Pellentesque libero ligula, gravida at egestas + sed, tempor vel nisi. Suspendisse convallis tortor et mi convallis, + vitae tempus felis dignissim. Nulla facilisi. Sed at augue eget purus + rutrum volutpat. Mauris auctor arcu a arcu placerat pulvinar. Maecenas + in tortor ipsum. Integer quam nulla, congue et velit sodales, ornare + semper mauris. Etiam luctus nisl vitae risus condimentum, vitae sagittis + metus vehicula. Etiam non nulla mi. Vivamus ornare accumsan est, sit + amet condimentum est pretium a. +

+

+ Donec faucibus erat quis libero lobortis dictum. Proin lacus arcu, + laoreet id semper a, dictum eget lacus. Sed consectetur metus turpis, + non sodales massa fringilla sit amet. Donec ultrices rutrum risus, vitae + facilisis est porta non. Integer congue scelerisque enim sed + condimentum. Cras rutrum ex ullamcorper ultrices malesuada. Suspendisse + potenti. Donec pretium eros ipsum, id pharetra tortor posuere non. Proin + accumsan tempus ipsum, eget lobortis tellus. Proin blandit mi ut orci + consectetur, sed elementum nibh tempor. Ut eu tincidunt nunc, sit amet + aliquam turpis. Nulla facilisis felis lectus, a ultricies dolor + efficitur eget. In a pellentesque libero. +

+

+ Aenean cursus orci sed est tincidunt finibus. Duis efficitur nec turpis + ut placerat. Aenean aliquet tortor lorem, eget elementum orci dapibus + eu. Etiam imperdiet congue sollicitudin. Nunc congue, ante quis molestie + porta, lorem nunc gravida felis, id lacinia lorem risus nec metus. Donec + orci magna, tempus id urna sed, feugiat malesuada felis. Aliquam nec + eleifend arcu. Vivamus ac lectus tempor, tempus leo in, lacinia est. Nam + ut viverra lectus. Sed sit amet rhoncus neque. Sed mollis in urna id + volutpat. +

+

+ Fusce vestibulum in odio nec tempor. Praesent nulla lorem, commodo vel + ligula et, accumsan bibendum mauris. Mauris ornare nulla sapien, sit + amet dapibus lacus dignissim in. Donec dapibus dui ac nisl feugiat, et + gravida risus facilisis. Sed gravida, odio non consectetur efficitur, + urna metus venenatis nisl, sit amet vulputate ante mauris in lectus. + Phasellus tincidunt, risus nec interdum dictum, mi tellus malesuada + justo, vel tempus leo enim quis orci. Sed in commodo ligula. Mauris non + interdum elit, id dapibus quam. +

+

+ Proin a velit sit amet nunc congue feugiat sit amet ut lorem. Mauris vel + iaculis nisi. Praesent purus massa, accumsan ac euismod hendrerit, + auctor et arcu. Curabitur tempus ornare velit interdum tempor. Morbi a + turpis vel mauris luctus tincidunt ut id quam. Aliquam tincidunt nec + erat a volutpat. Donec ultricies accumsan tortor, ut pulvinar mauris + consectetur in. Duis condimentum blandit ipsum, sit amet dapibus risus + dignissim et. Vestibulum sodales imperdiet facilisis. Donec gravida + tellus a posuere faucibus. Nunc vitae placerat lorem. +

+

+ Mauris dictum nunc sed turpis commodo, eget elementum nibh lobortis. + Vivamus ut condimentum sem. Etiam dui diam, facilisis vel egestas et, + fringilla eget odio. Pellentesque quis sapien mattis, dignissim nisi + quis, volutpat massa. Vivamus eget pulvinar risus. Aliquam tristique + elit vel viverra facilisis. Vestibulum ac malesuada ex. Lorem ipsum + dolor sit amet, consectetur adipiscing elit. Phasellus condimentum est + nec iaculis dignissim. Aenean congue, odio quis vestibulum malesuada, + justo enim tristique sapien, tempor lobortis erat tortor vitae felis. + Aliquam erat volutpat. Nullam vel mattis mauris, ut dictum lacus. Fusce + tincidunt accumsan magna quis tincidunt. Aenean nec diam convallis, + facilisis ex ut, varius nunc. +

+

+ Nullam facilisis congue semper. Aenean placerat ipsum ut felis semper, + ut sagittis eros congue. Duis non volutpat sapien, eu ullamcorper lorem. + Donec consectetur mauris vitae neque pharetra, eu sollicitudin ex + scelerisque. Donec at urna feugiat leo mollis viverra. Integer imperdiet + tincidunt quam, in congue turpis finibus vel. Phasellus porta maximus + ullamcorper. Etiam ultrices nec diam non tempus. Sed vitae lacus et leo + finibus elementum. Suspendisse potenti. Fusce tincidunt mattis sapien + nec maximus. +

+

+ Aenean ultricies purus sit amet dapibus vehicula. Fusce quis libero + orci. Vestibulum interdum rutrum augue eget ornare. Nullam euismod + tellus a erat ornare, sed feugiat est sodales. Donec iaculis orci nisl. + Nulla ligula nisi, euismod in varius et, consectetur eget lorem. Vivamus + eu magna vel turpis interdum varius non non magna. Pellentesque sit amet + velit eget justo eleifend pharetra. +

+

+ Fusce scelerisque sapien magna, eget mollis orci tincidunt a. Quisque at + dolor iaculis, euismod quam et, facilisis risus. Morbi eget tincidunt + turpis, nec sodales sem. Morbi ut sem eget dui pretium molestie eget ac + neque. Proin scelerisque tellus sed congue tincidunt. Suspendisse + sagittis condimentum elit, at lobortis mauris varius ut. Proin bibendum + ultricies maximus. Suspendisse tempor orci ultrices diam tristique + maximus. +

+

+ Cras et odio id ex eleifend varius ac at metus. Class aptent taciti + sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. + Proin congue quam efficitur sapien vestibulum pretium. Nulla fringilla, + augue ac consectetur maximus, nibh ligula elementum risus, eget + tincidunt dui sapien eu libero. Phasellus quis nulla quis nunc pretium + faucibus. Quisque vel lacinia massa, ac rhoncus nulla. Nullam a erat non + quam ultricies maximus. Donec non dui ac nisl aliquet eleifend. Praesent + elementum nunc eu ultrices viverra. Maecenas congue, tortor ut fringilla + sollicitudin, purus ligula aliquet neque, vel egestas ex lorem sit amet + nisl. Fusce dignissim leo quis ante placerat, eu interdum dolor + tristique. In porttitor vestibulum ante, sit amet imperdiet nulla + facilisis a. Duis posuere auctor orci, ut tristique nulla. Morbi congue + ante dignissim lacus varius, ac volutpat felis fermentum. Morbi ipsum + mi, tincidunt vitae nunc vel, molestie porta ante. +

+

+ Sed sollicitudin mauris a iaculis porta. Nunc sit amet congue lorem, vel + porta est. Nulla a nibh suscipit, cursus mi nec, rhoncus enim. Cras + hendrerit faucibus dui, sit amet viverra sem ultricies sit amet. + Pellentesque non luctus orci, ornare porttitor dolor. Cras id tincidunt + justo. Integer vestibulum, libero ac convallis malesuada, quam quam + feugiat sapien, ac eleifend sapien ante eget leo. Aenean eu nisl nisl. + Pellentesque et congue nunc. Integer odio ante, laoreet eu lectus in, + auctor blandit diam. Maecenas ultrices gravida magna, in mattis leo + aliquam ut. +

+

+ Pellentesque nec felis at felis auctor congue. Integer ut finibus lacus. + Nullam viverra porttitor elit, non rutrum metus auctor sit amet. Lorem + ipsum dolor sit amet, consectetur adipiscing elit. Integer vitae tellus + a nunc sodales placerat. Class aptent taciti sociosqu ad litora torquent + per conubia nostra, per inceptos himenaeos. Maecenas egestas rhoncus + quam, quis eleifend massa condimentum vel. In id nunc a tortor + pellentesque congue sed ac arcu. Cras vestibulum quis leo sed pulvinar. + Nulla nec commodo urna. Aliquam maximus vel ex sed efficitur. Praesent + tincidunt, metus sed facilisis mattis, felis ex mollis leo, sit amet + tincidunt ante tellus sit amet mi. +

+

+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per + inceptos himenaeos. Aliquam erat volutpat. Duis porta efficitur rhoncus. + Phasellus viverra nunc tellus, in mollis augue tempor non. Nullam at + metus urna. Fusce vitae ligula eu lectus vulputate porta. Nullam + convallis, nulla in placerat ultrices, lorem nunc lobortis diam, ac + sodales quam orci a mi. Ut sodales placerat lectus id aliquet. Morbi + vestibulum, eros et sodales molestie, felis justo aliquet dui, vitae + dignissim tortor sapien vel quam. Nunc in pretium metus. Aliquam erat + justo, vestibulum quis arcu sed, luctus sagittis lacus. Nam consectetur + efficitur volutpat. +

+

+ Etiam odio lorem, tincidunt a convallis eu, vestibulum eu massa. Integer + faucibus sed purus sed malesuada. Sed sollicitudin, nisi at fermentum + accumsan, eros odio luctus nisl, vel placerat ante turpis at metus. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque + faucibus dui odio, imperdiet aliquam ex ullamcorper ac. Aenean leo ex, + tincidunt sit amet venenatis eget, congue in elit. Vestibulum tempus + iaculis nunc vitae volutpat. Nullam eu maximus risus. Cras ut volutpat + nulla, et pulvinar orci. Nunc et vestibulum mauris. Donec sit amet nunc + hendrerit, malesuada neque non, malesuada neque. Aenean id sagittis + massa. Morbi non lectus ipsum. Donec facilisis fermentum augue non + laoreet. Nunc pulvinar tincidunt lacus laoreet condimentum. Aliquam + rutrum ultricies diam ac maximus. +

+

+ Sed sagittis nisi ut metus imperdiet, sed tempus sem ornare. Duis + vehicula interdum mauris, eu facilisis sapien egestas et. Donec eget + libero neque. Curabitur volutpat dolor sit amet neque ultrices, vitae + viverra ipsum congue. Cras et quam ullamcorper, tempor nisi vel, + convallis lectus. Donec sagittis, felis vitae finibus auctor, neque odio + mollis sapien, quis euismod lacus mi et augue. Vestibulum tincidunt + lorem vel auctor dictum. Nam ultrices nisi at blandit dignissim. +

+

+ Proin cursus, erat a hendrerit dictum, urna nisl laoreet orci, vel + posuere libero arcu vitae purus. Etiam ac nulla sit amet ligula feugiat + aliquam eu et ex. In hac habitasse platea dictumst. Nullam id + condimentum justo, quis vulputate enim. Donec consequat fringilla ipsum + sit amet vulputate. Suspendisse potenti. Etiam iaculis lorem non + facilisis aliquam. Ut at lobortis leo. Aenean fermentum augue vel leo + vehicula condimentum et vitae ex. +

+

+ Etiam auctor arcu placerat, semper turpis non, aliquet lorem. Proin + auctor pharetra augue vitae tempus. Duis semper magna metus. Nam quis + nunc non augue mattis porta nec sed est. Sed at porta tortor. Fusce eget + consequat turpis, et dignissim enim. Ut non mollis erat, nec volutpat + ipsum. Vivamus eget felis ipsum. Nullam aliquet bibendum semper. Proin + id sapien eget libero ultricies iaculis. Praesent dictum mauris et justo + porttitor luctus. Mauris imperdiet est eget feugiat egestas. Aenean + cursus facilisis ipsum, sed convallis eros vulputate ut. Vivamus laoreet + urna vel justo molestie tincidunt. Sed tristique justo quis nunc + tincidunt, viverra aliquet lorem sodales. Nam aliquam venenatis turpis, + quis accumsan lorem gravida nec. +

+

+ Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur + eget sapien in ipsum suscipit aliquam. Phasellus dui nulla, egestas quis + orci in, pulvinar hendrerit tellus. Sed faucibus, urna non dignissim + sollicitudin, turpis arcu imperdiet velit, ut tristique lacus lorem + bibendum elit. Donec ullamcorper justo nisl, nec rhoncus purus venenatis + quis. Praesent vestibulum venenatis erat, sit amet iaculis risus + ullamcorper at. Aenean ligula odio, pulvinar vitae metus placerat, porta + finibus enim. Etiam suscipit, odio vel accumsan varius, lorem leo + consectetur magna, id bibendum est dolor varius nibh. Nullam erat ante, + placerat ac ante sed, suscipit gravida nunc. Vivamus euismod fermentum + dolor, eu fermentum est dictum in. +

+

+ Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam + placerat volutpat iaculis. Morbi ligula felis, congue non hendrerit + vitae, ultricies eu felis. Fusce elementum, lorem id ultricies semper, + purus libero vulputate turpis, vitae pellentesque enim nisi vel urna. + Nunc mi odio, dapibus id velit eu, commodo varius sapien. Donec et nulla + volutpat, semper ipsum ac, molestie enim. Vivamus non quam mattis, + viverra massa nec, maximus lectus. Aliquam eleifend dolor quis fringilla + tincidunt. Donec tincidunt suscipit urna efficitur hendrerit. Cras + lectus metus, vulputate vitae dolor eu, condimentum lobortis nisi. Sed + sodales elementum orci. Suspendisse hendrerit tortor nulla. +

+

+ Proin bibendum vel justo non fringilla. Praesent auctor at tortor et + eleifend. Sed finibus tempor interdum. Sed dui diam, feugiat dictum + faucibus sit amet, dictum ut lacus. Suspendisse consectetur varius + fringilla. In sit amet leo nunc. Maecenas mollis mauris ut arcu rutrum + finibus. Pellentesque magna elit, efficitur at nisl ac, porta + scelerisque nisl. In vitae metus a libero euismod vestibulum. Donec + faucibus augue at dolor volutpat gravida. Vivamus feugiat sagittis leo + id hendrerit. Quisque placerat, augue at eleifend tempor, nibh lorem + sodales orci, quis maximus eros diam in dui. Pellentesque habitant morbi + tristique senectus et netus et malesuada fames ac turpis egestas. Nunc + luctus mauris scelerisque, ullamcorper erat a, volutpat diam. Mauris id + lacinia turpis. Proin in ligula bibendum, molestie leo ut, accumsan + elit. +

+

+ Integer tempus, elit ac tincidunt iaculis, nibh quam rutrum augue, at + maximus quam ligula non odio. Integer commodo ullamcorper tempor. Mauris + vestibulum, nisi eu maximus bibendum, sem est eleifend magna, porttitor + dignissim nulla lorem id ante. Aliquam erat volutpat. Donec vitae + ullamcorper quam, lobortis porttitor eros. Quisque vitae tempor eros. + Sed faucibus a arcu suscipit suscipit. Etiam eros justo, aliquet at + velit id, tempor rutrum elit. Quisque et purus sit amet felis lacinia + condimentum. Suspendisse potenti. Nunc ex mi, commodo blandit tempus in, + tempor vel lacus. Cras urna enim, tempor eget lacus sit amet, accumsan + maximus ligula. Donec quis lorem quam. Etiam at est ut justo rutrum + bibendum. Vivamus lectus felis, mollis a enim a, tincidunt vehicula + purus. +

+

+ Phasellus vehicula congue sapien, in volutpat felis posuere id. Aliquam + non urna eu purus placerat sollicitudin id a elit. Morbi pharetra est + non augue porttitor, sed volutpat ipsum fermentum. Fusce sed velit + risus. Sed id dolor ac dolor rhoncus lobortis eu ut leo. In dapibus est + lobortis, dictum nulla in, suscipit risus. Aliquam tincidunt, dolor sit + amet laoreet egestas, eros mi pulvinar nisi, sed varius elit arcu et + massa. Orci varius natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Mauris finibus neque nec gravida aliquet. Etiam + nec neque sem. Donec eget eros ex. Nam porta eget libero vitae + porttitor. In hac habitasse platea dictumst. +

+

+ Integer a dictum urna. Etiam nulla leo, molestie eu hendrerit non, + finibus at tellus. Sed hendrerit non mauris in imperdiet. Donec eleifend + elementum felis eget posuere. Proin in massa rutrum dui tempor bibendum. + Vivamus eget nisi porta tellus tempor volutpat nec non purus. Aenean eu + nisi dolor. Morbi in sodales lectus, at lacinia lacus. +

+

+ Morbi ligula lacus, auctor eu aliquet et, ultricies sagittis odio. + Phasellus in diam a metus congue dapibus ut ac mi. In hac habitasse + platea dictumst. Pellentesque et commodo magna, ut molestie nibh. Donec + ac justo ut nisl pharetra ultrices non vitae diam. Cras convallis erat + nulla, ac consequat justo condimentum vitae. Etiam vitae magna vitae + risus molestie luctus vitae at ipsum. Duis in nisl interdum risus + facilisis pretium. Donec tempus elementum faucibus. Etiam eu molestie + elit, ac egestas sem. Sed consequat diam vel venenatis pharetra. + Vestibulum efficitur eget sapien in ultricies. +

+

+ Nunc mauris magna, scelerisque vel aliquam ultrices, tincidunt vel nisl. + Duis luctus quis nisl quis vehicula. Nullam non tristique felis. + Curabitur semper ipsum non lacus dictum ullamcorper. Proin sed sodales + elit. Suspendisse volutpat vehicula elit, nec mattis tortor sagittis ut. + Praesent eu sem enim. Fusce sit amet odio in velit sagittis fringilla. + Nulla facilisi. Pellentesque non massa sit amet ligula vehicula + vestibulum ac et lorem. Ut elementum orci orci, ut facilisis massa + cursus in. +

+

+ Praesent ultrices urna eu ultrices ultricies. Phasellus sapien purus, + accumsan quis dui a, tempor ornare augue. Pellentesque tempor dolor et + placerat dapibus. Pellentesque fermentum dictum tellus vitae elementum. + Morbi in erat pharetra, posuere odio et, semper nisl. Integer quis + consectetur massa. Sed ac mi elit. Fusce aliquam enim nulla, in + consequat urna dignissim sit amet. Duis porttitor placerat elit vel + ornare. Praesent malesuada dictum mollis. Suspendisse vitae sem quis + felis posuere finibus. Donec a porta metus. Class aptent taciti sociosqu + ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus + mattis tempor sapien, non scelerisque turpis auctor venenatis. Aenean + sed metus vitae orci facilisis luctus. Pellentesque vestibulum varius + gravida. +

+

+ Nulla id elit sit amet ligula aliquam tristique. Vestibulum eleifend + suscipit velit a rutrum. Suspendisse ultrices iaculis libero ut + ullamcorper. Vivamus interdum dignissim mi, vitae tempus lectus + tristique non. Fusce consectetur feugiat fermentum. Donec sagittis non + est vel porttitor. Pellentesque iaculis massa vitae quam lobortis + vehicula accumsan in elit. Aliquam erat volutpat. Nam sit amet metus + odio. Nulla condimentum justo eget purus interdum faucibus. Quisque + facilisis nisl blandit, gravida nisl in, ornare neque. +

+

+ Orci varius natoque penatibus et magnis dis parturient montes, nascetur + ridiculus mus. Donec sed nunc vel lacus ornare dictum. Donec lacinia + iaculis sollicitudin. Nunc tempor eleifend ligula in dignissim. + Suspendisse id accumsan tellus. Praesent vulputate elit est, sed + sagittis neque suscipit ut. Etiam in tincidunt ipsum. +

+

+ Praesent tincidunt lectus eu nisl eleifend varius. Sed rutrum imperdiet + lorem, a volutpat augue posuere elementum. Praesent luctus aliquam + ligula id mollis. Curabitur lorem nisl, vulputate sit amet velit + dapibus, dictum aliquet nisl. Donec vitae ornare magna, ut facilisis + ipsum. Vestibulum volutpat ligula et ipsum hendrerit vehicula. Donec + scelerisque accumsan turpis sit amet porta. +

+

+ Curabitur aliquet varius felis, sit amet condimentum urna commodo vitae. + Pellentesque eget nisl tincidunt, dictum quam ut, feugiat est. In + vulputate metus at nunc faucibus aliquam. Aenean vulputate, nunc at + pharetra iaculis, dui lacus porta diam, id consectetur magna magna ac + massa. Class aptent taciti sociosqu ad litora torquent per conubia + nostra, per inceptos himenaeos. Ut dapibus sollicitudin neque, tempor + luctus dui lacinia ac. Fusce quis ex id ligula varius feugiat eget et + enim. Pellentesque bibendum elit massa, vel tempor neque gravida quis. + Nulla facilisi. Etiam ut mauris sit amet ipsum imperdiet venenatis. Ut + porta vestibulum dolor eu suscipit. Vivamus mollis interdum ipsum sed + posuere. +

+

+ Aliquam tristique mi eget lacinia maximus. Ut auctor leo sit amet risus + sodales, quis tempor justo accumsan. Sed eu tincidunt risus. Suspendisse + orci metus, rutrum sit amet risus non, lobortis facilisis augue. Nunc + dolor turpis, posuere ut condimentum nec, euismod sed eros. Donec mattis + dignissim mi in pulvinar. Curabitur ut metus magna. Nulla facilisi. +

+

+ Proin vel porttitor ipsum. Fusce in lectus in ipsum bibendum mollis. + Donec pellentesque iaculis tincidunt. Etiam sit amet mi porttitor, + condimentum nibh non, dictum purus. Nulla dapibus suscipit maximus. Cras + eu dignissim libero. Pellentesque habitant morbi tristique senectus et + netus et malesuada fames ac turpis egestas. Praesent tempor placerat + tincidunt. Vestibulum nec sapien blandit, mollis neque rhoncus, + sollicitudin sapien. Donec et consequat enim. +

+

+ Pellentesque fringilla egestas sapien eget egestas. Curabitur malesuada + elit quis nulla pretium, aliquam semper orci pharetra. In pellentesque, + metus quis tempus aliquet, ligula lorem scelerisque ligula, ut maximus + risus orci hendrerit lectus. Fusce in quam non turpis ultricies pulvinar + sed id nunc. Nullam blandit varius risus eget ultricies. Cras ut dolor + et elit eleifend ornare. In hac habitasse platea dictumst. Lorem ipsum + dolor sit amet, consectetur adipiscing elit. Suspendisse sollicitudin + vitae nibh eget pellentesque. Nam luctus congue massa, sed semper mauris + tincidunt non. Nulla facilisi. Suspendisse quis rutrum leo. Donec + aliquam sem nibh, quis egestas augue egestas vitae. Duis consequat + sapien eget sem venenatis ornare. Curabitur tincidunt lobortis orci, nec + viverra justo pulvinar tincidunt. Maecenas iaculis bibendum est, nec + dapibus massa tempor quis. +

+

+ Aliquam in nulla eget erat euismod posuere. Proin consectetur, lorem eu + fringilla posuere, ante quam imperdiet ligula, quis suscipit neque risus + ac est. Fusce congue nibh viverra nibh aliquet, sit amet ullamcorper + lectus lacinia. Fusce quis auctor diam. Orci varius natoque penatibus et + magnis dis parturient montes, nascetur ridiculus mus. Etiam tristique + elit ac arcu interdum ullamcorper. Pellentesque lobortis sollicitudin + metus sit amet faucibus. Sed vehicula elementum justo vel egestas. + Suspendisse varius laoreet lacinia. Nullam eget venenatis lacus. Aliquam + in orci consequat, rhoncus ligula in, faucibus lorem. Maecenas feugiat + luctus tristique. Nunc turpis felis, pellentesque sed molestie ac, + cursus eu arcu. In vitae metus hendrerit, interdum tortor eget, + scelerisque sem. Proin quis tortor libero. Sed euismod cursus tortor, + nec pellentesque magna. +

+

+ In a ex accumsan, semper nisl vitae, porttitor turpis. Morbi sed blandit + tortor, vitae venenatis ex. Mauris eu fermentum nunc, vitae rutrum + neque. Donec eu diam vulputate, venenatis sem dictum, placerat magna. + Cras ante enim, imperdiet eu lacinia ut, tempor eget lectus. Praesent + molestie turpis massa, sed commodo mauris dignissim at. Etiam ultrices + ex a purus mollis varius. Ut ac erat diam. Maecenas laoreet massa purus. +

+

+ Suspendisse luctus leo egestas est vulputate consectetur. Sed porttitor + nisi id orci lobortis consectetur. Praesent id neque ac eros tincidunt + mollis ac eget mi. Aliquam fringilla lorem ligula, quis maximus tortor + mattis vitae. Pellentesque faucibus neque vel commodo blandit. Integer a + dictum mi, id auctor metus. Vestibulum efficitur ornare felis eu + lobortis. Quisque ac viverra eros. Ut mattis accumsan justo. +

+

+ Curabitur convallis tellus neque, a ornare dui pulvinar ut. Nulla + sagittis sit amet tellus quis dignissim. Aliquam convallis commodo + tellus vitae ultricies. Pellentesque habitant morbi tristique senectus + et netus et malesuada fames ac turpis egestas. Proin nunc augue, maximus + at eros in, iaculis lobortis turpis. Aliquam ut sapien sit amet ante + condimentum sollicitudin. Quisque rhoncus sollicitudin imperdiet. + Integer luctus laoreet maximus. Donec pulvinar porta metus, sit amet + tristique erat tempor at. In convallis mi quis justo pretium varius sit + amet nec diam. Suspendisse potenti. Morbi pulvinar tristique elit id + semper. Donec pulvinar cursus volutpat. +

+

+ Nulla consequat felis felis, sed vehicula turpis rhoncus a. Suspendisse + potenti. Sed tortor ante, aliquet in eros in, tempor finibus diam. + Aliquam neque lorem, commodo pretium vestibulum id, tincidunt vel ipsum. + Nunc feugiat augue a magna tempor, vitae suscipit est elementum. Proin + imperdiet dui ante, ut auctor nulla consequat sed. Aenean bibendum massa + a lectus egestas, nec posuere nisi tempor. Praesent et rhoncus magna. + Maecenas nec diam vitae risus malesuada sodales nec id risus. Mauris + pulvinar mi at nulla feugiat, nec faucibus ex efficitur. Aenean a risus + vel diam commodo congue nec et purus. Curabitur lobortis hendrerit nibh + sed feugiat. Praesent sollicitudin porttitor mauris, consequat consequat + odio vulputate eget. Quisque convallis velit quam, ut ornare sem + faucibus et. In posuere ullamcorper hendrerit. Nam vel sapien risus. +

+

+ Mauris venenatis nunc sit amet diam faucibus accumsan. Maecenas sed + augue nec dolor eleifend placerat vulputate eu metus. Aliquam eu purus + quis orci dapibus aliquam id ut elit. Quisque ornare, dui blandit + ullamcorper posuere, magna urna dictum est, sit amet faucibus magna mi + eu neque. Cras sodales aliquet sem, ac vulputate eros facilisis + interdum. Duis ut gravida neque. Sed facilisis lectus et lorem finibus + imperdiet. Vestibulum quis libero et nunc tristique suscipit. Ut at + purus tortor. +

+

+ Sed dolor est, mattis eget molestie in, convallis a odio. Nulla quis + consectetur metus. Nulla et lacus blandit, rutrum eros sit amet, + condimentum lectus. Suspendisse feugiat felis dignissim eros accumsan + feugiat. Etiam lobortis orci massa, vel rutrum urna feugiat vel. Donec + mollis nisl eget ex laoreet aliquet. Donec neque arcu, finibus et nisl + sed, ultricies facilisis nibh. Sed condimentum tortor est, ut dignissim + nulla bibendum et. Donec sagittis posuere est dictum tempor. +

+

+ Vestibulum pretium justo metus, eget lobortis augue consectetur at. + Quisque sodales magna quis consectetur eleifend. Duis eu dapibus erat. + Donec eros nibh, facilisis eu neque at, egestas commodo purus. + Pellentesque id urna erat. Aenean lacinia diam eu leo convallis, et + volutpat justo dignissim. Etiam id dolor elementum, porta dui at, porta + arcu. Proin elementum ac ex sit amet viverra. Aenean nec sagittis magna. +

+

+ Aliquam suscipit est sed ipsum finibus laoreet. Proin porta lacus in + neque sollicitudin, non posuere lacus luctus. Cras ultricies imperdiet + tempor. Nulla sit amet magna facilisis, semper nunc a, placerat nunc. + Cras commodo, sapien vitae bibendum ornare, odio mi iaculis leo, eu + volutpat risus libero id turpis. Morbi sollicitudin, augue nec dictum + dignissim, nulla tortor tincidunt eros, nec finibus metus leo ac nisi. + Mauris rhoncus, urna sed lacinia posuere, ante magna suscipit arcu, + vitae vestibulum lacus odio vel ligula. Integer aliquam euismod + ullamcorper. Vestibulum ante ipsum primis in faucibus orci luctus et + ultrices posuere cubilia curae; Integer non lectus massa. In ac odio + condimentum urna aliquet aliquam sed nec ligula. Sed ut dolor varius + felis eleifend tincidunt quis sed sapien. +

+

+ Vestibulum ornare tellus orci, a pretium libero congue et. Praesent in + egestas diam, quis fermentum nisl. Vivamus eu scelerisque metus, et + sagittis justo. Aenean pulvinar justo felis, at cursus ante gravida ut. + Mauris tristique mi placerat rutrum elementum. Vestibulum porta sagittis + ligula, ut tristique urna egestas eget. Aliquam et sagittis turpis. + Vestibulum accumsan nunc in nibh iaculis, eget pretium justo elementum. + Donec varius ex sed quam pellentesque, eu auctor dolor pulvinar. +

+

+ Nulla et purus lacus. Sed fringilla finibus sodales. Etiam venenatis dui + vitae felis condimentum, vitae venenatis orci vulputate. Phasellus + rhoncus arcu id vehicula pretium. Sed sit amet dolor arcu. Fusce nec + commodo risus. Orci varius natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. +

+

+ Nam sit amet dapibus est. Mauris imperdiet ex ac elit mattis tempus. Nam + placerat felis eu mauris commodo euismod. In hac habitasse platea + dictumst. Quisque auctor nec arcu quis varius. Integer iaculis est sed + egestas elementum. Aenean gravida auctor erat sed efficitur. Donec + sapien urna, condimentum at libero eu, viverra porta metus. Proin + sodales erat lorem, eget hendrerit massa dictum vitae. Quisque sit amet + fringilla ex. Vivamus lobortis tempor nulla, id feugiat neque aliquet + et. Cras et placerat turpis, sed hendrerit justo. Ut in placerat risus. + Praesent id nibh eu velit consequat efficitur eget nec magna. +

+

+ Praesent nec consequat nisi. Integer ac nulla eget nunc condimentum + lobortis ac non lacus. In eu porta magna. Etiam dapibus ultrices turpis, + vel placerat tellus lacinia non. Pellentesque aliquam libero in massa + sodales, nec tincidunt ligula pretium. Nulla blandit neque turpis, nec + accumsan nibh pulvinar blandit. Cras facilisis gravida placerat. + Curabitur pellentesque velit quis lorem imperdiet vulputate viverra in + metus. Proin et tellus dictum dui lacinia varius ac ut metus. Nullam + dignissim eros erat, id dignissim ante sagittis sit amet. Sed lobortis, + quam volutpat rutrum consequat, velit nunc lobortis eros, at ultricies + ex nibh sit amet ligula. Phasellus tempor mi at dolor consequat, eget + sodales mauris consequat. Orci varius natoque penatibus et magnis dis + parturient montes, nascetur ridiculus mus. Aliquam convallis ex vel + lectus bibendum, in gravida lectus dictum. Orci varius natoque penatibus + et magnis dis parturient montes, nascetur ridiculus mus. Curabitur + volutpat lectus quis magna interdum ornare. +

+

+ Pellentesque rutrum maximus purus vitae commodo. Vivamus imperdiet vitae + mi sit amet cursus. Nullam bibendum consequat felis at tincidunt. + Quisque pellentesque diam leo, eget vestibulum risus gravida quis. + Phasellus ipsum sapien, scelerisque eget nisi eu, sodales fringilla est. + Nam purus lacus, euismod eget sem et, accumsan auctor lectus. Sed + porttitor ex non nunc commodo, et posuere erat hendrerit. Praesent eu + dolor id dolor ullamcorper cursus at eu risus. Nam tincidunt, ipsum in + tempus tristique, neque quam tempus mauris, quis fringilla quam purus + vitae elit. Maecenas finibus efficitur consequat. Curabitur et suscipit + nibh. Cras eleifend ultricies molestie. +

+

+ Fusce eu hendrerit nisl. Etiam at arcu in nisl aliquam ultricies sit + amet ut orci. Class aptent taciti sociosqu ad litora torquent per + conubia nostra, per inceptos himenaeos. Quisque volutpat ex quis ante + lobortis dignissim. Fusce pulvinar sodales aliquam. Mauris felis augue, + vulputate non nunc vitae, mollis volutpat odio. Suspendisse lobortis + ligula nec dolor hendrerit aliquet. +

+

+ Suspendisse gravida, diam vitae rutrum varius, mauris erat lacinia mi, + non fringilla nisi lorem eget lectus. Suspendisse iaculis condimentum + eros, quis sodales turpis maximus ut. Sed congue posuere mi at congue. + Phasellus eget lacus vitae urna molestie aliquet sed sed felis. Donec + fermentum urna at dolor condimentum consectetur. Aenean sed pretium + tortor. Suspendisse sed lectus risus. Sed sapien erat, tincidunt + scelerisque urna vitae, congue condimentum est. Mauris eget urna id + purus vestibulum lacinia ut non augue. Nunc id elementum nisi, interdum + sollicitudin neque. Pellentesque massa risus, varius nec leo nec, mollis + cursus nisl. Mauris aliquet hendrerit risus. Vestibulum congue interdum + massa, ac ultrices nibh mollis id. Phasellus justo lorem, malesuada sit + amet accumsan vel, mollis ut urna. +

+

+ Curabitur nec tortor sodales, maximus diam in, efficitur quam. Ut rutrum + mi id libero tristique luctus. Phasellus et neque sollicitudin purus + interdum dapibus eget at ex. Duis non mi at massa molestie tempor a sed + tortor. In placerat metus nec condimentum fermentum. Maecenas varius + velit in velit tempus dignissim fermentum id justo. Pellentesque + dignissim tempus porttitor. Fusce scelerisque bibendum sagittis. Donec + tristique tellus ut placerat auctor. Nunc porta tempus risus, at maximus + quam lobortis id. +

+

+ Vivamus volutpat tortor sed justo lobortis porttitor. Etiam a aliquam + lorem. Nullam rutrum accumsan augue, eget sagittis tellus posuere nec. + Aliquam dignissim urna at tellus blandit fermentum. Nunc fermentum justo + ut odio pharetra fringilla. Nunc aliquet eleifend magna nec mattis. + Pellentesque ac tristique dui. Suspendisse nec nunc et lorem tincidunt + congue egestas nec tortor. Suspendisse tincidunt eros felis, nec + ultricies turpis varius ut. Morbi leo sapien, eleifend vitae aliquet at, + suscipit ut arcu. Nulla pretium placerat nunc, quis tempus lorem semper + sit amet. Vestibulum tempus, libero ut ultricies egestas, nisi ex mollis + diam, vel porta nisl augue vitae nulla. Nullam ante ipsum, convallis + finibus sem id, hendrerit ultrices velit. Praesent tincidunt, urna ac + viverra facilisis, nisi massa eleifend sapien, ac congue leo ex vitae + lorem. Cras at pulvinar elit, non vestibulum urna. +

+

+ Quisque consectetur posuere erat, eget finibus turpis semper in. Ut + commodo leo turpis, a elementum tortor lacinia id. Nunc tempor ligula eu + nunc semper porta. Maecenas luctus turpis in justo faucibus iaculis. + Integer rutrum, nunc ac tincidunt pellentesque, velit ipsum gravida + urna, a pretium nunc eros eget dui. Phasellus maximus consectetur elit, + in fermentum odio interdum id. In feugiat tincidunt semper. Phasellus + porta ultricies orci a gravida. Etiam lobortis velit sapien, vitae + consectetur felis laoreet at. Morbi ac metus velit. Aliquam mollis neque + at purus tempus lobortis. +

+

+ Ut ornare justo nec euismod iaculis. Orci varius natoque penatibus et + magnis dis parturient montes, nascetur ridiculus mus. In sit amet + tristique sem. Donec ut tincidunt eros, nec sagittis risus. Morbi at + tellus leo. Cras eu feugiat velit. Praesent a fermentum urna, eget + dapibus felis. Nullam tempor dictum neque semper faucibus. Vestibulum et + sem accumsan, dignissim metus quis, pretium massa. Proin vestibulum + convallis ante, at tempor metus rhoncus vitae. Nunc bibendum nisi ac + magna viverra condimentum. Sed vehicula neque ex, ut auctor leo mollis + eu. Etiam dapibus pellentesque diam, eget cursus odio. Proin sit amet + eleifend est. In eget ex vitae tellus mattis dictum. +

+

+ Vestibulum ullamcorper ut felis vitae placerat. Proin suscipit orci + sapien, in tincidunt magna tempor at. Proin ut dui sed sapien mattis + sollicitudin. Aliquam id velit vitae nunc tincidunt sagittis vitae ut + enim. Nulla vulputate placerat mollis. Nullam facilisis nulla iaculis + risus molestie pulvinar. Quisque iaculis, dui eget vulputate auctor, + lacus turpis ultricies diam, vel faucibus sem nulla quis orci. Nulla et + commodo massa. +

+

+ Proin aliquet purus ut eros tincidunt dignissim. Phasellus vel diam quis + elit malesuada dignissim. Pellentesque habitant morbi tristique senectus + et netus et malesuada fames ac turpis egestas. Sed pretium at purus sed + tempus. Curabitur pulvinar euismod augue, sed convallis sem tempus + fringilla. Curabitur a dictum arcu. Vivamus finibus ante nec ante + consequat venenatis. Nulla mi magna, placerat ut laoreet non, lobortis + nec lorem. Curabitur quis pharetra arcu. Sed sit amet diam in diam + tincidunt iaculis eget a leo. Fusce laoreet felis vitae tristique + efficitur. Fusce feugiat risus non mi scelerisque, nec fringilla risus + efficitur. Aenean arcu enim, condimentum a neque scelerisque, dignissim + gravida quam. Praesent viverra euismod sapien, quis tristique diam + dictum id. +

+

+ Fusce convallis vulputate justo in egestas. Proin at dui volutpat, + eleifend lectus at, pretium nulla. Mauris mi ex, rhoncus non sodales + nec, volutpat vel libero. Praesent nisl tellus, egestas non sem a, + malesuada tincidunt magna. Integer condimentum, lacus nec mattis luctus, + urna risus tristique nibh, nec euismod sapien eros a ex. Morbi pharetra, + ex eget dapibus aliquam, ligula leo semper turpis, sit amet ullamcorper + tortor mi sed neque. Proin congue velit urna, a viverra elit consectetur + et. Pellentesque turpis massa, iaculis maximus nibh ac, elementum + aliquet sapien. Aliquam at nulla nec nisi accumsan pharetra ac a velit. +

+

+ Quisque ac vulputate sapien, nec suscipit augue. Integer vehicula + convallis faucibus. Fusce nec felis auctor, scelerisque augue sit amet, + vulputate risus. Nullam suscipit blandit mauris sed ultricies. Sed non + magna non lorem molestie vestibulum. Proin dictum malesuada nisl, nec + efficitur ipsum venenatis et. Nulla non sapien et tortor ultricies + vulputate et id turpis. Etiam consectetur suscipit quam, in fermentum + augue tincidunt eget. Nullam mattis gravida gravida. Sed posuere quam at + accumsan maximus. Nam commodo elit eu sem dapibus, a ullamcorper tortor + elementum. Vivamus et sapien in nibh bibendum semper vel eget lorem. Sed + nec risus erat. +

+

+ Sed porttitor elementum tempor. Integer egestas scelerisque turpis ut + consectetur. Integer eu nisl nisi. Nunc nec ligula eget neque dapibus + feugiat sit amet quis turpis. Nullam volutpat consequat est, et tempor + neque egestas vel. In sit amet massa sem. Sed nibh libero, elementum ac + maximus a, fringilla vel leo. Praesent at sapien ultrices, interdum elit + eget, scelerisque risus. +

+

+ Morbi dictum urna sit amet augue fringilla pellentesque vitae vitae + dolor. Aliquam dignissim, odio nec blandit tempor, diam nulla blandit + urna, nec sagittis sapien libero id libero. Ut libero sem, ultrices non + viverra non, iaculis sed magna. Aenean at dignissim turpis. Donec nibh + dolor, sodales vitae mollis ut, sodales nec arcu. Sed vitae nunc + ullamcorper augue fermentum tincidunt et et sem. Proin erat dolor, + venenatis non leo eget, cursus facilisis urna. Ut non orci sollicitudin, + blandit neque et, molestie ipsum. Pellentesque finibus, nulla vitae + lobortis luctus, magna est ultrices est, ut aliquam elit nisi sit amet + nisl. Suspendisse id sodales nunc. Maecenas tempus mattis diam ac + feugiat. Donec quis efficitur lorem. Vestibulum lobortis dapibus sem, a + ultricies quam feugiat non. Pellentesque consectetur semper nunc non + iaculis. +

+

+ Nulla ullamcorper eros nec ullamcorper pharetra. Quisque eget mi rutrum + nunc hendrerit porta vel nec ex. Sed lacinia arcu ex, eu faucibus ante + porta quis. Phasellus vitae tincidunt enim. Nunc in purus in justo + luctus dignissim. In pellentesque, lorem id dictum consectetur, dolor + massa sodales tellus, eget efficitur odio nunc vestibulum nunc. Donec + sit amet quam eget erat convallis fringilla. Etiam at pretium sem. + Mauris hendrerit justo mauris, ut ornare felis condimentum eu. + Pellentesque habitant morbi tristique senectus et netus et malesuada + fames ac turpis egestas. +

+

+ Cras ut lectus lacus. In maximus, lectus sit amet ultrices venenatis, + quam urna feugiat velit, vel tempor libero est ut diam. Praesent et + ligula sit amet leo egestas vulputate eget non lorem. Integer a diam ac + justo molestie feugiat vel bibendum diam. Donec aliquam, magna vulputate + auctor volutpat, sem nunc pharetra libero, at condimentum purus tortor a + magna. Maecenas non mollis arcu. Aliquam vulputate neque at lectus + gravida sodales. +

+

+ Sed sit amet maximus lectus. Fusce id lorem quis mi sodales varius a + quis justo. Proin est dui, porta non nisl sodales, feugiat faucibus + nisl. Sed id ex consectetur, faucibus eros sit amet, efficitur quam. Ut + vitae venenatis urna, non convallis magna. Integer dapibus ac sem eget + dictum. Integer ut nisi tincidunt, sollicitudin dui vel, faucibus purus. + Nunc rhoncus turpis eget sem commodo, eu ornare lorem vulputate. + Praesent libero nunc, lacinia sit amet lectus vitae, eleifend hendrerit + diam. In nec lacus mattis, rutrum orci fermentum, vestibulum eros. Nunc + a lobortis purus. +

+

+ Duis nisl diam, pharetra non leo id, facilisis sollicitudin tortor. + Donec efficitur augue lorem, lacinia scelerisque magna eleifend in. + Nulla vestibulum dolor nec fermentum maximus. Maecenas facilisis, nisi + at tempor eleifend, quam tortor ultricies tellus, id convallis enim + dolor nec sapien. Pellentesque sed blandit est. Morbi eu nisi + sollicitudin, elementum libero et, tempor justo. Donec et justo quis + dolor semper tempus et at arcu. Donec in sem id erat maximus feugiat. + Cras ultricies nisi turpis, vitae tincidunt sapien mollis ac. Maecenas + efficitur sed lectus a ullamcorper. Ut lacus tellus, viverra vel + consequat et, suscipit a urna. Proin id ultricies arcu. Etiam non + condimentum enim, sodales laoreet orci. +

+

+ Aliquam bibendum bibendum ornare. Aliquam ultricies diam quis dui + maximus, nec dictum velit convallis. Proin tempor scelerisque libero ac + convallis. Cras sollicitudin dignissim ipsum quis iaculis. Sed aliquam + congue magna, vitae malesuada ipsum dignissim porttitor. Maecenas libero + ante, faucibus nec pretium ut, sollicitudin sit amet neque. Donec + sodales risus neque, a tincidunt purus ultrices quis. In mattis nisi + ipsum, et lacinia dui suscipit ac. Donec dictum, dolor id tincidunt + accumsan, tortor lorem posuere nisl, id consequat dui nisi fringilla + nisi. Fusce arcu justo, ornare nec vehicula eget, egestas tincidunt + lorem. Nunc dignissim lobortis velit, id efficitur magna aliquam id. + Donec at massa at purus elementum ornare. Cras id finibus sapien. In hac + habitasse platea dictumst. +

+

+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per + inceptos himenaeos. Nunc et urna leo. Etiam mattis ornare eros. Etiam + aliquet nibh orci, in iaculis orci pretium id. Etiam nibh elit, viverra + non viverra eu, imperdiet eu tellus. Pellentesque lobortis leo a finibus + cursus. Orci varius natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Donec tristique tempor tristique. Morbi iaculis + posuere aliquet. Donec mattis sed dui a fringilla. Sed urna tellus, + pulvinar in mollis ut, congue vel risus. +

+

+ Nullam scelerisque quam at lorem tempus, sed vulputate dui venenatis. + Donec porttitor, tellus at vehicula laoreet, lacus nulla consectetur + nisi, ut posuere massa turpis a arcu. Quisque tellus arcu, consectetur + vitae molestie eu, posuere vitae nunc. Sed vestibulum justo ultricies + magna rutrum molestie. Pellentesque lobortis erat vel fermentum + faucibus. Phasellus in arcu sit amet quam tincidunt sollicitudin quis + sed ipsum. Nullam bibendum ac metus et mollis. Nunc tempor vel mi id + fermentum. Morbi maximus turpis lobortis, pretium nibh ac, blandit + metus. In elementum nisl ac ex maximus, in commodo libero egestas. Morbi + molestie cursus massa, laoreet tempor tortor vestibulum venenatis. + Curabitur semper fringilla dolor, ut sagittis lorem elementum ut. +

+

+ Duis porttitor massa nulla, id congue erat luctus at. Aliquam id + tristique quam. Morbi pharetra scelerisque elit. Donec porttitor ipsum + vitae risus hendrerit faucibus. Praesent finibus placerat leo nec + facilisis. Vestibulum ante ipsum primis in faucibus orci luctus et + ultrices posuere cubilia curae; Proin molestie interdum turpis id + ornare. Ut vestibulum libero in magna porta, sed porta dolor viverra. +

+

+ Duis eget congue orci, ac malesuada leo. Fusce ut mattis nibh. Ut + pretium lectus in neque hendrerit, ut eleifend neque ultrices. Nam id + sollicitudin est. Suspendisse eget vehicula urna, a eleifend diam. + Aenean nibh nisl, finibus ullamcorper cursus sit amet, efficitur gravida + nulla. Proin leo massa, volutpat sit amet dolor ut, suscipit rutrum + justo. Proin nec elit blandit, porttitor ipsum sed, sodales ipsum. In + venenatis dolor lacus. Vestibulum et volutpat augue. Cras vehicula sem + ac augue vestibulum, id feugiat elit consectetur. Nullam consectetur + elit enim, eget aliquam eros venenatis eget. +

+

+ Donec sodales ipsum ut ante aliquam, non molestie massa faucibus. + Aliquam eget vehicula metus. Aenean lacinia nulla quis nulla ultricies + tristique. Vestibulum sit amet consequat nunc. Nullam sagittis dolor at + sodales varius. Integer odio eros, sodales quis orci id, commodo luctus + diam. Nullam dapibus ullamcorper lobortis. Suspendisse posuere ipsum + ipsum, ac ultrices quam porttitor quis. Maecenas auctor justo et egestas + congue. Etiam pharetra libero eget nisl rhoncus congue. Cras vehicula + dolor finibus tortor dignissim ultrices. +

+

+ Vestibulum quis aliquet neque, non pretium ligula. Donec imperdiet + tellus nec dolor accumsan, at congue ante euismod. Praesent egestas ac + lectus in auctor. Praesent euismod faucibus enim eget vulputate. Lorem + ipsum dolor sit amet, consectetur adipiscing elit. Nunc vitae convallis + tortor. Sed eleifend tellus quis nisl faucibus, sit amet aliquet tellus + placerat. Morbi ultrices enim mauris, et tincidunt lorem semper sed. +

+

+ Duis tincidunt ligula lacus, a cursus tortor tincidunt ac. Duis nulla + nulla, viverra sit amet augue vitae, lobortis consequat nibh. Morbi + dignissim, est nec consequat elementum, dolor est tempus velit, eu + laoreet mauris nibh eu nunc. Aenean finibus at justo eget consectetur. + Nam cursus sem felis, vitae blandit quam tincidunt sed. Praesent + bibendum luctus enim ut tempor. Phasellus tincidunt lectus vel laoreet + elementum. Maecenas sed quam sodales, porttitor augue in, faucibus eros. + Cras lacinia tortor sed lacus auctor, sit amet imperdiet dolor + hendrerit. +

+

+ Aliquam vel porta libero. In nec aliquam nisi. Donec pulvinar sapien + lacus, eu condimentum enim vestibulum eu. Aliquam erat volutpat. Aliquam + erat volutpat. Integer ac nisl ipsum. Nulla at congue quam. Etiam nec + libero ac dolor efficitur sodales ut et diam. Cras in sodales dolor. + Quisque elementum mattis elit non sagittis. +

+

+ Aliquam maximus nisl semper tristique blandit. Vivamus et porttitor + urna. Nulla facilisi. Nullam vitae condimentum purus. Lorem ipsum dolor + sit amet, consectetur adipiscing elit. Nulla dignissim dolor id felis + pellentesque, quis sodales dolor tincidunt. Morbi non posuere est. + Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere + cubilia curae; +

+

+ Fusce nec metus sit amet ligula pulvinar lobortis. Suspendisse dictum + lorem vitae quam tempor facilisis. Duis cursus eu lacus in volutpat. + Suspendisse fermentum nulla a purus vestibulum, in blandit lacus + hendrerit. Praesent rhoncus, nulla non varius dapibus, sapien nulla + blandit lorem, a aliquet ex ligula a sapien. Vivamus ac tortor + ultricies, molestie ex vitae, placerat elit. Ut lobortis nisl nec sapien + pretium ullamcorper convallis sed tortor. Suspendisse blandit urna eu + dolor pharetra, a pretium nibh consequat. Integer at vulputate erat. Nam + in mi urna. Aenean lectus mauris, condimentum in porttitor et, posuere + vitae elit. Aenean tempor ipsum ut pharetra porta. +

+

+ Fusce efficitur elementum lectus, vitae euismod leo luctus quis. Duis + faucibus, metus non rhoncus ornare, neque sapien pretium nisi, ut + ullamcorper massa justo at turpis. Maecenas vestibulum facilisis nulla + vel tristique. Aliquam vitae mi dolor. Aliquam imperdiet ullamcorper + auctor. Sed nibh nulla, imperdiet vel sagittis in, dapibus a turpis. Ut + pulvinar ac dolor ac euismod. Morbi quis euismod nisl, sagittis interdum + velit. Integer condimentum quam ac quam malesuada, in ultrices risus + pulvinar. +

+
+ ); +} diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 132747e60a883..6cb21229c34e8 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -366,6 +366,7 @@ type Response = { _debugRootOwner?: null | ReactComponentInfo, // DEV-only _debugRootStack?: null | Error, // DEV-only _debugRootTask?: null | ConsoleTask, // DEV-only + _debugStartTime: number, // DEV-only _debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only _debugChannel?: void | DebugChannel, // DEV-only _blockedConsole?: null | SomeChunk, // DEV-only @@ -822,6 +823,7 @@ type InitializationReference = { key: string, ) => any, path: Array, + isDebug?: boolean, // DEV-only }; type InitializationHandler = { parent: null | InitializationHandler, @@ -872,6 +874,7 @@ function initializeDebugChunk( response, initializeDebugInfo, [''], // path + true, ); break; } @@ -894,6 +897,7 @@ function initializeDebugChunk( response, initializeDebugInfo, [''], // path + true, ); break; } @@ -1407,8 +1411,6 @@ function fulfillReference( const mappedValue = map(response, value, parentObject, key); parentObject[key] = mappedValue; - transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue); - // If this is the root object for a model reference, where `handler.value` // is a stale `null`, the resolved value can be used directly. if (key === '' && handler.value === null) { @@ -1427,19 +1429,27 @@ function fulfillReference( const element: any = handler.value; switch (key) { case '3': + transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue); element.props = mappedValue; break; case '4': + // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk. if (__DEV__) { element._owner = mappedValue; } break; case '5': + // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk. if (__DEV__) { element._debugStack = mappedValue; } break; + default: + transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue); + break; } + } else if (__DEV__ && !reference.isDebug) { + transferReferencedDebugInfo(handler.chunk, fulfilledChunk, mappedValue); } handler.deps--; @@ -1518,6 +1528,7 @@ function waitForReference( response: Response, map: (response: Response, model: any, parentObject: Object, key: string) => T, path: Array, + isAwaitingDebugInfo: boolean, // DEV-only ): T { if ( __DEV__ && @@ -1562,6 +1573,9 @@ function waitForReference( map, path, }; + if (__DEV__) { + reference.isDebug = isAwaitingDebugInfo; + } // Add "listener". if (referencedChunk.value === null) { @@ -1865,6 +1879,7 @@ function getOutlinedModel( response, map, path.slice(i - 1), + false, ); } case HALTED: { @@ -1910,11 +1925,27 @@ function getOutlinedModel( value = value[path[i]]; } const chunkValue = map(response, value, parentObject, key); - transferReferencedDebugInfo(initializingChunk, chunk, chunkValue); + if ( + parentObject[0] === REACT_ELEMENT_TYPE && + (key === '4' || key === '5') + ) { + // If we're resolving the "owner" or "stack" slot of an Element array, we don't call + // transferReferencedDebugInfo because this reference is to a debug chunk. + } else { + transferReferencedDebugInfo(initializingChunk, chunk, chunkValue); + } return chunkValue; case PENDING: case BLOCKED: - return waitForReference(chunk, parentObject, key, response, map, path); + return waitForReference( + chunk, + parentObject, + key, + response, + map, + path, + false, + ); case HALTED: { // Add a dependency that will never resolve. // TODO: Mark downstreams as halted too. @@ -2452,6 +2483,13 @@ function ResponseInstance( '"use ' + rootEnv.toLowerCase() + '"', ); } + if (enableAsyncDebugInfo) { + // Track the start of the fetch to the best of our knowledge. + // Note: createFromFetch allows this to be marked at the start of the fetch + // where as if you use createFromReadableStream from the body of the fetch + // then the start time is when the headers resolved. + this._debugStartTime = performance.now(); + } this._debugFindSourceMapURL = findSourceMapURL; this._debugChannel = debugChannel; this._blockedConsole = null; @@ -2520,16 +2558,99 @@ export type StreamState = { _rowTag: number, // 0 indicates that we're currently parsing the row ID _rowLength: number, // remaining bytes in the row. 0 indicates that we're looking for a newline. _buffer: Array, // chunks received so far as part of this row + _debugInfo: ReactIOInfo, // DEV-only + _debugTargetChunkSize: number, // DEV-only }; -export function createStreamState(): StreamState { - return { +export function createStreamState( + weakResponse: WeakResponse, // DEV-only + streamDebugValue: mixed, // DEV-only +): StreamState { + const streamState: StreamState = (({ _rowState: 0, _rowID: 0, _rowTag: 0, _rowLength: 0, _buffer: [], - }; + }: Omit): any); + if (__DEV__ && enableAsyncDebugInfo) { + const response = unwrapWeakResponse(weakResponse); + // Create an entry for the I/O to load the stream itself. + const debugValuePromise = Promise.resolve(streamDebugValue); + (debugValuePromise: any).status = 'fulfilled'; + (debugValuePromise: any).value = streamDebugValue; + streamState._debugInfo = { + name: 'RSC stream', + start: response._debugStartTime, + end: response._debugStartTime, // will be updated once we finish a chunk + byteSize: 0, // will be updated as we resolve a data chunk + value: debugValuePromise, + owner: response._debugRootOwner, + debugStack: response._debugRootStack, + debugTask: response._debugRootTask, + }; + streamState._debugTargetChunkSize = MIN_CHUNK_SIZE; + } + return streamState; +} + +// Depending on set up the chunks of a TLS connection can vary in size. However in practice it's often +// at 64kb or even multiples of 64kb. It can also be smaller but in practice it also happens that 64kb +// is around what you can download on fast 4G connection in 300ms which is what we throttle reveals at +// anyway. The net effect is that in practice, you won't really reveal anything in smaller units than +// 64kb if they're revealing at maximum speed in production. Therefore we group smaller chunks into +// these larger chunks since in production that's more realistic. +// TODO: If the stream is compressed, then you could fit much more in a single 300ms so maybe it should +// actually be larger. +const MIN_CHUNK_SIZE = 65536; + +function incrementChunkDebugInfo( + streamState: StreamState, + chunkLength: number, +): void { + if (__DEV__ && enableAsyncDebugInfo) { + const debugInfo: ReactIOInfo = streamState._debugInfo; + const endTime = performance.now(); + const previousEndTime = debugInfo.end; + const newByteLength = ((debugInfo.byteSize: any): number) + chunkLength; + if ( + newByteLength > streamState._debugTargetChunkSize || + endTime > previousEndTime + 10 + ) { + // This new chunk would overshoot the chunk size so therefore we treat it as its own new chunk + // by cloning the old one. Similarly, if some time has passed we assume that it was actually + // due to the server being unable to flush chunks faster e.g. due to I/O so it would be a + // new chunk in production even if the buffer hasn't been reached. + streamState._debugInfo = { + name: debugInfo.name, + start: debugInfo.start, + end: endTime, + byteSize: newByteLength, + value: debugInfo.value, + owner: debugInfo.owner, + debugStack: debugInfo.debugStack, + debugTask: debugInfo.debugTask, + }; + streamState._debugTargetChunkSize = newByteLength + MIN_CHUNK_SIZE; + } else { + // Otherwise we reuse the old chunk but update the end time and byteSize to the latest. + // $FlowFixMe[cannot-write] + debugInfo.end = endTime; + // $FlowFixMe[cannot-write] + debugInfo.byteSize = newByteLength; + } + } +} + +function resolveChunkDebugInfo( + streamState: StreamState, + chunk: SomeChunk, +): void { + if (__DEV__ && enableAsyncDebugInfo) { + // Push the currently resolving chunk's debug info representing the stream on the Promise + // that was waiting on the stream. + chunk._debugInfo.push({awaited: streamState._debugInfo}); + } } function resolveDebugHalt(response: Response, id: number): void { @@ -2553,17 +2674,33 @@ function resolveModel( response: Response, id: number, model: UninitializedModel, + streamState: StreamState, ): void { const chunks = response._chunks; const chunk = chunks.get(id); if (!chunk) { - chunks.set(id, createResolvedModelChunk(response, model)); + const newChunk: ResolvedModelChunk = createResolvedModelChunk( + response, + model, + ); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } else { + if (__DEV__) { + resolveChunkDebugInfo(streamState, chunk); + } resolveModelChunk(response, chunk, model); } } -function resolveText(response: Response, id: number, text: string): void { +function resolveText( + response: Response, + id: number, + text: string, + streamState: StreamState, +): void { const chunks = response._chunks; const chunk = chunks.get(id); if (chunk && chunk.status !== PENDING) { @@ -2577,13 +2714,18 @@ function resolveText(response: Response, id: number, text: string): void { if (chunk) { releasePendingChunk(response, chunk); } - chunks.set(id, createInitializedTextChunk(response, text)); + const newChunk = createInitializedTextChunk(response, text); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } function resolveBuffer( response: Response, id: number, buffer: $ArrayBufferView | ArrayBuffer, + streamState: StreamState, ): void { const chunks = response._chunks; const chunk = chunks.get(id); @@ -2598,13 +2740,18 @@ function resolveBuffer( if (chunk) { releasePendingChunk(response, chunk); } - chunks.set(id, createInitializedBufferChunk(response, buffer)); + const newChunk = createInitializedBufferChunk(response, buffer); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } function resolveModule( response: Response, id: number, model: UninitializedModel, + streamState: StreamState, ): void { const chunks = response._chunks; const chunk = chunks.get(id); @@ -2641,14 +2788,24 @@ function resolveModule( blockedChunk = (chunk: any); blockedChunk.status = BLOCKED; } + if (__DEV__) { + resolveChunkDebugInfo(streamState, blockedChunk); + } promise.then( () => resolveModuleChunk(response, blockedChunk, clientReference), error => triggerErrorOnChunk(response, blockedChunk, error), ); } else { if (!chunk) { - chunks.set(id, createResolvedModuleChunk(response, clientReference)); + const newChunk = createResolvedModuleChunk(response, clientReference); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } else { + if (__DEV__) { + resolveChunkDebugInfo(streamState, chunk); + } // This can't actually happen because we don't have any forward // references to modules. resolveModuleChunk(response, chunk, clientReference); @@ -2661,13 +2818,21 @@ function resolveStream>( id: number, stream: T, controller: FlightStreamController, + streamState: StreamState, ): void { const chunks = response._chunks; const chunk = chunks.get(id); if (!chunk) { - chunks.set(id, createInitializedStreamChunk(response, stream, controller)); + const newChunk = createInitializedStreamChunk(response, stream, controller); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); return; } + if (__DEV__) { + resolveChunkDebugInfo(streamState, chunk); + } if (chunk.status !== PENDING) { // We already resolved. We didn't expect to see this. return; @@ -2723,6 +2888,7 @@ function startReadableStream( response: Response, id: number, type: void | 'bytes', + streamState: StreamState, ): void { let controller: ReadableStreamController = (null: any); const stream = new ReadableStream({ @@ -2803,7 +2969,7 @@ function startReadableStream( } }, }; - resolveStream(response, id, stream, flightController); + resolveStream(response, id, stream, flightController, streamState); } function asyncIterator(this: $AsyncIterator) { @@ -2829,6 +2995,7 @@ function startAsyncIterable( response: Response, id: number, iterator: boolean, + streamState: StreamState, ): void { const buffer: Array>> = []; let closed = false; @@ -2946,6 +3113,7 @@ function startAsyncIterable( id, iterator ? iterable[ASYNC_ITERATOR]() : iterable, flightController, + streamState, ); } @@ -3025,7 +3193,11 @@ function resolveErrorDev( return error; } -function resolvePostponeProd(response: Response, id: number): void { +function resolvePostponeProd( + response: Response, + id: number, + streamState: StreamState, +): void { if (__DEV__) { // These errors should never make it into a build so we don't need to encode them in codes.json // eslint-disable-next-line react-internal/prod-error-codes @@ -3043,7 +3215,11 @@ function resolvePostponeProd(response: Response, id: number): void { const chunks = response._chunks; const chunk = chunks.get(id); if (!chunk) { - chunks.set(id, createErrorChunk(response, postponeInstance)); + const newChunk: ErroredChunk = createErrorChunk( + response, + postponeInstance, + ); + chunks.set(id, newChunk); } else { triggerErrorOnChunk(response, chunk, postponeInstance); } @@ -3055,6 +3231,7 @@ function resolvePostponeDev( reason: string, stack: ReactStackTrace, env: string, + streamState: StreamState, ): void { if (!__DEV__) { // These errors should never make it into a build so we don't need to encode them in codes.json @@ -3082,8 +3259,18 @@ function resolvePostponeDev( const chunks = response._chunks; const chunk = chunks.get(id); if (!chunk) { - chunks.set(id, createErrorChunk(response, postponeInstance)); + const newChunk: ErroredChunk = createErrorChunk( + response, + postponeInstance, + ); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } else { + if (__DEV__) { + resolveChunkDebugInfo(streamState, chunk); + } triggerErrorOnChunk(response, chunk, postponeInstance); } } @@ -3092,6 +3279,7 @@ function resolveErrorModel( response: Response, id: number, row: UninitializedModel, + streamState: StreamState, ): void { const chunks = response._chunks; const chunk = chunks.get(id); @@ -3105,8 +3293,18 @@ function resolveErrorModel( (error: any).digest = errorInfo.digest; const errorWithDigest: ErrorWithDigest = (error: any); if (!chunk) { - chunks.set(id, createErrorChunk(response, errorWithDigest)); + const newChunk: ErroredChunk = createErrorChunk( + response, + errorWithDigest, + ); + if (__DEV__) { + resolveChunkDebugInfo(streamState, newChunk); + } + chunks.set(id, newChunk); } else { + if (__DEV__) { + resolveChunkDebugInfo(streamState, chunk); + } triggerErrorOnChunk(response, chunk, errorWithDigest); } } @@ -3841,6 +4039,7 @@ function resolveTypedArray( lastChunk: Uint8Array, constructor: any, bytesPerElement: number, + streamState: StreamState, ): void { // If the view fits into one original buffer, we just reuse that buffer instead of // copying it out to a separate copy. This means that it's not always possible to @@ -3860,7 +4059,7 @@ function resolveTypedArray( chunk.byteOffset, chunk.byteLength / bytesPerElement, ); - resolveBuffer(response, id, view); + resolveBuffer(response, id, view, streamState); } function logComponentInfo( @@ -4177,6 +4376,7 @@ function flushInitialRenderPerformance(response: Response): void { function processFullBinaryRow( response: Response, + streamState: StreamState, id: number, tag: number, buffer: Array, @@ -4185,47 +4385,125 @@ function processFullBinaryRow( switch (tag) { case 65 /* "A" */: // We must always clone to extract it into a separate buffer instead of just a view. - resolveBuffer(response, id, mergeBuffer(buffer, chunk).buffer); + resolveBuffer( + response, + id, + mergeBuffer(buffer, chunk).buffer, + streamState, + ); return; case 79 /* "O" */: - resolveTypedArray(response, id, buffer, chunk, Int8Array, 1); + resolveTypedArray(response, id, buffer, chunk, Int8Array, 1, streamState); return; case 111 /* "o" */: resolveBuffer( response, id, buffer.length === 0 ? chunk : mergeBuffer(buffer, chunk), + streamState, ); return; case 85 /* "U" */: - resolveTypedArray(response, id, buffer, chunk, Uint8ClampedArray, 1); + resolveTypedArray( + response, + id, + buffer, + chunk, + Uint8ClampedArray, + 1, + streamState, + ); return; case 83 /* "S" */: - resolveTypedArray(response, id, buffer, chunk, Int16Array, 2); + resolveTypedArray( + response, + id, + buffer, + chunk, + Int16Array, + 2, + streamState, + ); return; case 115 /* "s" */: - resolveTypedArray(response, id, buffer, chunk, Uint16Array, 2); + resolveTypedArray( + response, + id, + buffer, + chunk, + Uint16Array, + 2, + streamState, + ); return; case 76 /* "L" */: - resolveTypedArray(response, id, buffer, chunk, Int32Array, 4); + resolveTypedArray( + response, + id, + buffer, + chunk, + Int32Array, + 4, + streamState, + ); return; case 108 /* "l" */: - resolveTypedArray(response, id, buffer, chunk, Uint32Array, 4); + resolveTypedArray( + response, + id, + buffer, + chunk, + Uint32Array, + 4, + streamState, + ); return; case 71 /* "G" */: - resolveTypedArray(response, id, buffer, chunk, Float32Array, 4); + resolveTypedArray( + response, + id, + buffer, + chunk, + Float32Array, + 4, + streamState, + ); return; case 103 /* "g" */: - resolveTypedArray(response, id, buffer, chunk, Float64Array, 8); + resolveTypedArray( + response, + id, + buffer, + chunk, + Float64Array, + 8, + streamState, + ); return; case 77 /* "M" */: - resolveTypedArray(response, id, buffer, chunk, BigInt64Array, 8); + resolveTypedArray( + response, + id, + buffer, + chunk, + BigInt64Array, + 8, + streamState, + ); return; case 109 /* "m" */: - resolveTypedArray(response, id, buffer, chunk, BigUint64Array, 8); + resolveTypedArray( + response, + id, + buffer, + chunk, + BigUint64Array, + 8, + streamState, + ); return; case 86 /* "V" */: - resolveTypedArray(response, id, buffer, chunk, DataView, 1); + resolveTypedArray(response, id, buffer, chunk, DataView, 1, streamState); return; } @@ -4235,18 +4513,19 @@ function processFullBinaryRow( row += readPartialStringChunk(stringDecoder, buffer[i]); } row += readFinalStringChunk(stringDecoder, chunk); - processFullStringRow(response, id, tag, row); + processFullStringRow(response, streamState, id, tag, row); } function processFullStringRow( response: Response, + streamState: StreamState, id: number, tag: number, row: string, ): void { switch (tag) { case 73 /* "I" */: { - resolveModule(response, id, row); + resolveModule(response, id, row, streamState); return; } case 72 /* "H" */: { @@ -4255,11 +4534,11 @@ function processFullStringRow( return; } case 69 /* "E" */: { - resolveErrorModel(response, id, row); + resolveErrorModel(response, id, row, streamState); return; } case 84 /* "T" */: { - resolveText(response, id, row); + resolveText(response, id, row, streamState); return; } case 78 /* "N" */: { @@ -4304,22 +4583,22 @@ function processFullStringRow( ); } case 82 /* "R" */: { - startReadableStream(response, id, undefined); + startReadableStream(response, id, undefined, streamState); return; } // Fallthrough case 114 /* "r" */: { - startReadableStream(response, id, 'bytes'); + startReadableStream(response, id, 'bytes', streamState); return; } // Fallthrough case 88 /* "X" */: { - startAsyncIterable(response, id, false); + startAsyncIterable(response, id, false, streamState); return; } // Fallthrough case 120 /* "x" */: { - startAsyncIterable(response, id, true); + startAsyncIterable(response, id, true, streamState); return; } // Fallthrough @@ -4338,9 +4617,10 @@ function processFullStringRow( postponeInfo.reason, postponeInfo.stack, postponeInfo.env, + streamState, ); } else { - resolvePostponeProd(response, id); + resolvePostponeProd(response, id, streamState); } return; } @@ -4352,7 +4632,7 @@ function processFullStringRow( return; } // We assume anything else is JSON. - resolveModel(response, id, row); + resolveModel(response, id, row, streamState); return; } } @@ -4375,6 +4655,7 @@ export function processBinaryChunk( let rowLength = streamState._rowLength; const buffer = streamState._buffer; const chunkLength = chunk.length; + incrementChunkDebugInfo(streamState, chunkLength); while (i < chunkLength) { let lastIdx = -1; switch (rowState) { @@ -4454,7 +4735,14 @@ export function processBinaryChunk( // We found the last chunk of the row const length = lastIdx - i; const lastChunk = new Uint8Array(chunk.buffer, offset, length); - processFullBinaryRow(response, rowID, rowTag, buffer, lastChunk); + processFullBinaryRow( + response, + streamState, + rowID, + rowTag, + buffer, + lastChunk, + ); // Reset state machine for a new row i = lastIdx; if (rowState === ROW_CHUNK_BY_NEWLINE) { @@ -4509,6 +4797,7 @@ export function processStringChunk( let rowLength = streamState._rowLength; const buffer = streamState._buffer; const chunkLength = chunk.length; + incrementChunkDebugInfo(streamState, chunkLength); while (i < chunkLength) { let lastIdx = -1; switch (rowState) { @@ -4607,7 +4896,7 @@ export function processStringChunk( ); } const lastChunk = chunk.slice(i, lastIdx); - processFullStringRow(response, rowID, rowTag, lastChunk); + processFullStringRow(response, streamState, rowID, rowTag, lastChunk); // Reset state machine for a new row i = lastIdx; if (rowState === ROW_CHUNK_BY_NEWLINE) { diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 3bcf4988feb86..da1dff04820fc 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -85,7 +85,11 @@ function getDebugInfo(obj) { if (debugInfo) { const copy = []; for (let i = 0; i < debugInfo.length; i++) { - copy.push(normalizeComponentInfo(debugInfo[i])); + if (debugInfo[i].awaited && debugInfo[i].awaited.name === 'RSC stream') { + // Ignore RSC stream I/O info. + } else { + copy.push(normalizeComponentInfo(debugInfo[i])); + } } return copy; } @@ -2832,7 +2836,7 @@ describe('ReactFlight', () => { transport: expect.arrayContaining([]), }, }, - {time: gate(flags => flags.enableAsyncDebugInfo) ? 23 : 21}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 53 : 21}, ] : undefined, ); @@ -2843,7 +2847,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyChildren[0])).toEqual( __DEV__ ? [ - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, // Clamped to the start + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, // Clamped to the start { name: 'ThirdPartyComponent', env: 'third-party', @@ -2851,15 +2855,15 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, - {time: gate(flags => flags.enableAsyncDebugInfo) ? 25 : 23}, // This last one is when the promise resolved into the first party. + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 55 : 23}, // This last one is when the promise resolved into the first party. ] : undefined, ); expect(getDebugInfo(thirdPartyChildren[1])).toEqual( __DEV__ ? [ - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, // Clamped to the start + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, // Clamped to the start { name: 'ThirdPartyLazyComponent', env: 'third-party', @@ -2867,14 +2871,14 @@ describe('ReactFlight', () => { stack: ' in myLazy (at **)\n in lazyInitializer (at **)', props: {}, }, - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, ] : undefined, ); expect(getDebugInfo(thirdPartyChildren[2])).toEqual( __DEV__ ? [ - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, { name: 'ThirdPartyFragmentComponent', env: 'third-party', @@ -2882,7 +2886,7 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 22}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 54 : 22}, ] : undefined, ); @@ -2963,7 +2967,7 @@ describe('ReactFlight', () => { { time: 16, }, - {time: 17}, + {time: 31}, ] : undefined, ); @@ -2972,7 +2976,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyFragment)).toEqual( __DEV__ ? [ - {time: 18}, + {time: 32}, { name: 'Keyed', env: 'Server', @@ -2983,12 +2987,14 @@ describe('ReactFlight', () => { }, }, { - time: 19, + time: 33, + }, + { + time: 33, }, { - time: 19, + time: 33, }, - {time: 19}, ] : undefined, ); @@ -2996,7 +3002,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyFragment.props.children)).toEqual( __DEV__ ? [ - {time: 19}, // Clamp to the start + {time: 33}, // Clamp to the start { name: 'ThirdPartyAsyncIterableComponent', env: 'third-party', @@ -3004,7 +3010,7 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: 19}, + {time: 33}, ] : undefined, ); @@ -3067,7 +3073,7 @@ describe('ReactFlight', () => { props: {}, }, {time: 16}, - {time: 17}, + {time: gate(flags => flags.enableAsyncDebugInfo) ? 24 : 17}, ] : undefined, ); diff --git a/packages/react-markup/src/ReactMarkupServer.js b/packages/react-markup/src/ReactMarkupServer.js index 5f69c3d44a317..0b35404cb80aa 100644 --- a/packages/react-markup/src/ReactMarkupServer.js +++ b/packages/react-markup/src/ReactMarkupServer.js @@ -81,7 +81,18 @@ export function experimental_renderToHTML( options?: MarkupOptions, ): Promise { return new Promise((resolve, reject) => { - const streamState = createFlightStreamState(); + const flightResponse = createFlightResponse( + null, + null, + null, + noServerCallOrFormAction, + noServerCallOrFormAction, + undefined, + undefined, + undefined, + false, + ); + const streamState = createFlightStreamState(flightResponse, null); const flightDestination = { push(chunk: string | null): boolean { if (chunk !== null) { @@ -175,17 +186,6 @@ export function experimental_renderToHTML( undefined, false, ); - const flightResponse = createFlightResponse( - null, - null, - null, - noServerCallOrFormAction, - noServerCallOrFormAction, - undefined, - undefined, - undefined, - false, - ); const resumableState = createResumableState( options ? options.identifierPrefix : undefined, undefined, diff --git a/packages/react-noop-renderer/src/ReactNoopFlightClient.js b/packages/react-noop-renderer/src/ReactNoopFlightClient.js index 3d8984a07fb18..fb6259f1a6acc 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightClient.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightClient.js @@ -77,7 +77,7 @@ function read(source: Source, options: ReadOptions): Thenable { ? options.debugChannel.onMessage : undefined, ); - const streamState = createStreamState(); + const streamState = createStreamState(response, source); for (let i = 0; i < source.length; i++) { processBinaryChunk(response, streamState, source[i], 0); } diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js index d61f132310547..cfc8dcf5f1b25 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js @@ -115,7 +115,7 @@ function startReadingFromUniversalStream( // This is the same as startReadingFromStream except this allows WebSocketStreams which // return ArrayBuffer and string chunks instead of Uint8Array chunks. We could potentially // always allow streams with variable chunk types. - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); const reader = stream.getReader(); function progress({ done, @@ -149,8 +149,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -194,9 +195,14 @@ function createFromReadableStream( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); } @@ -225,12 +231,13 @@ function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js index 3500b3f41f2b9..2bf32729472d2 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js @@ -66,7 +66,7 @@ function startReadingFromStream( stream: Readable, onEnd: () => void, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); stream.on('data', chunk => { if (typeof chunk === 'string') { diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js index 808b49d9d75d0..2f71ce2fa9597 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js @@ -141,7 +141,7 @@ function startReadingFromUniversalStream( // This is the same as startReadingFromStream except this allows WebSocketStreams which // return ArrayBuffer and string chunks instead of Uint8Array chunks. We could potentially // always allow streams with variable chunk types. - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); const reader = stream.getReader(); function progress({ done, @@ -175,8 +175,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -228,9 +229,14 @@ export function createFromReadableStream( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); } @@ -259,12 +265,13 @@ export function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js index 54c72968c22fe..f9c63ccbc3370 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js @@ -115,8 +115,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -158,9 +159,14 @@ export function createFromReadableStream( } }; startReadingFromStream(response, options.debugChannel.readable, handleDone); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); @@ -190,12 +196,13 @@ export function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js index b513fd3faca33..b874bcd7d44f5 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js @@ -61,7 +61,7 @@ function startReadingFromStream( stream: Readable, onEnd: () => void, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); stream.on('data', chunk => { if (typeof chunk === 'string') { diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js index dc4c99dabd055..b4b84f1c41b23 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js @@ -114,7 +114,7 @@ function startReadingFromUniversalStream( // This is the same as startReadingFromStream except this allows WebSocketStreams which // return ArrayBuffer and string chunks instead of Uint8Array chunks. We could potentially // always allow streams with variable chunk types. - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); const reader = stream.getReader(); function progress({ done, @@ -148,8 +148,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -194,9 +195,14 @@ function createFromReadableStream( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); } @@ -225,12 +231,13 @@ function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js index 245761b2722a6..b994841be18b3 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js @@ -117,8 +117,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -160,9 +161,14 @@ function createFromReadableStream( } }; startReadingFromStream(response, options.debugChannel.readable, handleDone); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); @@ -192,12 +198,13 @@ function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js index af8b7f41bc837..f174f10c0b88f 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js @@ -69,7 +69,7 @@ function startReadingFromStream( stream: Readable, onEnd: () => void, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); stream.on('data', chunk => { if (typeof chunk === 'string') { diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 98bc21576b08a..55b434ce3eeff 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -1239,17 +1239,24 @@ describe('ReactFlightDOMEdge', () => { name: 'Greeting', env: 'Server', }); - expect(lazyWrapper._debugInfo).toEqual([ - {time: 12}, - greetInfo, - {time: 13}, - expect.objectContaining({ - name: 'Container', - env: 'Server', - owner: greetInfo, - }), - {time: 14}, - ]); + if (gate(flags => flags.enableAsyncDebugInfo)) { + expect(lazyWrapper._debugInfo).toEqual([ + {time: 12}, + greetInfo, + {time: 13}, + expect.objectContaining({ + name: 'Container', + env: 'Server', + owner: greetInfo, + }), + {time: 14}, + expect.objectContaining({ + awaited: expect.objectContaining({ + name: 'RSC stream', + }), + }), + ]); + } // The owner that created the span was the outer server component. // We expect the debug info to be referentially equal to the owner. expect(greeting._owner).toBe(lazyWrapper._debugInfo[1]); diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js index dc4c99dabd055..b4b84f1c41b23 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js @@ -114,7 +114,7 @@ function startReadingFromUniversalStream( // This is the same as startReadingFromStream except this allows WebSocketStreams which // return ArrayBuffer and string chunks instead of Uint8Array chunks. We could potentially // always allow streams with variable chunk types. - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); const reader = stream.getReader(); function progress({ done, @@ -148,8 +148,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -194,9 +195,14 @@ function createFromReadableStream( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); } @@ -225,12 +231,13 @@ function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js index 7c9a707a546a6..63dae4954550a 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js @@ -117,8 +117,9 @@ function startReadingFromStream( response: FlightResponse, stream: ReadableStream, onDone: () => void, + debugValue: mixed, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, debugValue); const reader = stream.getReader(); function progress({ done, @@ -160,9 +161,14 @@ function createFromReadableStream( } }; startReadingFromStream(response, options.debugChannel.readable, handleDone); - startReadingFromStream(response, stream, handleDone); + startReadingFromStream(response, stream, handleDone, stream); } else { - startReadingFromStream(response, stream, close.bind(null, response)); + startReadingFromStream( + response, + stream, + close.bind(null, response), + stream, + ); } return getRoot(response); @@ -192,12 +198,13 @@ function createFromFetch( options.debugChannel.readable, handleDone, ); - startReadingFromStream(response, (r.body: any), handleDone); + startReadingFromStream(response, (r.body: any), handleDone, r); } else { startReadingFromStream( response, (r.body: any), close.bind(null, response), + r, ); } }, diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js index af8b7f41bc837..f174f10c0b88f 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js @@ -69,7 +69,7 @@ function startReadingFromStream( stream: Readable, onEnd: () => void, ): void { - const streamState = createStreamState(); + const streamState = createStreamState(response, stream); stream.on('data', chunk => { if (typeof chunk === 'string') { diff --git a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js index 0a98d87e363ca..ab4a054b019be 100644 --- a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js +++ b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js @@ -58,9 +58,16 @@ function normalizeIOInfo(ioInfo) { if (promise) { promise.then(); // init if (promise.status === 'fulfilled') { - copy.value = { - value: promise.value, - }; + if (ioInfo.name === 'RSC stream') { + copy.byteSize = 0; + copy.value = { + value: 'stream', + }; + } else { + copy.value = { + value: promise.value, + }; + } } else if (promise.status === 'rejected') { copy.value = { reason: promise.reason, @@ -76,11 +83,11 @@ function normalizeIOInfo(ioInfo) { function normalizeDebugInfo(original) { const {debugTask, debugStack, debugLocation, ...debugInfo} = original; - if (debugInfo.owner) { - debugInfo.owner = normalizeDebugInfo(debugInfo.owner); + if (original.owner) { + debugInfo.owner = normalizeDebugInfo(original.owner); } - if (debugInfo.awaited) { - debugInfo.awaited = normalizeIOInfo(debugInfo.awaited); + if (original.awaited) { + debugInfo.awaited = normalizeIOInfo(original.awaited); } if (debugInfo.props) { debugInfo.props = {}; @@ -90,8 +97,6 @@ function normalizeDebugInfo(original) { return debugInfo; } else if (typeof debugInfo.time === 'number') { return {...debugInfo, time: 0}; - } else if (debugInfo.awaited) { - return {...debugInfo, awaited: normalizeIOInfo(debugInfo.awaited)}; } else { return debugInfo; } @@ -242,9 +247,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -266,9 +271,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -277,25 +282,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 193, + 198, 13, - 192, + 197, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 200, + 205, 26, - 199, + 204, 5, ], ], @@ -314,9 +319,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -325,17 +330,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 193, + 198, 13, - 192, + 197, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 200, + 205, 26, - 199, + 204, 5, ], ], @@ -360,9 +365,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -371,25 +376,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 194, + 199, 21, - 192, + 197, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 200, + 205, 20, - 199, + 204, 5, ], ], @@ -408,9 +413,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -419,17 +424,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 195, + 200, 21, - 192, + 197, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 200, + 205, 20, - 199, + 204, 5, ], ], @@ -449,9 +454,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 202, + 207, 60, - 199, + 204, 5, ], ], @@ -473,9 +478,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 211, + 216, 109, - 191, + 196, 50, ], ], @@ -484,17 +489,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 194, + 199, 21, - 192, + 197, 5, ], ], @@ -513,9 +518,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 202, + 207, 60, - 199, + 204, 5, ], ], @@ -524,9 +529,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "InnerComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 208, + 213, 35, - 205, + 210, 5, ], ], @@ -537,6 +542,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -695,9 +712,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 659, + 676, 40, - 640, + 657, 49, ], [ @@ -727,9 +744,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 659, + 676, 40, - 640, + 657, 49, ], [ @@ -746,25 +763,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 642, + 659, 13, - 641, + 658, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 647, + 664, 36, - 646, + 663, 5, ], ], @@ -783,9 +800,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 659, + 676, 40, - 640, + 657, 49, ], [ @@ -802,17 +819,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 642, + 659, 13, - 641, + 658, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 647, + 664, 36, - 646, + 663, 5, ], ], @@ -832,9 +849,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 649, + 666, 60, - 646, + 663, 5, ], ], @@ -853,9 +870,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 659, + 676, 40, - 640, + 657, 49, ], [ @@ -872,25 +889,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 642, + 659, 13, - 641, + 658, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 648, + 665, 22, - 646, + 663, 5, ], ], @@ -909,9 +926,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 649, + 666, 60, - 646, + 663, 5, ], ], @@ -920,9 +937,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "InnerComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 655, + 672, 40, - 652, + 669, 5, ], ], @@ -933,6 +950,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -985,9 +1014,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 954, + 983, 109, - 941, + 970, 80, ], ], @@ -1006,9 +1035,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 954, + 983, 109, - 941, + 970, 80, ], ], @@ -1025,9 +1054,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 954, + 983, 109, - 941, + 970, 80, ], ], @@ -1039,6 +1068,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -1087,9 +1128,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1056, + 1097, 109, - 1047, + 1088, 94, ], ], @@ -1097,6 +1138,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -1160,9 +1213,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1129, + 1182, 109, - 1105, + 1158, 50, ], ], @@ -1188,6 +1241,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -1244,9 +1309,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1213, + 1278, 109, - 1196, + 1261, 63, ], ], @@ -1263,17 +1328,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "fetchThirdParty", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 176, + 181, 40, - 174, + 179, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1209, + 1274, 24, - 1208, + 1273, 5, ], ], @@ -1295,17 +1360,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "fetchThirdParty", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 176, + 181, 40, - 174, + 179, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1209, + 1274, 24, - 1208, + 1273, 5, ], ], @@ -1314,25 +1379,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1198, + 1263, 13, - 1197, + 1262, 5, ], [ "ThirdPartyComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1204, + 1269, 24, - 1203, + 1268, 5, ], ], @@ -1351,17 +1416,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "fetchThirdParty", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 176, + 181, 40, - 174, + 179, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1209, + 1274, 24, - 1208, + 1273, 5, ], ], @@ -1370,17 +1435,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1198, + 1263, 13, - 1197, + 1262, 5, ], [ "ThirdPartyComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1204, + 1269, 24, - 1203, + 1268, 5, ], ], @@ -1405,17 +1470,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "fetchThirdParty", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 176, + 181, 40, - 174, + 179, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1209, + 1274, 24, - 1208, + 1273, 5, ], ], @@ -1424,25 +1489,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1199, + 1264, 13, - 1197, + 1262, 5, ], [ "ThirdPartyComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1204, + 1269, 18, - 1203, + 1268, 5, ], ], @@ -1461,17 +1526,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "fetchThirdParty", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 176, + 181, 40, - 174, + 179, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1209, + 1274, 24, - 1208, + 1273, 5, ], ], @@ -1480,17 +1545,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1199, + 1264, 13, - 1197, + 1262, 5, ], [ "ThirdPartyComponent", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1204, + 1269, 18, - 1203, + 1268, 5, ], ], @@ -1501,9 +1566,32 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "start": 0, + "value": { + "value": "stream", + }, + }, + }, { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -1565,9 +1653,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1529, + 1617, 40, - 1512, + 1600, 62, ], [ @@ -1597,9 +1685,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1529, + 1617, 40, - 1512, + 1600, 62, ], [ @@ -1616,25 +1704,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1514, + 1602, 13, - 1513, + 1601, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1524, + 1612, 13, - 1523, + 1611, 5, ], ], @@ -1653,9 +1741,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1529, + 1617, 40, - 1512, + 1600, 62, ], [ @@ -1672,17 +1760,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1514, + 1602, 13, - 1513, + 1601, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1524, + 1612, 13, - 1523, + 1611, 5, ], ], @@ -1702,9 +1790,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1525, + 1613, 60, - 1523, + 1611, 5, ], ], @@ -1726,9 +1814,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1529, + 1617, 40, - 1512, + 1600, 62, ], [ @@ -1745,25 +1833,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1514, + 1602, 13, - 1513, + 1601, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1524, + 1612, 13, - 1523, + 1611, 5, ], ], @@ -1782,9 +1870,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1525, + 1613, 60, - 1523, + 1611, 5, ], ], @@ -1793,9 +1881,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Child", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1519, + 1607, 28, - 1518, + 1606, 5, ], ], @@ -1806,6 +1894,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -1866,9 +1966,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1830, + 1930, 40, - 1814, + 1914, 57, ], [ @@ -1898,9 +1998,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1830, + 1930, 40, - 1814, + 1914, 57, ], [ @@ -1917,25 +2017,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1816, + 1916, 13, - 1815, + 1915, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1825, + 1925, 23, - 1824, + 1924, 5, ], ], @@ -1954,9 +2054,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1830, + 1930, 40, - 1814, + 1914, 57, ], [ @@ -1973,17 +2073,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1816, + 1916, 13, - 1815, + 1915, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1825, + 1925, 23, - 1824, + 1924, 5, ], ], @@ -2003,9 +2103,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1826, + 1926, 60, - 1824, + 1924, 5, ], ], @@ -2024,9 +2124,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1830, + 1930, 40, - 1814, + 1914, 57, ], [ @@ -2043,25 +2143,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1816, + 1916, 13, - 1815, + 1915, 25, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1825, + 1925, 23, - 1824, + 1924, 5, ], ], @@ -2075,9 +2175,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 1826, + 1926, 60, - 1824, + 1924, 5, ], ], @@ -2088,6 +2188,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -2150,9 +2262,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2182,9 +2294,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2201,25 +2313,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "delayTrice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2104, + 2216, 13, - 2102, + 2214, 5, ], [ "Bar", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2109, + 2221, 13, - 2108, + 2220, 5, ], ], @@ -2238,9 +2350,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2257,17 +2369,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delayTrice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2104, + 2216, 13, - 2102, + 2214, 5, ], [ "Bar", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2109, + 2221, 13, - 2108, + 2220, 5, ], ], @@ -2289,9 +2401,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2308,33 +2420,33 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "delayTwice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2098, + 2210, 13, - 2097, + 2209, 5, ], [ "delayTrice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2103, + 2215, 15, - 2102, + 2214, 5, ], [ "Bar", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2109, + 2221, 13, - 2108, + 2220, 5, ], ], @@ -2353,9 +2465,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2372,25 +2484,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delayTwice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2098, + 2210, 13, - 2097, + 2209, 5, ], [ "delayTrice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2103, + 2215, 15, - 2102, + 2214, 5, ], [ "Bar", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2109, + 2221, 13, - 2108, + 2220, 5, ], ], @@ -2412,9 +2524,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2431,17 +2543,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "delayTwice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2099, + 2211, 13, - 2097, + 2209, 5, ], ], @@ -2460,9 +2572,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2114, + 2226, 40, - 2096, + 2208, 80, ], [ @@ -2479,9 +2591,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delayTwice", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2099, + 2211, 13, - 2097, + 2209, 5, ], ], @@ -2492,6 +2604,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -2542,9 +2666,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2511, + 2635, 109, - 2500, + 2624, 58, ], ], @@ -2566,9 +2690,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2511, + 2635, 109, - 2500, + 2624, 58, ], ], @@ -2577,25 +2701,25 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2502, + 2626, 14, - 2501, + 2625, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2508, + 2632, 20, - 2507, + 2631, 5, ], ], @@ -2614,9 +2738,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2511, + 2635, 109, - 2500, + 2624, 58, ], ], @@ -2625,17 +2749,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "getData", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2502, + 2626, 23, - 2501, + 2625, 5, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2508, + 2632, 20, - 2507, + 2631, 5, ], ], @@ -2646,6 +2770,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -2702,9 +2838,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2666, + 2802, 40, - 2654, + 2790, 56, ], [ @@ -2734,9 +2870,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2666, + 2802, 40, - 2654, + 2790, 56, ], [ @@ -2753,17 +2889,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "delay", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 169, + 174, 12, - 168, + 173, 3, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2662, + 2798, 20, - 2661, + 2797, 5, ], ], @@ -2782,9 +2918,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2666, + 2802, 40, - 2654, + 2790, 56, ], [ @@ -2801,9 +2937,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2662, + 2798, 20, - 2661, + 2797, 5, ], ], @@ -2814,6 +2950,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } @@ -2884,9 +3032,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2843, + 2991, 40, - 2822, + 2970, 42, ], [ @@ -2916,9 +3064,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2843, + 2991, 40, - 2822, + 2970, 42, ], [ @@ -2935,17 +3083,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2829, + 2977, 15, - 2828, + 2976, 15, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2838, + 2986, 19, - 2837, + 2985, 5, ], ], @@ -2964,9 +3112,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2843, + 2991, 40, - 2822, + 2970, 42, ], [ @@ -2983,17 +3131,17 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2829, + 2977, 15, - 2828, + 2976, 15, ], [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2838, + 2986, 19, - 2837, + 2985, 5, ], ], @@ -3015,9 +3163,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2843, + 2991, 40, - 2822, + 2970, 42, ], [ @@ -3034,9 +3182,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2838, + 2986, 25, - 2837, + 2985, 5, ], ], @@ -3055,9 +3203,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2843, + 2991, 40, - 2822, + 2970, 42, ], [ @@ -3074,9 +3222,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 2838, + 2986, 25, - 2837, + 2985, 5, ], ], @@ -3087,6 +3235,18 @@ describe('ReactFlightAsyncDebugInfo', () => { { "time": 0, }, + { + "awaited": { + "byteSize": 0, + "end": 0, + "name": "RSC stream", + "owner": null, + "start": 0, + "value": { + "value": "stream", + }, + }, + }, ] `); } From acada3035fe2a0dacfafc0ad78914e77d11fb823 Mon Sep 17 00:00:00 2001 From: Joseph Savona <6425824+josephsavona@users.noreply.github.com> Date: Tue, 9 Sep 2025 14:07:47 -0700 Subject: [PATCH 3/3] [compiler] Fix false positive hook return mutation error (#34424) This was fun. We previously added the MaybeAlias effect in #33984 in order to describe the semantic that an unknown function call _may_ alias its return value in its result, but that we don't know this for sure. We record mutations through MaybeAlias edges when walking backward in the data flow graph, but downgrade them to conditional mutations. See the original PR for full context. That change was sufficient for the original case like ```js const frozen = useContext(); useEffect(() => { frozen.method().property = true; }, [...]); ``` But it wasn't sufficient for cases where the aliasing occured between operands: ```js const dispatch = useDispatch();
{ dispatch(...e.target.value) e.target.value = ...; }} /> ``` Here we would record a `Capture dispatch <- e.target` effect. Then during processing of the `event.target.value = ...` assignment we'd eventually _forward_ from `event` to `dispatch` (along a MaybeAlias edge). But in #33984 I missed that this forward walk also has to downgrade to conditional. In addition to that change, we also have to be a bit more precise about which set of effects we create for alias/capture/maybe-alias. The new logic is a bit clearer, I think: * If the value is frozen, it's an ImmutableCapture edge * If the values are mutable, it's a Capture * If it's a context->context, context->mutable, or mutable->context, count it as MaybeAlias. --- .../Inference/InferMutationAliasingEffects.ts | 74 +++++++++++------ .../Inference/InferMutationAliasingRanges.ts | 15 +++- ...-spread-event-marks-event-frozen.expect.md | 82 +++++++++++++++++++ ...ispatch-spread-event-marks-event-frozen.js | 30 +++++++ 4 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts index 8b82e685b0bd3..911f71329b440 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts @@ -748,10 +748,14 @@ function applyEffect( case 'Alias': case 'Capture': { CompilerError.invariant( - effect.kind === 'Capture' || initialized.has(effect.into.identifier.id), + effect.kind === 'Capture' || + effect.kind === 'MaybeAlias' || + initialized.has(effect.into.identifier.id), { - reason: `Expected destination value to already be initialized within this instruction for Alias effect`, - description: `Destination ${printPlace(effect.into)} is not initialized in this instruction`, + reason: `Expected destination to already be initialized within this instruction`, + description: + `Destination ${printPlace(effect.into)} is not initialized in this ` + + `instruction for effect ${printAliasingEffect(effect)}`, details: [ { kind: 'error', @@ -767,49 +771,67 @@ function applyEffect( * copy-on-write semantics, then we can prune the effect */ const intoKind = state.kind(effect.into).kind; - let isMutableDesination: boolean; + let destinationType: 'context' | 'mutable' | null = null; switch (intoKind) { - case ValueKind.Context: - case ValueKind.Mutable: - case ValueKind.MaybeFrozen: { - isMutableDesination = true; + case ValueKind.Context: { + destinationType = 'context'; break; } - default: { - isMutableDesination = false; + case ValueKind.Mutable: + case ValueKind.MaybeFrozen: { + destinationType = 'mutable'; break; } } const fromKind = state.kind(effect.from).kind; - let isMutableReferenceType: boolean; + let sourceType: 'context' | 'mutable' | 'frozen' | null = null; switch (fromKind) { + case ValueKind.Context: { + sourceType = 'context'; + break; + } case ValueKind.Global: case ValueKind.Primitive: { - isMutableReferenceType = false; break; } case ValueKind.Frozen: { - isMutableReferenceType = false; - applyEffect( - context, - state, - { - kind: 'ImmutableCapture', - from: effect.from, - into: effect.into, - }, - initialized, - effects, - ); + sourceType = 'frozen'; break; } default: { - isMutableReferenceType = true; + sourceType = 'mutable'; break; } } - if (isMutableDesination && isMutableReferenceType) { + + if (sourceType === 'frozen') { + applyEffect( + context, + state, + { + kind: 'ImmutableCapture', + from: effect.from, + into: effect.into, + }, + initialized, + effects, + ); + } else if ( + (sourceType === 'mutable' && destinationType === 'mutable') || + effect.kind === 'MaybeAlias' + ) { effects.push(effect); + } else if ( + (sourceType === 'context' && destinationType != null) || + (sourceType === 'mutable' && destinationType === 'context') + ) { + applyEffect( + context, + state, + {kind: 'MaybeAlias', from: effect.from, into: effect.into}, + initialized, + effects, + ); } break; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts index 32f84e1e28b2a..43148dc4c67fa 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts @@ -779,7 +779,13 @@ class AliasingState { if (edge.index >= index) { break; } - queue.push({place: edge.node, transitive, direction: 'forwards', kind}); + queue.push({ + place: edge.node, + transitive, + direction: 'forwards', + // Traversing a maybeAlias edge always downgrades to conditional mutation + kind: edge.kind === 'maybeAlias' ? MutationKind.Conditional : kind, + }); } for (const [alias, when] of node.createdFrom) { if (when >= index) { @@ -807,7 +813,12 @@ class AliasingState { if (when >= index) { continue; } - queue.push({place: alias, transitive, direction: 'backwards', kind}); + queue.push({ + place: alias, + transitive, + direction: 'backwards', + kind, + }); } /** * MaybeAlias indicates potential data flow from unknown function calls, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.expect.md new file mode 100644 index 0000000000000..699140137ac26 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.expect.md @@ -0,0 +1,82 @@ + +## Input + +```javascript +// @compilationMode:"infer" +function Component() { + const dispatch = useDispatch(); + // const [state, setState] = useState(0); + + return ( +
+ { + dispatch(...event.target); + event.target.value = ''; + }} + /> +
+ ); +} + +function useDispatch() { + 'use no memo'; + // skip compilation to make it easier to debug the above function + return (...values) => { + console.log(...values); + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @compilationMode:"infer" +function Component() { + const $ = _c(2); + const dispatch = useDispatch(); + let t0; + if ($[0] !== dispatch) { + t0 = ( +
+ { + dispatch(...event.target); + event.target.value = ""; + }} + /> +
+ ); + $[0] = dispatch; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +function useDispatch() { + "use no memo"; + // skip compilation to make it easier to debug the above function + return (...values) => { + console.log(...values); + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +### Eval output +(kind: ok)
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.js new file mode 100644 index 0000000000000..386729e407897 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dispatch-spread-event-marks-event-frozen.js @@ -0,0 +1,30 @@ +// @compilationMode:"infer" +function Component() { + const dispatch = useDispatch(); + // const [state, setState] = useState(0); + + return ( +
+ { + dispatch(...event.target); + event.target.value = ''; + }} + /> +
+ ); +} + +function useDispatch() { + 'use no memo'; + // skip compilation to make it easier to debug the above function + return (...values) => { + console.log(...values); + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +};