Suspense Component — Async Rendering Boundaries
Summary
Add a <Suspense> component to Gea that provides declarative async rendering boundaries with fallback UI, error handling, and SSR streaming integration — all while staying true to Gea's "just JavaScript" philosophy.
Unlike React's Suspense (which relies on promise-throwing), Gea's Suspense builds on existing primitives: async created(), GEA_SWAP_CHILD, and SSR deferreds. The result is a zero-new-concepts async boundary that feels native to the framework.
Motivation
The Problem Today
Gea currently has no unified way to handle async component loading states:
-
Lazy routes show empty containers — When resolveLazy() loads a route component (router.ts:320-346), the user sees nothing until the module resolves. There's no loading indicator, no error fallback.
-
async created() has no loading boundary — Components with async lifecycle hooks (component.tsx:434) block silently. Parent components can't display fallback UI while children load.
-
SSR deferreds are disconnected from client components — The SSR streaming system (context.deferreds) is powerful but operates at the server level only. There's no client-side equivalent that hydrates into the same boundary.
-
No error recovery — When async operations fail, components either crash silently or require manual try/catch in every component.
Why Now
- The router already needs loading states for lazy routes
- SSR streaming (
deferreds) already implements the server-side pattern — the client side is the missing half
docs/philosophy.md line 64 explicitly lists suspense boundaries as something Gea doesn't have — this proposal addresses that gap while respecting the philosophy
Design Principles
- No new primitives — No
createResource(), no use() hook, no promise-throwing. Just classes, methods, and props.
- Explicit over magic — Loading states, error states, and timeouts are all visible in the template.
- Composable — Works with existing
async created(), lazy routes, and SSR deferreds.
- Safe by default — Built-in race condition prevention, memory leak cleanup, and flicker avoidance.
Proposed API
Basic Usage
import { Suspense } from '@geajs/core'
class Dashboard extends Component {
template() {
return (
<Suspense
fallback={<Spinner />}
error={(err, retry) => <ErrorCard message={err.message} onRetry={retry} />}
>
<UserProfile /> {/* has async created() */}
<ActivityFeed /> {/* has async created() */}
</Suspense>
)
}
}
Props Interface
interface SuspenseProps {
// --- Core ---
fallback: Component | (() => JSX) // UI shown while children load
error?: (err: Error, retry: () => void) => JSX // Error boundary with retry
// --- Timing (Flicker Prevention) ---
timeout?: number // ms before showing fallback (default: 0, instant)
minimumFallback?: number // ms minimum fallback display (default: 300)
// --- Advanced ---
staleWhileRefresh?: boolean // Keep old content during re-fetch (default: false)
onResolve?: () => void // Callback when all children resolve
onError?: (err: Error) => void // Callback on error (for logging/telemetry)
onFallback?: () => void // Callback when fallback becomes visible
// --- SSR ---
ssrStreamId?: string // Links to SSR deferred chunk ID
}
Advanced: Stale-While-Refresh
Inspired by Solid.js's 5-state resource model. When staleWhileRefresh is enabled, re-fetches show the previous content with an optional overlay instead of flashing back to the fallback:
<Suspense
fallback={<Skeleton />}
staleWhileRefresh={true}
>
<DataTable query={this.currentQuery} />
</Suspense>
State transitions:
[initial] → fallback → content
↓ (re-fetch triggered)
content + refreshing class → new content
Advanced: Trigger-Based Loading (Inspired by Angular @defer)
<Suspense
fallback={<Placeholder />}
trigger="viewport" // Load when entering viewport
prefetch="idle" // Prefetch during browser idle
>
<HeavyChart />
</Suspense>
Supported triggers:
| Trigger |
Behavior |
Inspired By |
"immediate" |
Load on mount (default) |
React |
"idle" |
Load during requestIdleCallback |
Angular @defer(on idle) |
"viewport" |
Load when boundary enters viewport |
Angular @defer(on viewport) |
"interaction" |
Load on first user interaction |
Angular @defer(on interaction) |
"timer(ms)" |
Load after delay |
Angular @defer(on timer) |
"hover" |
Load on hover |
Angular @defer(on hover) |
Advanced: Nested Suspense
Nested boundaries are independent — an inner Suspense doesn't bubble up to the outer one:
<Suspense fallback={<PageSkeleton />}>
<Header />
<Suspense fallback={<FeedSkeleton />}>
<ActivityFeed />
</Suspense>
<Suspense fallback={<SidebarSkeleton />}>
<Recommendations />
</Suspense>
</Suspense>
Unlike React, where nested Suspense boundaries interact in confusing ways (especially during hydration), each Gea Suspense boundary is fully self-contained.
React Suspense Problems This Design Solves
This proposal explicitly addresses 10 documented problems with React's Suspense implementation:
1. Waterfall Problem
React: Sequential promise-throwing causes child components to load one-by-one instead of in parallel.
Gea: All async created() calls within a Suspense boundary are collected and run with Promise.all(). Children always load in parallel.
2. Promise-Throwing Anti-Pattern
React: Components communicate loading state by throwing promises — an abuse of the exception mechanism. Libraries must implement complex caching to avoid re-throws.
Gea: No promise-throwing. async created() is a normal async method. The Suspense boundary detects pending children through the component lifecycle, not exceptions.
3. No Native Data Fetching
React: Suspense has no built-in fetch integration — requires React Query, SWR, or the unfinished use() hook.
Gea: async created() already serves as the data-fetching lifecycle hook. Any async operation works — no wrapper library needed.
4. Race Conditions
React: Documented in facebook/react#35399 and facebook/react#33939. Rapid re-renders with Suspense can show stale data or flicker between states.
Gea: Built-in generation counter. Each async operation gets a monotonically increasing ID. When results arrive, stale responses (where generation < currentGeneration) are silently discarded.
5. Error Handling Gaps
React: Suspense has no error handling — requires a separate <ErrorBoundary> component wrapping each Suspense. Two components for one concern.
Gea: The error prop is built into <Suspense> itself, with a retry callback. One component handles both loading and error states.
6. SSR Streaming Bugs
React: Hydration mismatches with Suspense boundaries are a persistent source of bugs (facebook/react#36003).
Gea: SSR integration uses the existing deferreds streaming system. The server renders the fallback HTML with a known ID; the client Suspense boundary picks up where SSR left off via ssrStreamId. No hydration mismatch possible because both sides use the same placeholder→replace mechanism (GEA_SWAP_CHILD).
7. Hardcoded FALLBACK_THROTTLE_MS
React: A 300ms hardcoded throttle (FALLBACK_THROTTLE_MS) controls when fallbacks appear. It's not configurable. 128 upvotes requesting configurability.
Gea: Both timeout (delay before showing fallback) and minimumFallback (minimum display duration) are configurable per-boundary. Defaults are sensible (0ms and 300ms) but fully overridable.
8. Nested Suspense Complexity
React: Nested Suspense boundaries have complex interaction rules. Inner boundaries can "reveal" before outer ones in unintuitive ways during concurrent rendering.
Gea: Each Suspense boundary is fully independent. No cross-boundary state leaking. Nesting is simple composition — inner boundaries resolve on their own timeline.
9. Memory Leaks
React: When a Suspense-wrapped component unmounts during an async operation, the pending promise may hold references to unmounted component state.
Gea: Built-in AbortController integration (inspired by Qwik's cleanup()). When a Suspense boundary unmounts, all pending async operations are aborted. The created() lifecycle receives an abort signal.
10. Poor Developer Experience
React: Suspense error messages are cryptic. The "thrown promise" mechanism makes debugging difficult — stack traces point to React internals, not user code.
Gea: Standard async/await errors with clear stack traces. No promise-throwing means errors propagate naturally through try/catch. The onError callback provides a hook for logging and telemetry.
Framework Comparison: What We Adopted and Why
| Feature |
Source Framework |
Why Adopted |
Gea Adaptation |
| Fallback + Error in one component |
Vue |
Eliminates the two-component pattern (Suspense + ErrorBoundary) |
error prop with retry callback |
| Configurable timing |
Vue (timeout prop) |
Prevents UI flicker for fast responses |
timeout + minimumFallback props |
| Stale-while-refresh |
Solid.js (5-state resource) |
Better UX for data re-fetching |
staleWhileRefresh prop, CSS class-based |
| Trigger-based loading |
Angular (@defer) |
Reduces initial bundle size, loads on demand |
trigger prop with same trigger types |
| Prefetch during idle |
Angular (prefetch) |
Improves perceived performance |
prefetch prop |
| Abort on unmount |
Qwik (cleanup()) |
Prevents memory leaks |
AbortController passed to async created() |
| Event callbacks |
Vue (onResolve, onPending, onFallback) |
Enables telemetry and coordination |
onResolve, onError, onFallback callbacks |
| SSR streaming integration |
Gea's own deferreds |
Unified server/client async boundary |
ssrStreamId prop linking to SSR deferreds |
| Parallel child loading |
Original design |
Solves React's waterfall problem |
Promise.all() for all children in boundary |
| Generation-based race prevention |
Original design |
Solves React's documented race conditions |
Monotonic generation counter |
What We Deliberately Did NOT Adopt
| Rejected Feature |
Source |
Why Rejected |
| Promise-throwing |
React |
Anti-pattern: abuses exception mechanism, makes debugging hard |
createResource() / signals |
Solid.js |
New primitive — violates "just JavaScript" philosophy |
$state runes for async |
Svelte |
Compiler-specific syntax — Gea prefers standard JS |
Separate <ErrorBoundary> |
React |
Unnecessary indirection — error handling belongs in the async boundary |
v-model / emit for Suspense state |
Vue |
Framework-specific communication — Gea uses direct prop mutation |
| Concurrent mode / lanes |
React |
Massive complexity for marginal benefit in Gea's architecture |
Technical Architecture
Integration Points in Existing Codebase
packages/gea/src/lib/
├── base/
│ ├── component.tsx # Hook into async created() lifecycle (line 434)
│ │ # Reuse GEA_SWAP_CHILD for fallback↔content swap (line 1266)
│ │ # Reuse GEA_PATCH_COND for conditional rendering (line 1031)
│ └── component-manager.ts # Track Suspense boundaries in component tree
├── suspense/ # NEW — Suspense module
│ ├── suspense.ts # Core Suspense component class
│ ├── types.ts # SuspenseProps, SuspenseState interfaces
│ ├── abort.ts # AbortController lifecycle integration
│ └── triggers.ts # Viewport/idle/interaction trigger implementations
├── router/
│ ├── lazy.ts # Integrate with Suspense for loading states
│ └── router.ts # Wrap lazy route resolution in Suspense boundary
└── index.ts # Export Suspense from @geajs/core
How It Works: Lifecycle
1. <Suspense> mounts
├── Checks for ssrStreamId → if found, hydrate from SSR deferred
├── Evaluates trigger prop → if not "immediate", sets up observer/listener
└── When triggered:
├── Collects all child components with async created()
├── Starts timeout timer (if timeout > 0)
├── Runs Promise.all(children.map(c => c.created()))
│ ├── On resolve:
│ │ ├── If minimumFallback not elapsed → wait remaining time
│ │ ├── GEA_SWAP_CHILD: replace fallback with resolved content
│ │ └── Call onResolve()
│ └── On reject:
│ ├── Increment error state
│ ├── Render error prop with (err, retry) args
│ └── Call onError(err)
└── On unmount:
├── AbortController.abort() for all pending operations
└── Clean up IntersectionObserver/listeners
How It Works: GEA_SWAP_CHILD Reuse
The existing GEA_SWAP_CHILD mechanism (component.tsx:1266-1292) is a perfect fit:
// Current: swaps child component at a marker position
Component.prototype[GEA_SWAP_CHILD] = function(markerId, newChild) {
const marker = _getEl(this.id + '-' + markerId)
const oldEl = marker.nextElementSibling
if (oldEl) oldEl.remove()
if (!newChild) return
marker.insertAdjacentHTML('afterend', String(newChild.template(newChild.props)).trim())
// ... mount new child
}
// Suspense uses same mechanism:
// 1. Initial render: insert fallback after marker
// 2. On resolve: GEA_SWAP_CHILD replaces fallback with actual content
// 3. On error: GEA_SWAP_CHILD replaces fallback with error UI
How It Works: SSR Integration
Server:
1. Suspense renders fallback HTML with ssrStreamId as element ID
2. Adds a DeferredChunk to context.deferreds
3. When async data resolves, streams <script> that replaces placeholder
Client (Hydration):
1. Suspense finds existing element with ssrStreamId
2. If content already replaced by SSR stream → skip to "resolved" state
3. If still showing fallback → take over async operation client-side
Compiler Support (@geajs/vite-plugin)
The Vite plugin needs minimal changes:
- Detect
<Suspense> in JSX — Treat as a built-in component (like how fragments are handled)
- Generate observer bindings — Wire up reactive props (
fallback, error, timeout, etc.)
- Async child detection — At compile time, identify children with
async created() and generate the collection code
Implementation Plan
Phase 1: Core Suspense (Fallback + Resolve)
Scope: Minimal viable Suspense with fallback and content swapping.
Deliverable: <Suspense fallback={<Spinner />}><AsyncChild /></Suspense> works.
Phase 2: Error Handling + Retry
Scope: Integrated error boundary with retry mechanism.
Deliverable: error={(err, retry) => <ErrorUI onRetry={retry} />} works.
Phase 3: Timing + Flicker Prevention
Scope: Configurable timing to prevent UI flicker.
Deliverable: Fast responses don't flash a loading spinner. Slow responses show spinner for at least minimumFallback ms.
Phase 4: Stale-While-Refresh + Abort
Scope: Advanced data fetching patterns.
Deliverable: Re-fetching data shows old content with a "refreshing" indicator instead of flashing to skeleton.
Phase 5: Trigger-Based Loading
Scope: Deferred loading based on viewport, interaction, idle, etc.
Deliverable: <Suspense trigger="viewport"><HeavyChart /></Suspense> loads the chart only when scrolled into view.
Phase 6: SSR Streaming Integration
Scope: Unified server/client Suspense boundary.
Deliverable: Suspense works identically in SSR and CSR, with streaming support for slow server-side data.
Philosophy Alignment
docs/philosophy.md states:
"Gea offers fewer abstractions than React or Vue. It doesn't have hooks, context providers, portals, suspense boundaries, or server components."
This proposal adds suspense boundaries while respecting every other principle in that document:
- No new primitives — Uses
async created() (existing), GEA_SWAP_CHILD (existing), and standard AbortController (web platform)
- No arbitrary rules — No "hooks must be called at the top level", no dependency arrays, no
.value unwrapping
- JavaScript semantics — Props are props. Callbacks are functions. Async is async/await.
- The magic is invisible — The Vite plugin handles wiring. User code is plain classes and JSX.
- Object-oriented — Suspense is a class extending Component. It has methods, properties, and lifecycle hooks — standard OOP.
The philosophy doc will need a small update: remove "suspense boundaries" from the list of things Gea doesn't have, and add a note about how Gea's Suspense differs from React's (no promise-throwing, no new concepts).
Open Questions
-
Should trigger be Phase 1 or Phase 5? — Trigger-based loading is valuable but adds complexity. Starting simple and iterating seems prudent.
-
Should staleWhileRefresh use a CSS class or a render prop? — CSS class (suspense-refreshing) is simpler. Render prop gives more control. Could support both.
-
Should lazy routes automatically wrap in Suspense? — The router currently shows empty containers during lazy loading. Auto-wrapping would be a breaking change (now there's a fallback where there wasn't one). Opt-in via router config might be better.
-
AbortController API — Should async created() receive the signal as a parameter, or should it be available as this.abortSignal? The latter is more "Gea-like" (property on class).
References
Suspense Component — Async Rendering Boundaries
Summary
Add a
<Suspense>component to Gea that provides declarative async rendering boundaries with fallback UI, error handling, and SSR streaming integration — all while staying true to Gea's "just JavaScript" philosophy.Unlike React's Suspense (which relies on promise-throwing), Gea's Suspense builds on existing primitives:
async created(),GEA_SWAP_CHILD, and SSRdeferreds. The result is a zero-new-concepts async boundary that feels native to the framework.Motivation
The Problem Today
Gea currently has no unified way to handle async component loading states:
Lazy routes show empty containers — When
resolveLazy()loads a route component (router.ts:320-346), the user sees nothing until the module resolves. There's no loading indicator, no error fallback.async created()has no loading boundary — Components with async lifecycle hooks (component.tsx:434) block silently. Parent components can't display fallback UI while children load.SSR deferreds are disconnected from client components — The SSR streaming system (
context.deferreds) is powerful but operates at the server level only. There's no client-side equivalent that hydrates into the same boundary.No error recovery — When async operations fail, components either crash silently or require manual try/catch in every component.
Why Now
deferreds) already implements the server-side pattern — the client side is the missing halfdocs/philosophy.mdline 64 explicitly lists suspense boundaries as something Gea doesn't have — this proposal addresses that gap while respecting the philosophyDesign Principles
createResource(), nouse()hook, no promise-throwing. Just classes, methods, and props.async created(), lazy routes, and SSR deferreds.Proposed API
Basic Usage
Props Interface
Advanced: Stale-While-Refresh
Inspired by Solid.js's 5-state resource model. When
staleWhileRefreshis enabled, re-fetches show the previous content with an optional overlay instead of flashing back to the fallback:State transitions:
Advanced: Trigger-Based Loading (Inspired by Angular
@defer)Supported triggers:
"immediate""idle"requestIdleCallback@defer(on idle)"viewport"@defer(on viewport)"interaction"@defer(on interaction)"timer(ms)"@defer(on timer)"hover"@defer(on hover)Advanced: Nested Suspense
Nested boundaries are independent — an inner Suspense doesn't bubble up to the outer one:
Unlike React, where nested Suspense boundaries interact in confusing ways (especially during hydration), each Gea Suspense boundary is fully self-contained.
React Suspense Problems This Design Solves
This proposal explicitly addresses 10 documented problems with React's Suspense implementation:
1. Waterfall Problem
React: Sequential promise-throwing causes child components to load one-by-one instead of in parallel.
Gea: All
async created()calls within a Suspense boundary are collected and run withPromise.all(). Children always load in parallel.2. Promise-Throwing Anti-Pattern
React: Components communicate loading state by throwing promises — an abuse of the exception mechanism. Libraries must implement complex caching to avoid re-throws.
Gea: No promise-throwing.
async created()is a normal async method. The Suspense boundary detects pending children through the component lifecycle, not exceptions.3. No Native Data Fetching
React: Suspense has no built-in fetch integration — requires React Query, SWR, or the unfinished
use()hook.Gea:
async created()already serves as the data-fetching lifecycle hook. Any async operation works — no wrapper library needed.4. Race Conditions
React: Documented in facebook/react#35399 and facebook/react#33939. Rapid re-renders with Suspense can show stale data or flicker between states.
Gea: Built-in generation counter. Each async operation gets a monotonically increasing ID. When results arrive, stale responses (where
generation < currentGeneration) are silently discarded.5. Error Handling Gaps
React: Suspense has no error handling — requires a separate
<ErrorBoundary>component wrapping each Suspense. Two components for one concern.Gea: The
errorprop is built into<Suspense>itself, with aretrycallback. One component handles both loading and error states.6. SSR Streaming Bugs
React: Hydration mismatches with Suspense boundaries are a persistent source of bugs (facebook/react#36003).
Gea: SSR integration uses the existing
deferredsstreaming system. The server renders the fallback HTML with a known ID; the client Suspense boundary picks up where SSR left off viassrStreamId. No hydration mismatch possible because both sides use the same placeholder→replace mechanism (GEA_SWAP_CHILD).7. Hardcoded
FALLBACK_THROTTLE_MSReact: A 300ms hardcoded throttle (
FALLBACK_THROTTLE_MS) controls when fallbacks appear. It's not configurable. 128 upvotes requesting configurability.Gea: Both
timeout(delay before showing fallback) andminimumFallback(minimum display duration) are configurable per-boundary. Defaults are sensible (0ms and 300ms) but fully overridable.8. Nested Suspense Complexity
React: Nested Suspense boundaries have complex interaction rules. Inner boundaries can "reveal" before outer ones in unintuitive ways during concurrent rendering.
Gea: Each Suspense boundary is fully independent. No cross-boundary state leaking. Nesting is simple composition — inner boundaries resolve on their own timeline.
9. Memory Leaks
React: When a Suspense-wrapped component unmounts during an async operation, the pending promise may hold references to unmounted component state.
Gea: Built-in
AbortControllerintegration (inspired by Qwik'scleanup()). When a Suspense boundary unmounts, all pending async operations are aborted. Thecreated()lifecycle receives an abort signal.10. Poor Developer Experience
React: Suspense error messages are cryptic. The "thrown promise" mechanism makes debugging difficult — stack traces point to React internals, not user code.
Gea: Standard async/await errors with clear stack traces. No promise-throwing means errors propagate naturally through
try/catch. TheonErrorcallback provides a hook for logging and telemetry.Framework Comparison: What We Adopted and Why
errorprop withretrycallbacktimeoutprop)timeout+minimumFallbackpropsstaleWhileRefreshprop, CSS class-based@defer)triggerprop with same trigger typesprefetch)prefetchpropcleanup())AbortControllerpassed toasync created()onResolve,onPending,onFallback)onResolve,onError,onFallbackcallbacksdeferredsssrStreamIdprop linking to SSR deferredsPromise.all()for all children in boundaryWhat We Deliberately Did NOT Adopt
createResource()/ signals$staterunes for async<ErrorBoundary>v-model/emitfor Suspense stateTechnical Architecture
Integration Points in Existing Codebase
How It Works: Lifecycle
How It Works: GEA_SWAP_CHILD Reuse
The existing
GEA_SWAP_CHILDmechanism (component.tsx:1266-1292) is a perfect fit:How It Works: SSR Integration
Compiler Support (
@geajs/vite-plugin)The Vite plugin needs minimal changes:
<Suspense>in JSX — Treat as a built-in component (like how fragments are handled)fallback,error,timeout, etc.)async created()and generate the collection codeImplementation Plan
Phase 1: Core Suspense (Fallback + Resolve)
Scope: Minimal viable Suspense with fallback and content swapping.
packages/gea/src/lib/suspense/suspense.ts— Suspense class extending Componentfallbackprop rendering viaGEA_SWAP_CHILDPromise.all()@geajs/coreDeliverable:
<Suspense fallback={<Spinner />}><AsyncChild /></Suspense>works.Phase 2: Error Handling + Retry
Scope: Integrated error boundary with retry mechanism.
errorprop —(err: Error, retry: () => void) => JSXasync created()on all failed childrenonErrorcallback for telemetryDeliverable:
error={(err, retry) => <ErrorUI onRetry={retry} />}works.Phase 3: Timing + Flicker Prevention
Scope: Configurable timing to prevent UI flicker.
timeoutprop — delay before showing fallbackminimumFallbackprop — minimum fallback display durationonResolveandonFallbackcallbacksDeliverable: Fast responses don't flash a loading spinner. Slow responses show spinner for at least
minimumFallbackms.Phase 4: Stale-While-Refresh + Abort
Scope: Advanced data fetching patterns.
staleWhileRefresh— keep old content during re-fetch, add CSS classAbortController— pass signal toasync created(), abort on unmountDeliverable: Re-fetching data shows old content with a "refreshing" indicator instead of flashing to skeleton.
Phase 5: Trigger-Based Loading
Scope: Deferred loading based on viewport, interaction, idle, etc.
triggerprop withIntersectionObserver(viewport),requestIdleCallback(idle), event listeners (interaction, hover)prefetchprop for early loadingtimer(ms)triggerDeliverable:
<Suspense trigger="viewport"><HeavyChart /></Suspense>loads the chart only when scrolled into view.Phase 6: SSR Streaming Integration
Scope: Unified server/client Suspense boundary.
ssrStreamIdprop linking to SSRdeferredsDeliverable: Suspense works identically in SSR and CSR, with streaming support for slow server-side data.
Philosophy Alignment
docs/philosophy.mdstates:This proposal adds suspense boundaries while respecting every other principle in that document:
async created()(existing),GEA_SWAP_CHILD(existing), and standardAbortController(web platform).valueunwrappingThe philosophy doc will need a small update: remove "suspense boundaries" from the list of things Gea doesn't have, and add a note about how Gea's Suspense differs from React's (no promise-throwing, no new concepts).
Open Questions
Should
triggerbe Phase 1 or Phase 5? — Trigger-based loading is valuable but adds complexity. Starting simple and iterating seems prudent.Should
staleWhileRefreshuse a CSS class or a render prop? — CSS class (suspense-refreshing) is simpler. Render prop gives more control. Could support both.Should lazy routes automatically wrap in Suspense? — The router currently shows empty containers during lazy loading. Auto-wrapping would be a breaking change (now there's a fallback where there wasn't one). Opt-in via router config might be better.
AbortController API — Should
async created()receive the signal as a parameter, or should it be available asthis.abortSignal? The latter is more "Gea-like" (property on class).References
component.tsx(async created(),GEA_SWAP_CHILD,GEA_PATCH_COND),lazy.ts, SSRdeferreds