@rsc-kit/core 0.20.6 → 0.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/cache.d.ts +11 -0
  2. package/dist/cache.js +13 -0
  3. package/dist/cache.js.map +1 -1
  4. package/dist/clientEntries.js +4 -1
  5. package/dist/clientEntries.js.map +1 -1
  6. package/dist/files.d.ts +38 -0
  7. package/dist/files.js +109 -0
  8. package/dist/files.js.map +1 -1
  9. package/dist/host.d.ts +11 -2
  10. package/dist/host.js +37 -6
  11. package/dist/host.js.map +1 -1
  12. package/dist/js/ActivityRouter.d.ts +11 -0
  13. package/dist/js/ActivityRouter.js +24 -4
  14. package/dist/js/ActivityRouter.js.map +1 -1
  15. package/dist/js/Form.js +24 -2
  16. package/dist/js/Form.js.map +1 -1
  17. package/dist/js/Link.d.ts +1 -1
  18. package/dist/js/Link.js +25 -9
  19. package/dist/js/Link.js.map +1 -1
  20. package/dist/js/PageTransition.d.ts +27 -0
  21. package/dist/js/PageTransition.js +57 -0
  22. package/dist/js/PageTransition.js.map +1 -0
  23. package/dist/js/PathnameProvider.d.ts +15 -2
  24. package/dist/js/PathnameProvider.js +17 -2
  25. package/dist/js/PathnameProvider.js.map +1 -1
  26. package/dist/js/SegmentBoundary.js +27 -3
  27. package/dist/js/SegmentBoundary.js.map +1 -1
  28. package/dist/js/activityMarkers.d.ts +7 -0
  29. package/dist/js/activityMarkers.js +18 -0
  30. package/dist/js/activityMarkers.js.map +1 -0
  31. package/dist/js/createViteRscApp.js +122 -76
  32. package/dist/js/createViteRscApp.js.map +1 -1
  33. package/dist/js/earlyClicks.d.ts +3 -1
  34. package/dist/js/earlyClicks.js +28 -5
  35. package/dist/js/earlyClicks.js.map +1 -1
  36. package/dist/js/errors.d.ts +2 -0
  37. package/dist/js/errors.js +11 -0
  38. package/dist/js/errors.js.map +1 -1
  39. package/dist/js/imagePreload.d.ts +37 -0
  40. package/dist/js/imagePreload.js +116 -0
  41. package/dist/js/imagePreload.js.map +1 -0
  42. package/dist/js/navigate.d.ts +47 -1
  43. package/dist/js/navigate.js +249 -10
  44. package/dist/js/navigate.js.map +1 -1
  45. package/dist/js/perf.d.ts +25 -0
  46. package/dist/js/perf.js +57 -0
  47. package/dist/js/perf.js.map +1 -0
  48. package/dist/js/router.d.ts +10 -1
  49. package/dist/js/router.js +9 -2
  50. package/dist/js/router.js.map +1 -1
  51. package/dist/js/segmentStore.d.ts +52 -0
  52. package/dist/js/segmentStore.js +123 -2
  53. package/dist/js/segmentStore.js.map +1 -1
  54. package/dist/js/staleAssets.d.ts +10 -0
  55. package/dist/js/staleAssets.js +23 -2
  56. package/dist/js/staleAssets.js.map +1 -1
  57. package/dist/js/viewportPrefetch.d.ts +19 -10
  58. package/dist/js/viewportPrefetch.js +50 -15
  59. package/dist/js/viewportPrefetch.js.map +1 -1
  60. package/dist/shellHead.d.ts +22 -0
  61. package/dist/shellHead.js +43 -0
  62. package/dist/shellHead.js.map +1 -0
  63. package/dist/vite.js +250 -55
  64. package/dist/vite.js.map +1 -1
  65. package/package.json +5 -1
