@timber-js/app 0.2.0-alpha.213 → 0.2.0-alpha.214
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/{actions-CEootpB1.js → actions-CUh3cClk.js} +2 -2
- package/dist/_chunks/{actions-CEootpB1.js.map → actions-CUh3cClk.js.map} +1 -1
- package/dist/_chunks/{canonicalize-CgHoscYO.js → canonicalize-BAWkWiKK.js} +28 -2
- package/dist/_chunks/{canonicalize-CgHoscYO.js.map → canonicalize-BAWkWiKK.js.map} +1 -1
- package/dist/_chunks/{chains-BfoPFraI.js → chains-CjK1Eu6a.js} +2 -2
- package/dist/_chunks/{chains-BfoPFraI.js.map → chains-CjK1Eu6a.js.map} +1 -1
- package/dist/_chunks/{classify-BT66U83D.js → classify-GAdt6aiA.js} +2 -2
- package/dist/_chunks/{classify-BT66U83D.js.map → classify-GAdt6aiA.js.map} +1 -1
- package/dist/_chunks/{cli-check-BfQ54-UJ.js → cli-check-DOzjlycg.js} +3 -3
- package/dist/_chunks/{cli-check-BfQ54-UJ.js.map → cli-check-DOzjlycg.js.map} +1 -1
- package/dist/_chunks/{cli-schema-sync-czh2dsLs.js → cli-schema-sync-B9wDaQvW.js} +2 -2
- package/dist/_chunks/{cli-schema-sync-czh2dsLs.js.map → cli-schema-sync-B9wDaQvW.js.map} +1 -1
- package/dist/_chunks/{client-dep-entries-CQwpb8dI.js → client-dep-entries-DyDqXOF9.js} +2 -2
- package/dist/_chunks/{client-dep-entries-CQwpb8dI.js.map → client-dep-entries-DyDqXOF9.js.map} +1 -1
- package/dist/_chunks/{convention-lint-BEVW4EID.js → convention-lint-B3QEGJX7.js} +2 -2
- package/dist/_chunks/{convention-lint-BEVW4EID.js.map → convention-lint-B3QEGJX7.js.map} +1 -1
- package/dist/_chunks/{dev-server-FKxptbnI.js → dev-server-_9L4KWC-.js} +3 -3
- package/dist/_chunks/{dev-server-FKxptbnI.js.map → dev-server-_9L4KWC-.js.map} +1 -1
- package/dist/_chunks/{error-boundary-9g_Lb2na.js → error-boundary-xxxLtXt6.js} +38 -5
- package/dist/_chunks/{error-boundary-9g_Lb2na.js.map → error-boundary-xxxLtXt6.js.map} +1 -1
- package/dist/_chunks/{live-graph-C_4v-fHv.js → live-graph-Cd3YHvrH.js} +4 -4
- package/dist/_chunks/{live-graph-C_4v-fHv.js.map → live-graph-Cd3YHvrH.js.map} +1 -1
- package/dist/_chunks/{poison-scan-C92liMAr.js → poison-scan-BnJjBOkn.js} +2 -2
- package/dist/_chunks/{poison-scan-C92liMAr.js.map → poison-scan-BnJjBOkn.js.map} +1 -1
- package/dist/_chunks/{scanner-CQt12vE2.js → scanner-CKAT5gRx.js} +2 -2
- package/dist/_chunks/{scanner-CQt12vE2.js.map → scanner-CKAT5gRx.js.map} +1 -1
- package/dist/_chunks/{walkers-DAT4avhZ.js → walkers-DwCXEyRu.js} +3 -3
- package/dist/_chunks/{walkers-DAT4avhZ.js.map → walkers-DwCXEyRu.js.map} +1 -1
- package/dist/adapters/nitro-preview.d.ts.map +1 -1
- package/dist/adapters/nitro.js +11 -2
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/analyze/crawl-entry.js +3 -3
- package/dist/analyze/graph-command.js +2 -2
- package/dist/cli.js +3 -3
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/internal.js +22 -7
- package/dist/client/internal.js.map +1 -1
- package/dist/client/router-effects.d.ts +70 -12
- package/dist/client/router-effects.d.ts.map +1 -1
- package/dist/client/router.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/routing/index.js +2 -2
- package/dist/server/canonicalize.d.ts +22 -0
- package/dist/server/canonicalize.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/internal.js +54 -3
- package/dist/server/internal.js.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +31 -0
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/nitro-preview.ts +10 -1
- package/src/client/router-effects.ts +99 -14
- package/src/client/router.ts +29 -13
- package/src/server/canonicalize.ts +29 -0
- package/src/server/pipeline-helpers.ts +61 -0
- package/src/server/pipeline.ts +13 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { a as reportConfigErrors, i as formatMissingPeers, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-
|
|
1
|
+
import { a as reportConfigErrors, i as formatMissingPeers, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-_9L4KWC-.js";
|
|
2
2
|
import { i as resolveRenderTimeoutMs } from "./_chunks/render-timeout-DOf_BBcO.js";
|
|
3
3
|
import { t as writeFileAtomic } from "./_chunks/write-file-atomic-D1qWqHb4.js";
|
|
4
|
-
import { d as collectInterceptionRewrites, l as getPrerenderExport, n as scanRoutes } from "./_chunks/scanner-
|
|
4
|
+
import { d as collectInterceptionRewrites, l as getPrerenderExport, n as scanRoutes } from "./_chunks/scanner-CKAT5gRx.js";
|
|
5
5
|
import { a as isDynamicMetadataExtension } from "./_chunks/metadata-routes-DSDjM_hJ.js";
|
|
6
6
|
import { v as swallow } from "./_chunks/logger-CbLdcy-W.js";
|
|
7
|
-
import { c as buildDirToRouteMap, n as formatConventionWarnings, o as flushCodegenWrites, r as lintConventions, s as writeRouteCodegen, t as checkAppDirExists } from "./_chunks/convention-lint-
|
|
7
|
+
import { c as buildDirToRouteMap, n as formatConventionWarnings, o as flushCodegenWrites, r as lintConventions, s as writeRouteCodegen, t as checkAppDirExists } from "./_chunks/convention-lint-B3QEGJX7.js";
|
|
8
8
|
import { a as loadTimberConfigFile, c as resolveAppDir, d as createNoopTimer, l as resolveBuildDir, n as contextAdapter, o as mergeFileConfig, r as createPluginContext, t as adapterConfig, u as resolveClientJavascript } from "./_chunks/plugin-context-BPBPc3lL.js";
|
|
9
9
|
import { i as withBasePath, n as normalizeBasePath, t as isRelativeBase } from "./_chunks/base-path-DaQrzbez.js";
|
|
10
|
-
import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-
|
|
10
|
+
import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-DwCXEyRu.js";
|
|
11
11
|
import { i as poisonPillSource, r as markerFor, t as EXPLICIT_MARKERS } from "./_chunks/poison-rules-DoEhbqaY.js";
|
|
12
12
|
import { n as rscPayloadPath, t as RSC_PAYLOAD_DIR } from "./_chunks/rsc-payload-path-dffcs4jp.js";
|
|
13
13
|
import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-BRKuL215.js";
|
|
@@ -9637,7 +9637,7 @@ function timber(config) {
|
|
|
9637
9637
|
let environments = envOutDirs;
|
|
9638
9638
|
if (!isPreview) {
|
|
9639
9639
|
ctx.timer.start("client-deps-scan");
|
|
9640
|
-
const { clientOptimizeDepsEntries } = await import("./_chunks/client-dep-entries-
|
|
9640
|
+
const { clientOptimizeDepsEntries } = await import("./_chunks/client-dep-entries-DyDqXOF9.js");
|
|
9641
9641
|
const entries = await clientOptimizeDepsEntries(resolvedRoot, BROWSER_ENTRY_FILE, buildDir);
|
|
9642
9642
|
ctx.timer.end("client-deps-scan");
|
|
9643
9643
|
environments = {
|
package/dist/routing/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as INTERCEPTION_MARKERS, i as DEFAULT_PAGE_EXTENSIONS, n as classifyUrlSegment, t as classifySegment } from "../_chunks/segment-classify-C539Pa2O.js";
|
|
2
|
-
import { d as collectInterceptionRewrites, n as scanRoutes } from "../_chunks/scanner-
|
|
2
|
+
import { d as collectInterceptionRewrites, n as scanRoutes } from "../_chunks/scanner-CKAT5gRx.js";
|
|
3
3
|
import { t as generateRouteMap } from "../_chunks/codegen-Bps1sLKJ.js";
|
|
4
|
-
import { n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-
|
|
4
|
+
import { n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-DwCXEyRu.js";
|
|
5
5
|
export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectDynamicSegmentsFromTree, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes, validateSchemaAgainstRoutes };
|
|
@@ -37,4 +37,26 @@ export declare const NULL_BYTE_RE: RegExp;
|
|
|
37
37
|
* @param stripTrailingSlash - Whether to strip trailing slashes. Default: true.
|
|
38
38
|
*/
|
|
39
39
|
export declare function canonicalize(rawPathname: string, stripTrailingSlash?: boolean): CanonicalizeResult;
|
|
40
|
+
/**
|
|
41
|
+
* The path a safe request for `rawPathname` should be redirected to — or
|
|
42
|
+
* `rawPathname` itself when it is already canonical.
|
|
43
|
+
*
|
|
44
|
+
* Applies the structural steps of `canonicalize()` — collapse `//` (step 3)
|
|
45
|
+
* and strip the trailing slash (step 5) — to the **encoded** path, without
|
|
46
|
+
* decoding. That is what the browser will send back, so the redirect is
|
|
47
|
+
* idempotent: `/%61dmin` differs from its canonical form `/admin` only by
|
|
48
|
+
* percent-encoding, and redirecting it to `/admin` would compare against the
|
|
49
|
+
* decoded form forever. Decoding cannot introduce a `/` (`%2f` is rejected),
|
|
50
|
+
* so the structure is the same before and after decoding.
|
|
51
|
+
*
|
|
52
|
+
* Dot segments are not handled here: the request URL is parsed by the WHATWG
|
|
53
|
+
* URL parser, which has already resolved `.`, `..` and their encoded forms.
|
|
54
|
+
*
|
|
55
|
+
* Only meaningful for a path `canonicalize()` accepted. The collapse also
|
|
56
|
+
* means the result can never start with `//`, so it is never a
|
|
57
|
+
* protocol-relative redirect target.
|
|
58
|
+
*
|
|
59
|
+
* See design/07-routing.md §"Non-Canonical URLs Redirect"
|
|
60
|
+
*/
|
|
61
|
+
export declare function canonicalRequestPath(rawPathname: string, stripTrailingSlash?: boolean): string;
|
|
40
62
|
//# sourceMappingURL=canonicalize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../../src/server/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAE/C,2DAA2D;AAC3D,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,kBAAkB,CAwD/F"}
|
|
1
|
+
{"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../../src/server/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAE/C,2DAA2D;AAC3D,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,kBAAkB,CAwD/F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,MAAM,CAM3F"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A as getSpanId, C as isRedirectSignal, E as addSpanEvent, F as withSpan, S as isDenySignal, T as isDevMode, j as getTraceId, w as isDebug } from "../_chunks/logger-CbLdcy-W.js";
|
|
2
2
|
import { t as formatSize } from "../_chunks/format-BKclgVbk.js";
|
|
3
|
-
import { _ as getHeaders, a as parseFormData, c as deny, d as waitUntil, i as coerce, l as redirect, n as revalidatePath, p as getCookieJar, r as revalidateTag, u as redirectExternal, y as getSegmentParams } from "../_chunks/actions-
|
|
3
|
+
import { _ as getHeaders, a as parseFormData, c as deny, d as waitUntil, i as coerce, l as redirect, n as revalidatePath, p as getCookieJar, r as revalidateTag, u as redirectExternal, y as getSegmentParams } from "../_chunks/actions-CUh3cClk.js";
|
|
4
4
|
//#region src/shared/redirect-type.ts
|
|
5
5
|
/**
|
|
6
6
|
* Next.js redirect type discriminator.
|
package/dist/server/internal.js
CHANGED
|
@@ -3,12 +3,12 @@ import { n as isRscContentType, r as isRscRequest } from "../_chunks/rsc-media-t
|
|
|
3
3
|
import { n as classifyUrlSegment, r as effectiveUrlSegment } from "../_chunks/segment-classify-C539Pa2O.js";
|
|
4
4
|
import { t as computeSegmentTreePaths } from "../_chunks/segment-keys-D5hu1hz4.js";
|
|
5
5
|
import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-DSDjM_hJ.js";
|
|
6
|
-
import {
|
|
6
|
+
import { i as canonicalize, r as canonicalRequestPath } from "../_chunks/canonicalize-BAWkWiKK.js";
|
|
7
7
|
import { i as requestContextAls, n as prebuiltCaptureAls, o as timingAls, t as earlyHintsSenderAls } from "../_chunks/als-registry-BZqHCtq-.js";
|
|
8
8
|
import { C as isRedirectSignal, F as withSpan, M as replaceTraceId, N as runWithTraceId, O as generateTraceId, P as setSpanAttribute, S as isDenySignal, _ as setLogger, a as logMiddlewareError, c as logRenderError, d as logRouteError, f as logRouteSetupError, g as logWaitUntilUnsupported, h as logWaitUntilRejected, j as getTraceId, k as getOtelTraceId, l as logRequestCompleted, m as logSwrRefetchFailed, n as logCacheMiss, o as logMiddlewareShortCircuit, p as logSlowRequest, r as logCsrfRejected, s as logProxyError, t as getLogger, u as logRequestReceived, v as swallow, w as isDebug, x as isControlFlowSignal } from "../_chunks/logger-CbLdcy-W.js";
|
|
9
9
|
import { i as collectRouteModulepreloads, n as collectRouteCss, r as collectRouteFonts } from "../_chunks/build-manifest-DWppEdLB.js";
|
|
10
|
-
import { i as isRscCacheKeyShape, n as RSC_KEY_PARAM, s as rscCacheKey, t as RSC_KEY_HEADERS } from "../_chunks/rsc-cache-key-ClUiXQnK.js";
|
|
11
|
-
import { C as setMutableCookieContext, S as setMatchedSegmentPath, T as runOutsideReactCacheScope, b as markResponseFlushed, f as getCookie, g as getHeader, h as applyRequestHeaderOverlay, i as coerce, m as getSetCookieHeaders, o as DenySignal, s as RedirectSignal, t as executeAction, v as getSearchParams, w as setSegmentParams, x as runWithRequestContext, y as getSegmentParams } from "../_chunks/actions-
|
|
10
|
+
import { i as isRscCacheKeyShape, n as RSC_KEY_PARAM, r as appVisibleSearch, s as rscCacheKey, t as RSC_KEY_HEADERS } from "../_chunks/rsc-cache-key-ClUiXQnK.js";
|
|
11
|
+
import { C as setMutableCookieContext, S as setMatchedSegmentPath, T as runOutsideReactCacheScope, b as markResponseFlushed, f as getCookie, g as getHeader, h as applyRequestHeaderOverlay, i as coerce, m as getSetCookieHeaders, o as DenySignal, s as RedirectSignal, t as executeAction, v as getSearchParams, w as setSegmentParams, x as runWithRequestContext, y as getSegmentParams } from "../_chunks/actions-CUh3cClk.js";
|
|
12
12
|
import { a as getManifestEntry, b as wasInvalidatedSince, c as lookupPrebuiltPayload, d as checkVersionSkew, i as storeOverlayEntry, l as setPrebuiltPayloadSource, n as lookupOverlay, o as hasPrebuiltPayloadSource, r as overlayKey, s as isParamIndependent, u as applyReloadHeaders, v as currentInvalidationEpoch, x as getWaitUntil, y as lastInvalidationEpochFor } from "../_chunks/cache-api-LA3sBpUS.js";
|
|
13
13
|
import { n as createSingleflight } from "../_chunks/singleflight-BeVMMKi0.js";
|
|
14
14
|
import { n as toBracketKey } from "../_chunks/resolve-schema-CBR6Lm4i.js";
|
|
@@ -580,6 +580,55 @@ function csrfGate(req, canonicalPath, csrf, matchRoute) {
|
|
|
580
580
|
return exempt ? null : csrfRejectionResponse(req, result);
|
|
581
581
|
}
|
|
582
582
|
/**
|
|
583
|
+
* The redirect for a safe request whose URL is not canonical, or null to
|
|
584
|
+
* serve it.
|
|
585
|
+
*
|
|
586
|
+
* A GET for `/admin/` used to render with a 200 at that URL. The server
|
|
587
|
+
* rendered with the canonical `/admin` while the browser hydrated with the
|
|
588
|
+
* raw `/admin/`, so anything branching on `usePathname()` hydrated
|
|
589
|
+
* mismatched (TIM-1571). Redirecting makes the address bar — the client's
|
|
590
|
+
* copy of the URL — the canonical one, and leaves one URL per page for
|
|
591
|
+
* caches and crawlers.
|
|
592
|
+
*
|
|
593
|
+
* Only GET and HEAD are redirected. Every other method — an action's POST,
|
|
594
|
+
* a CORS preflight — is served at the canonical path, as before: a
|
|
595
|
+
* redirected POST is re-sent by the browser, which an action must not
|
|
596
|
+
* depend on.
|
|
597
|
+
*
|
|
598
|
+
* Runs inside proxy.ts's `next()`, not before it, so proxy.ts still runs
|
|
599
|
+
* on every request and its headers land on the redirect. A cross-origin
|
|
600
|
+
* `GET /api/items/` needs proxy.ts's CORS headers on the 308 itself:
|
|
601
|
+
* browsers apply the CORS check to a redirect response.
|
|
602
|
+
*
|
|
603
|
+
* A document request gets a relative 308 (never built from `Host`). A page
|
|
604
|
+
* navigation gets the router's soft redirect, decided exactly as for a
|
|
605
|
+
* `redirect()` outcome: an RSC request that does not match a route.ts
|
|
606
|
+
* handler (`buildRedirectResponse`). The framework's `_rsc` cache key is
|
|
607
|
+
* dropped from the soft target; the router appends a fresh one when it
|
|
608
|
+
* fetches the canonical URL.
|
|
609
|
+
*
|
|
610
|
+
* See design/07-routing.md §"Non-Canonical URLs Redirect".
|
|
611
|
+
*/
|
|
612
|
+
function canonicalRedirect(req, canonicalPath, stripTrailingSlash, matchRoute) {
|
|
613
|
+
if (req.method !== "GET" && req.method !== "HEAD") return null;
|
|
614
|
+
const url = new URL(req.url);
|
|
615
|
+
const target = canonicalRequestPath(url.pathname, stripTrailingSlash);
|
|
616
|
+
if (target === url.pathname) return null;
|
|
617
|
+
const navigation = isRscRequest(req) && !isApiRoutePath(canonicalPath, matchRoute);
|
|
618
|
+
const search = navigation ? appVisibleSearch(url).search : url.search;
|
|
619
|
+
return buildRedirectResponse(new RedirectSignal(target + search, 308), new Headers(), navigation);
|
|
620
|
+
}
|
|
621
|
+
/** Whether `path` matches a route.ts handler. A matcher that throws: no. */
|
|
622
|
+
function isApiRoutePath(path, matchRoute) {
|
|
623
|
+
try {
|
|
624
|
+
const match = matchRoute(path);
|
|
625
|
+
return match !== null && isApiRouteChain(match.segments);
|
|
626
|
+
} catch (err) {
|
|
627
|
+
swallow(err, "canonical redirect: route match for route.ts lookup threw");
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
583
632
|
* Build a proxy resolver closure from the declared source. Called exactly
|
|
584
633
|
* once at `createPipeline` setup time, so the hot path sees only the branch
|
|
585
634
|
* that corresponds to this pipeline's configured variant.
|
|
@@ -2027,6 +2076,8 @@ function createPipeline(config) {
|
|
|
2027
2076
|
return csrfRejection;
|
|
2028
2077
|
}
|
|
2029
2078
|
const innerHandler = async () => {
|
|
2079
|
+
const redirect = canonicalRedirect(req, canonicalPath, stripTrailingSlash, config.matchRoute);
|
|
2080
|
+
if (redirect) return redirect;
|
|
2030
2081
|
if (config.dispatchAction) {
|
|
2031
2082
|
const actionResult = await config.dispatchAction(canonicalReq, canonicalPath, pipelineFn);
|
|
2032
2083
|
if (actionResult) return actionResult;
|