@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c
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/AGENTS.md +8 -0
- package/README.md +126 -16
- package/dist/bin/rango.js +319 -95
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2724 -1053
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +11 -9
- package/skills/hooks/SKILL.md +243 -29
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +68 -19
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +190 -23
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +18 -10
- package/skills/migrate-nextjs/SKILL.md +43 -19
- package/skills/migrate-react-router/SKILL.md +8 -2
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +18 -7
- package/skills/prerender/SKILL.md +65 -60
- package/skills/rango/SKILL.md +251 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +115 -48
- package/skills/route/SKILL.md +46 -5
- package/skills/router-setup/SKILL.md +30 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +121 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +115 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +147 -128
- package/src/browser/prefetch/cache.ts +107 -56
- package/src/browser/prefetch/fetch.ts +204 -34
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +30 -7
- package/src/browser/react/NavigationProvider.tsx +283 -118
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +11 -11
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +91 -24
- package/src/browser/scroll-restoration.ts +30 -17
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +214 -55
- package/src/browser/types.ts +80 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +117 -14
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +117 -23
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +55 -28
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +36 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +96 -12
- package/src/index.ts +94 -14
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +34 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +82 -55
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +27 -43
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +10 -47
- package/src/router/loader-resolution.ts +155 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +9 -24
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +44 -74
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +247 -166
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +85 -94
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +104 -65
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +83 -35
- package/src/router/router-options.ts +136 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +97 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +272 -320
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +162 -64
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +110 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +264 -220
- package/src/rsc/helpers.ts +100 -6
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +117 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +88 -188
- package/src/rsc/rsc-rendering.ts +98 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +281 -117
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +23 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +31 -4
- package/src/segment-system.tsx +254 -143
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +163 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +287 -178
- package/src/ssr/index.tsx +21 -16
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +29 -12
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +126 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +96 -68
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +44 -0
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +2 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +126 -8
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +109 -118
- package/src/vite/router-discovery.ts +718 -119
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +54 -39
- package/src/vite/utils/shared-utils.ts +90 -41
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
generateHistoryKey,
|
|
9
9
|
} from "./navigation-store.js";
|
|
10
10
|
import { createEventController } from "./event-controller.js";
|
|
11
|
+
import { validateRedirectOrigin } from "./validate-redirect-origin.js";
|
|
11
12
|
import { createNavigationClient } from "./navigation-client.js";
|
|
12
13
|
import { createServerActionBridge } from "./server-action-bridge.js";
|
|
13
14
|
import { createNavigationBridge } from "./navigation-bridge.js";
|
|
@@ -22,12 +23,15 @@ import type {
|
|
|
22
23
|
import type { EventController } from "./event-controller.js";
|
|
23
24
|
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
24
25
|
import { initRangoState } from "./rango-state.js";
|
|
26
|
+
import { registerNavigationStore } from "./navigation-store-handle.js";
|
|
25
27
|
import { initPrefetchCache } from "./prefetch/cache.js";
|
|
28
|
+
import { setPrefetchDecoder } from "./prefetch/fetch.js";
|
|
26
29
|
import { setAppVersion } from "./app-version.js";
|
|
27
30
|
import {
|
|
28
31
|
isInterceptSegment,
|
|
29
32
|
splitInterceptSegments,
|
|
30
33
|
} from "./intercept-utils.js";
|
|
34
|
+
import { createAppShellRef } from "./app-shell.js";
|
|
31
35
|
|
|
32
36
|
// Vite HMR types are provided by vite/client
|
|
33
37
|
|
|
@@ -112,15 +116,26 @@ export interface BrowserAppContext {
|
|
|
112
116
|
initialTheme?: Theme;
|
|
113
117
|
/** Whether connection warmup is enabled */
|
|
114
118
|
warmupEnabled?: boolean;
|
|
119
|
+
/** Whether the hydrated tree should be wrapped in React.StrictMode */
|
|
120
|
+
strictMode?: boolean;
|
|
115
121
|
/** App version for prefetch version mismatch detection */
|
|
116
122
|
version?: string;
|
|
123
|
+
/**
|
|
124
|
+
* App-shell ref, read through on each render so renderSegments and the
|
|
125
|
+
* NavigationProvider see rootLayout/basename/version without closing over a
|
|
126
|
+
* stale snapshot. Set once from the initial payload and not swapped within a
|
|
127
|
+
* session: a cross-app navigation is a full document load (X-RSC-Reload), so
|
|
128
|
+
* the target app establishes its own shell on load. Theme, warmup, and
|
|
129
|
+
* prefetch TTL are document-lifetime too (see AppShell).
|
|
130
|
+
*/
|
|
131
|
+
appShellRef?: import("./app-shell.js").AppShellRef;
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
// Module-level state for the initialized app
|
|
120
135
|
let browserAppContext: BrowserAppContext | null = null;
|
|
121
136
|
|
|
122
137
|
/**
|
|
123
|
-
* Initialize the browser app. Must be called before rendering
|
|
138
|
+
* Initialize the browser app. Must be called before rendering Rango.
|
|
124
139
|
*
|
|
125
140
|
* This function:
|
|
126
141
|
* - Loads the initial RSC payload from the stream
|
|
@@ -164,6 +179,12 @@ export async function initBrowserApp(
|
|
|
164
179
|
...(storeOptions?.cacheSize && { cacheSize: storeOptions.cacheSize }),
|
|
165
180
|
});
|
|
166
181
|
|
|
182
|
+
// Register the active store on the module-level handle and wire the
|
|
183
|
+
// jar-divergence observer before any getRangoState() read can detect a
|
|
184
|
+
// cross-tab/server rotation. There is no global store singleton, so this
|
|
185
|
+
// handle is the live reference.
|
|
186
|
+
registerNavigationStore(store);
|
|
187
|
+
|
|
167
188
|
// Seed router identity from the initial SSR payload so the first
|
|
168
189
|
// cross-app SPA navigation can detect the app switch.
|
|
169
190
|
if (initialPayload.metadata?.routerId) {
|
|
@@ -204,13 +225,24 @@ export async function initBrowserApp(
|
|
|
204
225
|
// Create composable utilities
|
|
205
226
|
const client = createNavigationClient(deps);
|
|
206
227
|
|
|
207
|
-
//
|
|
208
|
-
|
|
228
|
+
// Capture the per-router app-shell. rootLayout, basename, and version live
|
|
229
|
+
// here and are read through the ref at call time rather than closed over.
|
|
230
|
+
// It is set once from the initial payload and not swapped within a session:
|
|
231
|
+
// a cross-app navigation is a full document load (X-RSC-Reload), so the
|
|
232
|
+
// target app establishes its own shell on load.
|
|
209
233
|
const version = initialPayload.metadata?.version;
|
|
234
|
+
const appShellRef = createAppShellRef({
|
|
235
|
+
routerId: initialPayload.metadata?.routerId,
|
|
236
|
+
rootLayout: initialPayload.metadata?.rootLayout,
|
|
237
|
+
basename: initialPayload.metadata?.basename,
|
|
238
|
+
version,
|
|
239
|
+
});
|
|
210
240
|
|
|
211
|
-
// Initialize the
|
|
212
|
-
//
|
|
213
|
-
|
|
241
|
+
// Initialize the rango state cookie for cache invalidation. The build version
|
|
242
|
+
// busts cached prefetches on deploy; the server-resolved cookie name
|
|
243
|
+
// namespaces the cookie so sibling apps on the same origin don't collide
|
|
244
|
+
// (falls back to the bare default prefix if metadata lacks the name).
|
|
245
|
+
initRangoState(version ?? "0", initialPayload.metadata?.stateCookieName);
|
|
214
246
|
setAppVersion(version);
|
|
215
247
|
|
|
216
248
|
// Initialize the in-memory prefetch cache TTL from server config.
|
|
@@ -220,11 +252,22 @@ export async function initBrowserApp(
|
|
|
220
252
|
initPrefetchCache(prefetchCacheTTL);
|
|
221
253
|
}
|
|
222
254
|
|
|
223
|
-
//
|
|
255
|
+
// Wire the RSC decoder so prefetches decode eagerly and warm the route's
|
|
256
|
+
// client chunks (same createFromFetch the navigation client uses).
|
|
257
|
+
setPrefetchDecoder((response) => deps.createFromFetch<RscPayload>(response));
|
|
258
|
+
|
|
259
|
+
// Create a bound renderSegments that reads rootLayout through the shell ref.
|
|
260
|
+
// The shell is set once at init and not swapped within a session (a cross-app
|
|
261
|
+
// navigation is a full document load), so this always renders this app's
|
|
262
|
+
// Document; reading through the ref just avoids closing over a stale value.
|
|
224
263
|
const renderSegments = (
|
|
225
264
|
segments: ResolvedSegment[],
|
|
226
265
|
options?: RenderSegmentsOptions,
|
|
227
|
-
) =>
|
|
266
|
+
) =>
|
|
267
|
+
baseRenderSegments(segments, {
|
|
268
|
+
...options,
|
|
269
|
+
rootLayout: appShellRef.get().rootLayout,
|
|
270
|
+
});
|
|
228
271
|
|
|
229
272
|
// Lazy reference for navigation bridge — the action bridge is created first
|
|
230
273
|
// but may need to trigger SPA navigation for action redirects.
|
|
@@ -240,7 +283,13 @@ export async function initBrowserApp(
|
|
|
240
283
|
renderSegments,
|
|
241
284
|
onNavigate: (url, options) => {
|
|
242
285
|
if (!navigateFn) {
|
|
243
|
-
|
|
286
|
+
// Navigation bridge not wired yet: hard-navigate, but re-validate
|
|
287
|
+
// same-origin defensively so this init-window fallback cannot become an
|
|
288
|
+
// open redirect (the normal path validates inside the navigation bridge).
|
|
289
|
+
const safe = validateRedirectOrigin(url, window.location.origin);
|
|
290
|
+
if (safe) {
|
|
291
|
+
window.location.href = safe;
|
|
292
|
+
}
|
|
244
293
|
return Promise.resolve();
|
|
245
294
|
}
|
|
246
295
|
return navigateFn(url, options);
|
|
@@ -300,11 +349,11 @@ export async function initBrowserApp(
|
|
|
300
349
|
// full lifecycle (fetching + streaming, before commit) without
|
|
301
350
|
// blocking on server actions.
|
|
302
351
|
if (eventController.getState().isNavigating) {
|
|
303
|
-
console.log("[
|
|
352
|
+
console.log("[Rango] HMR: Skipping — navigation in progress");
|
|
304
353
|
return;
|
|
305
354
|
}
|
|
306
355
|
|
|
307
|
-
console.log("[
|
|
356
|
+
console.log("[Rango] HMR: Server update, refetching RSC");
|
|
308
357
|
|
|
309
358
|
const abort = new AbortController();
|
|
310
359
|
hmrAbort = abort;
|
|
@@ -339,11 +388,18 @@ export async function initBrowserApp(
|
|
|
339
388
|
// Update version BEFORE rebuilding state so that
|
|
340
389
|
// clearHistoryCache() runs first, then the fresh segment
|
|
341
390
|
// cache entry we create below survives.
|
|
391
|
+
//
|
|
392
|
+
// Compare against the bridge's live version, not the init-time
|
|
393
|
+
// `version` const: after the first HMR bump the const is stale, so a
|
|
394
|
+
// later update with an unchanged version would otherwise re-clear the
|
|
395
|
+
// cache and re-broadcast across tabs/apps. The live read fires only
|
|
396
|
+
// on a genuine version change.
|
|
342
397
|
const newVersion = payload.metadata.version;
|
|
343
|
-
|
|
398
|
+
const currentVersion = navigationBridge.getVersion();
|
|
399
|
+
if (newVersion && newVersion !== currentVersion) {
|
|
344
400
|
console.log(
|
|
345
|
-
"[
|
|
346
|
-
|
|
401
|
+
"[Rango] HMR: version changed",
|
|
402
|
+
currentVersion,
|
|
347
403
|
"→",
|
|
348
404
|
newVersion,
|
|
349
405
|
"clearing caches",
|
|
@@ -351,6 +407,13 @@ export async function initBrowserApp(
|
|
|
351
407
|
navigationBridge.updateVersion(newVersion);
|
|
352
408
|
}
|
|
353
409
|
|
|
410
|
+
// Apply only partial segment updates. A non-partial payload during
|
|
411
|
+
// HMR is transient: the worker route table is still rebuilding after
|
|
412
|
+
// the edit, so the URL momentarily resolves to not-found/catch-all.
|
|
413
|
+
// Skip it -- the debounced follow-up refetch returns the settled
|
|
414
|
+
// route's partial payload and renders it below. We never reload here:
|
|
415
|
+
// a paramless document GET would run the SSR path and surface the
|
|
416
|
+
// not-found page during that same transient.
|
|
354
417
|
if (payload.metadata?.isPartial) {
|
|
355
418
|
const segments = payload.metadata.segments || [];
|
|
356
419
|
const matched = payload.metadata.matched || [];
|
|
@@ -390,10 +453,10 @@ export async function initBrowserApp(
|
|
|
390
453
|
|
|
391
454
|
await streamComplete;
|
|
392
455
|
handle.complete(new URL(window.location.href));
|
|
393
|
-
console.log("[
|
|
456
|
+
console.log("[Rango] HMR: RSC stream complete");
|
|
394
457
|
} catch (err) {
|
|
395
458
|
if (abort.signal.aborted) return;
|
|
396
|
-
console.warn("[
|
|
459
|
+
console.warn("[Rango] HMR: Refetch failed, reloading page", err);
|
|
397
460
|
window.location.reload();
|
|
398
461
|
return;
|
|
399
462
|
} finally {
|
|
@@ -405,7 +468,7 @@ export async function initBrowserApp(
|
|
|
405
468
|
});
|
|
406
469
|
}
|
|
407
470
|
|
|
408
|
-
// Store context for
|
|
471
|
+
// Store context for Rango component
|
|
409
472
|
const context: BrowserAppContext = {
|
|
410
473
|
store,
|
|
411
474
|
eventController,
|
|
@@ -415,7 +478,9 @@ export async function initBrowserApp(
|
|
|
415
478
|
themeConfig: effectiveThemeConfig,
|
|
416
479
|
initialTheme: effectiveInitialTheme,
|
|
417
480
|
warmupEnabled: initialPayload.metadata?.warmupEnabled ?? true,
|
|
481
|
+
strictMode: initialPayload.metadata?.strictMode ?? true,
|
|
418
482
|
version,
|
|
483
|
+
appShellRef,
|
|
419
484
|
};
|
|
420
485
|
browserAppContext = context;
|
|
421
486
|
|
|
@@ -428,7 +493,7 @@ export async function initBrowserApp(
|
|
|
428
493
|
export function getBrowserAppContext(): BrowserAppContext {
|
|
429
494
|
if (!browserAppContext) {
|
|
430
495
|
throw new Error(
|
|
431
|
-
"
|
|
496
|
+
"Rango: initBrowserApp() must be called before rendering Rango",
|
|
432
497
|
);
|
|
433
498
|
}
|
|
434
499
|
return browserAppContext;
|
|
@@ -442,18 +507,18 @@ export function resetBrowserAppContext(): void {
|
|
|
442
507
|
}
|
|
443
508
|
|
|
444
509
|
/**
|
|
445
|
-
* Props for the
|
|
510
|
+
* Props for the Rango component
|
|
446
511
|
*/
|
|
447
|
-
export interface
|
|
512
|
+
export interface RangoProps {}
|
|
448
513
|
|
|
449
514
|
/**
|
|
450
|
-
*
|
|
515
|
+
* Rango component - renders the RSC router with all internal wiring.
|
|
451
516
|
*
|
|
452
517
|
* Must be called after initBrowserApp() has completed.
|
|
453
518
|
*
|
|
454
519
|
* @example
|
|
455
520
|
* ```tsx
|
|
456
|
-
* import { initBrowserApp,
|
|
521
|
+
* import { initBrowserApp, Rango } from "rsc-router/browser";
|
|
457
522
|
* import { rscStream } from "rsc-html-stream/client";
|
|
458
523
|
* import * as rscBrowser from "@vitejs/plugin-rsc/browser";
|
|
459
524
|
*
|
|
@@ -463,14 +528,14 @@ export interface RSCRouterProps {}
|
|
|
463
528
|
* hydrateRoot(
|
|
464
529
|
* document,
|
|
465
530
|
* <React.StrictMode>
|
|
466
|
-
* <
|
|
531
|
+
* <Rango />
|
|
467
532
|
* </React.StrictMode>
|
|
468
533
|
* );
|
|
469
534
|
* }
|
|
470
535
|
* main();
|
|
471
536
|
* ```
|
|
472
537
|
*/
|
|
473
|
-
export function
|
|
538
|
+
export function Rango(_props: RangoProps): React.ReactElement {
|
|
474
539
|
const {
|
|
475
540
|
store,
|
|
476
541
|
eventController,
|
|
@@ -481,6 +546,7 @@ export function RSCRouter(_props: RSCRouterProps): React.ReactElement {
|
|
|
481
546
|
initialTheme,
|
|
482
547
|
warmupEnabled,
|
|
483
548
|
version,
|
|
549
|
+
appShellRef,
|
|
484
550
|
} = getBrowserAppContext();
|
|
485
551
|
|
|
486
552
|
// Signal that the React tree has hydrated. useEffect only fires after
|
|
@@ -501,6 +567,7 @@ export function RSCRouter(_props: RSCRouterProps): React.ReactElement {
|
|
|
501
567
|
warmupEnabled={warmupEnabled}
|
|
502
568
|
version={version}
|
|
503
569
|
basename={initialPayload.metadata?.basename}
|
|
570
|
+
appShellRef={appShellRef}
|
|
504
571
|
/>
|
|
505
572
|
);
|
|
506
573
|
}
|
|
@@ -191,10 +191,15 @@ export function saveCurrentScrollPosition(): void {
|
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* Persist scroll positions to sessionStorage.
|
|
194
|
-
* If the write fails
|
|
195
|
-
* entries and retry
|
|
194
|
+
* If the write fails (typically QuotaExceededError), evict the oldest ~1/4 of
|
|
195
|
+
* entries ONCE and retry the write a single time; if that still fails, remove
|
|
196
|
+
* our storage key entirely so we don't block other sessionStorage consumers.
|
|
197
|
+
* This is a single evict-then-retry-then-clear ladder, not a loop.
|
|
198
|
+
*
|
|
199
|
+
* Exported so that single eviction/retry/clear ladder is unit-testable directly.
|
|
200
|
+
* The browser drives it from the `pagehide` handler.
|
|
196
201
|
*/
|
|
197
|
-
function persistToSessionStorage(): void {
|
|
202
|
+
export function persistToSessionStorage(): void {
|
|
198
203
|
try {
|
|
199
204
|
sessionStorage.setItem(
|
|
200
205
|
SCROLL_STORAGE_KEY,
|
|
@@ -332,6 +337,8 @@ export function scrollToHash(): boolean {
|
|
|
332
337
|
* Scroll to top of page
|
|
333
338
|
*/
|
|
334
339
|
export function scrollToTop(): void {
|
|
340
|
+
if (typeof window === "undefined") return;
|
|
341
|
+
if (typeof window.scrollTo !== "function") return;
|
|
335
342
|
window.scrollTo(0, 0);
|
|
336
343
|
}
|
|
337
344
|
|
|
@@ -374,20 +381,26 @@ export function handleNavigationEnd(options: {
|
|
|
374
381
|
// Fall through to hash or top if no saved position
|
|
375
382
|
}
|
|
376
383
|
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
384
|
+
// scrollToHash / scrollToTop run synchronously here.
|
|
385
|
+
// handleNavigationEnd is invoked from NavigationProvider's
|
|
386
|
+
// useLayoutEffect (post-commit, pre-paint), so a sync scrollTo is
|
|
387
|
+
// captured by the upcoming paint AND by startViewTransition's snapshot.
|
|
388
|
+
// Deferring via rAF here pushed the call past the snapshot capture,
|
|
389
|
+
// making forward navigations wrapped in a layout/route view transition
|
|
390
|
+
// skip scroll-to-top — the live DOM scrolled but the captured snapshot
|
|
391
|
+
// was at the previous scroll position, so the user-facing page stayed
|
|
392
|
+
// visually clamped at the source page's scrollY (often the new tree's
|
|
393
|
+
// max scroll for tall→short navs). Y=0 / a hash element are robust
|
|
394
|
+
// against unmeasured layout, so sync scroll is correct here even
|
|
395
|
+
// before the new tree's scrollHeight settles.
|
|
396
|
+
//
|
|
397
|
+
// (The restore branch above keeps deferToNextPaint because savedY
|
|
398
|
+
// depends on the new tree's max scroll; sync scrollTo against an
|
|
399
|
+
// unmeasured DOM would clamp savedY to whatever the old/zero max was.)
|
|
400
|
+
if (scrollToHash()) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
scrollToTop();
|
|
391
404
|
}
|
|
392
405
|
|
|
393
406
|
/**
|
|
@@ -48,7 +48,7 @@ export function assertSegmentStructure(
|
|
|
48
48
|
|
|
49
49
|
if (cachedCategory !== incomingCategory) {
|
|
50
50
|
console.warn(
|
|
51
|
-
`[
|
|
51
|
+
`[Rango] Tree structure mismatch detected in ${context} ` +
|
|
52
52
|
`for segment "${cached.id}": loading category changed from ` +
|
|
53
53
|
`"${cachedCategory}" (${describeLoading(cached.loading)}) to ` +
|
|
54
54
|
`"${incomingCategory}" (${describeLoading(incoming.loading)}). ` +
|
|
@@ -64,7 +64,7 @@ export function assertSegmentStructure(
|
|
|
64
64
|
const incomingHasMount = !!incoming.mountPath;
|
|
65
65
|
if (cachedHasMount !== incomingHasMount) {
|
|
66
66
|
console.warn(
|
|
67
|
-
`[
|
|
67
|
+
`[Rango] MountContextProvider mismatch detected in ${context} ` +
|
|
68
68
|
`for segment "${cached.id}": mountPath changed from ` +
|
|
69
69
|
`${cachedHasMount ? `"${cached.mountPath}"` : "undefined"} to ` +
|
|
70
70
|
`${incomingHasMount ? `"${incoming.mountPath}"` : "undefined"}. ` +
|