@@ -1 +1 @@
1
- {"version":3,"file":"createViteRscApp.js","sourceRoot":"","sources":["../../src/js/createViteRscApp.ts"],"names":[],"mappings":"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","sourcesContent":["// Vite-engine client bootstrap. Uses @vitejs/plugin-rsc's browser runtime as\n// the Flight deserializer + action encoder, and drives the package's engine-agnostic\n// navigate.ts SPA engine (Link, prefetch, popstate) through it. This replaces\n// the bun engine's createRscApp + the hand-rolled webpack shim — the plugin\n// resolves client references itself.\nimport { SEARCH_PARAMS_FALLBACK } from \"./useSearchParams\";\nimport { recoverFromStaleAssets } from \"./staleAssets\";\nimport { afterHydration, replayEarlyClicks } from \"./earlyClicks\";\nimport { parseRedirectDigest } from \"../redirectDigest.js\";\nimport { showDevNotice } from \"./devNotice\";\nimport { caughtByLoading } from \"./fallbackReport\";\nimport { noteNavigation } from \"./segmentStore\";\nimport type { Route } from \"../routes.js\";\nimport { isSafeRedirect } from \"../safeUrl.js\";\nimport {\n createFromReadableStream,\n encodeReply,\n setServerCallback,\n} from \"@vitejs/plugin-rsc/browser\";\nimport { createElement } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { ActivityRoot } from \"./ActivityRouter\";\nimport { ServerRedirectError, noteRedirected, throwForFailedAction } from \"./errors\";\nimport { fetchPagePayload } from \"./pagePayload\";\nimport { claimRead, setQueryCodec } from \"./queryClient\";\nimport { clearSegments, restoreSegments, setSegment } from \"./segmentStore\";\nimport type { ReactNode } from \"react\";\nimport {\n cancelPrefetch,\n isPrefetched,\n navigate,\n setApiRoutes,\n refresh,\n applyRevalidated,\n prefetch,\n retentionKey,\n setCallServer,\n setHeldLayouts,\n setStaticPayloads,\n setStaticRoutes,\n seedStaticChain,\n payloadUrl,\n setDeserializer,\n setInterceptManifest,\n setNavigateHandler,\n setRestoreHandler,\n setVersion,\n} from \"./navigate\";\n\n\n/**\n * A redirect that travelled as an error: the digest of the error itself, or\n * of the cause React wraps it in when it reports client-rendering a boundary\n * the server had already failed.\n */\nfunction isRedirect(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest;\n const cause = (error as { cause?: { digest?: unknown } } | null)?.cause;\n\n return parseRedirectDigest(digest) !== null || parseRedirectDigest(cause?.digest) !== null;\n}\n\nexport async function createViteRscApp(\n container: Document | Element = document,\n interceptEntries: { urlPattern: string; slot: string }[] = [],\n options: { staticPayloads?: string | null; routes?: unknown[] | null; apiRoutes?: string[] } = {},\n): Promise<void> {\n // The urls a route.ts answers: a link to one is an anchor, never a\n // prefetch, never a payload fetch.\n if (options.apiRoutes) setApiRoutes(options.apiRoutes);\n // An exported build has no server to negotiate with, so payloads live at\n // their own urls rather than behind a header on the page's url.\n setStaticPayloads(options.staticPayloads ?? null);\n // Only an exported build ships this: with no server to negotiate with, the\n // client works out for itself how much of a page it still holds.\n if (options.routes) setStaticRoutes(options.routes as never);\n // The router has to recognise an intercepted link before it asks the server,\n // so the patterns are baked into the generated browser entry. Without them\n // every intercepted route falls through to a full-page navigation.\n setInterceptManifest(interceptEntries);\n\n async function callServer(id: string, args: unknown[]): Promise<unknown> {\n // A read opened a slot before calling the reference, and this is the first\n // point at which the id exists on the client — React keeps it private and\n // hands it over only here. Claiming the slot enqueues the read for the\n // batched GET instead of posting it.\n const read = claimRead(id, args);\n\n if (read) return await read;\n\n const encoded = await encodeReply(args);\n\n // encodeReply returns FormData as soon as an argument contains a File.\n // Sending that as multipart would make PHP consume php://input while\n // parsing it, leaving the action with an empty body — so serialize it to\n // raw bytes under an opaque content-type and send the real one in\n // X-RSC-Content-Type for the worker to rebuild FormData from.\n let body: BodyInit;\n let realContentType: string;\n\n if (encoded instanceof FormData) {\n const serialized = new Response(encoded);\n body = await serialized.arrayBuffer();\n realContentType =\n serialized.headers.get(\"content-type\") ?? \"multipart/form-data\";\n } else {\n body = encoded as BodyInit;\n realContentType = \"text/plain;charset=UTF-8\";\n }\n\n const res = await fetch(\"/_rsc/action\", {\n method: \"POST\",\n headers: {\n \"X-RSC-Action\": id,\n // Where the action was invoked from. The host resolves it to the\n // components that render the page, so anything the action says it\n // invalidated can come back with the answer instead of being fetched\n // afterwards.\n \"X-RSC-Referer\": window.location.pathname + window.location.search,\n \"X-RSC-Content-Type\": realContentType,\n \"Content-Type\": \"application/octet-stream\",\n \"X-XSRF-TOKEN\": decodeURIComponent(\n document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] ?? \"\",\n ),\n },\n body,\n });\n\n // A failed action does not answer with a Flight stream, and the decoder\n // cannot tell — it reports its own parse failure instead of what the\n // server said.\n try {\n await throwForFailedAction(res);\n } catch (err) {\n // A redirect is an instruction, not something for a form to display:\n // an expired session answers this way, and the destination is the login\n // page rather than a message about one.\n if (err instanceof ServerRedirectError) {\n // The destination came off a response header, so it is checked here\n // too — the engine refuses these at the source, but a proxy or a host\n // in front of it can write whatever it likes. A navigation, not a\n // document load: the layouts stay mounted and the history entry the\n // form was on is replaced, the way a redirect after a POST should\n // be - Back does not return to the submitted form.\n if (isSafeRedirect(err.location)) {\n void navigate(err.location as Route, { replace: true });\n }\n\n // Performed, so resolved - not thrown. The call's answer is \"you are\n // being taken somewhere\": an object saying so, the shape an action's\n // result already has, so a caller reading result.validationErrors\n // reads undefined rather than throwing. A caller that awaited the\n // action in a plain startTransition had no catch for a throw, the\n // rejection reached React, and the root unmounted to a white page\n // on every logout.\n noteRedirected(err.location);\n\n return { redirected: err.location };\n }\n\n throw err;\n }\n\n const answer = await createFromReadableStream(res.body!, { callServer });\n\n return unwrapRevalidated(answer);\n }\n\n /**\n * Put anything the action re-rendered on screen, and hand back its result.\n *\n * The trees travel with the answer rather than being fetched afterwards, so\n * the caller sees only what its action returned and never knows the page\n * was updated around it.\n */\n function unwrapRevalidated(answer: unknown): unknown {\n if (\n answer === null ||\n typeof answer !== \"object\" ||\n !(\"__rscRevalidated\" in answer)\n ) {\n return answer;\n }\n\n const envelope = answer as {\n __rscRevalidated: Record<string, unknown>;\n result: unknown;\n };\n\n for (const [target, tree] of Object.entries(envelope.__rscRevalidated)) {\n applyRevalidated(target, tree);\n }\n\n return envelope.result;\n }\n\n setDeserializer(createFromReadableStream as never);\n // The query transport speaks Flight too, and reaches the runtime through here\n // rather than importing it: a second import of the browser runtime from the\n // client-component graph is a second client-reference registry, and the\n // symptom is components that render as undefined with nothing logged.\n setQueryCodec({\n deserialize: (stream) =>\n createFromReadableStream(stream, { callServer }) as Promise<unknown>,\n encode: (args) => encodeReply(args) as Promise<string | FormData>,\n // The way back when a read cannot ride in a url. Re-entering callServer is\n // safe: the slot was cleared the moment the reference returned, so this\n // takes the ordinary POST path rather than claiming a read again.\n asAction: (id, args) => callServer(id, args),\n });\n setCallServer(callServer);\n // The plugin's own \"use server\" client stubs route through its registered\n // server callback — register the same transport there too.\n setServerCallback(callServer as never);\n\n // Link / Form / router live in a separate build graph and reach the SPA\n // engine through these globals.\n (window as unknown as { __rsc_navigate: typeof navigate }).__rsc_navigate =\n navigate;\n (window as unknown as { __rsc_prefetch: typeof prefetch }).__rsc_prefetch =\n prefetch;\n (\n window as unknown as { __rsc_cancel_prefetch: typeof cancelPrefetch }\n ).__rsc_cancel_prefetch = cancelPrefetch;\n (\n window as unknown as { __rsc_is_prefetched: typeof isPrefetched }\n ).__rsc_is_prefetched = isPrefetched;\n (window as unknown as { __rsc_refresh: typeof refresh }).__rsc_refresh =\n refresh;\n\n // Hydrate from the RSC endpoint (same url + X-RSC, no version header).\n //\n // Everything from here to the decode is failure handling, because this is\n // the one request with nothing watching it. A page whose shell is already\n // rendered looks fine while this fails, and its fallbacks stay on screen\n // indefinitely with nothing reported anywhere.\n const res = await fetchPagePayload(payloadUrl(window.location.href));\n\n // Seed the router with the build this DOCUMENT is, so a redeploy is caught\n // on the next navigation: the host answers 409 to a client of another\n // build, and the document is loaded again. From the document's own meta\n // first - a document a service worker served from its cache is the last\n // build's, while the payload just fetched is this one's, and seeding from\n // the payload would have the old client claim to be new. The header is\n // the fallback for a document with no meta.\n const documentBuild = document.querySelector('meta[name=\"rsc-kit:build\"]')?.getAttribute(\"content\");\n const servedVersion = documentBuild || res.headers.get(\"X-RSC-Version\");\n\n if (servedVersion) {\n setVersion(servedVersion);\n }\n\n // The chain this page is built from, so the next navigation can say what is\n // already mounted and be sent only what changed.\n // A server says what this page is built from; a file server says nothing,\n // so an exported build works it out from the table it was given.\n const servedLayouts = res.headers.get(\"X-RSC-Layouts\");\n\n if (servedLayouts !== null) {\n setHeldLayouts(servedLayouts.split(\",\"));\n } else if (!seedStaticChain(window.location.href)) {\n setHeldLayouts([]);\n }\n const tree = await createFromReadableStream(res.body!, { callServer });\n\n // Retaining the previous page behind <Activity> needs a wrapper above the\n // page, and React will not hydrate a *document* container through one: the\n // root child of a document has to be <html>, and wrapping it hangs the\n // renderer outright (React 19.2.7). An app whose root layout owns <html>\n // therefore hydrates the tree directly and navigations replace it, as before.\n //\n // Apps that hydrate into an element get retention. Giving it to document-\n // rooted apps means SPA navigation returning only the changed segment rather\n // than a whole document, which is an engine change, not a client one.\n const retains = container !== document;\n\n const shell = retains\n ? createElement(ActivityRoot, {\n initialKey: retentionKey(window.location.href, null),\n initialTree: tree as ReactNode,\n })\n : (tree as ReactNode);\n\n const root = hydrateRoot(container, shell, {\n onRecoverableError(error: unknown, errorInfo: unknown) {\n // A client component whose chunk the server no longer has fails while\n // hydrating, and React reports that as recoverable: the page is loaded\n // again, with the current names.\n if (recoverFromStaleAssets(error)) return;\n\n // A redirect decided inside a boundary: the server's row carried it as\n // the digest, React client-renders the boundary and reports that it\n // did, and the RedirectBoundary is already performing it. Nothing to\n // read in the console about a page doing what it was told.\n if (isRedirect(error)) return;\n\n // A PPR shell is served with its Suspense boundaries deliberately\n // unfinished — the build aborts the render once the static part is out.\n // React reports that as #419 and client-renders the boundary from the\n // Flight payload, which is the intended path, not a fault to report.\n const message = String((error as { message?: string })?.message ?? error);\n\n if (\n message.includes(\"419\") ||\n message.includes(\"did not finish this Suspense boundary\")\n ) {\n return;\n }\n\n // A view transition the browser cancelled because the tab was not\n // visible. React reports it here; the update still landed, and a tab\n // in the background is not a fault in the page.\n if (message.includes(\"Transition was aborted\")) return;\n\n // A navigation another one overtook: its request was aborted, and a\n // row of its payload that a component was still reading rejects with\n // the abort. React recovers by rendering the root again synchronously\n // and reports the recovery here as #520, with the abort as the cause.\n // The page it recovered to is the one the second navigation asked for;\n // the abort was the design, not a fault.\n const cause = (error as { cause?: { name?: string; message?: string } } | null)?.cause;\n\n if (\n cause?.name === \"AbortError\" ||\n /\\baborted\\b/i.test(cause?.message ?? \"\") ||\n /\\baborted\\b/i.test(message)\n ) {\n return;\n }\n // A page whose query string was read under a boundary carries the\n // fallback there; React reports the recovery on hydration. Under a\n // boundary the developer wrote, that is the designed path and nothing\n // is said. With nothing closer than a loading.tsx, the whole segment\n // showed the fallback until the query arrived - said here, in the\n // page and the console, for whoever is looking at either. Production\n // has no stack to tell the two apart; the build's note on the route is\n // the record there.\n if (\n (error as { digest?: string })?.digest === SEARCH_PARAMS_FALLBACK ||\n message.includes(\"useSearchParams()\")\n ) {\n const stack = (errorInfo as { componentStack?: string } | null)\n ?.componentStack;\n\n if (!caughtByLoading(stack)) return;\n\n const hint =\n \"useSearchParams() was read on the server with nothing closer than a loading.tsx, so the \" +\n \"whole segment showed that fallback until the query arrived. A <Suspense> around the \" +\n \"component that reads keeps the rest of the page painted.\";\n\n showDevNotice(hint, stack);\n console.error(new Error(hint), errorInfo);\n\n return;\n }\n\n console.error(error, errorInfo);\n },\n // A chunk the deploy no longer serves is not a fault in the page; the\n // document is loaded again, and the new names come with it. Anything\n // else is reported the way React would have.\n onUncaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n\n console.error(error, errorInfo);\n },\n onCaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n // Caught by the RedirectBoundary, which is performing it.\n if (isRedirect(error)) return;\n\n console.error(error, errorInfo);\n },\n });\n\n // Depth 0 is a whole document and replaces the root. Anything deeper is one\n // segment: handing it to the boundary at that depth leaves the layouts above\n // it mounted, which is the point of asking for a partial render at all.\n setNavigateHandler((tree: unknown, key: string, segmentDepth: number) => {\n const newTree = tree as ReactNode;\n\n // From here on the boundary animates; the seed commit before this did not.\n noteNavigation();\n\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, newTree);\n\n return;\n }\n\n clearSegments();\n root.render(newTree);\n });\n\n // Back and forward reveal a page the boundaries are still holding, with the\n // form you were filling in still filled in, and without asking the server.\n setRestoreHandler((key: string, maxAge?: number) =>\n restoreSegments(key, maxAge),\n );\n\n window.addEventListener(\"popstate\", () => {\n // restore: back and forward reveal the page you were on, with its state.\n // Wherever the browser just went; not a literal this app wrote.\n navigate(window.location.href as Route, { replace: true, restore: true });\n });\n\n history.replaceState(\n { rscUrl: window.location.href },\n \"\",\n window.location.href,\n );\n\n // A tap held by the bootstrap script, navigated to once hydration has\n // committed - not now: the runtime's script running is seconds before\n // React can dispatch a click on a phone still downloading chunks, and\n // a tap in that window would be the browser's. See earlyClicks.ts.\n afterHydration(() => replayEarlyClicks((url) => navigate(url as Route)));\n}\n"]}
1
+ {"version":3,"file":"createViteRscApp.js","sourceRoot":"","sources":["../../src/js/createViteRscApp.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qFAAqF;AACrF,8EAA8E;AAC9E,4EAA4E;AAC5E,qCAAqC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,wBAAwB,EACxB,WAAW,EACX,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAElH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAAc;IAChC,MAAM,MAAM,GAAI,KAAqC,EAAE,MAAM,CAAC;IAC9D,MAAM,KAAK,GAAI,KAAiD,EAAE,KAAK,CAAC;IAExE,OAAO,mBAAmB,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;AAC7F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAAS,GAAuB,QAAQ,EACxC,gBAAgB,GAA2C,EAAE,EAC7D,OAAO,GAAwF,EAAE;IAEjG,mEAAmE;IACnE,mCAAmC;IACnC,IAAI,OAAO,CAAC,SAAS;QAAE,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvD,yEAAyE;IACzE,gEAAgE;IAChE,iBAAiB,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC;IAClD,2EAA2E;IAC3E,iEAAiE;IACjE,IAAI,OAAO,CAAC,MAAM;QAAE,eAAe,CAAC,OAAO,CAAC,MAAe,CAAC,CAAC;IAC7D,6EAA6E;IAC7E,2EAA2E;IAC3E,mEAAmE;IACnE,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAEvC,KAAK,UAAU,UAAU,CAAC,EAAU,EAAE,IAAe;QACnD,2EAA2E;QAC3E,0EAA0E;QAC1E,uEAAuE;QACvE,qCAAqC;QACrC,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAEjC,IAAI,IAAI;YAAE,OAAO,MAAM,IAAI,CAAC;QAE5B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;QAExC,uEAAuE;QACvE,qEAAqE;QACrE,yEAAyE;QACzE,kEAAkE;QAClE,8DAA8D;QAC9D,IAAI,IAAc,CAAC;QACnB,IAAI,eAAuB,CAAC;QAE5B,IAAI,OAAO,YAAY,QAAQ,EAAE,CAAC;YAChC,MAAM,UAAU,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC;YACtC,eAAe;gBACb,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,qBAAqB,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,OAAmB,CAAC;YAC3B,eAAe,GAAG,0BAA0B,CAAC;QAC/C,CAAC;QAED,iEAAiE;QACjE,kEAAkE;QAClE,qEAAqE;QACrE,sEAAsE;QACtE,yBAAyB;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAE/D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;YACtC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,EAAE;gBAClB,eAAe,EAAE,IAAI;gBACrB,oBAAoB,EAAE,eAAe;gBACrC,cAAc,EAAE,0BAA0B;gBAC1C,cAAc,EAAE,kBAAkB,CAChC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CACvD;aACF;YACD,IAAI;SACL,CAAC,CAAC;QAEH,wEAAwE;QACxE,qEAAqE;QACrE,eAAe;QACf,IAAI,CAAC;YACH,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,wEAAwE;YACxE,wCAAwC;YACxC,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;gBACvC,oEAAoE;gBACpE,sEAAsE;gBACtE,kEAAkE;gBAClE,oEAAoE;gBACpE,kEAAkE;gBAClE,mDAAmD;gBACnD,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACjC,KAAK,QAAQ,CAAC,GAAG,CAAC,QAAiB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBAED,qEAAqE;gBACrE,qEAAqE;gBACrE,kEAAkE;gBAClE,kEAAkE;gBAClE,kEAAkE;gBAClE,kEAAkE;gBAClE,mBAAmB;gBACnB,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAE7B,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC;YACtC,CAAC;YAED,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,GAAG,CAAC,IAAK,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAEzE,OAAO,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,SAAS,iBAAiB,CAAC,MAAe,EAAE,IAAY;QACtD,IACE,MAAM,KAAK,IAAI;YACf,OAAO,MAAM,KAAK,QAAQ;YAC1B,CAAC,CAAC,kBAAkB,IAAI,MAAM,CAAC,EAC/B,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,QAAQ,GAAG,MAGhB,CAAC;QAEF,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,gBAA6C,CAAC,CAAC;QAEjF,OAAO,QAAQ,CAAC,MAAM,CAAC;IACzB,CAAC;IAED,eAAe,CAAC,wBAAiC,CAAC,CAAC;IACnD,8EAA8E;IAC9E,4EAA4E;IAC5E,wEAAwE;IACxE,sEAAsE;IACtE,aAAa,CAAC;QACZ,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CACtB,wBAAwB,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAqB;QACtE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAA+B;QACjE,2EAA2E;QAC3E,wEAAwE;QACxE,kEAAkE;QAClE,QAAQ,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC;KAC7C,CAAC,CAAC;IACH,aAAa,CAAC,UAAU,CAAC,CAAC;IAC1B,0EAA0E;IAC1E,2DAA2D;IAC3D,iBAAiB,CAAC,UAAmB,CAAC,CAAC;IAEvC,wEAAwE;IACxE,gCAAgC;IAC/B,MAAyD,CAAC,cAAc;QACvE,QAAQ,CAAC;IACV,MAAyD,CAAC,cAAc;QACvE,QAAQ,CAAC;IAET,MACD,CAAC,qBAAqB,GAAG,cAAc,CAAC;IAEvC,MACD,CAAC,mBAAmB,GAAG,YAAY,CAAC;IACpC,MAAuD,CAAC,aAAa;QACpE,OAAO,CAAC;IAEV,uEAAuE;IACvE,EAAE;IACF,0EAA0E;IAC1E,0EAA0E;IAC1E,yEAAyE;IACzE,+CAA+C;IAC/C,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAErE,2EAA2E;IAC3E,sEAAsE;IACtE,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1E,uEAAuE;IACvE,4CAA4C;IAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,4BAA4B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACpG,MAAM,aAAa,GAAG,aAAa,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAExE,IAAI,aAAa,EAAE,CAAC;QAClB,UAAU,CAAC,aAAa,CAAC,CAAC;IAC5B,CAAC;IAED,4EAA4E;IAC5E,iDAAiD;IACjD,0EAA0E;IAC1E,iEAAiE;IACjE,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAEvD,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,CAAC;SAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,cAAc,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC,GAAG,CAAC,IAAK,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAEvE,0EAA0E;IAC1E,2EAA2E;IAC3E,uEAAuE;IACvE,yEAAyE;IACzE,8EAA8E;IAC9E,EAAE;IACF,0EAA0E;IAC1E,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,OAAO,GAAG,SAAS,KAAK,QAAQ,CAAC;IAEvC,MAAM,KAAK,GAAG,OAAO;QACnB,CAAC,CAAC,aAAa,CAAC,YAAY,EAAE;YAC1B,UAAU,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;YACpD,WAAW,EAAE,IAAiB;SAC/B,CAAC;QACJ,CAAC,CAAE,IAAkB,CAAC;IAExB,qEAAqE;IACrE,wEAAwE;IACxE,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,uEAAuE;IACvE,uEAAuE;IACvE,qCAAqC;IACrC,MAAM,OAAO,GAAG,OAAO,IAAI,cAAc,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAE7F,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CACV,qMAAqM,CACtM,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,yEAAyE;IACzE,yEAAyE;IACzE,kCAAkC;IAClC,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IAE1B,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAAE,SAAkB,EAAE,EAAE;QAC9D,sEAAsE;QACtE,uEAAuE;QACvE,iCAAiC;QACjC,IAAI,sBAAsB,CAAC,KAAK,CAAC;YAAE,OAAO;QAE1C,uEAAuE;QACvE,oEAAoE;QACpE,qEAAqE;QACrE,2DAA2D;QAC3D,IAAI,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO;QAE9B,kEAAkE;QAClE,wEAAwE;QACxE,sEAAsE;QACtE,qEAAqE;QACrE,MAAM,OAAO,GAAG,MAAM,CAAE,KAA8B,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;QAE1E,IACE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YACvB,OAAO,CAAC,QAAQ,CAAC,uCAAuC,CAAC,EACzD,CAAC;YACD,OAAO;QACT,CAAC;QAED,kEAAkE;QAClE,qEAAqE;QACrE,gDAAgD;QAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAAE,OAAO;QAEvD,oEAAoE;QACpE,qEAAqE;QACrE,sEAAsE;QACtE,sEAAsE;QACtE,uEAAuE;QACvE,yCAAyC;QACzC,MAAM,KAAK,GAAI,KAAgE,EAAE,KAAK,CAAC;QAEvF,IACE,KAAK,EAAE,IAAI,KAAK,YAAY;YAC5B,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC;YACzC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAC5B,CAAC;YACD,OAAO;QACT,CAAC;QACD,kEAAkE;QAClE,mEAAmE;QACnE,sEAAsE;QACtE,qEAAqE;QACrE,kEAAkE;QAClE,qEAAqE;QACrE,uEAAuE;QACvE,oBAAoB;QACpB,IACG,KAA6B,EAAE,MAAM,KAAK,sBAAsB;YACjE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EACrC,CAAC;YACD,MAAM,KAAK,GAAI,SAAgD;gBAC7D,EAAE,cAAc,CAAC;YAEnB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;gBAAE,OAAO;YAEpC,MAAM,IAAI,GACR,0FAA0F;gBAC1F,sFAAsF;gBACtF,0DAA0D,CAAC;YAE7D,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;YAE1C,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG;QAClB,kBAAkB;QAClB,sEAAsE;QACtE,qEAAqE;QACrE,6CAA6C;QAC7C,eAAe,CAAC,KAAc,EAAE,SAAkB;YAChD,IAAI,sBAAsB,CAAC,KAAK,CAAC;gBAAE,OAAO;YAE1C,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAClC,CAAC;QACD,aAAa,CAAC,KAAc,EAAE,SAAkB;YAC9C,IAAI,sBAAsB,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC1C,0DAA0D;YAC1D,IAAI,UAAU,CAAC,KAAK,CAAC;gBAAE,OAAO;YAE9B,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;IAEF,MAAM,IAAI,GAAG,OAAO,IAAI,SAAS;QAC/B,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,OAAO,GAAG,UAAU,CAAC,SAAoB,EAAE,WAAW,CAAC,CAAC;YAE9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAEtB,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IAE/C,4EAA4E;IAC5E,6EAA6E;IAC7E,wEAAwE;IACxE,kBAAkB,CAAC,CAAC,IAAa,EAAE,GAAW,EAAE,YAAoB,EAAE,EAAE;QACtE,MAAM,OAAO,GAAG,IAAiB,CAAC;QAElC,2EAA2E;QAC3E,cAAc,EAAE,CAAC;QAEjB,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,UAAU,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YAEvC,OAAO;QACT,CAAC;QAED,aAAa,EAAE,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,uEAAuE;IACvE,2EAA2E;IAC3E,0EAA0E;IAC1E,qDAAqD;IACrD,qBAAqB,CAAC,CAAC,IAAa,EAAE,EAAE;QACtC,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAiB,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,2EAA2E;IAC3E,iBAAiB,CAAC,CAAC,GAAW,EAAE,MAAe,EAAE,EAAE,CACjD,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAC7B,CAAC;IACF,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAEpC,uEAAuE;IACvE,oEAAoE;IACpE,mBAAmB,CAAC,CAAC,IAAa,EAAE,GAAW,EAAE,YAAoB,EAAE,EAAE;QACvE,gBAAgB,CAAC,YAAY,EAAE,GAAG,EAAE,IAAiB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE;QACvC,yEAAyE;QACzE,gEAAgE;QAChE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,YAAY,CAClB,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAChC,EAAE,EACF,MAAM,CAAC,QAAQ,CAAC,IAAI,CACrB,CAAC;IAEF,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,mEAAmE;IACnE,cAAc,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAY,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["// Vite-engine client bootstrap. Uses @vitejs/plugin-rsc's browser runtime as\n// the Flight deserializer + action encoder, and drives the package's engine-agnostic\n// navigate.ts SPA engine (Link, prefetch, popstate) through it. This replaces\n// the bun engine's createRscApp + the hand-rolled webpack shim — the plugin\n// resolves client references itself.\nimport { SEARCH_PARAMS_FALLBACK } from \"./useSearchParams\";\nimport { recoverFromStaleAssets } from \"./staleAssets\";\nimport { afterHydration, replayEarlyClicks } from \"./earlyClicks\";\nimport { parseRedirectDigest } from \"../redirectDigest.js\";\nimport { showDevNotice } from \"./devNotice\";\nimport { caughtByLoading } from \"./fallbackReport\";\nimport { noteNavigation } from \"./segmentStore\";\nimport type { Route } from \"../routes.js\";\nimport { isSafeRedirect } from \"../safeUrl.js\";\nimport {\n createFromReadableStream,\n encodeReply,\n setServerCallback,\n} from \"@vitejs/plugin-rsc/browser\";\nimport { createElement, startTransition } from \"react\";\nimport { createRoot, hydrateRoot } from \"react-dom/client\";\nimport { activityMarkersIn } from \"./activityMarkers\";\nimport { ActivityRoot } from \"./ActivityRouter\";\nimport { ServerRedirectError, noteRedirected, throwForFailedAction } from \"./errors\";\nimport { fetchPagePayload } from \"./pagePayload\";\nimport { claimRead, setQueryCodec } from \"./queryClient\";\nimport { clearSegments, dropHidden, isHeld, prerenderSegment, restoreSegments, setSegment } from \"./segmentStore\";\nimport type { ReactNode } from \"react\";\nimport {\n cancelPrefetch,\n isPrefetched,\n navigate,\n setApiRoutes,\n refresh,\n applyRevalidations,\n prefetch,\n retentionKey,\n setCallServer,\n setHeldLayouts,\n setStaticPayloads,\n setStaticRoutes,\n seedStaticChain,\n payloadUrl,\n setDeserializer,\n setInterceptManifest,\n getHeldLayouts,\n setNavigateHandler,\n setHeldHandlers,\n setPrerenderHandler,\n setReplaceRootHandler,\n setRestoreHandler,\n setVersion,\n} from \"./navigate\";\n\n\n/**\n * A redirect that travelled as an error: the digest of the error itself, or\n * of the cause React wraps it in when it reports client-rendering a boundary\n * the server had already failed.\n */\nfunction isRedirect(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest;\n const cause = (error as { cause?: { digest?: unknown } } | null)?.cause;\n\n return parseRedirectDigest(digest) !== null || parseRedirectDigest(cause?.digest) !== null;\n}\n\nexport async function createViteRscApp(\n container: Document | Element = document,\n interceptEntries: { urlPattern: string; slot: string }[] = [],\n options: { staticPayloads?: string | null; routes?: unknown[] | null; apiRoutes?: string[] } = {},\n): Promise<void> {\n // The urls a route.ts answers: a link to one is an anchor, never a\n // prefetch, never a payload fetch.\n if (options.apiRoutes) setApiRoutes(options.apiRoutes);\n // An exported build has no server to negotiate with, so payloads live at\n // their own urls rather than behind a header on the page's url.\n setStaticPayloads(options.staticPayloads ?? null);\n // Only an exported build ships this: with no server to negotiate with, the\n // client works out for itself how much of a page it still holds.\n if (options.routes) setStaticRoutes(options.routes as never);\n // The router has to recognise an intercepted link before it asks the server,\n // so the patterns are baked into the generated browser entry. Without them\n // every intercepted route falls through to a full-page navigation.\n setInterceptManifest(interceptEntries);\n\n async function callServer(id: string, args: unknown[]): Promise<unknown> {\n // A read opened a slot before calling the reference, and this is the first\n // point at which the id exists on the client — React keeps it private and\n // hands it over only here. Claiming the slot enqueues the read for the\n // batched GET instead of posting it.\n const read = claimRead(id, args);\n\n if (read) return await read;\n\n const encoded = await encodeReply(args);\n\n // encodeReply returns FormData as soon as an argument contains a File.\n // Sending that as multipart would make PHP consume php://input while\n // parsing it, leaving the action with an empty body — so serialize it to\n // raw bytes under an opaque content-type and send the real one in\n // X-RSC-Content-Type for the worker to rebuild FormData from.\n let body: BodyInit;\n let realContentType: string;\n\n if (encoded instanceof FormData) {\n const serialized = new Response(encoded);\n body = await serialized.arrayBuffer();\n realContentType =\n serialized.headers.get(\"content-type\") ?? \"multipart/form-data\";\n } else {\n body = encoded as BodyInit;\n realContentType = \"text/plain;charset=UTF-8\";\n }\n\n // Where the action was invoked from. The host resolves it to the\n // components that render the page, so anything the action says it\n // invalidated can come back with the answer instead of being fetched\n // afterwards - and what comes back is for this page, not whichever is\n // showing when it lands.\n const from = window.location.pathname + window.location.search;\n\n const res = await fetch(\"/_rsc/action\", {\n method: \"POST\",\n headers: {\n \"X-RSC-Action\": id,\n \"X-RSC-Referer\": from,\n \"X-RSC-Content-Type\": realContentType,\n \"Content-Type\": \"application/octet-stream\",\n \"X-XSRF-TOKEN\": decodeURIComponent(\n document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] ?? \"\",\n ),\n },\n body,\n });\n\n // A failed action does not answer with a Flight stream, and the decoder\n // cannot tell — it reports its own parse failure instead of what the\n // server said.\n try {\n await throwForFailedAction(res);\n } catch (err) {\n // A redirect is an instruction, not something for a form to display:\n // an expired session answers this way, and the destination is the login\n // page rather than a message about one.\n if (err instanceof ServerRedirectError) {\n // The destination came off a response header, so it is checked here\n // too — the engine refuses these at the source, but a proxy or a host\n // in front of it can write whatever it likes. A navigation, not a\n // document load: the layouts stay mounted and the history entry the\n // form was on is replaced, the way a redirect after a POST should\n // be - Back does not return to the submitted form.\n if (isSafeRedirect(err.location)) {\n void navigate(err.location as Route, { replace: true });\n }\n\n // Performed, so resolved - not thrown. The call's answer is \"you are\n // being taken somewhere\": an object saying so, the shape an action's\n // result already has, so a caller reading result.validationErrors\n // reads undefined rather than throwing. A caller that awaited the\n // action in a plain startTransition had no catch for a throw, the\n // rejection reached React, and the root unmounted to a white page\n // on every logout.\n noteRedirected(err.location);\n\n return { redirected: err.location };\n }\n\n throw err;\n }\n\n const answer = await createFromReadableStream(res.body!, { callServer });\n\n return unwrapRevalidated(answer, from);\n }\n\n /**\n * Put anything the action re-rendered on screen, and hand back its result.\n *\n * The trees travel with the answer rather than being fetched afterwards, so\n * the caller sees only what its action returned and never knows the page\n * was updated around it.\n */\n function unwrapRevalidated(answer: unknown, from: string): unknown {\n if (\n answer === null ||\n typeof answer !== \"object\" ||\n !(\"__rscRevalidated\" in answer)\n ) {\n return answer;\n }\n\n const envelope = answer as {\n __rscRevalidated: Record<string, unknown>;\n result: unknown;\n };\n\n applyRevalidations(from, envelope.__rscRevalidated as Record<string, ReactNode>);\n\n return envelope.result;\n }\n\n setDeserializer(createFromReadableStream as never);\n // The query transport speaks Flight too, and reaches the runtime through here\n // rather than importing it: a second import of the browser runtime from the\n // client-component graph is a second client-reference registry, and the\n // symptom is components that render as undefined with nothing logged.\n setQueryCodec({\n deserialize: (stream) =>\n createFromReadableStream(stream, { callServer }) as Promise<unknown>,\n encode: (args) => encodeReply(args) as Promise<string | FormData>,\n // The way back when a read cannot ride in a url. Re-entering callServer is\n // safe: the slot was cleared the moment the reference returned, so this\n // takes the ordinary POST path rather than claiming a read again.\n asAction: (id, args) => callServer(id, args),\n });\n setCallServer(callServer);\n // The plugin's own \"use server\" client stubs route through its registered\n // server callback — register the same transport there too.\n setServerCallback(callServer as never);\n\n // Link / Form / router live in a separate build graph and reach the SPA\n // engine through these globals.\n (window as unknown as { __rsc_navigate: typeof navigate }).__rsc_navigate =\n navigate;\n (window as unknown as { __rsc_prefetch: typeof prefetch }).__rsc_prefetch =\n prefetch;\n (\n window as unknown as { __rsc_cancel_prefetch: typeof cancelPrefetch }\n ).__rsc_cancel_prefetch = cancelPrefetch;\n (\n window as unknown as { __rsc_is_prefetched: typeof isPrefetched }\n ).__rsc_is_prefetched = isPrefetched;\n (window as unknown as { __rsc_refresh: typeof refresh }).__rsc_refresh =\n refresh;\n\n // Hydrate from the RSC endpoint (same url + X-RSC, no version header).\n //\n // Everything from here to the decode is failure handling, because this is\n // the one request with nothing watching it. A page whose shell is already\n // rendered looks fine while this fails, and its fallbacks stay on screen\n // indefinitely with nothing reported anywhere.\n const res = await fetchPagePayload(payloadUrl(window.location.href));\n\n // Seed the router with the build this DOCUMENT is, so a redeploy is caught\n // on the next navigation: the host answers 409 to a client of another\n // build, and the document is loaded again. From the document's own meta\n // first - a document a service worker served from its cache is the last\n // build's, while the payload just fetched is this one's, and seeding from\n // the payload would have the old client claim to be new. The header is\n // the fallback for a document with no meta.\n const documentBuild = document.querySelector('meta[name=\"rsc-kit:build\"]')?.getAttribute(\"content\");\n const servedVersion = documentBuild || res.headers.get(\"X-RSC-Version\");\n\n if (servedVersion) {\n setVersion(servedVersion);\n }\n\n // The chain this page is built from, so the next navigation can say what is\n // already mounted and be sent only what changed.\n // A server says what this page is built from; a file server says nothing,\n // so an exported build works it out from the table it was given.\n const servedLayouts = res.headers.get(\"X-RSC-Layouts\");\n\n if (servedLayouts !== null) {\n setHeldLayouts(servedLayouts.split(\",\"));\n } else if (!seedStaticChain(window.location.href)) {\n setHeldLayouts([]);\n }\n const tree = await createFromReadableStream(res.body!, { callServer });\n\n // Retaining the previous page behind <Activity> needs a wrapper above the\n // page, and React will not hydrate a *document* container through one: the\n // root child of a document has to be <html>, and wrapping it hangs the\n // renderer outright (React 19.2.7). An app whose root layout owns <html>\n // therefore hydrates the tree directly and navigations replace it, as before.\n //\n // Apps that hydrate into an element get retention. Giving it to document-\n // rooted apps means SPA navigation returning only the changed segment rather\n // than a whole document, which is an engine change, not a client one.\n const retains = container !== document;\n\n const shell = retains\n ? createElement(ActivityRoot, {\n initialKey: retentionKey(window.location.href, null),\n initialTree: tree as ReactNode,\n })\n : (tree as ReactNode);\n\n // A document that cannot be hydrated by this tree is client-rendered\n // instead - a repaint, not a freeze. React 19.2 does not recover from a\n // hydration mismatch at an <Activity>: it retries hydrating the boundary,\n // mismatches again, and never returns to the main thread. Every segment\n // boundary is an Activity, one per layout, so a document with none where\n // the page has layouts is that mismatch, guaranteed, before React starts.\n // What produced one: a parameterised route's PPR shell, rendered without\n // a page key and, by a build before this one, without the Activities too.\n // None rather than fewer: a boundary inside a Suspense hole the shell left\n // unfinished has no marker and is client-rendered by React on its own,\n // which is not a mismatch. The stale-asset reload does not apply - the\n // document would come back the same.\n const missing = retains && getHeldLayouts().length > 0 && activityMarkersIn(container) === 0;\n\n if (missing) {\n console.warn(\n \"[rsc-kit] The document has no <Activity> boundaries where the page has layouts, so it cannot be hydrated; rendering the page instead. A PPR shell built by an earlier release lacks them - rebuild.\",\n );\n }\n\n // The payload answered with an error status - the not-found page for a\n // url the shell was served for anyway. It is not the document on screen,\n // so it is rendered, not hydrated: a mismatch at an Activity is the loop\n // above, and this one is certain.\n const disagrees = !res.ok;\n\n const onRecoverableError = (error: unknown, errorInfo: unknown) => {\n // A client component whose chunk the server no longer has fails while\n // hydrating, and React reports that as recoverable: the page is loaded\n // again, with the current names.\n if (recoverFromStaleAssets(error)) return;\n\n // A redirect decided inside a boundary: the server's row carried it as\n // the digest, React client-renders the boundary and reports that it\n // did, and the RedirectBoundary is already performing it. Nothing to\n // read in the console about a page doing what it was told.\n if (isRedirect(error)) return;\n\n // A PPR shell is served with its Suspense boundaries deliberately\n // unfinished — the build aborts the render once the static part is out.\n // React reports that as #419 and client-renders the boundary from the\n // Flight payload, which is the intended path, not a fault to report.\n const message = String((error as { message?: string })?.message ?? error);\n\n if (\n message.includes(\"419\") ||\n message.includes(\"did not finish this Suspense boundary\")\n ) {\n return;\n }\n\n // A view transition the browser cancelled because the tab was not\n // visible. React reports it here; the update still landed, and a tab\n // in the background is not a fault in the page.\n if (message.includes(\"Transition was aborted\")) return;\n\n // A navigation another one overtook: its request was aborted, and a\n // row of its payload that a component was still reading rejects with\n // the abort. React recovers by rendering the root again synchronously\n // and reports the recovery here as #520, with the abort as the cause.\n // The page it recovered to is the one the second navigation asked for;\n // the abort was the design, not a fault.\n const cause = (error as { cause?: { name?: string; message?: string } } | null)?.cause;\n\n if (\n cause?.name === \"AbortError\" ||\n /\\baborted\\b/i.test(cause?.message ?? \"\") ||\n /\\baborted\\b/i.test(message)\n ) {\n return;\n }\n // A page whose query string was read under a boundary carries the\n // fallback there; React reports the recovery on hydration. Under a\n // boundary the developer wrote, that is the designed path and nothing\n // is said. With nothing closer than a loading.tsx, the whole segment\n // showed the fallback until the query arrived - said here, in the\n // page and the console, for whoever is looking at either. Production\n // has no stack to tell the two apart; the build's note on the route is\n // the record there.\n if (\n (error as { digest?: string })?.digest === SEARCH_PARAMS_FALLBACK ||\n message.includes(\"useSearchParams()\")\n ) {\n const stack = (errorInfo as { componentStack?: string } | null)\n ?.componentStack;\n\n if (!caughtByLoading(stack)) return;\n\n const hint =\n \"useSearchParams() was read on the server with nothing closer than a loading.tsx, so the \" +\n \"whole segment showed that fallback until the query arrived. A <Suspense> around the \" +\n \"component that reads keeps the rest of the page painted.\";\n\n showDevNotice(hint, stack);\n console.error(new Error(hint), errorInfo);\n\n return;\n }\n\n console.error(error, errorInfo);\n };\n\n const rootOptions = {\n onRecoverableError,\n // A chunk the deploy no longer serves is not a fault in the page; the\n // document is loaded again, and the new names come with it. Anything\n // else is reported the way React would have.\n onUncaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n\n console.error(error, errorInfo);\n },\n onCaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n // Caught by the RedirectBoundary, which is performing it.\n if (isRedirect(error)) return;\n\n console.error(error, errorInfo);\n },\n };\n\n const root = missing || disagrees\n ? (() => {\n const created = createRoot(container as Element, rootOptions);\n\n created.render(shell);\n\n return created;\n })()\n : hydrateRoot(container, shell, rootOptions);\n\n // Depth 0 is a whole document and replaces the root. Anything deeper is one\n // segment: handing it to the boundary at that depth leaves the layouts above\n // it mounted, which is the point of asking for a partial render at all.\n setNavigateHandler((tree: unknown, key: string, segmentDepth: number) => {\n const newTree = tree as ReactNode;\n\n // From here on the boundary animates; the seed commit before this did not.\n noteNavigation();\n\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, newTree);\n\n return;\n }\n\n clearSegments();\n root.render(newTree);\n });\n\n // The whole document again, in place - revalidate(\"all\"). Not through the\n // handler above: that clears the boundaries and renders the tree as a new\n // page, and a boundary emptied re-keys its Activity to the current url and\n // remounts everything under it. A port's \"added to cart\" went with the\n // form's state that way, on every product reached by a link. Here the root\n // takes the tree in a transition and each boundary hands its page the new\n // children under the key it has - see replaceActive.\n setReplaceRootHandler((tree: unknown) => {\n startTransition(() => root.render(tree as ReactNode));\n });\n\n // Back and forward reveal a page the boundaries are still holding, with the\n // form you were filling in still filled in, and without asking the server.\n setRestoreHandler((key: string, maxAge?: number) =>\n restoreSegments(key, maxAge),\n );\n setHeldHandlers(isHeld, dropHidden);\n\n // A touch or a settled hover: the page is rendered hidden now, and the\n // click reveals it. See warm() in navigate.ts and prerenderSegment.\n setPrerenderHandler((tree: unknown, key: string, segmentDepth: number) => {\n prerenderSegment(segmentDepth, key, tree as ReactNode);\n });\n\n window.addEventListener(\"popstate\", () => {\n // restore: back and forward reveal the page you were on, with its state.\n // Wherever the browser just went; not a literal this app wrote.\n navigate(window.location.href as Route, { replace: true, restore: true });\n });\n\n history.replaceState(\n { rscUrl: window.location.href },\n \"\",\n window.location.href,\n );\n\n // A tap held by the bootstrap script, navigated to once hydration has\n // committed - not now: the runtime's script running is seconds before\n // React can dispatch a click on a phone still downloading chunks, and\n // a tap in that window would be the browser's. See earlyClicks.ts.\n afterHydration(() => replayEarlyClicks((url) => navigate(url as Route)));\n}\n\n"]}
@@ -24,7 +24,9 @@
24
24
  export declare const EARLY_CLICKS: string;
25
25
  /**
26
26
  * Hand the clicks held before hydration to the router. The last one is the
27
- * page the visitor meant; the ones before it were on the way there.
27
+ * page the visitor meant; the ones before it were on the way there. A form
28
+ * held is submitted again, now that React's handler is on it - a tap on a
29
+ * link after it wins, since the visitor left the form.
28
30
  */
29
31
  export declare function replayEarlyClicks(navigate: (url: string) => Promise<void>): string | null;
30
32
  /**
@@ -21,7 +21,7 @@
21
21
  * route.ts, a mailto or a foreign origin is not a page the router swaps in.
22
22
  */
23
23
  /** Written into the bootstrap script, ahead of the runtime's import. No backticks: it lives in a template. */
24
- export const EARLY_CLICKS = "(function(){var q=[];function h(e){" +
24
+ export const EARLY_CLICKS = "(function(){var q=[],fq=[];function h(e){" +
25
25
  "if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;" +
26
26
  "var t=e.target;var a=t&&t.closest?t.closest('a[data-rsc]'):null;" +
27
27
  "if(!a||(a.target&&a.target!=='_self'))return;" +
@@ -36,11 +36,26 @@ export const EARLY_CLICKS = "(function(){var q=[];function h(e){" +
36
36
  // hard limit for a runtime that started and never finished.
37
37
  "setTimeout(function(){if(q.length&&!w.__rsc_hydrated&&!w.__rsc_navigate)location.href=q[q.length-1]},3000);" +
38
38
  "setTimeout(function(){if(q.length&&!w.__rsc_hydrated)location.href=q[q.length-1]},15000)}" +
39
- "var w=window;document.addEventListener('click',h,true);" +
40
- "w.__rsc_early={q:q,stop:function(){document.removeEventListener('click',h,true)}}})();";
39
+ // A form submitted before hydration. React renders a form whose action
40
+ // is a server action with $ACTION_ hidden fields so it posts natively
41
+ // without a runtime - the page reloads with the action applied, which is
42
+ // the right answer with no JavaScript and the wrong one with JavaScript
43
+ // a second away: "Add to cart" on a product page reloaded the page when
44
+ // tapped before the runtime hydrated. Held like a tap, and submitted
45
+ // again once hydration commits, when React's own handler takes it.
46
+ "function g(){var x=fq.pop();fq.length=0;if(x)HTMLFormElement.prototype.submit.call(x.f)}" +
47
+ "function s(e){if(e.defaultPrevented)return;var f=e.target;" +
48
+ "if(!f||f.tagName!=='FORM'||!f.querySelector('input[name^=\"$ACTION_\"]'))return;" +
49
+ "e.preventDefault();fq.push({f:f,s:e.submitter||null});f.setAttribute('data-pending','');" +
50
+ "setTimeout(function(){if(fq.length&&!w.__rsc_hydrated&&!w.__rsc_navigate)g()},3000);" +
51
+ "setTimeout(function(){if(fq.length&&!w.__rsc_hydrated)g()},15000)}" +
52
+ "var w=window;document.addEventListener('click',h,true);document.addEventListener('submit',s,true);" +
53
+ "w.__rsc_early={q:q,fq:fq,stop:function(){document.removeEventListener('click',h,true);document.removeEventListener('submit',s,true)}}})();";
41
54
  /**
42
55
  * Hand the clicks held before hydration to the router. The last one is the
43
- * page the visitor meant; the ones before it were on the way there.
56
+ * page the visitor meant; the ones before it were on the way there. A form
57
+ * held is submitted again, now that React's handler is on it - a tap on a
58
+ * link after it wins, since the visitor left the form.
44
59
  */
45
60
  export function replayEarlyClicks(navigate) {
46
61
  const early = window.__rsc_early;
@@ -48,9 +63,17 @@ export function replayEarlyClicks(navigate) {
48
63
  return null;
49
64
  early.stop();
50
65
  const last = early.q[early.q.length - 1] ?? null;
66
+ const form = early.fq?.[early.fq.length - 1] ?? null;
51
67
  early.q.length = 0;
52
- if (last)
68
+ if (early.fq)
69
+ early.fq.length = 0;
70
+ if (last) {
53
71
  void navigate(last);
72
+ }
73
+ else if (form) {
74
+ form.f.removeAttribute("data-pending");
75
+ form.f.requestSubmit(form.s instanceof HTMLElement ? form.s : undefined);
76
+ }
54
77
  return last;
55
78
  }
56
79
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"earlyClicks.js","sourceRoot":"","sources":["../../src/js/earlyClicks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,8GAA8G;AAC9G,MAAM,CAAC,MAAM,YAAY,GACvB,qCAAqC;IACrC,yFAAyF;IACzF,kEAAkE;IAClE,+CAA+C;IAC/C,4EAA4E;IAC5E,uCAAuC;IACvC,0FAA0F;IAC1F,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,oEAAoE;IACpE,uEAAuE;IACvE,4DAA4D;IAC5D,6GAA6G;IAC7G,2FAA2F;IAC3F,yDAAyD;IACzD,wFAAwF,CAAC;AAO3F;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAwC;IACxE,MAAM,KAAK,GAAI,MAAkC,CAAC,WAAW,CAAC;IAE9D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,KAAK,CAAC,IAAI,EAAE,CAAC;IAEb,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IAEjD,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAEnB,IAAI,IAAI;QAAE,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAc;IAC3C,MAAM,CAAC,GAAG,MAAsE,CAAC;IAEjF,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;QACrB,EAAE,EAAE,CAAC;QAEL,OAAO;IACT,CAAC;IAED,CAAC,CAAC,iBAAiB,GAAG,GAAG,EAAE;QACzB,CAAC,CAAC,iBAAiB,GAAG,SAAS,CAAC;QAChC,EAAE,EAAE,CAAC;IACP,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * A tap before the page can navigate.\n *\n * The document arrives, the bootstrap script starts the runtime, and on a\n * phone the runtime takes a while to arrive and hydrate - seconds, with a\n * tag manager or two on the main thread. A tap on a link in that window is\n * the browser's: a document load, of a page the router would have swapped\n * in. The port's \"Sign in is slow from the home page, and sometimes a full\n * reload\" was this, and only on a phone, where the window is wide.\n *\n * The first thing the bootstrap script does is listen: a click on one of\n * this package's links, before the page has hydrated, is held. When\n * hydration commits - not when the runtime's script runs, which is\n * seconds earlier on a phone whose chunks are still downloading, and where\n * a click is nobody's yet - the last one held is navigated to. A click\n * held for three seconds with no runtime started is given to the browser\n * as a document load, so nothing is lost if the runtime never comes; one\n * held while the runtime is running waits for hydration, up to fifteen.\n *\n * Only this package's links, marked `data-rsc`: a plain anchor to a file, a\n * route.ts, a mailto or a foreign origin is not a page the router swaps in.\n */\n\n/** Written into the bootstrap script, ahead of the runtime's import. No backticks: it lives in a template. */\nexport const EARLY_CLICKS =\n \"(function(){var q=[];function h(e){\" +\n \"if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;\" +\n \"var t=e.target;var a=t&&t.closest?t.closest('a[data-rsc]'):null;\" +\n \"if(!a||(a.target&&a.target!=='_self'))return;\" +\n \"var u;try{u=new URL(a.getAttribute('href'),location.href)}catch(x){return}\" +\n \"if(u.origin!==location.origin)return;\" +\n \"e.preventDefault();q.push(u.pathname+u.search+u.hash);a.setAttribute('data-pending','');\" +\n // Given to the browser only when the runtime has not even started three\n // seconds after the tap - a script blocked, a chunk that failed. While it\n // is running, hydration is coming, and a phone's takes longer than three\n // seconds: a tap given up at three landed as a document load half a\n // second before the page would have handled it. Fifteen seconds is the\n // hard limit for a runtime that started and never finished.\n \"setTimeout(function(){if(q.length&&!w.__rsc_hydrated&&!w.__rsc_navigate)location.href=q[q.length-1]},3000);\" +\n \"setTimeout(function(){if(q.length&&!w.__rsc_hydrated)location.href=q[q.length-1]},15000)}\" +\n \"var w=window;document.addEventListener('click',h,true);\" +\n \"w.__rsc_early={q:q,stop:function(){document.removeEventListener('click',h,true)}}})();\";\n\ninterface Early {\n q: string[];\n stop(): void;\n}\n\n/**\n * Hand the clicks held before hydration to the router. The last one is the\n * page the visitor meant; the ones before it were on the way there.\n */\nexport function replayEarlyClicks(navigate: (url: string) => Promise<void>): string | null {\n const early = (window as { __rsc_early?: Early }).__rsc_early;\n\n if (!early) return null;\n\n early.stop();\n\n const last = early.q[early.q.length - 1] ?? null;\n\n early.q.length = 0;\n\n if (last) void navigate(last);\n\n return last;\n}\n\n/**\n * Run once hydration has committed - now, if it already has, or when the\n * outermost client component's first effect says so. Before that a click\n * is nobody's: the listener above is still holding them, and React has no\n * fibers to dispatch to.\n */\nexport function afterHydration(fn: () => void): void {\n const w = window as { __rsc_hydrated?: boolean; __rsc_on_hydrated?: () => void };\n\n if (w.__rsc_hydrated) {\n fn();\n\n return;\n }\n\n w.__rsc_on_hydrated = () => {\n w.__rsc_on_hydrated = undefined;\n fn();\n };\n}\n"]}
1
+ {"version":3,"file":"earlyClicks.js","sourceRoot":"","sources":["../../src/js/earlyClicks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,8GAA8G;AAC9G,MAAM,CAAC,MAAM,YAAY,GACvB,2CAA2C;IAC3C,yFAAyF;IACzF,kEAAkE;IAClE,+CAA+C;IAC/C,4EAA4E;IAC5E,uCAAuC;IACvC,0FAA0F;IAC1F,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,oEAAoE;IACpE,uEAAuE;IACvE,4DAA4D;IAC5D,6GAA6G;IAC7G,2FAA2F;IAC3F,uEAAuE;IACvE,sEAAsE;IACtE,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,qEAAqE;IACrE,mEAAmE;IACnE,0FAA0F;IAC1F,4DAA4D;IAC5D,kFAAkF;IAClF,0FAA0F;IAC1F,sFAAsF;IACtF,oEAAoE;IACpE,oGAAoG;IACpG,4IAA4I,CAAC;AAQ/I;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAwC;IACxE,MAAM,KAAK,GAAI,MAAkC,CAAC,WAAW,CAAC;IAE9D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,KAAK,CAAC,IAAI,EAAE,CAAC;IAEb,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IACjD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IAErD,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACnB,IAAI,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IAElC,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,YAAY,WAAW,CAAC,CAAC,CAAE,IAAI,CAAC,CAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAc;IAC3C,MAAM,CAAC,GAAG,MAAsE,CAAC;IAEjF,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;QACrB,EAAE,EAAE,CAAC;QAEL,OAAO;IACT,CAAC;IAED,CAAC,CAAC,iBAAiB,GAAG,GAAG,EAAE;QACzB,CAAC,CAAC,iBAAiB,GAAG,SAAS,CAAC;QAChC,EAAE,EAAE,CAAC;IACP,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * A tap before the page can navigate.\n *\n * The document arrives, the bootstrap script starts the runtime, and on a\n * phone the runtime takes a while to arrive and hydrate - seconds, with a\n * tag manager or two on the main thread. A tap on a link in that window is\n * the browser's: a document load, of a page the router would have swapped\n * in. The port's \"Sign in is slow from the home page, and sometimes a full\n * reload\" was this, and only on a phone, where the window is wide.\n *\n * The first thing the bootstrap script does is listen: a click on one of\n * this package's links, before the page has hydrated, is held. When\n * hydration commits - not when the runtime's script runs, which is\n * seconds earlier on a phone whose chunks are still downloading, and where\n * a click is nobody's yet - the last one held is navigated to. A click\n * held for three seconds with no runtime started is given to the browser\n * as a document load, so nothing is lost if the runtime never comes; one\n * held while the runtime is running waits for hydration, up to fifteen.\n *\n * Only this package's links, marked `data-rsc`: a plain anchor to a file, a\n * route.ts, a mailto or a foreign origin is not a page the router swaps in.\n */\n\n/** Written into the bootstrap script, ahead of the runtime's import. No backticks: it lives in a template. */\nexport const EARLY_CLICKS =\n \"(function(){var q=[],fq=[];function h(e){\" +\n \"if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;\" +\n \"var t=e.target;var a=t&&t.closest?t.closest('a[data-rsc]'):null;\" +\n \"if(!a||(a.target&&a.target!=='_self'))return;\" +\n \"var u;try{u=new URL(a.getAttribute('href'),location.href)}catch(x){return}\" +\n \"if(u.origin!==location.origin)return;\" +\n \"e.preventDefault();q.push(u.pathname+u.search+u.hash);a.setAttribute('data-pending','');\" +\n // Given to the browser only when the runtime has not even started three\n // seconds after the tap - a script blocked, a chunk that failed. While it\n // is running, hydration is coming, and a phone's takes longer than three\n // seconds: a tap given up at three landed as a document load half a\n // second before the page would have handled it. Fifteen seconds is the\n // hard limit for a runtime that started and never finished.\n \"setTimeout(function(){if(q.length&&!w.__rsc_hydrated&&!w.__rsc_navigate)location.href=q[q.length-1]},3000);\" +\n \"setTimeout(function(){if(q.length&&!w.__rsc_hydrated)location.href=q[q.length-1]},15000)}\" +\n // A form submitted before hydration. React renders a form whose action\n // is a server action with $ACTION_ hidden fields so it posts natively\n // without a runtime - the page reloads with the action applied, which is\n // the right answer with no JavaScript and the wrong one with JavaScript\n // a second away: \"Add to cart\" on a product page reloaded the page when\n // tapped before the runtime hydrated. Held like a tap, and submitted\n // again once hydration commits, when React's own handler takes it.\n \"function g(){var x=fq.pop();fq.length=0;if(x)HTMLFormElement.prototype.submit.call(x.f)}\" +\n \"function s(e){if(e.defaultPrevented)return;var f=e.target;\" +\n \"if(!f||f.tagName!=='FORM'||!f.querySelector('input[name^=\\\"$ACTION_\\\"]'))return;\" +\n \"e.preventDefault();fq.push({f:f,s:e.submitter||null});f.setAttribute('data-pending','');\" +\n \"setTimeout(function(){if(fq.length&&!w.__rsc_hydrated&&!w.__rsc_navigate)g()},3000);\" +\n \"setTimeout(function(){if(fq.length&&!w.__rsc_hydrated)g()},15000)}\" +\n \"var w=window;document.addEventListener('click',h,true);document.addEventListener('submit',s,true);\" +\n \"w.__rsc_early={q:q,fq:fq,stop:function(){document.removeEventListener('click',h,true);document.removeEventListener('submit',s,true)}}})();\";\n\ninterface Early {\n q: string[];\n fq?: { f: HTMLFormElement; s: HTMLElement | null }[];\n stop(): void;\n}\n\n/**\n * Hand the clicks held before hydration to the router. The last one is the\n * page the visitor meant; the ones before it were on the way there. A form\n * held is submitted again, now that React's handler is on it - a tap on a\n * link after it wins, since the visitor left the form.\n */\nexport function replayEarlyClicks(navigate: (url: string) => Promise<void>): string | null {\n const early = (window as { __rsc_early?: Early }).__rsc_early;\n\n if (!early) return null;\n\n early.stop();\n\n const last = early.q[early.q.length - 1] ?? null;\n const form = early.fq?.[early.fq.length - 1] ?? null;\n\n early.q.length = 0;\n if (early.fq) early.fq.length = 0;\n\n if (last) {\n void navigate(last);\n } else if (form) {\n form.f.removeAttribute(\"data-pending\");\n form.f.requestSubmit(form.s instanceof HTMLElement ? (form.s as HTMLElement) : undefined);\n }\n\n return last;\n}\n\n/**\n * Run once hydration has committed - now, if it already has, or when the\n * outermost client component's first effect says so. Before that a click\n * is nobody's: the listener above is still holding them, and React has no\n * fibers to dispatch to.\n */\nexport function afterHydration(fn: () => void): void {\n const w = window as { __rsc_hydrated?: boolean; __rsc_on_hydrated?: () => void };\n\n if (w.__rsc_hydrated) {\n fn();\n\n return;\n }\n\n w.__rsc_on_hydrated = () => {\n w.__rsc_on_hydrated = undefined;\n fn();\n };\n}\n"]}
@@ -43,6 +43,8 @@ export declare function throwForFailedAction(response: Response): Promise<void>;
43
43
  * decoder reports the decoder's confusion rather than the status.
44
44
  */
45
45
  export declare function throwForFailedPayload(response: Response): void;
46
+ /** Whether a response is a Flight payload, whatever its status. */
47
+ export declare function isPayload(response: Response): boolean;
46
48
  /**
47
49
  * Announce a failure that nothing else will.
48
50
  *
package/dist/js/errors.js CHANGED
@@ -103,8 +103,19 @@ export async function throwForFailedAction(response) {
103
103
  export function throwForFailedPayload(response) {
104
104
  if (response.ok)
105
105
  return;
106
+ // A 404 that is a payload is the not-found page, rendered: a url a
107
+ // pattern's shell answered with a 200 and a page that, run for real,
108
+ // found no row - a subcategory under the wrong category. The page is
109
+ // what the visitor should see; refusing it left the shell's content on
110
+ // screen with nothing hydrated behind it, and every tap dead.
111
+ if (isPayload(response))
112
+ return;
106
113
  throw new Error(`RSC payload request failed with ${response.status}`);
107
114
  }
115
+ /** Whether a response is a Flight payload, whatever its status. */
116
+ export function isPayload(response) {
117
+ return (response.headers.get("Content-Type") ?? "").includes("text/x-component");
118
+ }
108
119
  /**
109
120
  * Announce a failure that nothing else will.
110
121
  *
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/js/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export class ServerValidationError extends Error {\n public readonly errors: Record<string, string[]>;\n\n constructor(message: string, errors: Record<string, string[]>) {\n super(message);\n this.name = \"ServerValidationError\";\n this.errors = errors;\n }\n}\n\n/** An action answered with a location instead of a result. */\n/**\n * The redirect the last action answered with, for the one caller that asks.\n *\n * An action that redirects resolves - the navigation is already under way\n * and the caller has nothing to do - which leaves a form that wants to know\n * whether to say \"saved\" with no way to tell a redirect from a void answer.\n * callServer notes the destination here; <Form> reads and clears it right\n * after its await. Nothing else needs to.\n */\nlet lastRedirect: string | null = null;\n\nexport function noteRedirected(location: string): void {\n lastRedirect = location;\n}\n\n/** The redirect the action just performed, if it did - read once. */\nexport function redirectedTo(): string | null {\n const location = lastRedirect;\n\n lastRedirect = null;\n\n return location;\n}\n\nexport class ServerRedirectError extends Error {\n public readonly location: string;\n\n constructor(location: string) {\n super(`Server action redirected to ${location}`);\n this.name = \"ServerRedirectError\";\n this.location = location;\n }\n}\n\nexport class ServerAuthenticationError extends Error {\n constructor(message: string = \"Unauthenticated.\") {\n super(message);\n this.name = \"ServerAuthenticationError\";\n }\n}\n\nexport class ServerAuthorizationError extends Error {\n constructor(message: string = \"This action is unauthorized.\") {\n super(message);\n this.name = \"ServerAuthorizationError\";\n }\n}\n\nexport class ServerDumpError extends Error {\n constructor() {\n super(\"Server returned a dump response.\");\n this.name = \"ServerDumpError\";\n }\n}\n\nexport class ServerSessionExpiredError extends Error {\n constructor(message: string = \"Your session has expired. Please refresh the page.\") {\n super(message);\n this.name = \"ServerSessionExpiredError\";\n }\n}\n\n/**\n * Turn a failed server-action response into the error it describes.\n *\n * A server action that does not succeed answers with JSON or a redirect\n * header rather than a Flight stream. Passing one of those to the Flight\n * decoder does not produce the server's message — it produces an internal\n * parser failure (\"enqueueModel is not a function\") or a truncated read\n * (\"Connection closed.\"), which is what reached onError before this existed.\n *\n * Returns without throwing when the response is a stream to be decoded.\n */\nexport async function throwForFailedAction(response: Response): Promise<void> {\n // Before the status: a redirect the action asked for is a 204 with the\n // destination in this header - an ok answer with no Flight in it - and\n // an expired session's is a 401. Read first, the header decides for both.\n // Read after `ok`, the 204 fell through to the Flight decoder with an\n // empty body, and the form waited forever.\n const location = response.headers.get(\"X-RSC-Redirect\");\n\n if (location !== null && location !== \"\") {\n throw new ServerRedirectError(location);\n }\n\n if (response.ok) return;\n\n if (response.status === 422) {\n const payload = (await response.json().catch(() => null)) as\n | { message?: string; errors?: Record<string, string[]> }\n | null;\n\n throw new ServerValidationError(\n payload?.message ?? \"Validation failed\",\n payload?.errors ?? {},\n );\n }\n\n if (response.status === 413) {\n throw new Error(\n \"Server action refused: the request body is larger than the server accepts. \" +\n \"A file upload is the usual cause; the limit is the host's maxActionBody.\",\n );\n }\n\n throw new Error(`Server action failed with ${response.status}`);\n}\n\n/**\n * Reject a payload response that is not one.\n *\n * The page a PPR route serves is a static shell: real HTML, status 200, with\n * its Suspense fallbacks showing. Everything below them arrives in a second\n * request. If that request fails there is nothing on screen to say so — the\n * skeletons simply stay, for ever — and handing the failure body to the Flight\n * decoder reports the decoder's confusion rather than the status.\n */\nexport function throwForFailedPayload(response: Response): void {\n if (response.ok) return;\n\n throw new Error(`RSC payload request failed with ${response.status}`);\n}\n\n/**\n * Announce a failure that nothing else will.\n *\n * Dispatched as well as logged: an app that wants to replace a stuck skeleton\n * with something honest has no other way to find out.\n */\nexport function reportClientFailure(scope: string, error: unknown): void {\n if (typeof window !== \"undefined\") {\n window.dispatchEvent(new CustomEvent(\"rsc-client-error\", { detail: { scope, error } }));\n }\n\n console.error(`[rsc-kit] ${scope}`, error);\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/js/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export class ServerValidationError extends Error {\n public readonly errors: Record<string, string[]>;\n\n constructor(message: string, errors: Record<string, string[]>) {\n super(message);\n this.name = \"ServerValidationError\";\n this.errors = errors;\n }\n}\n\n/** An action answered with a location instead of a result. */\n/**\n * The redirect the last action answered with, for the one caller that asks.\n *\n * An action that redirects resolves - the navigation is already under way\n * and the caller has nothing to do - which leaves a form that wants to know\n * whether to say \"saved\" with no way to tell a redirect from a void answer.\n * callServer notes the destination here; <Form> reads and clears it right\n * after its await. Nothing else needs to.\n */\nlet lastRedirect: string | null = null;\n\nexport function noteRedirected(location: string): void {\n lastRedirect = location;\n}\n\n/** The redirect the action just performed, if it did - read once. */\nexport function redirectedTo(): string | null {\n const location = lastRedirect;\n\n lastRedirect = null;\n\n return location;\n}\n\nexport class ServerRedirectError extends Error {\n public readonly location: string;\n\n constructor(location: string) {\n super(`Server action redirected to ${location}`);\n this.name = \"ServerRedirectError\";\n this.location = location;\n }\n}\n\nexport class ServerAuthenticationError extends Error {\n constructor(message: string = \"Unauthenticated.\") {\n super(message);\n this.name = \"ServerAuthenticationError\";\n }\n}\n\nexport class ServerAuthorizationError extends Error {\n constructor(message: string = \"This action is unauthorized.\") {\n super(message);\n this.name = \"ServerAuthorizationError\";\n }\n}\n\nexport class ServerDumpError extends Error {\n constructor() {\n super(\"Server returned a dump response.\");\n this.name = \"ServerDumpError\";\n }\n}\n\nexport class ServerSessionExpiredError extends Error {\n constructor(message: string = \"Your session has expired. Please refresh the page.\") {\n super(message);\n this.name = \"ServerSessionExpiredError\";\n }\n}\n\n/**\n * Turn a failed server-action response into the error it describes.\n *\n * A server action that does not succeed answers with JSON or a redirect\n * header rather than a Flight stream. Passing one of those to the Flight\n * decoder does not produce the server's message — it produces an internal\n * parser failure (\"enqueueModel is not a function\") or a truncated read\n * (\"Connection closed.\"), which is what reached onError before this existed.\n *\n * Returns without throwing when the response is a stream to be decoded.\n */\nexport async function throwForFailedAction(response: Response): Promise<void> {\n // Before the status: a redirect the action asked for is a 204 with the\n // destination in this header - an ok answer with no Flight in it - and\n // an expired session's is a 401. Read first, the header decides for both.\n // Read after `ok`, the 204 fell through to the Flight decoder with an\n // empty body, and the form waited forever.\n const location = response.headers.get(\"X-RSC-Redirect\");\n\n if (location !== null && location !== \"\") {\n throw new ServerRedirectError(location);\n }\n\n if (response.ok) return;\n\n if (response.status === 422) {\n const payload = (await response.json().catch(() => null)) as\n | { message?: string; errors?: Record<string, string[]> }\n | null;\n\n throw new ServerValidationError(\n payload?.message ?? \"Validation failed\",\n payload?.errors ?? {},\n );\n }\n\n if (response.status === 413) {\n throw new Error(\n \"Server action refused: the request body is larger than the server accepts. \" +\n \"A file upload is the usual cause; the limit is the host's maxActionBody.\",\n );\n }\n\n throw new Error(`Server action failed with ${response.status}`);\n}\n\n/**\n * Reject a payload response that is not one.\n *\n * The page a PPR route serves is a static shell: real HTML, status 200, with\n * its Suspense fallbacks showing. Everything below them arrives in a second\n * request. If that request fails there is nothing on screen to say so — the\n * skeletons simply stay, for ever — and handing the failure body to the Flight\n * decoder reports the decoder's confusion rather than the status.\n */\nexport function throwForFailedPayload(response: Response): void {\n if (response.ok) return;\n\n // A 404 that is a payload is the not-found page, rendered: a url a\n // pattern's shell answered with a 200 and a page that, run for real,\n // found no row - a subcategory under the wrong category. The page is\n // what the visitor should see; refusing it left the shell's content on\n // screen with nothing hydrated behind it, and every tap dead.\n if (isPayload(response)) return;\n\n throw new Error(`RSC payload request failed with ${response.status}`);\n}\n\n/** Whether a response is a Flight payload, whatever its status. */\nexport function isPayload(response: Response): boolean {\n return (response.headers.get(\"Content-Type\") ?? \"\").includes(\"text/x-component\");\n}\n\n/**\n * Announce a failure that nothing else will.\n *\n * Dispatched as well as logged: an app that wants to replace a stuck skeleton\n * with something honest has no other way to find out.\n */\nexport function reportClientFailure(scope: string, error: unknown): void {\n if (typeof window !== \"undefined\") {\n window.dispatchEvent(new CustomEvent(\"rsc-client-error\", { detail: { scope, error } }));\n }\n\n console.error(`[rsc-kit] ${scope}`, error);\n}\n"]}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The pictures a prefetched page shows, fetched before the click.
3
+ *
4
+ * A payload names them: every <img> the page renders is a row in it, src
5
+ * and srcSet included. The page is rendered hidden on touch, and a hidden
6
+ * page's images do load then - but a touch leads its click by a few hundred
7
+ * milliseconds, and a product picture on a phone's connection takes
8
+ * longer. The original of a port fetched the next page's image list from a
9
+ * route as each link came into view; here the list is already in the
10
+ * payload the router holds, so the images are asked for as it lands, at low
11
+ * priority, and the click finds them decoded.
12
+ *
13
+ * Only the ones the page would load at once: an <img loading="lazy"> waits
14
+ * for the viewport on the page too. Bounded per payload, and each url once
15
+ * per document; nothing under Save-Data.
16
+ */
17
+ export type Priority = 'low' | 'high';
18
+ export interface ImageProps {
19
+ src?: string;
20
+ srcSet?: string;
21
+ sizes?: string;
22
+ loading?: string;
23
+ alt?: string;
24
+ }
25
+ /** The props of every <img> element row in a flight payload, in order. */
26
+ export declare function imagesIn(payload: string): ImageProps[];
27
+ /**
28
+ * Ask the browser for the eager images a payload names.
29
+ *
30
+ * Low as the payload lands on sight, behind everything the page itself is
31
+ * loading. High on intent - the touch, the settled hover - when this is the
32
+ * page about to show: a listing's two dozen tiles each preload their
33
+ * product's picture, and the one tapped first was queued behind the rest,
34
+ * for three hundred milliseconds on a phone; asked again as urgent, the
35
+ * browser moves it to the front, ahead of the pictures nobody touched.
36
+ */
37
+ export declare function preloadImages(payload: string, priority?: Priority): number;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The pictures a prefetched page shows, fetched before the click.
3
+ *
4
+ * A payload names them: every <img> the page renders is a row in it, src
5
+ * and srcSet included. The page is rendered hidden on touch, and a hidden
6
+ * page's images do load then - but a touch leads its click by a few hundred
7
+ * milliseconds, and a product picture on a phone's connection takes
8
+ * longer. The original of a port fetched the next page's image list from a
9
+ * route as each link came into view; here the list is already in the
10
+ * payload the router holds, so the images are asked for as it lands, at low
11
+ * priority, and the click finds them decoded.
12
+ *
13
+ * Only the ones the page would load at once: an <img loading="lazy"> waits
14
+ * for the viewport on the page too. Bounded per payload, and each url once
15
+ * per document; nothing under Save-Data.
16
+ */
17
+ /**
18
+ * Per payload, the first few: the pictures a page shows first are the ones
19
+ * at the top of its payload - a product's own picture before its related
20
+ * ones. Twenty-four a page, times the dozen pages a listing prefetches on
21
+ * sight, was three hundred requests on a phone's radio the moment the home
22
+ * page settled, and the next tap's payload queued behind them: "clicking a
23
+ * category breaks navigation for a few seconds".
24
+ */
25
+ const PER_PAGE = 6;
26
+ /** Across the document: a home page with five hundred links in view is not five hundred pages of pictures. */
27
+ const PER_DOCUMENT = 96;
28
+ /** Each picture asked for, and how urgently. */
29
+ const asked = new Map();
30
+ /** The props of every <img> element row in a flight payload, in order. */
31
+ export function imagesIn(payload) {
32
+ const found = [];
33
+ let at = 0;
34
+ while (found.length < PER_PAGE) {
35
+ const start = payload.indexOf('["$","img",', at);
36
+ if (start === -1)
37
+ break;
38
+ // Past the key: `["$","img","key",{` or `["$","img",null,{`.
39
+ const brace = payload.indexOf('{', start);
40
+ if (brace === -1)
41
+ break;
42
+ const end = closingBrace(payload, brace);
43
+ at = end === -1 ? brace + 1 : end + 1;
44
+ if (end === -1)
45
+ continue;
46
+ try {
47
+ found.push(JSON.parse(payload.slice(brace, end + 1)));
48
+ }
49
+ catch {
50
+ // A props object with a reference in it that is not JSON; not a picture worth guessing at.
51
+ }
52
+ }
53
+ return found;
54
+ }
55
+ /** The index of the brace closing the object opened at `open`, honouring strings. */
56
+ function closingBrace(text, open) {
57
+ let depth = 0;
58
+ let inString = false;
59
+ for (let i = open; i < text.length; i++) {
60
+ const c = text[i];
61
+ if (inString) {
62
+ if (c === '\\')
63
+ i++;
64
+ else if (c === '"')
65
+ inString = false;
66
+ continue;
67
+ }
68
+ if (c === '"')
69
+ inString = true;
70
+ else if (c === '{')
71
+ depth++;
72
+ else if (c === '}' && --depth === 0)
73
+ return i;
74
+ }
75
+ return -1;
76
+ }
77
+ /**
78
+ * Ask the browser for the eager images a payload names.
79
+ *
80
+ * Low as the payload lands on sight, behind everything the page itself is
81
+ * loading. High on intent - the touch, the settled hover - when this is the
82
+ * page about to show: a listing's two dozen tiles each preload their
83
+ * product's picture, and the one tapped first was queued behind the rest,
84
+ * for three hundred milliseconds on a phone; asked again as urgent, the
85
+ * browser moves it to the front, ahead of the pictures nobody touched.
86
+ */
87
+ export function preloadImages(payload, priority = 'low') {
88
+ if (typeof document === 'undefined')
89
+ return 0;
90
+ if (navigator.connection?.saveData)
91
+ return 0;
92
+ let started = 0;
93
+ for (const props of imagesIn(payload)) {
94
+ if (asked.size >= PER_DOCUMENT && priority === 'low')
95
+ break;
96
+ if (props.loading === 'lazy' || !props.src)
97
+ continue;
98
+ const key = props.srcSet ?? props.src;
99
+ const before = asked.get(key);
100
+ if (before === 'high' || before === priority)
101
+ continue;
102
+ asked.set(key, priority);
103
+ const img = new Image();
104
+ img.decoding = 'async';
105
+ img.fetchPriority = priority;
106
+ // sizes before srcset before src: the browser chooses on assignment.
107
+ if (props.sizes)
108
+ img.sizes = props.sizes;
109
+ if (props.srcSet)
110
+ img.srcset = props.srcSet;
111
+ img.src = props.src;
112
+ started++;
113
+ }
114
+ return started;
115
+ }
116
+ //# sourceMappingURL=imagePreload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imagePreload.js","sourceRoot":"","sources":["../../src/js/imagePreload.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The pictures a prefetched page shows, fetched before the click.\n *\n * A payload names them: every <img> the page renders is a row in it, src\n * and srcSet included. The page is rendered hidden on touch, and a hidden\n * page's images do load then - but a touch leads its click by a few hundred\n * milliseconds, and a product picture on a phone's connection takes\n * longer. The original of a port fetched the next page's image list from a\n * route as each link came into view; here the list is already in the\n * payload the router holds, so the images are asked for as it lands, at low\n * priority, and the click finds them decoded.\n *\n * Only the ones the page would load at once: an <img loading=\"lazy\"> waits\n * for the viewport on the page too. Bounded per payload, and each url once\n * per document; nothing under Save-Data.\n */\n\n/**\n * Per payload, the first few: the pictures a page shows first are the ones\n * at the top of its payload - a product's own picture before its related\n * ones. Twenty-four a page, times the dozen pages a listing prefetches on\n * sight, was three hundred requests on a phone's radio the moment the home\n * page settled, and the next tap's payload queued behind them: \"clicking a\n * category breaks navigation for a few seconds\".\n */\nconst PER_PAGE = 6\n/** Across the document: a home page with five hundred links in view is not five hundred pages of pictures. */\nconst PER_DOCUMENT = 96\n/** Each picture asked for, and how urgently. */\nconst asked = new Map<string, Priority>()\n\nexport type Priority = 'low' | 'high'\n\nexport interface ImageProps {\n src?: string\n srcSet?: string\n sizes?: string\n loading?: string\n alt?: string\n}\n\n/** The props of every <img> element row in a flight payload, in order. */\nexport function imagesIn(payload: string): ImageProps[] {\n const found: ImageProps[] = []\n let at = 0\n\n while (found.length < PER_PAGE) {\n const start = payload.indexOf('[\"$\",\"img\",', at)\n\n if (start === -1) break\n\n // Past the key: `[\"$\",\"img\",\"key\",{` or `[\"$\",\"img\",null,{`.\n const brace = payload.indexOf('{', start)\n\n if (brace === -1) break\n\n const end = closingBrace(payload, brace)\n\n at = end === -1 ? brace + 1 : end + 1\n\n if (end === -1) continue\n\n try {\n found.push(JSON.parse(payload.slice(brace, end + 1)) as ImageProps)\n } catch {\n // A props object with a reference in it that is not JSON; not a picture worth guessing at.\n }\n }\n\n return found\n}\n\n/** The index of the brace closing the object opened at `open`, honouring strings. */\nfunction closingBrace(text: string, open: number): number {\n let depth = 0\n let inString = false\n\n for (let i = open; i < text.length; i++) {\n const c = text[i]\n\n if (inString) {\n if (c === '\\\\') i++\n else if (c === '\"') inString = false\n\n continue\n }\n\n if (c === '\"') inString = true\n else if (c === '{') depth++\n else if (c === '}' && --depth === 0) return i\n }\n\n return -1\n}\n\n/**\n * Ask the browser for the eager images a payload names.\n *\n * Low as the payload lands on sight, behind everything the page itself is\n * loading. High on intent - the touch, the settled hover - when this is the\n * page about to show: a listing's two dozen tiles each preload their\n * product's picture, and the one tapped first was queued behind the rest,\n * for three hundred milliseconds on a phone; asked again as urgent, the\n * browser moves it to the front, ahead of the pictures nobody touched.\n */\nexport function preloadImages(payload: string, priority: Priority = 'low'): number {\n if (typeof document === 'undefined') return 0\n if ((navigator as { connection?: { saveData?: boolean } }).connection?.saveData) return 0\n\n let started = 0\n\n for (const props of imagesIn(payload)) {\n if (asked.size >= PER_DOCUMENT && priority === 'low') break\n if (props.loading === 'lazy' || !props.src) continue\n\n const key = props.srcSet ?? props.src\n const before = asked.get(key)\n\n if (before === 'high' || before === priority) continue\n\n asked.set(key, priority)\n\n const img = new Image()\n\n img.decoding = 'async'\n ;(img as { fetchPriority?: string }).fetchPriority = priority\n // sizes before srcset before src: the browser chooses on assignment.\n if (props.sizes) img.sizes = props.sizes\n if (props.srcSet) img.srcset = props.srcSet\n img.src = props.src\n started++\n }\n\n return started\n}\n"]}
@@ -37,6 +37,20 @@ export declare function setVersion(v: string): void;
37
37
  export declare function setHeldLayouts(chain: string[]): void;
38
38
  export declare function getHeldLayouts(): string[];
39
39
  export declare function setNavigateHandler(fn: (tree: ReactNode, key: string, segmentDepth: number) => void): void;
40
+ export declare function setReplaceRootHandler(fn: ((tree: ReactNode) => void) | null): void;
41
+ export declare function setHeldHandlers(held: ((key: string, maxAge?: number) => boolean) | null, drop: (() => void) | null): void;
42
+ /**
43
+ * Everything the router holds about pages other than the one on screen,
44
+ * dropped: prefetched payloads, and pages kept behind this one.
45
+ *
46
+ * After an action that revalidated. The list a link prefetched before the
47
+ * mutation still shows the table without the new row - visit() landed on
48
+ * it with no request made, and a reload showed the row. What was fetched
49
+ * before a write is not a cache of what is true after it; the pages held
50
+ * for the back button are from before it too.
51
+ */
52
+ export declare function forgetOtherPages(): void;
53
+ export declare function setPrerenderHandler(fn: ((tree: ReactNode, key: string, segmentDepth: number) => void) | null): void;
40
54
  /**
41
55
  * How the router reveals a page that is still mounted behind the current one.
42
56
  *
@@ -88,6 +102,25 @@ export declare function navigate(url: Route, opts?: {
88
102
  * is the same apply path a navigation uses — without a request, a url change
89
103
  * or a history entry.
90
104
  */
105
+ /**
106
+ * Whether the page an action was invoked from is the one on screen.
107
+ *
108
+ * The one underneath, when an interception is showing: a modal opened after
109
+ * the submit sits over the same page.
110
+ */
111
+ export declare function stillShowing(url: string): boolean;
112
+ /**
113
+ * Put what an action re-rendered on screen.
114
+ *
115
+ * `from` is the url the action was invoked on - what the host rendered the
116
+ * trees for. A tap that left the page while the action was in flight has
117
+ * changed what is showing, and a document rendered for the page before
118
+ * cannot go under the url after: "Add to cart", then the brand link at
119
+ * once, showed the home page and then, when the answer landed, the product
120
+ * again under `/`. The write still happened, and the page on screen was
121
+ * fetched before it - so that page is asked for again, whole, instead.
122
+ */
123
+ export declare function applyRevalidations(from: string, revalidated: Record<string, ReactNode>): void;
91
124
  export declare function applyRevalidated(target: string, tree: ReactNode): void;
92
125
  /**
93
126
  * Ask the server for part of this page again.
@@ -107,7 +140,20 @@ export declare function applyRevalidated(target: string, tree: ReactNode): void;
107
140
  * count living in a layout will not move until you ask for 'all'.
108
141
  */
109
142
  export declare function refresh(target?: string): Promise<void>;
110
- export declare function prefetch(url: string, cacheForMs?: number): void;
143
+ /**
144
+ * Fetch a page's payload ahead of the navigation that may ask for it.
145
+ *
146
+ * `intent` is the difference between a link that came into view and one the
147
+ * visitor is about to follow: a hover that settled, a touch. Only then is the
148
+ * payload decoded on arrival - decoding is what loads the client chunks the
149
+ * page names, and a viewport prefetch that decoded put the sign-in page's
150
+ * thirty chunks onto a landing page for every phone visitor. A touch leads
151
+ * its click by 100-300 ms, about a round trip, and a tap that found the
152
+ * bytes here but not the chunks spent that round trip after the click
153
+ * instead of before it: the tap-to-paint that was "not zero" beside Next,
154
+ * whose prefetch decodes as it lands.
155
+ */
156
+ export declare function prefetch(url: string, cacheForMs?: number, intent?: boolean): void;
111
157
  /**
112
158
  * Drop a prefetch that is still in flight — the pointer left the link.
113
159
  *