@rangojs/router 0.0.0-experimental.8 → 0.0.0-experimental.8a4d0430
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 +5 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +4474 -867
- package/package.json +60 -51
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +50 -21
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +334 -72
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +89 -30
- package/skills/loader/SKILL.md +388 -38
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +78 -1
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +85 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +226 -14
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +318 -89
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +87 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +285 -553
- package/src/browser/navigation-client.ts +124 -71
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +258 -308
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +185 -73
- package/src/browser/react/NavigationProvider.tsx +51 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +32 -79
- package/src/browser/react/use-href.tsx +2 -2
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +107 -26
- package/src/browser/scroll-restoration.ts +92 -16
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +504 -599
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +109 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +235 -24
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +13 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +120 -303
- package/src/cache/cf/cf-cache-store.ts +119 -7
- package/src/cache/cf/index.ts +8 -2
- package/src/cache/document-cache.ts +101 -72
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +106 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +17 -7
- package/src/errors.ts +108 -2
- package/src/handle.ts +15 -29
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +119 -29
- package/src/index.rsc.ts +153 -19
- package/src/index.ts +211 -30
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +26 -157
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1428
- package/src/route-map-builder.ts +211 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +59 -8
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +374 -81
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +215 -122
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +148 -35
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +5 -3
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +80 -93
- package/src/router/match-middleware/cache-lookup.ts +382 -9
- package/src/router/match-middleware/cache-store.ts +51 -22
- package/src/router/match-middleware/intercept-resolution.ts +55 -17
- package/src/router/match-middleware/segment-resolution.ts +24 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +34 -28
- package/src/router/metrics.ts +235 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +324 -367
- package/src/router/pattern-matching.ts +211 -43
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +137 -38
- package/src/router/router-context.ts +36 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1241 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +77 -3
- package/src/router.ts +692 -4257
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +764 -754
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +38 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +25 -13
- package/src/server/context.ts +182 -51
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +430 -70
- package/src/server.ts +35 -130
- package/src/ssr/index.tsx +100 -31
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -1623
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -802
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -1133
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/href.ts +0 -255
- package/src/server/route-manifest-cache.ts +0 -173
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { sanitizeError } from "../errors";
|
|
3
|
+
import type { ErrorInfo, ErrorPhase, MatchResult } from "../types";
|
|
4
|
+
import type {
|
|
5
|
+
EntryData,
|
|
6
|
+
InterceptEntry,
|
|
7
|
+
InterceptSelectorContext,
|
|
8
|
+
} from "../server/context";
|
|
9
|
+
import type { MatchApiDeps } from "./types.js";
|
|
10
|
+
import type { RouterContext } from "./router-context.js";
|
|
11
|
+
import { runWithRouterContext } from "./router-context.js";
|
|
12
|
+
import {
|
|
13
|
+
type ActionContext,
|
|
14
|
+
type MatchContext,
|
|
15
|
+
createPipelineState,
|
|
16
|
+
} from "./match-context.js";
|
|
17
|
+
import { createMatchPartialPipeline } from "./match-pipelines.js";
|
|
18
|
+
import { collectMatchResult } from "./match-result.js";
|
|
19
|
+
import {
|
|
20
|
+
createMatchContextForFull as _createMatchContextForFull,
|
|
21
|
+
createMatchContextForPartial as _createMatchContextForPartial,
|
|
22
|
+
matchError as _matchError,
|
|
23
|
+
} from "./match-api.js";
|
|
24
|
+
import { previewMatch as _previewMatch } from "./preview-match.js";
|
|
25
|
+
import {
|
|
26
|
+
runWithRouterLogContext,
|
|
27
|
+
withRouterLogScope,
|
|
28
|
+
isRouterDebugEnabled,
|
|
29
|
+
startRevalidationTrace,
|
|
30
|
+
flushRevalidationTrace,
|
|
31
|
+
} from "./logging.js";
|
|
32
|
+
import type { ErrorBoundaryHandler, NotFoundBoundaryHandler } from "../types";
|
|
33
|
+
import type { MiddlewareFn } from "./middleware.js";
|
|
34
|
+
import {
|
|
35
|
+
type TelemetrySink,
|
|
36
|
+
safeEmit,
|
|
37
|
+
resolveSink,
|
|
38
|
+
getRequestId,
|
|
39
|
+
} from "./telemetry.js";
|
|
40
|
+
|
|
41
|
+
export interface MatchHandlerDeps<TEnv = any> {
|
|
42
|
+
buildRouterContext: () => RouterContext<TEnv>;
|
|
43
|
+
callOnError: (error: unknown, phase: ErrorPhase, context: any) => void;
|
|
44
|
+
matchApiDeps: MatchApiDeps<TEnv>;
|
|
45
|
+
defaultErrorBoundary: ReactNode | ErrorBoundaryHandler | undefined;
|
|
46
|
+
findMatch: (pathname: string, ms?: any) => any;
|
|
47
|
+
findInterceptForRoute: (
|
|
48
|
+
routeKey: string,
|
|
49
|
+
parentEntry: EntryData | null,
|
|
50
|
+
selectorContext: InterceptSelectorContext | null,
|
|
51
|
+
isAction: boolean,
|
|
52
|
+
) => { intercept: InterceptEntry; entry: EntryData } | null;
|
|
53
|
+
telemetry?: TelemetrySink;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface MatchHandlers<TEnv = any> {
|
|
57
|
+
match: (request: Request, env: TEnv) => Promise<MatchResult>;
|
|
58
|
+
matchPartial: (
|
|
59
|
+
request: Request,
|
|
60
|
+
context: TEnv,
|
|
61
|
+
actionContext?: ActionContext,
|
|
62
|
+
) => Promise<MatchResult | null>;
|
|
63
|
+
matchError: (
|
|
64
|
+
request: Request,
|
|
65
|
+
_context: TEnv,
|
|
66
|
+
error: unknown,
|
|
67
|
+
segmentType?: ErrorInfo["segmentType"],
|
|
68
|
+
) => Promise<MatchResult | null>;
|
|
69
|
+
previewMatch: (
|
|
70
|
+
request: Request,
|
|
71
|
+
_context: TEnv,
|
|
72
|
+
) => Promise<{
|
|
73
|
+
routeMiddleware?: Array<{
|
|
74
|
+
handler: MiddlewareFn;
|
|
75
|
+
params: Record<string, string>;
|
|
76
|
+
}>;
|
|
77
|
+
responseType?: string;
|
|
78
|
+
handler?: Function;
|
|
79
|
+
params?: Record<string, string>;
|
|
80
|
+
negotiated?: boolean;
|
|
81
|
+
manifestEntry?: EntryData;
|
|
82
|
+
} | null>;
|
|
83
|
+
createMatchContextForFull: (
|
|
84
|
+
request: Request,
|
|
85
|
+
env: TEnv,
|
|
86
|
+
) => Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }>;
|
|
87
|
+
createMatchContextForPartial: (
|
|
88
|
+
request: Request,
|
|
89
|
+
env: TEnv,
|
|
90
|
+
actionContext?: {
|
|
91
|
+
actionId?: string;
|
|
92
|
+
actionUrl?: URL;
|
|
93
|
+
actionResult?: any;
|
|
94
|
+
formData?: FormData;
|
|
95
|
+
},
|
|
96
|
+
) => Promise<MatchContext<TEnv> | null>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create match handler functions bound to router closure state.
|
|
101
|
+
* These are the main request-handling entry points for SSR, navigation,
|
|
102
|
+
* error recovery, and preview matching.
|
|
103
|
+
*/
|
|
104
|
+
export function createMatchHandlers<TEnv = any>(
|
|
105
|
+
deps: MatchHandlerDeps<TEnv>,
|
|
106
|
+
): MatchHandlers<TEnv> {
|
|
107
|
+
const {
|
|
108
|
+
buildRouterContext,
|
|
109
|
+
callOnError,
|
|
110
|
+
matchApiDeps,
|
|
111
|
+
defaultErrorBoundary,
|
|
112
|
+
findInterceptForRoute,
|
|
113
|
+
} = deps;
|
|
114
|
+
const hasTelemetry = !!deps.telemetry;
|
|
115
|
+
const telemetry = resolveSink(deps.telemetry);
|
|
116
|
+
|
|
117
|
+
async function createMatchContextForFull(
|
|
118
|
+
request: Request,
|
|
119
|
+
env: TEnv,
|
|
120
|
+
): Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }> {
|
|
121
|
+
return _createMatchContextForFull(
|
|
122
|
+
request,
|
|
123
|
+
env,
|
|
124
|
+
matchApiDeps,
|
|
125
|
+
findInterceptForRoute,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function createMatchContextForPartial(
|
|
130
|
+
request: Request,
|
|
131
|
+
env: TEnv,
|
|
132
|
+
actionContext?: {
|
|
133
|
+
actionId?: string;
|
|
134
|
+
actionUrl?: URL;
|
|
135
|
+
actionResult?: any;
|
|
136
|
+
formData?: FormData;
|
|
137
|
+
},
|
|
138
|
+
): Promise<MatchContext<TEnv> | null> {
|
|
139
|
+
return _createMatchContextForPartial(
|
|
140
|
+
request,
|
|
141
|
+
env,
|
|
142
|
+
matchApiDeps,
|
|
143
|
+
findInterceptForRoute,
|
|
144
|
+
actionContext,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Match request and return segments (document/SSR requests)
|
|
150
|
+
*
|
|
151
|
+
* Uses generator middleware pipeline for clean separation of concerns:
|
|
152
|
+
* - cache-lookup: Check cache first
|
|
153
|
+
* - segment-resolution: Resolve segments on cache miss
|
|
154
|
+
* - cache-store: Store results in cache
|
|
155
|
+
* - background-revalidation: SWR revalidation
|
|
156
|
+
*/
|
|
157
|
+
async function match(request: Request, env: TEnv): Promise<MatchResult> {
|
|
158
|
+
const requestId = hasTelemetry ? getRequestId(request) : undefined;
|
|
159
|
+
return runWithRouterLogContext({ request, transaction: "match" }, () => {
|
|
160
|
+
const routerCtx = buildRouterContext();
|
|
161
|
+
routerCtx.requestId = requestId;
|
|
162
|
+
return runWithRouterContext(routerCtx, async () =>
|
|
163
|
+
withRouterLogScope("match", async () => {
|
|
164
|
+
const matchStart = performance.now();
|
|
165
|
+
const pathname = new URL(request.url).pathname;
|
|
166
|
+
if (hasTelemetry) {
|
|
167
|
+
safeEmit(telemetry, {
|
|
168
|
+
type: "request.start",
|
|
169
|
+
timestamp: matchStart,
|
|
170
|
+
requestId,
|
|
171
|
+
method: request.method,
|
|
172
|
+
pathname,
|
|
173
|
+
transaction: "match",
|
|
174
|
+
isPartial: false,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const result = await createMatchContextForFull(request, env);
|
|
179
|
+
|
|
180
|
+
// Handle redirect case
|
|
181
|
+
if ("type" in result && result.type === "redirect") {
|
|
182
|
+
if (hasTelemetry) {
|
|
183
|
+
safeEmit(telemetry, {
|
|
184
|
+
type: "request.end",
|
|
185
|
+
timestamp: performance.now(),
|
|
186
|
+
requestId,
|
|
187
|
+
method: request.method,
|
|
188
|
+
pathname,
|
|
189
|
+
transaction: "match",
|
|
190
|
+
durationMs: performance.now() - matchStart,
|
|
191
|
+
segmentCount: 0,
|
|
192
|
+
cacheHit: false,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
segments: [],
|
|
197
|
+
matched: [],
|
|
198
|
+
diff: [],
|
|
199
|
+
params: {},
|
|
200
|
+
redirect: result.redirectUrl,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ctx = result as MatchContext<TEnv>;
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const state = createPipelineState();
|
|
208
|
+
const pipeline = createMatchPartialPipeline(ctx, state);
|
|
209
|
+
const matchResult = await collectMatchResult(pipeline, ctx, state);
|
|
210
|
+
if (hasTelemetry) {
|
|
211
|
+
safeEmit(telemetry, {
|
|
212
|
+
type: "cache.decision",
|
|
213
|
+
timestamp: performance.now(),
|
|
214
|
+
requestId,
|
|
215
|
+
pathname,
|
|
216
|
+
routeKey: ctx.routeKey,
|
|
217
|
+
hit: state.cacheHit,
|
|
218
|
+
shouldRevalidate: !!state.shouldRevalidate,
|
|
219
|
+
source: state.cacheSource,
|
|
220
|
+
});
|
|
221
|
+
safeEmit(telemetry, {
|
|
222
|
+
type: "request.end",
|
|
223
|
+
timestamp: performance.now(),
|
|
224
|
+
requestId,
|
|
225
|
+
method: request.method,
|
|
226
|
+
pathname,
|
|
227
|
+
transaction: "match",
|
|
228
|
+
durationMs: performance.now() - matchStart,
|
|
229
|
+
segmentCount: matchResult.segments.length,
|
|
230
|
+
cacheHit: state.cacheHit,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return matchResult;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (hasTelemetry) {
|
|
236
|
+
const errorObj =
|
|
237
|
+
error instanceof Error ? error : new Error(String(error));
|
|
238
|
+
safeEmit(telemetry, {
|
|
239
|
+
type: "request.error",
|
|
240
|
+
timestamp: performance.now(),
|
|
241
|
+
requestId,
|
|
242
|
+
method: request.method,
|
|
243
|
+
pathname,
|
|
244
|
+
transaction: "match",
|
|
245
|
+
error: errorObj,
|
|
246
|
+
phase: error instanceof Response ? "redirect" : "routing",
|
|
247
|
+
durationMs: performance.now() - matchStart,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (error instanceof Response) throw error;
|
|
251
|
+
// Report unhandled errors during full match pipeline
|
|
252
|
+
callOnError(error, "routing", {
|
|
253
|
+
request,
|
|
254
|
+
url: ctx.url,
|
|
255
|
+
env,
|
|
256
|
+
isPartial: false,
|
|
257
|
+
handledByBoundary: false,
|
|
258
|
+
});
|
|
259
|
+
throw sanitizeError(error);
|
|
260
|
+
}
|
|
261
|
+
}),
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function matchError(
|
|
267
|
+
request: Request,
|
|
268
|
+
_context: TEnv,
|
|
269
|
+
error: unknown,
|
|
270
|
+
segmentType: ErrorInfo["segmentType"] = "route",
|
|
271
|
+
): Promise<MatchResult | null> {
|
|
272
|
+
return runWithRouterLogContext({ request, transaction: "matchError" }, () =>
|
|
273
|
+
withRouterLogScope("matchError", () =>
|
|
274
|
+
_matchError(
|
|
275
|
+
request,
|
|
276
|
+
_context,
|
|
277
|
+
error,
|
|
278
|
+
matchApiDeps,
|
|
279
|
+
defaultErrorBoundary,
|
|
280
|
+
segmentType,
|
|
281
|
+
),
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Match partial request with revalidation
|
|
288
|
+
*
|
|
289
|
+
* Uses generator middleware pipeline for clean separation of concerns:
|
|
290
|
+
* - cache-lookup: Check cache first
|
|
291
|
+
* - segment-resolution: Resolve segments on cache miss
|
|
292
|
+
* - intercept-resolution: Handle intercept routes
|
|
293
|
+
* - cache-store: Store results in cache
|
|
294
|
+
* - background-revalidation: SWR revalidation
|
|
295
|
+
*/
|
|
296
|
+
async function matchPartial(
|
|
297
|
+
request: Request,
|
|
298
|
+
context: TEnv,
|
|
299
|
+
actionContext?: ActionContext,
|
|
300
|
+
): Promise<MatchResult | null> {
|
|
301
|
+
const partialRequestId = hasTelemetry ? getRequestId(request) : undefined;
|
|
302
|
+
return runWithRouterLogContext(
|
|
303
|
+
{ request, transaction: "matchPartial" },
|
|
304
|
+
() => {
|
|
305
|
+
const routerCtx = buildRouterContext();
|
|
306
|
+
routerCtx.requestId = partialRequestId;
|
|
307
|
+
return runWithRouterContext(routerCtx, async () =>
|
|
308
|
+
withRouterLogScope("matchPartial", async () => {
|
|
309
|
+
const matchStart = performance.now();
|
|
310
|
+
const pathname = new URL(request.url).pathname;
|
|
311
|
+
if (hasTelemetry) {
|
|
312
|
+
safeEmit(telemetry, {
|
|
313
|
+
type: "request.start",
|
|
314
|
+
timestamp: matchStart,
|
|
315
|
+
requestId: partialRequestId,
|
|
316
|
+
method: request.method,
|
|
317
|
+
pathname,
|
|
318
|
+
transaction: "matchPartial",
|
|
319
|
+
isPartial: true,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const ctx = await createMatchContextForPartial(
|
|
324
|
+
request,
|
|
325
|
+
context,
|
|
326
|
+
actionContext,
|
|
327
|
+
);
|
|
328
|
+
if (!ctx) {
|
|
329
|
+
if (hasTelemetry) {
|
|
330
|
+
safeEmit(telemetry, {
|
|
331
|
+
type: "request.end",
|
|
332
|
+
timestamp: performance.now(),
|
|
333
|
+
requestId: partialRequestId,
|
|
334
|
+
method: request.method,
|
|
335
|
+
pathname,
|
|
336
|
+
transaction: "matchPartial",
|
|
337
|
+
durationMs: performance.now() - matchStart,
|
|
338
|
+
segmentCount: 0,
|
|
339
|
+
cacheHit: false,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (isRouterDebugEnabled()) {
|
|
346
|
+
startRevalidationTrace({
|
|
347
|
+
method: request.method,
|
|
348
|
+
prevUrl: ctx.prevUrl.href,
|
|
349
|
+
nextUrl: ctx.url.href,
|
|
350
|
+
routeKey: ctx.routeKey,
|
|
351
|
+
isAction: !!actionContext,
|
|
352
|
+
stale: ctx.stale || undefined,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const state = createPipelineState();
|
|
358
|
+
const pipeline = createMatchPartialPipeline(ctx, state);
|
|
359
|
+
const matchResult = await collectMatchResult(
|
|
360
|
+
pipeline,
|
|
361
|
+
ctx,
|
|
362
|
+
state,
|
|
363
|
+
);
|
|
364
|
+
flushRevalidationTrace();
|
|
365
|
+
if (hasTelemetry) {
|
|
366
|
+
safeEmit(telemetry, {
|
|
367
|
+
type: "cache.decision",
|
|
368
|
+
timestamp: performance.now(),
|
|
369
|
+
requestId: partialRequestId,
|
|
370
|
+
pathname,
|
|
371
|
+
routeKey: ctx.routeKey,
|
|
372
|
+
hit: state.cacheHit,
|
|
373
|
+
shouldRevalidate: !!state.shouldRevalidate,
|
|
374
|
+
source: state.cacheSource,
|
|
375
|
+
});
|
|
376
|
+
safeEmit(telemetry, {
|
|
377
|
+
type: "request.end",
|
|
378
|
+
timestamp: performance.now(),
|
|
379
|
+
requestId: partialRequestId,
|
|
380
|
+
method: request.method,
|
|
381
|
+
pathname,
|
|
382
|
+
transaction: "matchPartial",
|
|
383
|
+
durationMs: performance.now() - matchStart,
|
|
384
|
+
segmentCount: matchResult.segments.length,
|
|
385
|
+
cacheHit: state.cacheHit,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return matchResult;
|
|
389
|
+
} catch (error) {
|
|
390
|
+
flushRevalidationTrace();
|
|
391
|
+
if (hasTelemetry) {
|
|
392
|
+
const errorObj =
|
|
393
|
+
error instanceof Error ? error : new Error(String(error));
|
|
394
|
+
const phase = actionContext ? "action" : "revalidation";
|
|
395
|
+
safeEmit(telemetry, {
|
|
396
|
+
type: "request.error",
|
|
397
|
+
timestamp: performance.now(),
|
|
398
|
+
requestId: partialRequestId,
|
|
399
|
+
method: request.method,
|
|
400
|
+
pathname,
|
|
401
|
+
transaction: "matchPartial",
|
|
402
|
+
error: errorObj,
|
|
403
|
+
phase: error instanceof Response ? "redirect" : phase,
|
|
404
|
+
durationMs: performance.now() - matchStart,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (error instanceof Response) throw error;
|
|
408
|
+
// Report unhandled errors during partial match pipeline
|
|
409
|
+
callOnError(error, actionContext ? "action" : "revalidation", {
|
|
410
|
+
request,
|
|
411
|
+
url: ctx.url,
|
|
412
|
+
env: context,
|
|
413
|
+
actionId: actionContext?.actionId,
|
|
414
|
+
isPartial: true,
|
|
415
|
+
handledByBoundary: false,
|
|
416
|
+
});
|
|
417
|
+
throw sanitizeError(error);
|
|
418
|
+
}
|
|
419
|
+
}),
|
|
420
|
+
);
|
|
421
|
+
},
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function previewMatch(
|
|
426
|
+
request: Request,
|
|
427
|
+
_context: TEnv,
|
|
428
|
+
): ReturnType<typeof _previewMatch> {
|
|
429
|
+
return _previewMatch(request, _context, { findMatch: deps.findMatch });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
match: match,
|
|
434
|
+
matchPartial: matchPartial,
|
|
435
|
+
matchError: matchError,
|
|
436
|
+
previewMatch: previewMatch,
|
|
437
|
+
createMatchContextForFull: createMatchContextForFull,
|
|
438
|
+
createMatchContextForPartial: createMatchContextForPartial,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
@@ -30,23 +30,15 @@
|
|
|
30
30
|
* |
|
|
31
31
|
* v (async, doesn't block response)
|
|
32
32
|
* +---------------------------+
|
|
33
|
-
* | Create fresh
|
|
33
|
+
* | Create fresh context | Fresh handleStore, handlerContext,
|
|
34
|
+
* | (full isolation) | and loaderPromises map
|
|
34
35
|
* +---------------------------+
|
|
35
36
|
* |
|
|
36
37
|
* v
|
|
37
|
-
*
|
|
38
|
-
* |
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* +-----+-----+
|
|
42
|
-
* | |
|
|
43
|
-
* yes no
|
|
44
|
-
* | |
|
|
45
|
-
* v v
|
|
46
|
-
* resolveAll resolveWithRevalidation
|
|
47
|
-
* Segments + resolveIntercepts
|
|
48
|
-
* | |
|
|
49
|
-
* +-----------+
|
|
38
|
+
* +---------------------------+
|
|
39
|
+
* | resolveAllSegments() | Fresh resolution (no revalidation)
|
|
40
|
+
* | + resolveIntercepts() | Ensures complete components
|
|
41
|
+
* +---------------------------+
|
|
50
42
|
* |
|
|
51
43
|
* v
|
|
52
44
|
* +---------------------------+
|
|
@@ -90,32 +82,28 @@
|
|
|
90
82
|
* ISOLATION FROM RESPONSE
|
|
91
83
|
* =======================
|
|
92
84
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
85
|
+
* Background revalidation creates fully isolated context:
|
|
86
|
+
* - Fresh handleStore (prevents polluting the response stream)
|
|
87
|
+
* - Fresh handlerContext + loaderPromises (prevents reusing memoized
|
|
88
|
+
* loader results from the foreground pass)
|
|
89
|
+
* - handleStore is saved/restored in try/finally
|
|
96
90
|
*
|
|
97
|
-
* This
|
|
98
|
-
* - Polluting the current response stream
|
|
99
|
-
* - Causing duplicate data in the client
|
|
100
|
-
* - Creating race conditions
|
|
91
|
+
* This matches the proactive caching pattern in cache-store.ts.
|
|
101
92
|
*
|
|
102
93
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
94
|
+
* FRESH RESOLUTION (NO REVALIDATION)
|
|
95
|
+
* ===================================
|
|
105
96
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* Partial Match (navigation):
|
|
111
|
-
* - resolveAllSegmentsWithRevalidation()
|
|
112
|
-
* - Also resolves intercept segments if applicable
|
|
113
|
-
* - More complex but handles all scenarios
|
|
97
|
+
* Both full and partial requests use resolveAllSegments() (without
|
|
98
|
+
* revalidation logic) to ensure all segments have complete components.
|
|
99
|
+
* Using revalidation-aware resolution would produce null components
|
|
100
|
+
* for skipped segments, which would corrupt the cache entry.
|
|
114
101
|
*/
|
|
115
102
|
import type { ResolvedSegment } from "../../types.js";
|
|
116
103
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
117
104
|
import { getRouterContext } from "../router-context.js";
|
|
118
105
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
106
|
+
import { debugLog, debugWarn } from "../logging.js";
|
|
119
107
|
|
|
120
108
|
/**
|
|
121
109
|
* Creates background revalidation middleware
|
|
@@ -127,10 +115,10 @@ import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
|
127
115
|
*/
|
|
128
116
|
export function withBackgroundRevalidation<TEnv>(
|
|
129
117
|
ctx: MatchContext<TEnv>,
|
|
130
|
-
state: MatchPipelineState
|
|
118
|
+
state: MatchPipelineState,
|
|
131
119
|
): GeneratorMiddleware<ResolvedSegment> {
|
|
132
120
|
return async function* (
|
|
133
|
-
source: AsyncGenerator<ResolvedSegment
|
|
121
|
+
source: AsyncGenerator<ResolvedSegment>,
|
|
134
122
|
): AsyncGenerator<ResolvedSegment> {
|
|
135
123
|
// Pass through all segments unchanged
|
|
136
124
|
for await (const segment of source) {
|
|
@@ -147,7 +135,8 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
147
135
|
const {
|
|
148
136
|
getRequestContext,
|
|
149
137
|
createHandleStore,
|
|
150
|
-
|
|
138
|
+
createHandlerContext,
|
|
139
|
+
setupLoaderAccess,
|
|
151
140
|
resolveAllSegments,
|
|
152
141
|
resolveInterceptEntry,
|
|
153
142
|
} = getRouterContext<TEnv>();
|
|
@@ -155,81 +144,79 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
155
144
|
const requestCtx = getRequestContext();
|
|
156
145
|
const cacheScope = ctx.cacheScope;
|
|
157
146
|
|
|
158
|
-
const logPrefix = ctx.isFullMatch ? "[Router.match]" : "[Router.matchPartial]";
|
|
159
|
-
|
|
160
147
|
requestCtx?.waitUntil(async () => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (requestCtx) {
|
|
166
|
-
requestCtx._handleStore = createHandleStore();
|
|
167
|
-
}
|
|
148
|
+
debugLog("backgroundRevalidation", "revalidating stale route", {
|
|
149
|
+
pathname: ctx.pathname,
|
|
150
|
+
fullMatch: ctx.isFullMatch,
|
|
151
|
+
});
|
|
168
152
|
|
|
169
|
-
|
|
153
|
+
// Save and replace handleStore to avoid polluting the response stream.
|
|
154
|
+
// Restore in finally (same pattern as proactive caching in cache-store).
|
|
155
|
+
const originalHandleStore = requestCtx._handleStore;
|
|
156
|
+
requestCtx._handleStore = createHandleStore();
|
|
170
157
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
158
|
+
try {
|
|
159
|
+
// Create fresh handler context and loader promises to avoid
|
|
160
|
+
// reusing memoized results from the foreground pass
|
|
161
|
+
const freshHandlerContext = createHandlerContext(
|
|
162
|
+
ctx.matched.params,
|
|
163
|
+
ctx.request,
|
|
164
|
+
ctx.url.searchParams,
|
|
165
|
+
ctx.pathname,
|
|
166
|
+
ctx.url,
|
|
167
|
+
ctx.env,
|
|
168
|
+
ctx.routeMap,
|
|
169
|
+
ctx.matched.routeKey,
|
|
170
|
+
ctx.matched.responseType,
|
|
171
|
+
ctx.matched.pt === true,
|
|
172
|
+
);
|
|
173
|
+
const freshLoaderPromises = new Map<string, Promise<any>>();
|
|
174
|
+
setupLoaderAccess(freshHandlerContext, freshLoaderPromises);
|
|
175
|
+
|
|
176
|
+
// Resolve all segments fresh (without revalidation logic)
|
|
177
|
+
// to ensure complete components for caching
|
|
178
|
+
const freshSegments = await ctx.Store.run(() =>
|
|
179
|
+
resolveAllSegments(
|
|
183
180
|
ctx.entries,
|
|
184
181
|
ctx.routeKey,
|
|
185
182
|
ctx.matched.params,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
ctx.prevUrl,
|
|
191
|
-
ctx.url,
|
|
192
|
-
ctx.loaderPromises,
|
|
193
|
-
ctx.actionContext,
|
|
194
|
-
ctx.interceptResult,
|
|
195
|
-
ctx.localRouteName,
|
|
196
|
-
ctx.pathname
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
freshSegments = freshResult.segments;
|
|
183
|
+
freshHandlerContext,
|
|
184
|
+
freshLoaderPromises,
|
|
185
|
+
),
|
|
186
|
+
);
|
|
200
187
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
188
|
+
// Also resolve intercept segments fresh if applicable
|
|
189
|
+
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
190
|
+
if (ctx.interceptResult) {
|
|
191
|
+
freshInterceptSegments = await ctx.Store.run(() =>
|
|
192
|
+
resolveInterceptEntry(
|
|
193
|
+
ctx.interceptResult!.intercept,
|
|
194
|
+
ctx.interceptResult!.entry,
|
|
206
195
|
ctx.matched.params,
|
|
207
|
-
|
|
196
|
+
freshHandlerContext,
|
|
208
197
|
true,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
prevParams: ctx.prevParams,
|
|
212
|
-
request: ctx.request,
|
|
213
|
-
prevUrl: ctx.prevUrl,
|
|
214
|
-
nextUrl: ctx.url,
|
|
215
|
-
routeKey: ctx.routeKey,
|
|
216
|
-
actionContext: ctx.actionContext,
|
|
217
|
-
stale: false,
|
|
218
|
-
}
|
|
219
|
-
);
|
|
220
|
-
freshSegments = [...freshSegments, ...freshInterceptSegments];
|
|
221
|
-
}
|
|
198
|
+
),
|
|
199
|
+
);
|
|
222
200
|
}
|
|
223
201
|
|
|
202
|
+
const completeSegments = [...freshSegments, ...freshInterceptSegments];
|
|
203
|
+
requestCtx._handleStore.seal();
|
|
224
204
|
await cacheScope.cacheRoute(
|
|
225
205
|
ctx.pathname,
|
|
226
206
|
ctx.matched.params,
|
|
227
|
-
|
|
228
|
-
ctx.isIntercept
|
|
207
|
+
completeSegments,
|
|
208
|
+
ctx.isIntercept,
|
|
229
209
|
);
|
|
230
|
-
|
|
210
|
+
debugLog("backgroundRevalidation", "revalidation complete", {
|
|
211
|
+
pathname: ctx.pathname,
|
|
212
|
+
});
|
|
231
213
|
} catch (error) {
|
|
232
|
-
|
|
214
|
+
debugWarn("backgroundRevalidation", "revalidation failed", {
|
|
215
|
+
pathname: ctx.pathname,
|
|
216
|
+
error: String(error),
|
|
217
|
+
});
|
|
218
|
+
} finally {
|
|
219
|
+
requestCtx._handleStore = originalHandleStore;
|
|
233
220
|
}
|
|
234
221
|
});
|
|
235
222
|
};
|