@timber-js/app 0.2.0-alpha.34 → 0.2.0-alpha.35
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.
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
- package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-B3Gypr3D.js → debug-ECi_61pb.js} +1 -1
- package/dist/_chunks/{debug-B3Gypr3D.js.map → debug-ECi_61pb.js.map} +1 -1
- package/dist/_chunks/define-cookie-w5GWm_bL.js +93 -0
- package/dist/_chunks/define-cookie-w5GWm_bL.js.map +1 -0
- package/dist/_chunks/error-boundary-TYEQJZ1-.js +211 -0
- package/dist/_chunks/error-boundary-TYEQJZ1-.js.map +1 -0
- package/dist/_chunks/{format-RyoGQL74.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-RyoGQL74.js.map → format-cX7wzEp2.js.map} +1 -1
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
- package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/{request-context-BQUC8PHn.js → request-context-CZz_T0Bc.js} +40 -71
- package/dist/_chunks/request-context-CZz_T0Bc.js.map +1 -0
- package/dist/_chunks/segment-context-Dpq2XOKg.js +34 -0
- package/dist/_chunks/segment-context-Dpq2XOKg.js.map +1 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
- package/dist/_chunks/{tracing-CemImE6h.js → tracing-BPyIzIdu.js} +2 -2
- package/dist/_chunks/{tracing-CemImE6h.js.map → tracing-BPyIzIdu.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
- package/dist/_chunks/wrappers-C1SN725w.js +331 -0
- package/dist/_chunks/wrappers-C1SN725w.js.map +1 -0
- package/dist/cache/index.js +1 -1
- package/dist/client/error-boundary.d.ts +10 -1
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +193 -90
- package/dist/client/index.js.map +1 -1
- package/dist/client/link.d.ts +8 -8
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +2 -2
- package/dist/client/router.d.ts +25 -3
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +23 -2
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +1 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +2 -2
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/codec.d.ts +21 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/cookies/define-cookie.d.ts +33 -12
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -81
- package/dist/index.d.ts +87 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +346 -210
- package/dist/index.js.map +1 -1
- package/dist/params/define.d.ts +76 -0
- package/dist/params/define.d.ts.map +1 -0
- package/dist/params/index.d.ts +8 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +104 -0
- package/dist/params/index.js.map +1 -0
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +6 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/search-params/codecs.d.ts +1 -1
- package/dist/search-params/define.d.ts +153 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -5
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +3 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +4 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +4 -0
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +78 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1819 -1629
- package/dist/server/index.js.map +1 -1
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +28 -40
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +7 -0
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +2 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +3 -0
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +12 -8
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +12 -8
- package/src/client/browser-entry.ts +55 -13
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/index.ts +9 -1
- package/src/client/link.tsx +9 -9
- package/src/client/navigation-context.ts +2 -2
- package/src/client/router.ts +102 -55
- package/src/client/rsc-fetch.ts +63 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/stale-reload.ts +28 -0
- package/src/client/top-loader.tsx +2 -2
- package/src/client/use-params.ts +3 -3
- package/src/client/use-query-states.ts +1 -1
- package/src/codec.ts +21 -0
- package/src/cookies/define-cookie.ts +69 -18
- package/src/index.ts +255 -65
- package/src/params/define.ts +260 -0
- package/src/params/index.ts +28 -0
- package/src/plugins/adapter-build.ts +6 -0
- package/src/plugins/build-manifest.ts +11 -0
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/entries.ts +3 -6
- package/src/plugins/routing.ts +40 -14
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +8 -4
- package/src/routing/codegen.ts +109 -88
- package/src/routing/scanner.ts +55 -6
- package/src/routing/status-file-lint.ts +2 -1
- package/src/routing/types.ts +7 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/search-params/codecs.ts +1 -1
- package/src/search-params/define.ts +504 -0
- package/src/search-params/index.ts +12 -18
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/server/access-gate.tsx +38 -8
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +16 -0
- package/src/server/als-registry.ts +4 -4
- package/src/server/build-manifest.ts +4 -4
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +57 -14
- package/src/server/flight-injection-state.ts +152 -0
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +42 -26
- package/src/server/index.ts +2 -4
- package/src/server/node-stream-transforms.ts +68 -41
- package/src/server/pipeline.ts +98 -26
- package/src/server/request-context.ts +49 -124
- package/src/server/route-element-builder.ts +102 -99
- package/src/server/route-matcher.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +3 -2
- package/src/server/rsc-entry/index.ts +26 -11
- package/src/server/rsc-entry/rsc-payload.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +4 -4
- package/src/server/slot-resolver.ts +204 -206
- package/src/server/ssr-entry.ts +3 -1
- package/src/server/ssr-render.ts +3 -0
- package/src/server/tree-builder.ts +84 -48
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -0
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +1 -1
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
package/dist/client/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
2
|
+
import { n as __exportAll } from "../_chunks/chunk-DYhsFzuS.js";
|
|
3
|
+
import { a as _setCachedSearch, c as cachedSearch, d as globalRouter, i as setSsrData, l as cachedSearchParams, n as clearSsrData, o as _setCurrentParams, r as getSsrData, s as _setGlobalRouter, t as TimberErrorBoundary, u as currentParams } from "../_chunks/error-boundary-TYEQJZ1-.js";
|
|
4
|
+
import { n as useSegmentContext, t as SegmentProvider } from "../_chunks/segment-context-Dpq2XOKg.js";
|
|
5
|
+
import { n as useQueryStates, t as bindUseQueryStates } from "../_chunks/use-query-states-BvW0TKDn.js";
|
|
6
|
+
import { t as _registerUseCookieModule } from "../_chunks/define-cookie-w5GWm_bL.js";
|
|
7
|
+
import React, { cloneElement, createContext, createElement, isValidElement, useActionState as useActionState$1, useContext, useSyncExternalStore, useTransition } from "react";
|
|
7
8
|
import { jsx } from "react/jsx-runtime";
|
|
8
9
|
//#region src/client/use-link-status.ts
|
|
9
10
|
/**
|
|
@@ -46,7 +47,7 @@ function useLinkStatus() {
|
|
|
46
47
|
*
|
|
47
48
|
* Holds the current route params and pathname, updated atomically
|
|
48
49
|
* with the RSC tree on each navigation. This replaces the previous
|
|
49
|
-
* useSyncExternalStore approach for
|
|
50
|
+
* useSyncExternalStore approach for useSegmentParams() and usePathname(),
|
|
50
51
|
* which suffered from a timing gap: the new tree could commit before
|
|
51
52
|
* the external store re-renders fired, causing a frame where both
|
|
52
53
|
* old and new active states were visible simultaneously.
|
|
@@ -107,7 +108,7 @@ function getOrCreateContext() {
|
|
|
107
108
|
/**
|
|
108
109
|
* Read the navigation context. Returns null during SSR (no provider)
|
|
109
110
|
* or in the RSC environment (no context available).
|
|
110
|
-
* Internal — used by
|
|
111
|
+
* Internal — used by useSegmentParams() and usePathname().
|
|
111
112
|
*/
|
|
112
113
|
function useNavigationContext() {
|
|
113
114
|
const ctx = getOrCreateContext();
|
|
@@ -324,13 +325,13 @@ function shouldInterceptClick(event, resolvedHref) {
|
|
|
324
325
|
* its own click handling.
|
|
325
326
|
*
|
|
326
327
|
* Supports typed routes via codegen overloads. At runtime:
|
|
327
|
-
* - `
|
|
328
|
+
* - `segmentParams` prop interpolates dynamic segments in the href pattern
|
|
328
329
|
* - `searchParams` prop serializes query parameters via a SearchParamsDefinition
|
|
329
330
|
*/
|
|
330
|
-
function Link({ href, prefetch, scroll,
|
|
331
|
+
function Link({ href, prefetch, scroll, segmentParams, searchParams, onNavigate, onClick: userOnClick, onMouseEnter: userOnMouseEnter, children, ...rest }) {
|
|
331
332
|
const { href: resolvedHref } = buildLinkProps({
|
|
332
333
|
href,
|
|
333
|
-
params,
|
|
334
|
+
params: segmentParams,
|
|
334
335
|
searchParams
|
|
335
336
|
});
|
|
336
337
|
const internal = isInternalHref(resolvedHref);
|
|
@@ -520,7 +521,7 @@ var HistoryStack = class {
|
|
|
520
521
|
function setCurrentParams(params) {
|
|
521
522
|
_setCurrentParams(params);
|
|
522
523
|
}
|
|
523
|
-
function
|
|
524
|
+
function useSegmentParams(_route) {
|
|
524
525
|
try {
|
|
525
526
|
const navContext = useNavigationContext();
|
|
526
527
|
if (navContext !== null) return navContext.params;
|
|
@@ -759,10 +760,28 @@ function generateCacheBustId() {
|
|
|
759
760
|
function appendRscParam(url) {
|
|
760
761
|
return `${url}${url.includes("?") ? "&" : "?"}_rsc=${generateCacheBustId()}`;
|
|
761
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* The client's deployment ID, set at bootstrap from the runtime config.
|
|
765
|
+
* Sent with every RSC/action request for version skew detection.
|
|
766
|
+
* Null in dev mode. See TIM-446.
|
|
767
|
+
*/
|
|
768
|
+
var clientDeploymentId = null;
|
|
769
|
+
/** Header name used by the server to signal a version skew reload. */
|
|
770
|
+
var RELOAD_HEADER = "X-Timber-Reload";
|
|
771
|
+
/** Header name for the client's deployment ID. */
|
|
772
|
+
var DEPLOYMENT_ID_HEADER = "X-Timber-Deployment-Id";
|
|
773
|
+
/**
|
|
774
|
+
* Check if a response signals a version skew reload.
|
|
775
|
+
* Triggers a full page reload if the server indicates the client is stale.
|
|
776
|
+
*/
|
|
777
|
+
function checkReloadSignal(response) {
|
|
778
|
+
return response.headers.get(RELOAD_HEADER) === "1";
|
|
779
|
+
}
|
|
762
780
|
function buildRscHeaders(stateTree, currentUrl) {
|
|
763
781
|
const headers = { Accept: RSC_CONTENT_TYPE };
|
|
764
782
|
if (stateTree) headers["X-Timber-State-Tree"] = JSON.stringify(stateTree);
|
|
765
783
|
if (currentUrl) headers["X-Timber-URL"] = currentUrl;
|
|
784
|
+
if (clientDeploymentId) headers[DEPLOYMENT_ID_HEADER] = clientDeploymentId;
|
|
766
785
|
return headers;
|
|
767
786
|
}
|
|
768
787
|
/**
|
|
@@ -817,7 +836,7 @@ function extractSkippedSegments(response) {
|
|
|
817
836
|
* Extract route params from the X-Timber-Params response header.
|
|
818
837
|
* Returns null if the header is missing or malformed.
|
|
819
838
|
*
|
|
820
|
-
* Used to populate
|
|
839
|
+
* Used to populate useSegmentParams() after client-side navigation.
|
|
821
840
|
*/
|
|
822
841
|
function extractParams(response) {
|
|
823
842
|
const header = response.headers.get("X-Timber-Params");
|
|
@@ -840,6 +859,16 @@ var RedirectError = class extends Error {
|
|
|
840
859
|
}
|
|
841
860
|
};
|
|
842
861
|
/**
|
|
862
|
+
* Thrown when the server signals a version skew (X-Timber-Reload header).
|
|
863
|
+
* Caught in navigate() to trigger a full page reload via triggerStaleReload().
|
|
864
|
+
* See TIM-446.
|
|
865
|
+
*/
|
|
866
|
+
var VersionSkewError = class extends Error {
|
|
867
|
+
constructor() {
|
|
868
|
+
super("Version skew detected — server has been redeployed");
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
/**
|
|
843
872
|
* Fetch an RSC payload from the server. If a decodeRsc function is provided,
|
|
844
873
|
* the response is decoded into a React element tree via createFromFetch.
|
|
845
874
|
* Otherwise, the raw response text is returned (test mode).
|
|
@@ -860,6 +889,7 @@ async function fetchRscPayload(url, deps, stateTree, currentUrl) {
|
|
|
860
889
|
let params = null;
|
|
861
890
|
let skippedSegments = null;
|
|
862
891
|
const wrappedPromise = fetchPromise.then((response) => {
|
|
892
|
+
if (checkReloadSignal(response)) throw new VersionSkewError();
|
|
863
893
|
const redirectLocation = response.headers.get("X-Timber-Redirect") || (response.status >= 300 && response.status < 400 ? response.headers.get("Location") : null);
|
|
864
894
|
if (redirectLocation) throw new RedirectError(redirectLocation);
|
|
865
895
|
headElements = extractHeadElements(response);
|
|
@@ -904,23 +934,20 @@ function isAbortError(error) {
|
|
|
904
934
|
if (error instanceof Error && error.name === "AbortError") return true;
|
|
905
935
|
return false;
|
|
906
936
|
}
|
|
907
|
-
/**
|
|
908
|
-
* Create a router instance. In production, called once at app hydration
|
|
909
|
-
* with real browser APIs. In tests, called with mock dependencies.
|
|
910
|
-
*/
|
|
911
937
|
function createRouter(deps) {
|
|
912
938
|
const segmentCache = new SegmentCache();
|
|
913
939
|
const prefetchCache = new PrefetchCache();
|
|
914
940
|
const historyStack = new HistoryStack();
|
|
915
941
|
const segmentElementCache = new SegmentElementCache();
|
|
916
|
-
let
|
|
917
|
-
let pendingUrl = null;
|
|
942
|
+
let routerPhase = { phase: "idle" };
|
|
918
943
|
const pendingListeners = /* @__PURE__ */ new Set();
|
|
919
944
|
function setPending(value, url) {
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
945
|
+
const next = value && url ? {
|
|
946
|
+
phase: "navigating",
|
|
947
|
+
targetUrl: url
|
|
948
|
+
} : { phase: "idle" };
|
|
949
|
+
if (routerPhase.phase === next.phase && (routerPhase.phase === "idle" || routerPhase.phase === "navigating" && next.phase === "navigating" && routerPhase.targetUrl === next.targetUrl)) return;
|
|
950
|
+
routerPhase = next;
|
|
924
951
|
for (const listener of pendingListeners) listener(value);
|
|
925
952
|
}
|
|
926
953
|
/** Update the segment cache from server-provided segment metadata. */
|
|
@@ -930,8 +957,8 @@ function createRouter(deps) {
|
|
|
930
957
|
if (tree) segmentCache.set("/", tree);
|
|
931
958
|
}
|
|
932
959
|
/** Render a decoded RSC payload into the DOM if a renderer is available. */
|
|
933
|
-
function renderPayload(payload) {
|
|
934
|
-
if (deps.renderRoot) deps.renderRoot(payload);
|
|
960
|
+
function renderPayload(payload, navState) {
|
|
961
|
+
if (deps.renderRoot) deps.renderRoot(payload, navState);
|
|
935
962
|
}
|
|
936
963
|
/**
|
|
937
964
|
* Merge a partial RSC payload with cached segment elements if segments
|
|
@@ -947,24 +974,26 @@ function createRouter(deps) {
|
|
|
947
974
|
/**
|
|
948
975
|
* Update navigation state (params + pathname) for the next render.
|
|
949
976
|
*
|
|
950
|
-
* Sets
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
977
|
+
* Sets the module-level fallback (for tests and SSR) and the
|
|
978
|
+
* globalThis bridge, then returns the NavigationState so callers
|
|
979
|
+
* can pass it explicitly to renderRoot/wrapPayload — eliminating
|
|
980
|
+
* temporal coupling with getNavigationState().
|
|
954
981
|
*/
|
|
955
982
|
function updateNavigationState(params, url) {
|
|
956
983
|
const resolvedParams = params ?? {};
|
|
957
984
|
setCurrentParams(resolvedParams);
|
|
958
|
-
|
|
985
|
+
const navState = {
|
|
959
986
|
params: resolvedParams,
|
|
960
987
|
pathname: url.startsWith("http") ? new URL(url).pathname : url.split("?")[0] || "/"
|
|
961
|
-
}
|
|
988
|
+
};
|
|
989
|
+
setNavigationState(navState);
|
|
990
|
+
return navState;
|
|
962
991
|
}
|
|
963
992
|
/**
|
|
964
993
|
* Render a payload via navigateTransition (production) or renderRoot (tests).
|
|
965
|
-
* The perform callback should fetch data, update state, and return the
|
|
966
|
-
*
|
|
967
|
-
*
|
|
994
|
+
* The perform callback should fetch data, update state, and return the
|
|
995
|
+
* FetchResult plus the NavigationState (so it can be passed explicitly
|
|
996
|
+
* to wrapPayload/renderRoot without temporal coupling).
|
|
968
997
|
*/
|
|
969
998
|
async function renderViaTransition(url, perform) {
|
|
970
999
|
if (deps.navigateTransition) {
|
|
@@ -978,7 +1007,7 @@ function createRouter(deps) {
|
|
|
978
1007
|
headElements: result.headElements,
|
|
979
1008
|
params: result.params
|
|
980
1009
|
});
|
|
981
|
-
return wrapPayload(merged);
|
|
1010
|
+
return wrapPayload(merged, result.navState);
|
|
982
1011
|
});
|
|
983
1012
|
return headElements;
|
|
984
1013
|
}
|
|
@@ -989,7 +1018,7 @@ function createRouter(deps) {
|
|
|
989
1018
|
headElements: result.headElements,
|
|
990
1019
|
params: result.params
|
|
991
1020
|
});
|
|
992
|
-
renderPayload(merged);
|
|
1021
|
+
renderPayload(merged, result.navState);
|
|
993
1022
|
return result.headElements;
|
|
994
1023
|
}
|
|
995
1024
|
/** Apply head elements (title, meta tags) to the DOM if available. */
|
|
@@ -1002,6 +1031,16 @@ function createRouter(deps) {
|
|
|
1002
1031
|
else callback();
|
|
1003
1032
|
}
|
|
1004
1033
|
/**
|
|
1034
|
+
* Schedule scroll restoration after the next paint and fire the
|
|
1035
|
+
* scroll-restored event. Used by navigate, popstate, and refresh.
|
|
1036
|
+
*/
|
|
1037
|
+
function restoreScrollAfterPaint(scrollY) {
|
|
1038
|
+
afterPaint(() => {
|
|
1039
|
+
deps.scrollTo(0, scrollY);
|
|
1040
|
+
window.dispatchEvent(new Event("timber:scroll-restored"));
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1005
1044
|
* Core navigation logic shared between the transition and fallback paths.
|
|
1006
1045
|
* Fetches the RSC payload, updates all state, and returns the result.
|
|
1007
1046
|
*/
|
|
@@ -1028,8 +1067,11 @@ function createRouter(deps) {
|
|
|
1028
1067
|
scrollY: 0
|
|
1029
1068
|
}, "", url);
|
|
1030
1069
|
updateSegmentCache(result.segmentInfo);
|
|
1031
|
-
updateNavigationState(result.params, url);
|
|
1032
|
-
return
|
|
1070
|
+
const navState = updateNavigationState(result.params, url);
|
|
1071
|
+
return {
|
|
1072
|
+
...result,
|
|
1073
|
+
navState
|
|
1074
|
+
};
|
|
1033
1075
|
}
|
|
1034
1076
|
async function navigate(url, options = {}) {
|
|
1035
1077
|
const scroll = options.scroll !== false;
|
|
@@ -1043,12 +1085,13 @@ function createRouter(deps) {
|
|
|
1043
1085
|
try {
|
|
1044
1086
|
applyHead(await renderViaTransition(url, () => performNavigationFetch(url, { replace })));
|
|
1045
1087
|
window.dispatchEvent(new Event("timber:navigation-end"));
|
|
1046
|
-
|
|
1047
|
-
if (scroll) deps.scrollTo(0, 0);
|
|
1048
|
-
else deps.scrollTo(0, currentScrollY);
|
|
1049
|
-
window.dispatchEvent(new Event("timber:scroll-restored"));
|
|
1050
|
-
});
|
|
1088
|
+
restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
|
|
1051
1089
|
} catch (error) {
|
|
1090
|
+
if (error instanceof VersionSkewError) {
|
|
1091
|
+
const { triggerStaleReload } = await import("../_chunks/stale-reload-C0ValzG7.js");
|
|
1092
|
+
triggerStaleReload();
|
|
1093
|
+
return new Promise(() => {});
|
|
1094
|
+
}
|
|
1052
1095
|
if (error instanceof RedirectError) {
|
|
1053
1096
|
setPending(false);
|
|
1054
1097
|
await navigate(error.redirectUrl, { replace: true });
|
|
@@ -1067,8 +1110,11 @@ function createRouter(deps) {
|
|
|
1067
1110
|
applyHead(await renderViaTransition(currentUrl, async () => {
|
|
1068
1111
|
const result = await fetchRscPayload(currentUrl, deps);
|
|
1069
1112
|
updateSegmentCache(result.segmentInfo);
|
|
1070
|
-
updateNavigationState(result.params, currentUrl);
|
|
1071
|
-
return
|
|
1113
|
+
const navState = updateNavigationState(result.params, currentUrl);
|
|
1114
|
+
return {
|
|
1115
|
+
...result,
|
|
1116
|
+
navState
|
|
1117
|
+
};
|
|
1072
1118
|
}));
|
|
1073
1119
|
} finally {
|
|
1074
1120
|
setPending(false);
|
|
@@ -1077,26 +1123,23 @@ function createRouter(deps) {
|
|
|
1077
1123
|
async function handlePopState(url, scrollY = 0) {
|
|
1078
1124
|
const entry = historyStack.get(url);
|
|
1079
1125
|
if (entry && entry.payload !== null) {
|
|
1080
|
-
updateNavigationState(entry.params, url);
|
|
1081
|
-
renderPayload(entry.payload);
|
|
1126
|
+
const navState = updateNavigationState(entry.params, url);
|
|
1127
|
+
renderPayload(entry.payload, navState);
|
|
1082
1128
|
applyHead(entry.headElements);
|
|
1083
|
-
|
|
1084
|
-
deps.scrollTo(0, scrollY);
|
|
1085
|
-
window.dispatchEvent(new Event("timber:scroll-restored"));
|
|
1086
|
-
});
|
|
1129
|
+
restoreScrollAfterPaint(scrollY);
|
|
1087
1130
|
} else {
|
|
1088
1131
|
setPending(true, url);
|
|
1089
1132
|
try {
|
|
1090
1133
|
applyHead(await renderViaTransition(url, async () => {
|
|
1091
1134
|
const result = await fetchRscPayload(url, deps, segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths()));
|
|
1092
1135
|
updateSegmentCache(result.segmentInfo);
|
|
1093
|
-
updateNavigationState(result.params, url);
|
|
1094
|
-
return
|
|
1136
|
+
const navState = updateNavigationState(result.params, url);
|
|
1137
|
+
return {
|
|
1138
|
+
...result,
|
|
1139
|
+
navState
|
|
1140
|
+
};
|
|
1095
1141
|
}));
|
|
1096
|
-
|
|
1097
|
-
deps.scrollTo(0, scrollY);
|
|
1098
|
-
window.dispatchEvent(new Event("timber:scroll-restored"));
|
|
1099
|
-
});
|
|
1142
|
+
restoreScrollAfterPaint(scrollY);
|
|
1100
1143
|
} finally {
|
|
1101
1144
|
setPending(false);
|
|
1102
1145
|
}
|
|
@@ -1117,8 +1160,8 @@ function createRouter(deps) {
|
|
|
1117
1160
|
navigate,
|
|
1118
1161
|
refresh,
|
|
1119
1162
|
handlePopState,
|
|
1120
|
-
isPending: () =>
|
|
1121
|
-
getPendingUrl: () =>
|
|
1163
|
+
isPending: () => routerPhase.phase === "navigating",
|
|
1164
|
+
getPendingUrl: () => routerPhase.phase === "navigating" ? routerPhase.targetUrl : null,
|
|
1122
1165
|
onPendingChange(listener) {
|
|
1123
1166
|
pendingListeners.add(listener);
|
|
1124
1167
|
return () => pendingListeners.delete(listener);
|
|
@@ -1131,7 +1174,7 @@ function createRouter(deps) {
|
|
|
1131
1174
|
payload: merged,
|
|
1132
1175
|
headElements
|
|
1133
1176
|
});
|
|
1134
|
-
renderPayload(merged);
|
|
1177
|
+
renderPayload(merged, getNavigationState());
|
|
1135
1178
|
applyHead(headElements);
|
|
1136
1179
|
},
|
|
1137
1180
|
initSegmentCache: (segments) => updateSegmentCache(segments),
|
|
@@ -1354,36 +1397,6 @@ function useSearchParams() {
|
|
|
1354
1397
|
return typeof window !== "undefined" ? getSearchParams() : getServerSearchParams();
|
|
1355
1398
|
}
|
|
1356
1399
|
//#endregion
|
|
1357
|
-
//#region src/client/segment-context.ts
|
|
1358
|
-
/**
|
|
1359
|
-
* Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.
|
|
1360
|
-
*
|
|
1361
|
-
* Each layout in the segment tree is wrapped with a SegmentProvider that stores
|
|
1362
|
-
* the URL segments from root to the current layout level. The hooks read this
|
|
1363
|
-
* context to determine which child segments are active below the calling layout.
|
|
1364
|
-
*
|
|
1365
|
-
* The context value is intentionally minimal: just the segment path array and
|
|
1366
|
-
* parallel route keys. No internal cache details are exposed.
|
|
1367
|
-
*
|
|
1368
|
-
* Design docs: design/19-client-navigation.md, design/14-ecosystem.md
|
|
1369
|
-
*/
|
|
1370
|
-
var SegmentContext = createContext(null);
|
|
1371
|
-
/** Read the segment context. Returns null if no provider is above this component. */
|
|
1372
|
-
function useSegmentContext() {
|
|
1373
|
-
return useContext(SegmentContext);
|
|
1374
|
-
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Wraps each layout to provide segment position context.
|
|
1377
|
-
* Injected by rsc-entry.ts during element tree construction.
|
|
1378
|
-
*/
|
|
1379
|
-
function SegmentProvider({ segments, segmentId: _segmentId, parallelRouteKeys, children }) {
|
|
1380
|
-
const value = useMemo(() => ({
|
|
1381
|
-
segments,
|
|
1382
|
-
parallelRouteKeys
|
|
1383
|
-
}), [segments.join("/"), parallelRouteKeys.join(",")]);
|
|
1384
|
-
return createElement(SegmentContext.Provider, { value }, children);
|
|
1385
|
-
}
|
|
1386
|
-
//#endregion
|
|
1387
1400
|
//#region src/client/use-selected-layout-segment.ts
|
|
1388
1401
|
/**
|
|
1389
1402
|
* useSelectedLayoutSegment / useSelectedLayoutSegments — client-side hooks
|
|
@@ -1589,6 +1602,96 @@ function useFormErrors(result) {
|
|
|
1589
1602
|
};
|
|
1590
1603
|
}
|
|
1591
1604
|
//#endregion
|
|
1592
|
-
|
|
1605
|
+
//#region src/client/use-cookie.ts
|
|
1606
|
+
/**
|
|
1607
|
+
* useCookie — reactive client-side cookie hook.
|
|
1608
|
+
*
|
|
1609
|
+
* Uses useSyncExternalStore for SSR-safe, reactive cookie access.
|
|
1610
|
+
* All components reading the same cookie name re-render on change.
|
|
1611
|
+
* No cross-tab sync (intentional — see design/29-cookies.md).
|
|
1612
|
+
*
|
|
1613
|
+
* See design/29-cookies.md §"useCookie(name) Hook"
|
|
1614
|
+
*/
|
|
1615
|
+
var use_cookie_exports = /* @__PURE__ */ __exportAll({ useCookie: () => useCookie });
|
|
1616
|
+
/** Per-name subscriber sets. */
|
|
1617
|
+
var listeners = /* @__PURE__ */ new Map();
|
|
1618
|
+
/** Parse a cookie name from document.cookie. */
|
|
1619
|
+
function getCookieValue(name) {
|
|
1620
|
+
if (typeof document === "undefined") return void 0;
|
|
1621
|
+
const match = document.cookie.match(new RegExp("(?:^|;\\s*)" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*=\\s*([^;]*)"));
|
|
1622
|
+
return match ? decodeURIComponent(match[1]) : void 0;
|
|
1623
|
+
}
|
|
1624
|
+
/** Serialize options into a cookie string suffix. */
|
|
1625
|
+
function serializeOptions(options) {
|
|
1626
|
+
if (!options) return "; Path=/; SameSite=Lax";
|
|
1627
|
+
const parts = [];
|
|
1628
|
+
parts.push(`Path=${options.path ?? "/"}`);
|
|
1629
|
+
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
1630
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
1631
|
+
if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
|
|
1632
|
+
const sameSite = options.sameSite ?? "lax";
|
|
1633
|
+
parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`);
|
|
1634
|
+
if (options.secure) parts.push("Secure");
|
|
1635
|
+
return "; " + parts.join("; ");
|
|
1636
|
+
}
|
|
1637
|
+
/** Notify all subscribers for a given cookie name. */
|
|
1638
|
+
function notify(name) {
|
|
1639
|
+
const subs = listeners.get(name);
|
|
1640
|
+
if (subs) for (const fn of subs) fn();
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Reactive hook for reading/writing a client-side cookie.
|
|
1644
|
+
*
|
|
1645
|
+
* Returns `[value, setCookie, deleteCookie]`:
|
|
1646
|
+
* - `value`: current cookie value (string | undefined)
|
|
1647
|
+
* - `setCookie`: sets the cookie and triggers re-renders
|
|
1648
|
+
* - `deleteCookie`: deletes the cookie and triggers re-renders
|
|
1649
|
+
*
|
|
1650
|
+
* @param name - Cookie name.
|
|
1651
|
+
* @param defaultOptions - Default options for setCookie calls.
|
|
1652
|
+
*/
|
|
1653
|
+
function useCookie(name, defaultOptions) {
|
|
1654
|
+
const subscribe = (callback) => {
|
|
1655
|
+
let subs = listeners.get(name);
|
|
1656
|
+
if (!subs) {
|
|
1657
|
+
subs = /* @__PURE__ */ new Set();
|
|
1658
|
+
listeners.set(name, subs);
|
|
1659
|
+
}
|
|
1660
|
+
subs.add(callback);
|
|
1661
|
+
return () => {
|
|
1662
|
+
subs.delete(callback);
|
|
1663
|
+
if (subs.size === 0) listeners.delete(name);
|
|
1664
|
+
};
|
|
1665
|
+
};
|
|
1666
|
+
const getSnapshot = () => getCookieValue(name);
|
|
1667
|
+
const getServerSnapshot = () => getSsrData()?.cookies.get(name);
|
|
1668
|
+
const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1669
|
+
const setCookie = (newValue, options) => {
|
|
1670
|
+
const merged = {
|
|
1671
|
+
...defaultOptions,
|
|
1672
|
+
...options
|
|
1673
|
+
};
|
|
1674
|
+
document.cookie = `${name}=${encodeURIComponent(newValue)}${serializeOptions(merged)}`;
|
|
1675
|
+
notify(name);
|
|
1676
|
+
};
|
|
1677
|
+
const deleteCookie = () => {
|
|
1678
|
+
const path = defaultOptions?.path ?? "/";
|
|
1679
|
+
const domain = defaultOptions?.domain;
|
|
1680
|
+
let cookieStr = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${path}`;
|
|
1681
|
+
if (domain) cookieStr += `; Domain=${domain}`;
|
|
1682
|
+
document.cookie = cookieStr;
|
|
1683
|
+
notify(name);
|
|
1684
|
+
};
|
|
1685
|
+
return [
|
|
1686
|
+
value,
|
|
1687
|
+
setCookie,
|
|
1688
|
+
deleteCookie
|
|
1689
|
+
];
|
|
1690
|
+
}
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/client/index.ts
|
|
1693
|
+
_registerUseCookieModule(use_cookie_exports);
|
|
1694
|
+
//#endregion
|
|
1695
|
+
export { HistoryStack, Link, LinkStatusContext, NavigationProvider, PrefetchCache, SegmentCache, SegmentProvider, TimberErrorBoundary, bindUseQueryStates, buildLinkProps, clearSsrData, createRouter, getNavigationState, getRouter, getSsrData, interpolateParams, resolveHref, setCurrentParams, setGlobalRouter, setNavigationState, setSsrData, useActionState, useCookie, useFormAction, useFormErrors, useLinkStatus, useNavigationPending, usePathname, useQueryStates, useRouter, useSearchParams, useSegmentContext, useSegmentParams, useSelectedLayoutSegment, useSelectedLayoutSegments, validateLinkHref };
|
|
1593
1696
|
|
|
1594
1697
|
//# sourceMappingURL=index.js.map
|