Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/next/src/client/app-call-server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
import { startTransition } from 'react'
import { ACTION_SERVER_ACTION } from './components/router-reducer/router-reducer-types'
import { dispatchAppRouterAction } from './components/use-action-queue'
import {
getServerActionDispatchContext,
type ServerActionDispatchScope,
} from './server-action-dispatch'

export async function callServer(actionId: string, actionArgs: any[]) {
function dispatchServerAction(
actionId: string,
actionArgs: any[],
scope?: ServerActionDispatchScope
) {
return new Promise((resolve, reject) => {
startTransition(() => {
dispatchAppRouterAction({
type: ACTION_SERVER_ACTION,
actionId,
actionArgs,
actionDispatchContext: getServerActionDispatchContext(actionId, scope),
resolve,
reject,
})
})
})
}

export async function callServer(actionId: string, actionArgs: any[]) {
return dispatchServerAction(actionId, actionArgs)
}

export function createScopedCallServer(scope: ServerActionDispatchScope) {
return (actionId: string, actionArgs: any[]) =>
dispatchServerAction(actionId, actionArgs, scope)
}
21 changes: 17 additions & 4 deletions packages/next/src/client/app-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
onCaughtError,
onUncaughtError,
} from './react-client-callbacks/error-boundary-callbacks'
import { callServer } from './app-call-server'
import { createScopedCallServer } from './app-call-server'
import { findSourceMapURL } from './app-find-source-map-url'
import {
type AppRouterActionQueue,
Expand All @@ -29,6 +29,10 @@ import { getDeploymentId } from '../shared/lib/deployment-id'
import { setNavigationBuildId } from './navigation-build-id'
import type { ClientInstrumentationModules } from './router-transition-types'
import { initializeRouterTransitionModules } from './components/router-transition'
import {
createServerActionDispatchScope,
setServerActionDispatchScopeRoutingKeys,
} from './server-action-dispatch'

/// <reference types="react-dom/experimental" />

Expand Down Expand Up @@ -225,13 +229,18 @@ if (
}

let initialServerResponse: Promise<InitialRSCPayload>
const initialActionDispatchScope = createServerActionDispatchScope(
window.location.href,
null
)
const initialCallServer = createScopedCallServer(initialActionDispatchScope)
if (instantTestStaticFetch) {
// Instant Navigation Testing API: hydrate from the static RSC payload
// fetch kicked off by an injected <script> tag, instead of the inline
// Flight data (which is not present in the static shell).
initialServerResponse = Promise.resolve(
createFromFetch<InitialRSCPayload>(instantTestStaticFetch, {
callServer,
callServer: initialCallServer,
findSourceMapURL,
debugChannel,
// The static fetch response is a partial stream (static-only Flight
Expand All @@ -255,7 +264,7 @@ if (instantTestStaticFetch) {
window.__NEXT_CLIENT_RESUME
initialServerResponse = Promise.resolve(
createFromFetch<InitialRSCPayload>(clientResumeFetch, {
callServer,
callServer: initialCallServer,
findSourceMapURL,
debugChannel,
})
Expand All @@ -269,7 +278,7 @@ if (instantTestStaticFetch) {
initialServerResponse = createFromReadableStream<InitialRSCPayload>(
readable,
{
callServer,
callServer: initialCallServer,
findSourceMapURL,
debugChannel,
startTime: 0,
Expand Down Expand Up @@ -358,6 +367,10 @@ export async function hydrate(
webSocket = createWebSocket(assetPrefix, staticIndicatorState)
}
const initialRSCPayload = await initialServerResponse
setServerActionDispatchScopeRoutingKeys(
initialActionDispatchScope,
initialRSCPayload.A
)

// Initialize the offline module to register browser event listeners
// (offline/online) before any components hydrate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { decodeStageUntilBoundary } from './fetch-server-response'
import { discoverKnownRoute } from '../segment-cache/optimistic-routes'
import type { NormalizedSearch } from '../segment-cache/cache-key'
import { registerServerActionDispatchContext } from '../../server-action-dispatch'

export interface InitialRouterStateParameters {
navigatedAt: number
Expand Down Expand Up @@ -68,6 +69,10 @@ export function createInitialRouterState({
createHrefFromUrl(location)
: initialCanonicalUrl

if (location !== null) {
registerServerActionDispatchContext(initialRSCPayload.A, canonicalUrl, null)
}

// Decode the initial transport tree into the RouteTree type, with the
// payload's render output embedded on each node. (discoverKnownRoute below
// stores this tree in the route cache, which strips the data on write —
Expand Down Expand Up @@ -106,7 +111,7 @@ export function createInitialRouterState({
// route learning nor segment cache state persists from SSR to client.
if (location !== null && metadataVaryPath !== null) {
// Learn the route pattern so we can predict it for future navigations.
discoverKnownRoute(
const fulfilledRoute = discoverKnownRoute(
Date.now(),
location.pathname,
location.search as NormalizedSearch,
Expand All @@ -119,6 +124,7 @@ export function createInitialRouterState({
initialSupportsPerSegmentPrefetching,
false // hasDynamicRewrite
)
fulfilledRoute.actionRoutingKeys = initialRSCPayload.A ?? null

// TODO: Implement Shell extraction as part of Cached Navigations.
// Intentionally holding off on doing this until we decide how the Cached
Expand All @@ -141,7 +147,12 @@ export function createInitialRouterState({
await decodeStageUntilBoundary<InitialRSCPayload>(
initialFlightStreamForCache,
byteLength,
undefined
undefined,
{
url: canonicalUrl,
nextUrl: null,
actionRoutingKeys: initialRSCPayload.A,
}
)
const now = Date.now()
const staleAt = await resolveStaleAt(now, staticStageResponse.s)
Expand Down Expand Up @@ -206,7 +217,12 @@ export function createInitialRouterState({
Date.now(),
initialRuntimePrefetchStream,
initialTree,
initialRenderedSearch
initialRenderedSearch,
{
url: canonicalUrl,
nextUrl: null,
actionRoutingKeys: initialRSCPayload.A,
}
)
.then((processed) => {
if (processed !== null) {
Expand Down
Loading
Loading