vinext 0.1.4 → 0.1.5
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/build/css-url-assets.d.ts +1 -1
- package/dist/build/css-url-assets.js +9 -7
- package/dist/build/prerender.js +2 -1
- package/dist/cache/cache-adapters-virtual.js +1 -1
- package/dist/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +1 -1
- package/dist/entries/app-rsc-entry.js +24 -20
- package/dist/entries/pages-server-entry.js +2 -1
- package/dist/index.js +187 -146
- package/dist/plugins/css-data-url.js +30 -26
- package/dist/plugins/extensionless-dynamic-import.js +27 -24
- package/dist/plugins/import-meta-url.js +21 -15
- package/dist/plugins/instrumentation-client.js +1 -1
- package/dist/plugins/middleware-server-only.js +7 -6
- package/dist/plugins/og-assets.js +48 -46
- package/dist/plugins/optimize-imports.js +9 -3
- package/dist/plugins/remove-console.d.ts +7 -1
- package/dist/plugins/remove-console.js +4 -1
- package/dist/plugins/require-context.js +21 -20
- package/dist/plugins/strip-server-exports.d.ts +7 -1
- package/dist/plugins/strip-server-exports.js +4 -1
- package/dist/server/app-bfcache-identity.d.ts +26 -0
- package/dist/server/app-bfcache-identity.js +127 -0
- package/dist/server/app-browser-entry.js +14 -11
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-state.d.ts +2 -21
- package/dist/server/app-browser-state.js +4 -128
- package/dist/server/app-browser-stream.js +1 -1
- package/dist/server/app-browser-visible-commit.js +3 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-layout-param-observation.js +1 -1
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -1
- package/dist/server/app-page-boundary.js +1 -1
- package/dist/server/app-page-cache-finalizer.d.ts +62 -0
- package/dist/server/app-page-cache-finalizer.js +122 -0
- package/dist/server/app-page-cache-render.d.ts +2 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -53
- package/dist/server/app-page-cache.js +5 -131
- package/dist/server/app-page-dispatch.d.ts +2 -2
- package/dist/server/app-page-dispatch.js +10 -8
- package/dist/server/app-page-probe.js +3 -2
- package/dist/server/app-page-render-observation.js +2 -2
- package/dist/server/app-page-render.d.ts +3 -3
- package/dist/server/app-page-render.js +3 -2
- package/dist/server/app-page-stream.d.ts +2 -9
- package/dist/server/app-page-stream.js +1 -35
- package/dist/server/app-request-context.d.ts +1 -2
- package/dist/server/app-request-context.js +2 -1
- package/dist/server/app-route-handler-dispatch.js +3 -2
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-handler-execution.js +1 -1
- package/dist/server/app-route-handler-response.d.ts +1 -1
- package/dist/server/app-router-entry.js +2 -1
- package/dist/server/app-rsc-handler.js +22 -16
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-server-action-execution.d.ts +1 -1
- package/dist/server/app-server-action-execution.js +5 -4
- package/dist/server/app-ssr-entry.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +11 -9
- package/dist/server/app-ssr-router-instance.d.ts +6 -0
- package/dist/server/app-ssr-router-instance.js +24 -0
- package/dist/server/app-ssr-stream.js +1 -1
- package/dist/server/artifact-compatibility.js +1 -1
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/defer-until-stream-consumed.d.ts +7 -0
- package/dist/server/defer-until-stream-consumed.js +34 -0
- package/dist/server/dev-server.js +1 -1
- package/dist/server/instrumentation.js +1 -1
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +1 -1
- package/dist/server/isr-decision.d.ts +1 -1
- package/dist/server/middleware-matcher.js +8 -6
- package/dist/server/middleware-runtime.js +2 -2
- package/dist/server/open-redirect.d.ts +12 -0
- package/dist/server/open-redirect.js +21 -0
- package/dist/server/pages-page-data.d.ts +1 -1
- package/dist/server/pages-page-response.d.ts +1 -1
- package/dist/server/pages-page-response.js +2 -2
- package/dist/server/prod-server.js +2 -1
- package/dist/server/request-pipeline.d.ts +1 -24
- package/dist/server/request-pipeline.js +1 -33
- package/dist/server/seed-cache.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +106 -0
- package/dist/shims/cache-handler.js +176 -0
- package/dist/shims/cache-request-state.d.ts +47 -0
- package/dist/shims/cache-request-state.js +126 -0
- package/dist/shims/cache-runtime.d.ts +2 -2
- package/dist/shims/cache-runtime.js +3 -14
- package/dist/shims/cache.d.ts +3 -231
- package/dist/shims/cache.js +17 -383
- package/dist/shims/cdn-cache.d.ts +1 -1
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +44 -0
- package/dist/shims/error-boundary.js +10 -8
- package/dist/shims/error.js +2 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/form.js +1 -1
- package/dist/shims/image.js +67 -9
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -0
- package/dist/shims/internal/app-page-props-cache-key.js +16 -0
- package/dist/shims/internal/navigation-untracked.js +2 -1
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/layout-segment-context.js +2 -1
- package/dist/shims/link.js +2 -2
- package/dist/shims/navigation-context-state.d.ts +40 -0
- package/dist/shims/navigation-context-state.js +116 -0
- package/dist/shims/navigation-errors.d.ts +55 -0
- package/dist/shims/navigation-errors.js +110 -0
- package/dist/shims/navigation-server.d.ts +3 -0
- package/dist/shims/navigation-server.js +3 -0
- package/dist/shims/navigation-state.d.ts +1 -2
- package/dist/shims/navigation-state.js +2 -1
- package/dist/shims/navigation.d.ts +3 -291
- package/dist/shims/navigation.js +14 -445
- package/dist/shims/navigation.react-server.d.ts +2 -2
- package/dist/shims/navigation.react-server.js +3 -1
- package/dist/shims/request-state-types.d.ts +3 -3
- package/dist/shims/script.js +1 -1
- package/dist/shims/slot.js +3 -1
- package/dist/shims/unified-request-context.d.ts +2 -2
- package/dist/utils/virtual-module.d.ts +5 -0
- package/dist/utils/virtual-module.js +0 -0
- package/package.json +5 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import "./server-globals.js";
|
|
2
2
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
3
|
-
import { isOpenRedirectShaped } from "./
|
|
4
|
-
import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
|
|
3
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
5
4
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
6
5
|
import "./app-elements.js";
|
|
6
|
+
import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
|
|
7
7
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
8
8
|
import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
9
|
-
import { ServerInsertedHTMLContext,
|
|
9
|
+
import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
|
|
10
|
+
import "../shims/navigation-server.js";
|
|
10
11
|
import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
11
12
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
12
13
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
|
|
@@ -15,14 +16,15 @@ import DefaultGlobalError from "../shims/default-global-error.js";
|
|
|
15
16
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
16
17
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
17
18
|
import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
|
|
18
|
-
import {
|
|
19
|
-
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
20
|
-
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-browser-state.js";
|
|
19
|
+
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-bfcache-identity.js";
|
|
21
20
|
import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
22
21
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
23
|
-
import { deferUntilStreamConsumed } from "./
|
|
22
|
+
import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
|
|
23
|
+
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
24
|
+
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
24
25
|
import { renderBeforeInteractiveInlineScripts } from "./before-interactive-head.js";
|
|
25
26
|
import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
|
|
27
|
+
import { ssrAppRouterInstance } from "./app-ssr-router-instance.js";
|
|
26
28
|
import { Fragment, createElement, use } from "react";
|
|
27
29
|
import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
|
|
28
30
|
import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
|
|
@@ -192,8 +194,8 @@ async function handleSsr(rscStream, navContext, fontData, options) {
|
|
|
192
194
|
return BfcacheIdMapContext ? createElement(BfcacheIdMapContext.Provider, { value: createInitialBfcacheIdMap(elements) }, stateKeyTree) : stateKeyTree;
|
|
193
195
|
}
|
|
194
196
|
const flightRootElement = createElement(VinextFlightRoot);
|
|
195
|
-
const root = AppRouterContext ? createElement(AppRouterContext.Provider, { value:
|
|
196
|
-
const ssrTree = ServerInsertedHTMLContext ? createElement(ServerInsertedHTMLContext.Provider, { value:
|
|
197
|
+
const root = AppRouterContext ? createElement(AppRouterContext.Provider, { value: ssrAppRouterInstance }, flightRootElement) : flightRootElement;
|
|
198
|
+
const ssrTree = ServerInsertedHTMLContext ? createElement(ServerInsertedHTMLContext.Provider, { value: registerServerInsertedHTMLCallback }, root) : root;
|
|
197
199
|
const beforeInteractiveInlineScripts = [];
|
|
198
200
|
const registerBeforeInteractiveInlineScript = (script) => {
|
|
199
201
|
beforeInteractiveInlineScripts.push(script);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { assertSafeNavigationUrl } from "../shims/url-safety.js";
|
|
2
|
+
import "./app-bfcache-id.js";
|
|
3
|
+
//#region src/server/app-ssr-router-instance.ts
|
|
4
|
+
function validateNavigationHref(href) {
|
|
5
|
+
assertSafeNavigationUrl(href);
|
|
6
|
+
}
|
|
7
|
+
const ssrAppRouterInstance = {
|
|
8
|
+
bfcacheId: "0",
|
|
9
|
+
back() {},
|
|
10
|
+
forward() {},
|
|
11
|
+
refresh() {},
|
|
12
|
+
push(href, _options) {
|
|
13
|
+
validateNavigationHref(href);
|
|
14
|
+
},
|
|
15
|
+
replace(href, _options) {
|
|
16
|
+
validateNavigationHref(href);
|
|
17
|
+
},
|
|
18
|
+
prefetch(href) {
|
|
19
|
+
validateNavigationHref(href);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
if (process.env.__NEXT_GESTURE_TRANSITION) ssrAppRouterInstance.experimental_gesturePush = validateNavigationHref;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { ssrAppRouterInstance };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
|
|
2
1
|
import { createInlineScriptTag, escapeHtmlAttr, htmlTokenListContains, safeJsonStringify } from "./html.js";
|
|
3
2
|
import { bytesToBase64, concatUint8Arrays } from "./app-rsc-embedded-chunks.js";
|
|
3
|
+
import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
|
|
4
4
|
//#region src/server/app-ssr-stream.ts
|
|
5
5
|
const NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION)})]`;
|
|
6
6
|
function navigationRuntimeRscBootstrapExpression() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fnv1a64 } from "../utils/hash.js";
|
|
2
1
|
import { isUnknownRecord } from "../utils/record.js";
|
|
2
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
3
3
|
//#region src/server/artifact-compatibility.ts
|
|
4
4
|
const ARTIFACT_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
5
5
|
const APP_ELEMENTS_SCHEMA_VERSION = 1;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fnv1a64 } from "../utils/hash.js";
|
|
2
1
|
import { isUnknownRecord } from "../utils/record.js";
|
|
2
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
3
3
|
import { parseArtifactCompatibilityEnvelope } from "./artifact-compatibility.js";
|
|
4
4
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
5
5
|
import { isNonNegativeSafeInteger } from "../utils/number.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/server/defer-until-stream-consumed.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Defers cleanup until the downstream consumer drains or cancels the stream.
|
|
4
|
+
*/
|
|
5
|
+
declare function deferUntilStreamConsumed(stream: ReadableStream<Uint8Array>, onFlush: () => void): ReadableStream<Uint8Array>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { deferUntilStreamConsumed };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/server/defer-until-stream-consumed.ts
|
|
2
|
+
/**
|
|
3
|
+
* Defers cleanup until the downstream consumer drains or cancels the stream.
|
|
4
|
+
*/
|
|
5
|
+
function deferUntilStreamConsumed(stream, onFlush) {
|
|
6
|
+
let called = false;
|
|
7
|
+
const once = () => {
|
|
8
|
+
if (!called) {
|
|
9
|
+
called = true;
|
|
10
|
+
onFlush();
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const cleanup = new TransformStream({ flush() {
|
|
14
|
+
once();
|
|
15
|
+
} });
|
|
16
|
+
const reader = stream.pipeThrough(cleanup).getReader();
|
|
17
|
+
return new ReadableStream({
|
|
18
|
+
pull(controller) {
|
|
19
|
+
return reader.read().then(({ done, value }) => {
|
|
20
|
+
if (done) controller.close();
|
|
21
|
+
else controller.enqueue(value);
|
|
22
|
+
}, (error) => {
|
|
23
|
+
once();
|
|
24
|
+
controller.error(error);
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
cancel(reason) {
|
|
28
|
+
once();
|
|
29
|
+
return reader.cancel(reason);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { deferUntilStreamConsumed };
|
|
@@ -7,10 +7,10 @@ import { normalizeStaticPathname } from "../routing/route-pattern.js";
|
|
|
7
7
|
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
8
8
|
import { buildCacheStateHeaders } from "./cache-headers.js";
|
|
9
9
|
import { isUnknownRecord } from "../utils/record.js";
|
|
10
|
-
import { _runWithCacheState } from "../shims/cache.js";
|
|
11
10
|
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
12
11
|
import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
|
|
13
12
|
import { PRERENDER_REVALIDATE_HEADER, buildPagesCacheValue, getRevalidateDuration, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
|
|
13
|
+
import { _runWithCacheState } from "../shims/cache-request-state.js";
|
|
14
14
|
import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
|
|
15
15
|
import { runWithPrivateCache } from "../shims/cache-runtime.js";
|
|
16
16
|
import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
|
|
@@ -51,7 +51,7 @@ async function importModule(runner, id) {
|
|
|
51
51
|
const INSTRUMENTATION_LOCATIONS = ["", "src/"];
|
|
52
52
|
function findInstrumentationHookFile(root, basename, fileMatcher) {
|
|
53
53
|
for (const dir of INSTRUMENTATION_LOCATIONS) for (const ext of fileMatcher.dottedExtensions) {
|
|
54
|
-
const fullPath = path.join(root, dir, `${basename}${ext}`);
|
|
54
|
+
const fullPath = path.posix.join(root, dir, `${basename}${ext}`);
|
|
55
55
|
if (fs.existsSync(fullPath)) return fullPath;
|
|
56
56
|
}
|
|
57
57
|
return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RenderObservation } from "./cache-proof.js";
|
|
2
|
-
import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache.js";
|
|
2
|
+
import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
|
|
3
3
|
import { OnRequestErrorContext } from "./instrumentation.js";
|
|
4
4
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
5
5
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
package/dist/server/isr-cache.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
2
|
import { reportRequestError } from "./instrumentation.js";
|
|
3
|
-
import { fnv1a64 } from "../utils/hash.js";
|
|
4
3
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
4
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
5
5
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
6
6
|
import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./app-rsc-render-mode.js";
|
|
7
7
|
import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
|
|
@@ -48,17 +48,19 @@ function stripLocalePrefix(pathname, i18nConfig) {
|
|
|
48
48
|
const segments = pathname.split("/");
|
|
49
49
|
const firstSegment = segments[1];
|
|
50
50
|
if (!firstSegment || !i18nConfig.locales.includes(firstSegment)) return null;
|
|
51
|
-
return
|
|
51
|
+
return "/" + segments.slice(2).join("/");
|
|
52
52
|
}
|
|
53
53
|
function matchPattern(pathname, pattern) {
|
|
54
|
-
|
|
54
|
+
const normalizedPattern = /[\\():*+?]/.test(pattern) ? pattern : removeTrailingSlash(pattern);
|
|
55
|
+
let cached = _mwPatternCache.get(normalizedPattern);
|
|
55
56
|
if (cached === void 0) {
|
|
56
|
-
cached = compileMatcherPattern(
|
|
57
|
-
_mwPatternCache.set(
|
|
57
|
+
cached = compileMatcherPattern(normalizedPattern);
|
|
58
|
+
_mwPatternCache.set(normalizedPattern, cached);
|
|
58
59
|
}
|
|
59
60
|
if (cached === UNSAFE_MATCHER_PATTERN) return true;
|
|
60
|
-
if (cached === null) return pathname ===
|
|
61
|
-
|
|
61
|
+
if (cached === null) return removeTrailingSlash(pathname) === normalizedPattern;
|
|
62
|
+
if (cached.test(pathname)) return true;
|
|
63
|
+
return pathname.endsWith("/") && cached.test(removeTrailingSlash(pathname));
|
|
62
64
|
}
|
|
63
65
|
function extractConstraint(str, re) {
|
|
64
66
|
if (str[re.lastIndex] !== "(") return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
|
-
import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
2
|
+
import { addBasePathToPathname, hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
3
3
|
import "./server-globals.js";
|
|
4
4
|
import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
|
|
5
5
|
import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
|
|
@@ -130,7 +130,7 @@ async function executeMiddleware(options) {
|
|
|
130
130
|
const matchPathname = options.basePath ? stripBasePath(normalizedPathname, options.basePath) : normalizedPathname;
|
|
131
131
|
if (!matchesMiddleware(matchPathname, middlewareMatcher(options.module), options.request, options.i18nConfig)) return { continue: true };
|
|
132
132
|
const nextRequest = createNextRequest(options.request, normalizedPathname, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath);
|
|
133
|
-
const fetchEvent = new NextFetchEvent({ page: matchPathname });
|
|
133
|
+
const fetchEvent = new NextFetchEvent({ page: removeTrailingSlash(matchPathname) });
|
|
134
134
|
let response;
|
|
135
135
|
try {
|
|
136
136
|
response = await middlewareFn(nextRequest, fetchEvent);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/server/open-redirect.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if a request pathname looks like a protocol-relative open
|
|
4
|
+
* redirect, in either literal or percent-encoded form.
|
|
5
|
+
*
|
|
6
|
+
* A pathname is considered "open redirect shaped" when its first segment,
|
|
7
|
+
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
8
|
+
* to resolve a `Location` containing the pathname as protocol-relative.
|
|
9
|
+
*/
|
|
10
|
+
declare function isOpenRedirectShaped(rawPathname: string): boolean;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { isOpenRedirectShaped };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/server/open-redirect.ts
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if a request pathname looks like a protocol-relative open
|
|
4
|
+
* redirect, in either literal or percent-encoded form.
|
|
5
|
+
*
|
|
6
|
+
* A pathname is considered "open redirect shaped" when its first segment,
|
|
7
|
+
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
8
|
+
* to resolve a `Location` containing the pathname as protocol-relative.
|
|
9
|
+
*/
|
|
10
|
+
function isOpenRedirectShaped(rawPathname) {
|
|
11
|
+
if (!rawPathname.startsWith("/")) return false;
|
|
12
|
+
const afterSlash = rawPathname.slice(1);
|
|
13
|
+
if (afterSlash.startsWith("/") || afterSlash.startsWith("\\")) return true;
|
|
14
|
+
if (afterSlash.length >= 3 && afterSlash[0] === "%") {
|
|
15
|
+
const encoded = afterSlash.slice(0, 3).toLowerCase();
|
|
16
|
+
if (encoded === "%5c" || encoded === "%2f") return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { isOpenRedirectShaped };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Route } from "../routing/pages-router.js";
|
|
2
2
|
import { VinextNextData } from "../client/vinext-next-data.js";
|
|
3
|
-
import { CachedPagesValue } from "../shims/cache.js";
|
|
3
|
+
import { CachedPagesValue } from "../shims/cache-handler.js";
|
|
4
4
|
import { ISRCacheEntry } from "./isr-cache.js";
|
|
5
5
|
import { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras } from "./pages-page-response.js";
|
|
6
6
|
import { ReactNode } from "react";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VinextNextData } from "../client/vinext-next-data.js";
|
|
2
|
-
import { CachedPagesValue } from "../shims/cache.js";
|
|
2
|
+
import { CachedPagesValue } from "../shims/cache-handler.js";
|
|
3
3
|
import { RenderPageEnhancers } from "./pages-document-initial-props.js";
|
|
4
4
|
import { ComponentType, ReactNode } from "react";
|
|
5
5
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
2
|
import { reportRequestError } from "./instrumentation.js";
|
|
3
3
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
4
|
-
import { fnv1a52 } from "../utils/hash.js";
|
|
5
|
-
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
6
4
|
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
|
|
7
5
|
import { buildMissIsrCacheControl } from "./isr-decision.js";
|
|
6
|
+
import { fnv1a52 } from "../utils/hash.js";
|
|
7
|
+
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
8
8
|
import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
9
9
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
10
10
|
import { createNonceAttribute, escapeHtmlAttr } from "./html.js";
|
|
@@ -3,7 +3,8 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
3
3
|
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
4
4
|
import { normalizePath } from "./normalize-path.js";
|
|
5
5
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
6
|
-
import {
|
|
6
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
7
|
+
import { filterInternalHeaders } from "./request-pipeline.js";
|
|
7
8
|
import { isUnknownRecord } from "../utils/record.js";
|
|
8
9
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
9
10
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextHeader } from "../config/next-config.js";
|
|
2
2
|
import { BasePathMatchState, RequestContext } from "../config/config-matchers.js";
|
|
3
3
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
4
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
5
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
5
6
|
|
|
6
7
|
//#region src/server/request-pipeline.d.ts
|
|
@@ -42,30 +43,6 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
42
43
|
* @returns A 404 Response if the path is protocol-relative, or null to continue
|
|
43
44
|
*/
|
|
44
45
|
declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
|
|
45
|
-
/**
|
|
46
|
-
* Returns true if a request pathname looks like a protocol-relative open
|
|
47
|
-
* redirect, in either literal or percent-encoded form.
|
|
48
|
-
*
|
|
49
|
-
* Exported for call sites that need to replicate the guard inline (Pages
|
|
50
|
-
* Router worker codegen, Node production server) and for defense-in-depth
|
|
51
|
-
* checks inside redirect emitters.
|
|
52
|
-
*
|
|
53
|
-
* A pathname is considered "open redirect shaped" when its first segment,
|
|
54
|
-
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
55
|
-
* to resolve a `Location` containing the pathname as protocol-relative:
|
|
56
|
-
*
|
|
57
|
-
* - literal `//evil.com`
|
|
58
|
-
* - literal `/\evil.com` (browsers normalize `\` to `/`)
|
|
59
|
-
* - encoded `/%5Cevil.com` (`%5C` decodes to `\` in Location)
|
|
60
|
-
* - encoded `/%2F/evil.com` (`%2F` decodes to `/` → `//`)
|
|
61
|
-
* - mixed `/%5C%2F`, `/%5C%5C` (and other combinations)
|
|
62
|
-
*
|
|
63
|
-
* We explicitly do not require a valid percent sequence elsewhere in the
|
|
64
|
-
* pathname — we only examine the leading bytes (up to the second real or
|
|
65
|
-
* encoded delimiter) so malformed suffixes can still reach the normal
|
|
66
|
-
* "400 Bad Request" decode path instead of being masked as "404".
|
|
67
|
-
*/
|
|
68
|
-
declare function isOpenRedirectShaped(rawPathname: string): boolean;
|
|
69
46
|
type HeaderRecord = Record<string, string | string[]>;
|
|
70
47
|
type ApplyConfigHeadersOptions = {
|
|
71
48
|
configHeaders: NextHeader[];
|
|
@@ -2,6 +2,7 @@ import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-p
|
|
|
2
2
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
3
3
|
import { matchHeaders } from "../config/config-matchers.js";
|
|
4
4
|
import { forbiddenResponse, notFoundResponse } from "./http-error-responses.js";
|
|
5
|
+
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
5
6
|
//#region src/server/request-pipeline.ts
|
|
6
7
|
/**
|
|
7
8
|
* Shared request pipeline utilities.
|
|
@@ -44,39 +45,6 @@ function guardProtocolRelativeUrl(rawPathname) {
|
|
|
44
45
|
if (isOpenRedirectShaped(rawPathname)) return notFoundResponse();
|
|
45
46
|
return null;
|
|
46
47
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Returns true if a request pathname looks like a protocol-relative open
|
|
49
|
-
* redirect, in either literal or percent-encoded form.
|
|
50
|
-
*
|
|
51
|
-
* Exported for call sites that need to replicate the guard inline (Pages
|
|
52
|
-
* Router worker codegen, Node production server) and for defense-in-depth
|
|
53
|
-
* checks inside redirect emitters.
|
|
54
|
-
*
|
|
55
|
-
* A pathname is considered "open redirect shaped" when its first segment,
|
|
56
|
-
* after decoding backslashes and encoded delimiters, would cause a browser
|
|
57
|
-
* to resolve a `Location` containing the pathname as protocol-relative:
|
|
58
|
-
*
|
|
59
|
-
* - literal `//evil.com`
|
|
60
|
-
* - literal `/\evil.com` (browsers normalize `\` to `/`)
|
|
61
|
-
* - encoded `/%5Cevil.com` (`%5C` decodes to `\` in Location)
|
|
62
|
-
* - encoded `/%2F/evil.com` (`%2F` decodes to `/` → `//`)
|
|
63
|
-
* - mixed `/%5C%2F`, `/%5C%5C` (and other combinations)
|
|
64
|
-
*
|
|
65
|
-
* We explicitly do not require a valid percent sequence elsewhere in the
|
|
66
|
-
* pathname — we only examine the leading bytes (up to the second real or
|
|
67
|
-
* encoded delimiter) so malformed suffixes can still reach the normal
|
|
68
|
-
* "400 Bad Request" decode path instead of being masked as "404".
|
|
69
|
-
*/
|
|
70
|
-
function isOpenRedirectShaped(rawPathname) {
|
|
71
|
-
if (!rawPathname.startsWith("/")) return false;
|
|
72
|
-
const afterSlash = rawPathname.slice(1);
|
|
73
|
-
if (afterSlash.startsWith("/") || afterSlash.startsWith("\\")) return true;
|
|
74
|
-
if (afterSlash.length >= 3 && afterSlash[0] === "%") {
|
|
75
|
-
const encoded = afterSlash.slice(0, 3).toLowerCase();
|
|
76
|
-
if (encoded === "%5c" || encoded === "%2f") return true;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
48
|
const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
|
|
81
49
|
function isWellKnownPathname(pathname) {
|
|
82
50
|
return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { RenderObservation } from "../server/cache-proof.js";
|
|
2
|
+
|
|
3
|
+
//#region src/shims/cache-handler.d.ts
|
|
4
|
+
type CacheHandlerValue = {
|
|
5
|
+
lastModified: number;
|
|
6
|
+
age?: number;
|
|
7
|
+
cacheState?: string;
|
|
8
|
+
cacheControl?: CacheControlMetadata;
|
|
9
|
+
value: IncrementalCacheValue | null;
|
|
10
|
+
};
|
|
11
|
+
type CacheControlMetadata = {
|
|
12
|
+
revalidate: number;
|
|
13
|
+
expire?: number;
|
|
14
|
+
};
|
|
15
|
+
type IncrementalCacheValue = CachedFetchValue | CachedAppPageValue | CachedPagesValue | CachedRouteValue | CachedRedirectValue | CachedImageValue;
|
|
16
|
+
type CachedFetchValue = {
|
|
17
|
+
kind: "FETCH";
|
|
18
|
+
data: {
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
body: string;
|
|
21
|
+
url: string;
|
|
22
|
+
status?: number;
|
|
23
|
+
};
|
|
24
|
+
tags?: string[];
|
|
25
|
+
revalidate: number | false;
|
|
26
|
+
};
|
|
27
|
+
type CachedAppPageValue = {
|
|
28
|
+
kind: "APP_PAGE";
|
|
29
|
+
html: string;
|
|
30
|
+
rscData: ArrayBuffer | undefined;
|
|
31
|
+
headers: Record<string, string | string[]> | undefined;
|
|
32
|
+
postponed: string | undefined;
|
|
33
|
+
renderObservation?: RenderObservation;
|
|
34
|
+
status: number | undefined;
|
|
35
|
+
};
|
|
36
|
+
type CachedPagesValue = {
|
|
37
|
+
kind: "PAGES";
|
|
38
|
+
html: string;
|
|
39
|
+
pageData: object;
|
|
40
|
+
generatedFromDataRequest?: boolean;
|
|
41
|
+
headers: Record<string, string | string[]> | undefined;
|
|
42
|
+
status: number | undefined;
|
|
43
|
+
};
|
|
44
|
+
type CachedRouteValue = {
|
|
45
|
+
kind: "APP_ROUTE";
|
|
46
|
+
body: ArrayBuffer;
|
|
47
|
+
status: number;
|
|
48
|
+
headers: Record<string, string | string[]>;
|
|
49
|
+
};
|
|
50
|
+
type CachedRedirectValue = {
|
|
51
|
+
kind: "REDIRECT";
|
|
52
|
+
props: object;
|
|
53
|
+
};
|
|
54
|
+
type CachedImageValue = {
|
|
55
|
+
kind: "IMAGE";
|
|
56
|
+
etag: string;
|
|
57
|
+
buffer: ArrayBuffer;
|
|
58
|
+
extension: string;
|
|
59
|
+
revalidate?: number;
|
|
60
|
+
};
|
|
61
|
+
type CacheHandlerContext = {
|
|
62
|
+
dev?: boolean;
|
|
63
|
+
maxMemoryCacheSize?: number;
|
|
64
|
+
revalidatedTags?: string[];
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
};
|
|
67
|
+
type CacheHandler = {
|
|
68
|
+
get(key: string, ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
69
|
+
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
70
|
+
revalidateTag(tags: string | string[], durations?: {
|
|
71
|
+
expire?: number;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
resetRequestCache?(): void;
|
|
74
|
+
};
|
|
75
|
+
declare class NoOpCacheHandler implements CacheHandler {
|
|
76
|
+
get(_key: string, _ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
77
|
+
set(_key: string, _data: IncrementalCacheValue | null, _ctx?: Record<string, unknown>): Promise<void>;
|
|
78
|
+
revalidateTag(_tags: string | string[], _durations?: {
|
|
79
|
+
expire?: number;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
type MemoryCacheHandlerOptions = Pick<CacheHandlerContext, "maxMemoryCacheSize"> & {
|
|
83
|
+
cacheMaxMemorySize?: number;
|
|
84
|
+
};
|
|
85
|
+
declare class MemoryCacheHandler implements CacheHandler {
|
|
86
|
+
private store;
|
|
87
|
+
private tagRevalidatedAt;
|
|
88
|
+
private readonly maxMemoryCacheSize;
|
|
89
|
+
private currentMemoryCacheSize;
|
|
90
|
+
constructor(options?: number | MemoryCacheHandlerOptions);
|
|
91
|
+
private estimateEntrySize;
|
|
92
|
+
private deleteEntry;
|
|
93
|
+
private touchEntry;
|
|
94
|
+
private evictLeastRecentlyUsed;
|
|
95
|
+
get(key: string, ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
|
|
96
|
+
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
97
|
+
revalidateTag(tags: string | string[]): Promise<void>;
|
|
98
|
+
resetRequestCache(): void;
|
|
99
|
+
}
|
|
100
|
+
declare function configureMemoryCacheHandler(options?: MemoryCacheHandlerOptions): void;
|
|
101
|
+
declare function setDataCacheHandler(handler: CacheHandler): void;
|
|
102
|
+
declare function getDataCacheHandler(): CacheHandler;
|
|
103
|
+
declare function setCacheHandler(handler: CacheHandler): void;
|
|
104
|
+
declare function getCacheHandler(): CacheHandler;
|
|
105
|
+
//#endregion
|
|
106
|
+
export { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler };
|