vinext 1.4.1 → 1.4.3
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/README.md +5 -2
- package/dist/build/action-owner-manifest.js +1 -0
- package/dist/build/prerender-paths.d.ts +11 -1
- package/dist/build/prerender-paths.js +36 -10
- package/dist/check.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/dev-error-overlay.d.ts +2 -1
- package/dist/client/dev-error-overlay.js +8 -3
- package/dist/config/config-matchers.js +8 -4
- package/dist/config/destination-params.js +5 -2
- package/dist/config/next-config.d.ts +25 -1
- package/dist/config/next-config.js +31 -1
- package/dist/entries/app-rsc-entry.js +65 -30
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/pages-server-entry.js +19 -4
- package/dist/index.js +37 -37
- package/dist/init-cloudflare.js +18 -2
- package/dist/init-platform.js +2 -2
- package/dist/init.js +42 -5
- package/dist/plugins/css-target.d.ts +18 -0
- package/dist/plugins/css-target.js +93 -0
- package/dist/routing/app-route-graph.d.ts +10 -0
- package/dist/routing/app-route-graph.js +15 -1
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +10 -8
- package/dist/server/app-action-forwarding.js +3 -1
- package/dist/server/app-browser-entry.js +37 -41
- package/dist/server/app-browser-error.d.ts +1 -1
- package/dist/server/app-browser-error.js +13 -4
- package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
- package/dist/server/app-browser-supplemental-refresh.js +36 -4
- package/dist/server/app-elements.d.ts +3 -0
- package/dist/server/app-elements.js +14 -1
- package/dist/server/app-fallback-renderer.js +2 -2
- package/dist/server/app-middleware.d.ts +2 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.d.ts +12 -1
- package/dist/server/app-optimistic-routing.js +38 -16
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -1
- package/dist/server/app-page-dispatch.js +78 -31
- package/dist/server/app-page-element-builder.d.ts +11 -20
- package/dist/server/app-page-element-builder.js +39 -50
- package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
- package/dist/server/app-page-intercept-segment-config.js +90 -0
- package/dist/server/app-page-navigation-params.d.ts +23 -0
- package/dist/server/app-page-navigation-params.js +49 -0
- package/dist/server/app-page-render.d.ts +5 -2
- package/dist/server/app-page-render.js +17 -3
- package/dist/server/app-page-request.d.ts +24 -2
- package/dist/server/app-page-request.js +10 -10
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +19 -13
- package/dist/server/app-page-segment-plan.js +9 -1
- package/dist/server/app-page-segment-state.d.ts +9 -0
- package/dist/server/app-page-segment-state.js +54 -1
- package/dist/server/app-pages-bridge.d.ts +6 -0
- package/dist/server/app-pages-bridge.js +8 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -3
- package/dist/server/app-response-stage-entry.js +1 -0
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.js +2 -3
- package/dist/server/app-rsc-cache-busting.d.ts +8 -0
- package/dist/server/app-rsc-cache-busting.js +13 -1
- package/dist/server/app-rsc-combined-handler.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +14 -1
- package/dist/server/app-rsc-handler.js +111 -62
- package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
- package/dist/server/app-rsc-response-finalizer.js +61 -7
- package/dist/server/app-rsc-route-matching.d.ts +14 -0
- package/dist/server/app-rsc-route-matching.js +42 -18
- package/dist/server/app-server-action-execution.js +16 -10
- package/dist/server/app-ssr-entry.js +8 -0
- package/dist/server/cache-control.d.ts +2 -0
- package/dist/server/cache-control.js +3 -1
- package/dist/server/config-headers.d.ts +7 -0
- package/dist/server/config-headers.js +20 -1
- package/dist/server/image-animation.d.ts +9 -0
- package/dist/server/image-animation.js +118 -0
- package/dist/server/image-local-patterns.d.ts +21 -0
- package/dist/server/image-local-patterns.js +59 -0
- package/dist/server/image-optimization.d.ts +48 -27
- package/dist/server/image-optimization.js +316 -22
- package/dist/server/image-source-reader.d.ts +34 -0
- package/dist/server/image-source-reader.js +126 -0
- package/dist/server/metadata-route-response.d.ts +6 -1
- package/dist/server/metadata-route-response.js +46 -10
- package/dist/server/middleware-matcher.d.ts +1 -0
- package/dist/server/middleware-matcher.js +1 -1
- package/dist/server/middleware-runtime.d.ts +5 -0
- package/dist/server/middleware-runtime.js +48 -15
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/nitro-public-files-plugin.js +20 -8
- package/dist/server/nitro-request-target-plugin.d.ts +17 -0
- package/dist/server/nitro-request-target-plugin.js +22 -0
- package/dist/server/open-redirect.d.ts +2 -1
- package/dist/server/open-redirect.js +2 -1
- package/dist/server/pages-client-assets.d.ts +1 -0
- package/dist/server/pages-data-route.d.ts +5 -1
- package/dist/server/pages-data-route.js +14 -6
- package/dist/server/pages-i18n.d.ts +7 -0
- package/dist/server/pages-i18n.js +14 -1
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-handler.js +4 -4
- package/dist/server/pages-request-pipeline.d.ts +9 -0
- package/dist/server/pages-request-pipeline.js +35 -15
- package/dist/server/pages-request-stage-entry.js +8 -2
- package/dist/server/pages-router-entry.js +3 -0
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +64 -62
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +11 -1
- package/dist/server/response-stage-cacheability.d.ts +7 -0
- package/dist/server/response-stage-cacheability.js +6 -5
- package/dist/server/srvx-middleware-origin.d.ts +13 -0
- package/dist/server/srvx-middleware-origin.js +19 -0
- package/dist/server/srvx-request-target.d.ts +12 -0
- package/dist/server/srvx-request-target.js +20 -0
- package/dist/server/static-file-cache.js +10 -22
- package/dist/server/worker-image-optimization.d.ts +10 -3
- package/dist/server/worker-image-optimization.js +43 -9
- package/dist/server/worker-revalidation-context.js +1 -0
- package/dist/server/worker-utils.d.ts +8 -0
- package/dist/server/worker-utils.js +19 -2
- package/dist/shims/error-boundary.d.ts +6 -2
- package/dist/shims/error-boundary.js +4 -2
- package/dist/shims/image-props.d.ts +6 -0
- package/dist/shims/image-props.js +39 -3
- package/dist/shims/image.js +3 -3
- package/dist/shims/request-context.d.ts +3 -1
- package/dist/shims/slot.d.ts +3 -0
- package/dist/shims/slot.js +67 -9
- package/dist/shims/url-utils.js +6 -1
- package/dist/utils/base-path.d.ts +7 -0
- package/dist/utils/base-path.js +11 -1
- package/dist/utils/client-entry-manifest.d.ts +5 -0
- package/dist/utils/client-entry-manifest.js +1 -0
- package/dist/utils/client-runtime-metadata.d.ts +5 -0
- package/dist/utils/client-runtime-metadata.js +33 -7
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/server/image-local-patterns.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `images.localPatterns` matching for the image optimization endpoint, kept
|
|
4
|
+
* apart from the remote-pattern shim so the request path doesn't load its IP
|
|
5
|
+
* parsing.
|
|
6
|
+
*/
|
|
7
|
+
export type LocalPattern = {
|
|
8
|
+
pathname?: string;
|
|
9
|
+
search?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Check whether a local URL matches a single local pattern.
|
|
13
|
+
* Follows the same semantics as Next.js's matchLocalPattern().
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchLocalPattern(pattern: LocalPattern, url: URL): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Check whether a local URL matches any configured local pattern. Like
|
|
18
|
+
* Next.js's hasLocalMatch(), an unset list allows every local URL.
|
|
19
|
+
*/
|
|
20
|
+
export declare function hasLocalMatch(localPatterns: LocalPattern[] | undefined, url: URL): boolean;
|
|
21
|
+
//#endregion
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/server/image-local-patterns.ts
|
|
2
|
+
const localPathnameRegexCache = /* @__PURE__ */ new Map();
|
|
3
|
+
/**
|
|
4
|
+
* Convert a local pathname glob to a RegExp, reading `*` and `**` like Next.js's
|
|
5
|
+
* picomatch (`makeRe(pathname, { dot: true })`):
|
|
6
|
+
* - a `**` segment matches zero or more whole segments, but needs its own
|
|
7
|
+
* separator right after a leading `/`, and at the end after a segment
|
|
8
|
+
* that ends in `*`;
|
|
9
|
+
* - any other `*` run matches characters within one segment;
|
|
10
|
+
* - a last segment ending in `*` also matches one trailing slash, and a
|
|
11
|
+
* whole last `*` matches one character or more (that slash included).
|
|
12
|
+
* Other picomatch syntax (braces, `?`, classes) is matched literally.
|
|
13
|
+
*/
|
|
14
|
+
function localPathnameGlobToRegex(pattern) {
|
|
15
|
+
const cached = localPathnameRegexCache.get(pattern);
|
|
16
|
+
if (cached !== void 0) return cached;
|
|
17
|
+
const segments = pattern.split("/").filter((segment, i, all) => !(segment === "**" && all[i - 1] === "**"));
|
|
18
|
+
let regexStr = "^";
|
|
19
|
+
for (let i = 0; i < segments.length; i++) {
|
|
20
|
+
const segment = segments[i];
|
|
21
|
+
const previous = segments[i - 1];
|
|
22
|
+
const isLast = i === segments.length - 1;
|
|
23
|
+
if (segment === "**") {
|
|
24
|
+
if (i === 0) regexStr += isLast ? ".*" : "(?:.*/)?";
|
|
25
|
+
else if (previous === "" || isLast && previous.endsWith("*")) regexStr += "/.*";
|
|
26
|
+
else regexStr += "(?:/.*)?";
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (i > 0 && !(i === 1 && previous === "**")) regexStr += "/";
|
|
30
|
+
if (segment === "*" && isLast) {
|
|
31
|
+
regexStr += "(?:[^/]+/?|/)";
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
regexStr += segment.split(/\*+/).map((literal) => literal.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*");
|
|
35
|
+
if (isLast && segment.endsWith("*")) regexStr += "/?";
|
|
36
|
+
}
|
|
37
|
+
regexStr += "$";
|
|
38
|
+
const re = new RegExp(regexStr);
|
|
39
|
+
localPathnameRegexCache.set(pattern, re);
|
|
40
|
+
return re;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check whether a local URL matches a single local pattern.
|
|
44
|
+
* Follows the same semantics as Next.js's matchLocalPattern().
|
|
45
|
+
*/
|
|
46
|
+
function matchLocalPattern(pattern, url) {
|
|
47
|
+
if (pattern.search !== void 0 && pattern.search !== url.search) return false;
|
|
48
|
+
return localPathnameGlobToRegex(pattern.pathname ?? "**").test(url.pathname);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check whether a local URL matches any configured local pattern. Like
|
|
52
|
+
* Next.js's hasLocalMatch(), an unset list allows every local URL.
|
|
53
|
+
*/
|
|
54
|
+
function hasLocalMatch(localPatterns, url) {
|
|
55
|
+
if (!localPatterns) return true;
|
|
56
|
+
return localPatterns.some((p) => matchLocalPattern(p, url));
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { hasLocalMatch, matchLocalPattern };
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
+
import { LocalPattern } from "./image-local-patterns.js";
|
|
1
2
|
//#region src/server/image-optimization.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Image optimization request handler.
|
|
4
|
-
*
|
|
5
|
-
* Handles `/_next/image?url=...&w=...&q=...` requests. In production
|
|
6
|
-
* on Cloudflare Workers, uses the Images binding (`env.IMAGES`) to
|
|
7
|
-
* resize and transcode on the fly. On other runtimes (Node.js dev/prod
|
|
8
|
-
* server), serves the original file as a passthrough with appropriate
|
|
9
|
-
* Cache-Control headers.
|
|
10
|
-
*
|
|
11
|
-
* Format negotiation: inspects the `Accept` header and serves AVIF, WebP,
|
|
12
|
-
* or JPEG depending on client support.
|
|
13
|
-
*
|
|
14
|
-
* Security: All image responses include Content-Security-Policy and
|
|
15
|
-
* X-Content-Type-Options headers to prevent XSS via SVG or Content-Type
|
|
16
|
-
* spoofing. SVG content is blocked by default (following Next.js behavior).
|
|
17
|
-
* When `dangerouslyAllowSVG` is enabled in next.config.js, SVGs are served
|
|
18
|
-
* as-is (no transformation) with security headers applied.
|
|
19
|
-
*/
|
|
20
3
|
/** The pathname that triggers image optimization (matches Next.js). */
|
|
21
4
|
export declare const IMAGE_OPTIMIZATION_PATH = "/_next/image";
|
|
22
5
|
/**
|
|
@@ -49,11 +32,22 @@ export type ImageConfig = {
|
|
|
49
32
|
/** Allowed fixed-image widths. Defaults to Next.js image sizes. */
|
|
50
33
|
imageSizes?: number[];
|
|
51
34
|
/**
|
|
52
|
-
* Allowed output qualities. When
|
|
53
|
-
*
|
|
54
|
-
*
|
|
35
|
+
* Allowed output qualities. When set, only the listed qualities are
|
|
36
|
+
* accepted, as in Next.js. When unset, any quality from 1-100 is accepted.
|
|
37
|
+
*
|
|
38
|
+
* Intentional divergence from Next.js 16, which defaults this to `[75]`:
|
|
39
|
+
* vinext keeps the Next.js 14/15 behaviour for an unset list so apps that
|
|
40
|
+
* adopt vinext from those versions and pass `quality={N}` keep working
|
|
41
|
+
* instead of getting 400s. Apps opt into Next.js 16's strict list by
|
|
42
|
+
* configuring `images.qualities`.
|
|
55
43
|
*/
|
|
56
44
|
qualities?: number[];
|
|
45
|
+
/**
|
|
46
|
+
* Local sources the endpoint may serve, resolved like Next.js: an app list
|
|
47
|
+
* gains the static-import pattern, and an unset list becomes
|
|
48
|
+
* DEFAULT_LOCAL_PATTERNS when checked.
|
|
49
|
+
*/
|
|
50
|
+
localPatterns?: LocalPattern[];
|
|
57
51
|
/** Allow SVG through the image optimization endpoint. Default: false. */
|
|
58
52
|
dangerouslyAllowSVG?: boolean;
|
|
59
53
|
/**
|
|
@@ -70,6 +64,13 @@ export type ImageConfig = {
|
|
|
70
64
|
contentDispositionType?: "inline" | "attachment";
|
|
71
65
|
/** Content-Security-Policy header value. Default: "script-src 'none'; frame-src 'none'; sandbox;" */
|
|
72
66
|
contentSecurityPolicy?: string;
|
|
67
|
+
/** Least max-age, in seconds, of an image served as-is. Default: 14400. */
|
|
68
|
+
minimumCacheTTL?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Most bytes of a GIF, PNG or WebP source read for optimization before
|
|
71
|
+
* answering 413. Default: 50000000.
|
|
72
|
+
*/
|
|
73
|
+
maximumResponseBody?: number;
|
|
73
74
|
};
|
|
74
75
|
/**
|
|
75
76
|
* Next.js default device sizes and image sizes.
|
|
@@ -80,10 +81,12 @@ export declare const DEFAULT_DEVICE_SIZES: number[];
|
|
|
80
81
|
export declare const DEFAULT_IMAGE_SIZES: number[];
|
|
81
82
|
export type ParseImageParamsOptions = {
|
|
82
83
|
isDev?: boolean;
|
|
84
|
+
/** `images.localPatterns`; DEFAULT_LOCAL_PATTERNS when unset. */
|
|
85
|
+
localPatterns?: LocalPattern[];
|
|
83
86
|
};
|
|
84
87
|
export declare function resolveDevImageRedirect(requestUrl: URL, allowedWidths?: number[], allowedQualities?: number[], options?: ParseImageParamsOptions): string | null;
|
|
85
88
|
export type ImageOptimizationSignal = {
|
|
86
|
-
/**
|
|
89
|
+
/** next.config and middleware response headers to send with the image. */
|
|
87
90
|
middlewareHeaders: Headers;
|
|
88
91
|
/** The request headers after middleware's overrides, when it set any. */
|
|
89
92
|
requestHeaders: Headers | null;
|
|
@@ -120,6 +123,11 @@ export declare function isImageSourceBodyStatus(status: number): boolean;
|
|
|
120
123
|
*/
|
|
121
124
|
export declare const INVALID_IMAGE_SOURCE_MESSAGE = "The requested resource isn't a valid image.";
|
|
122
125
|
export declare function invalidImageSourceResponse(): Response;
|
|
126
|
+
/**
|
|
127
|
+
* Next.js applies this when `images.localPatterns` is unset: every local path,
|
|
128
|
+
* but no query string.
|
|
129
|
+
*/
|
|
130
|
+
export declare const DEFAULT_LOCAL_PATTERNS: LocalPattern[];
|
|
123
131
|
/**
|
|
124
132
|
* Parse and validate image optimization query parameters.
|
|
125
133
|
* Returns null if the request is malformed.
|
|
@@ -143,11 +151,6 @@ export declare function negotiateImageFormat(acceptHeader: string | null): strin
|
|
|
143
151
|
* Optimized images are immutable because the URL encodes the transform params.
|
|
144
152
|
*/
|
|
145
153
|
export declare const IMAGE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
146
|
-
/**
|
|
147
|
-
* Next.js's Cache-Control for an image whose source can change at its URL
|
|
148
|
-
* (anything but a hashed build asset), at the default `images.minimumCacheTTL`.
|
|
149
|
-
*/
|
|
150
|
-
export declare const MUTABLE_IMAGE_SOURCE_CACHE_CONTROL = "public, max-age=14400, must-revalidate";
|
|
151
154
|
/**
|
|
152
155
|
* Content-Security-Policy for image optimization responses.
|
|
153
156
|
* Blocks script execution and framing to prevent XSS via SVG or other
|
|
@@ -155,6 +158,10 @@ export declare const MUTABLE_IMAGE_SOURCE_CACHE_CONTROL = "public, max-age=14400
|
|
|
155
158
|
* Matches Next.js default: script-src 'none'; frame-src 'none'; sandbox;
|
|
156
159
|
*/
|
|
157
160
|
export declare const IMAGE_CONTENT_SECURITY_POLICY = "script-src 'none'; frame-src 'none'; sandbox;";
|
|
161
|
+
/** Next.js's 400 body for a source whose type it does not serve, such as SVG. */
|
|
162
|
+
export declare const IMAGE_TYPE_NOT_ALLOWED_MESSAGE = "\"url\" parameter is valid but image type is not allowed";
|
|
163
|
+
/** The image type Next.js detects from a source's first bytes, or null. */
|
|
164
|
+
export declare function sniffImageContentType(bytes: Uint8Array): string | null;
|
|
158
165
|
/**
|
|
159
166
|
* Check if a Content-Type header value is a safe image type.
|
|
160
167
|
* Returns false for SVG (unless dangerouslyAllowSVG is true), HTML, or any non-image type.
|
|
@@ -176,6 +183,20 @@ export declare function isSafeImageContentType(contentType: string | null, dange
|
|
|
176
183
|
* go out without it. Only full image responses carry either header here too.
|
|
177
184
|
*/
|
|
178
185
|
export declare const IMAGE_RESPONSE_CACHE_STATE = "MISS";
|
|
186
|
+
/**
|
|
187
|
+
* Mark a source the host fetched from its `_next/static` build output, which
|
|
188
|
+
* is served immutably like Next.js' `isStatic` sources. A public file is never
|
|
189
|
+
* marked, whatever its path.
|
|
190
|
+
*/
|
|
191
|
+
export declare function markImageBuildAsset(response: Response): Response;
|
|
192
|
+
/**
|
|
193
|
+
* Next.js's Cache-Control for a source it serves as-is: a build asset stays
|
|
194
|
+
* immutable, and any other source is revalidated after the larger of
|
|
195
|
+
* `images.minimumCacheTTL` and the source's own max-age. Next.js ignores the
|
|
196
|
+
* source's max-age when it falls back after a failed transform, so that
|
|
197
|
+
* fallback passes null.
|
|
198
|
+
*/
|
|
199
|
+
export declare function sourceImageCacheControl(isBuildAsset: boolean, upstreamCacheControl: string | null, config?: ImageConfig): string;
|
|
179
200
|
/**
|
|
180
201
|
* Handlers for image optimization I/O operations.
|
|
181
202
|
* Workers provide these callbacks to adapt their specific bindings.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { badRequestResponse } from "./http-error-responses.js";
|
|
2
2
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
3
|
+
import { isAnimatedImage } from "./image-animation.js";
|
|
4
|
+
import { ImageSourceReader, ImageSourceTooLargeError } from "./image-source-reader.js";
|
|
5
|
+
import { hasLocalMatch } from "./image-local-patterns.js";
|
|
3
6
|
//#region src/server/image-optimization.ts
|
|
4
7
|
/**
|
|
5
8
|
* Image optimization request handler.
|
|
@@ -18,6 +21,9 @@ import { setCacheStateHeaders } from "./cache-headers.js";
|
|
|
18
21
|
* spoofing. SVG content is blocked by default (following Next.js behavior).
|
|
19
22
|
* When `dangerouslyAllowSVG` is enabled in next.config.js, SVGs are served
|
|
20
23
|
* as-is (no transformation) with security headers applied.
|
|
24
|
+
*
|
|
25
|
+
* Like Next.js, formats it does not optimize (SVG, ICO, ICNS, BMP, JPEG XL,
|
|
26
|
+
* HEIC) and animated images are served as-is too.
|
|
21
27
|
*/
|
|
22
28
|
/** The pathname that triggers image optimization (matches Next.js). */
|
|
23
29
|
const IMAGE_OPTIMIZATION_PATH = "/_next/image";
|
|
@@ -166,6 +172,14 @@ function invalidImageSourceResponse() {
|
|
|
166
172
|
}
|
|
167
173
|
const RECURSIVE_IMAGE_URL_RE = /\/_(?:next|vinext)\/image(?:$|\/)/;
|
|
168
174
|
/**
|
|
175
|
+
* Next.js applies this when `images.localPatterns` is unset: every local path,
|
|
176
|
+
* but no query string.
|
|
177
|
+
*/
|
|
178
|
+
const DEFAULT_LOCAL_PATTERNS = [{
|
|
179
|
+
pathname: "**",
|
|
180
|
+
search: ""
|
|
181
|
+
}];
|
|
182
|
+
/**
|
|
169
183
|
* Parse and validate image optimization query parameters.
|
|
170
184
|
* Returns null if the request is malformed.
|
|
171
185
|
*
|
|
@@ -204,6 +218,7 @@ function parseImageParams(url, allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFA
|
|
|
204
218
|
if (resolved.origin !== base) return null;
|
|
205
219
|
if (resolved.pathname.startsWith("//")) return null;
|
|
206
220
|
if (RECURSIVE_IMAGE_URL_RE.test(decodeURIComponent(resolved.pathname))) return null;
|
|
221
|
+
if (!options.isDev && !hasLocalMatch(options.localPatterns ?? DEFAULT_LOCAL_PATTERNS, resolved)) return null;
|
|
207
222
|
} catch {
|
|
208
223
|
return null;
|
|
209
224
|
}
|
|
@@ -228,11 +243,13 @@ function negotiateImageFormat(acceptHeader) {
|
|
|
228
243
|
* Optimized images are immutable because the URL encodes the transform params.
|
|
229
244
|
*/
|
|
230
245
|
const IMAGE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
246
|
+
/** Next.js's default `images.minimumCacheTTL`, in seconds. */
|
|
247
|
+
const DEFAULT_MINIMUM_CACHE_TTL = 14400;
|
|
231
248
|
/**
|
|
232
|
-
* Next.js's
|
|
233
|
-
*
|
|
249
|
+
* Next.js's default `images.maximumResponseBody`: the most it reads of a
|
|
250
|
+
* source before answering 413.
|
|
234
251
|
*/
|
|
235
|
-
const
|
|
252
|
+
const DEFAULT_MAXIMUM_RESPONSE_BODY = 5e7;
|
|
236
253
|
/**
|
|
237
254
|
* Content-Security-Policy for image optimization responses.
|
|
238
255
|
* Blocks script execution and framing to prevent XSS via SVG or other
|
|
@@ -253,16 +270,207 @@ const SAFE_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
253
270
|
"image/avif",
|
|
254
271
|
"image/x-icon",
|
|
255
272
|
"image/vnd.microsoft.icon",
|
|
273
|
+
"image/x-icns",
|
|
274
|
+
"image/bmp",
|
|
275
|
+
"image/tiff",
|
|
276
|
+
"image/jxl",
|
|
277
|
+
"image/heic"
|
|
278
|
+
]);
|
|
279
|
+
/**
|
|
280
|
+
* Formats Next.js serves as-is instead of optimizing (`BYPASS_TYPES` in
|
|
281
|
+
* packages/next/src/server/image-optimizer.ts), with ICO's registered name.
|
|
282
|
+
*/
|
|
283
|
+
const BYPASS_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
284
|
+
"image/svg+xml",
|
|
285
|
+
"image/x-icon",
|
|
286
|
+
"image/vnd.microsoft.icon",
|
|
287
|
+
"image/x-icns",
|
|
256
288
|
"image/bmp",
|
|
257
|
-
"image/
|
|
289
|
+
"image/jxl",
|
|
290
|
+
"image/heic"
|
|
291
|
+
]);
|
|
292
|
+
/** Formats Next.js serves as-is when animated (`ANIMATABLE_TYPES`). */
|
|
293
|
+
const ANIMATABLE_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
294
|
+
"image/webp",
|
|
295
|
+
"image/png",
|
|
296
|
+
"image/gif"
|
|
258
297
|
]);
|
|
298
|
+
/** Next.js's 400 body for a source whose type it does not serve, such as SVG. */
|
|
299
|
+
const IMAGE_TYPE_NOT_ALLOWED_MESSAGE = "\"url\" parameter is valid but image type is not allowed";
|
|
300
|
+
function imageTypeNotAllowedResponse() {
|
|
301
|
+
return new Response(IMAGE_TYPE_NOT_ALLOWED_MESSAGE, { status: 400 });
|
|
302
|
+
}
|
|
303
|
+
function imageMediaType(contentType) {
|
|
304
|
+
return contentType?.split(";")[0].trim().toLowerCase() ?? "";
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Next.js's image signatures (`detectContentType` in
|
|
308
|
+
* packages/next/src/server/image-optimizer.ts); a null byte matches any value.
|
|
309
|
+
*/
|
|
310
|
+
const IMAGE_SIGNATURES = [
|
|
311
|
+
["image/jpeg", [
|
|
312
|
+
255,
|
|
313
|
+
216,
|
|
314
|
+
255
|
|
315
|
+
]],
|
|
316
|
+
["image/png", [
|
|
317
|
+
137,
|
|
318
|
+
80,
|
|
319
|
+
78,
|
|
320
|
+
71,
|
|
321
|
+
13,
|
|
322
|
+
10,
|
|
323
|
+
26,
|
|
324
|
+
10
|
|
325
|
+
]],
|
|
326
|
+
["image/gif", [
|
|
327
|
+
71,
|
|
328
|
+
73,
|
|
329
|
+
70,
|
|
330
|
+
56
|
|
331
|
+
]],
|
|
332
|
+
["image/webp", [
|
|
333
|
+
82,
|
|
334
|
+
73,
|
|
335
|
+
70,
|
|
336
|
+
70,
|
|
337
|
+
null,
|
|
338
|
+
null,
|
|
339
|
+
null,
|
|
340
|
+
null,
|
|
341
|
+
87,
|
|
342
|
+
69,
|
|
343
|
+
66,
|
|
344
|
+
80
|
|
345
|
+
]],
|
|
346
|
+
["image/svg+xml", [
|
|
347
|
+
60,
|
|
348
|
+
63,
|
|
349
|
+
120,
|
|
350
|
+
109,
|
|
351
|
+
108
|
|
352
|
+
]],
|
|
353
|
+
["image/svg+xml", [
|
|
354
|
+
60,
|
|
355
|
+
115,
|
|
356
|
+
118,
|
|
357
|
+
103
|
|
358
|
+
]],
|
|
359
|
+
["image/avif", [
|
|
360
|
+
null,
|
|
361
|
+
null,
|
|
362
|
+
null,
|
|
363
|
+
null,
|
|
364
|
+
102,
|
|
365
|
+
116,
|
|
366
|
+
121,
|
|
367
|
+
112,
|
|
368
|
+
97,
|
|
369
|
+
118,
|
|
370
|
+
105,
|
|
371
|
+
102
|
|
372
|
+
]],
|
|
373
|
+
["image/x-icon", [
|
|
374
|
+
0,
|
|
375
|
+
0,
|
|
376
|
+
1,
|
|
377
|
+
0
|
|
378
|
+
]],
|
|
379
|
+
["image/x-icns", [
|
|
380
|
+
105,
|
|
381
|
+
99,
|
|
382
|
+
110,
|
|
383
|
+
115
|
|
384
|
+
]],
|
|
385
|
+
["image/tiff", [
|
|
386
|
+
73,
|
|
387
|
+
73,
|
|
388
|
+
42,
|
|
389
|
+
0
|
|
390
|
+
]],
|
|
391
|
+
["image/bmp", [66, 77]],
|
|
392
|
+
["image/jxl", [255, 10]],
|
|
393
|
+
["image/jxl", [
|
|
394
|
+
0,
|
|
395
|
+
0,
|
|
396
|
+
0,
|
|
397
|
+
12,
|
|
398
|
+
74,
|
|
399
|
+
88,
|
|
400
|
+
76,
|
|
401
|
+
32,
|
|
402
|
+
13,
|
|
403
|
+
10,
|
|
404
|
+
135,
|
|
405
|
+
10
|
|
406
|
+
]],
|
|
407
|
+
["image/heic", [
|
|
408
|
+
null,
|
|
409
|
+
null,
|
|
410
|
+
null,
|
|
411
|
+
null,
|
|
412
|
+
102,
|
|
413
|
+
116,
|
|
414
|
+
121,
|
|
415
|
+
112,
|
|
416
|
+
104,
|
|
417
|
+
101,
|
|
418
|
+
105,
|
|
419
|
+
99
|
|
420
|
+
]]
|
|
421
|
+
];
|
|
422
|
+
const IMAGE_SIGNATURE_LENGTH = 12;
|
|
423
|
+
/** The image type Next.js detects from a source's first bytes, or null. */
|
|
424
|
+
function sniffImageContentType(bytes) {
|
|
425
|
+
for (const [type, signature] of IMAGE_SIGNATURES) if (signature.every((byte, i) => byte === null || bytes[i] === byte)) return type;
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Label a source that arrives without an image Content-Type by its first
|
|
430
|
+
* bytes, as Next.js types every source. Workers static assets have no type for
|
|
431
|
+
* some formats Next.js serves, such as ICNS.
|
|
432
|
+
*/
|
|
433
|
+
async function withSniffedImageContentType(source) {
|
|
434
|
+
if (!source.ok || !source.body || imageMediaType(source.headers.get("Content-Type")).startsWith("image/")) return source;
|
|
435
|
+
const reader = source.body.getReader();
|
|
436
|
+
const head = [];
|
|
437
|
+
const signature = new Uint8Array(IMAGE_SIGNATURE_LENGTH);
|
|
438
|
+
let length = 0;
|
|
439
|
+
while (length < IMAGE_SIGNATURE_LENGTH) {
|
|
440
|
+
const { done, value } = await reader.read();
|
|
441
|
+
if (done) break;
|
|
442
|
+
head.push(value);
|
|
443
|
+
signature.set(value.subarray(0, IMAGE_SIGNATURE_LENGTH - length), length);
|
|
444
|
+
length += Math.min(value.byteLength, IMAGE_SIGNATURE_LENGTH - length);
|
|
445
|
+
}
|
|
446
|
+
const body = new ReadableStream({
|
|
447
|
+
start(controller) {
|
|
448
|
+
for (const chunk of head) controller.enqueue(chunk);
|
|
449
|
+
},
|
|
450
|
+
async pull(controller) {
|
|
451
|
+
const { done, value } = await reader.read();
|
|
452
|
+
if (done) controller.close();
|
|
453
|
+
else controller.enqueue(value);
|
|
454
|
+
},
|
|
455
|
+
cancel(reason) {
|
|
456
|
+
return reader.cancel(reason);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
const headers = new Headers(source.headers);
|
|
460
|
+
const type = sniffImageContentType(signature.subarray(0, length));
|
|
461
|
+
if (type !== null) headers.set("Content-Type", type);
|
|
462
|
+
return new Response(body, {
|
|
463
|
+
status: source.status,
|
|
464
|
+
statusText: source.statusText,
|
|
465
|
+
headers
|
|
466
|
+
});
|
|
467
|
+
}
|
|
259
468
|
/**
|
|
260
469
|
* Check if a Content-Type header value is a safe image type.
|
|
261
470
|
* Returns false for SVG (unless dangerouslyAllowSVG is true), HTML, or any non-image type.
|
|
262
471
|
*/
|
|
263
472
|
function isSafeImageContentType(contentType, dangerouslyAllowSVG = false) {
|
|
264
|
-
|
|
265
|
-
const mediaType = contentType.split(";")[0].trim().toLowerCase();
|
|
473
|
+
const mediaType = imageMediaType(contentType);
|
|
266
474
|
if (SAFE_IMAGE_CONTENT_TYPES.has(mediaType)) return true;
|
|
267
475
|
if (dangerouslyAllowSVG && mediaType === "image/svg+xml") return true;
|
|
268
476
|
return false;
|
|
@@ -295,20 +503,65 @@ function setImageSecurityHeaders(headers, config) {
|
|
|
295
503
|
*/
|
|
296
504
|
const IMAGE_RESPONSE_CACHE_STATE = "MISS";
|
|
297
505
|
/** Headers shared by every successful image response, transformed or not. */
|
|
298
|
-
function setImageResponseHeaders(headers, config) {
|
|
299
|
-
headers.set("Cache-Control",
|
|
506
|
+
function setImageResponseHeaders(headers, cacheControl, config) {
|
|
507
|
+
headers.set("Cache-Control", cacheControl);
|
|
300
508
|
headers.set("Vary", "Accept");
|
|
301
509
|
setImageSecurityHeaders(headers, config);
|
|
302
510
|
setCacheStateHeaders(headers, IMAGE_RESPONSE_CACHE_STATE);
|
|
303
511
|
}
|
|
304
|
-
|
|
512
|
+
/** The `s-maxage` or `max-age` of a Cache-Control value, like Next.js' getMaxAge. */
|
|
513
|
+
function cacheControlMaxAge(cacheControl) {
|
|
514
|
+
const directives = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const directive of cacheControl?.split(",") ?? []) {
|
|
516
|
+
const [key, value] = directive.trim().split("=", 2);
|
|
517
|
+
directives.set(key.toLowerCase(), value?.toLowerCase());
|
|
518
|
+
}
|
|
519
|
+
let age = directives.get("s-maxage") || directives.get("max-age") || "";
|
|
520
|
+
if (age.startsWith("\"") && age.endsWith("\"")) age = age.slice(1, -1);
|
|
521
|
+
const seconds = Number.parseInt(age, 10);
|
|
522
|
+
return Number.isNaN(seconds) ? 0 : seconds;
|
|
523
|
+
}
|
|
524
|
+
const IMAGE_BUILD_ASSET = Symbol.for("vinext.imageBuildAsset");
|
|
525
|
+
/**
|
|
526
|
+
* Mark a source the host fetched from its `_next/static` build output, which
|
|
527
|
+
* is served immutably like Next.js' `isStatic` sources. A public file is never
|
|
528
|
+
* marked, whatever its path.
|
|
529
|
+
*/
|
|
530
|
+
function markImageBuildAsset(response) {
|
|
531
|
+
Reflect.set(response, IMAGE_BUILD_ASSET, true);
|
|
532
|
+
return response;
|
|
533
|
+
}
|
|
534
|
+
function isImageBuildAsset(response) {
|
|
535
|
+
return Reflect.get(response, IMAGE_BUILD_ASSET) === true;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Next.js's Cache-Control for a source it serves as-is: a build asset stays
|
|
539
|
+
* immutable, and any other source is revalidated after the larger of
|
|
540
|
+
* `images.minimumCacheTTL` and the source's own max-age. Next.js ignores the
|
|
541
|
+
* source's max-age when it falls back after a failed transform, so that
|
|
542
|
+
* fallback passes null.
|
|
543
|
+
*/
|
|
544
|
+
function sourceImageCacheControl(isBuildAsset, upstreamCacheControl, config) {
|
|
545
|
+
if (isBuildAsset) return IMAGE_CACHE_CONTROL;
|
|
546
|
+
return `public, max-age=${Math.max(config?.minimumCacheTTL ?? DEFAULT_MINIMUM_CACHE_TTL, cacheControlMaxAge(upstreamCacheControl))}, must-revalidate`;
|
|
547
|
+
}
|
|
548
|
+
function createPassthroughImageResponse(source, body, cacheControl, config, readLength = null) {
|
|
305
549
|
const headers = new Headers(source.headers);
|
|
306
|
-
setImageResponseHeaders(headers, config);
|
|
307
|
-
|
|
550
|
+
setImageResponseHeaders(headers, cacheControl, config);
|
|
551
|
+
if (readLength !== null) headers.set("Content-Length", String(readLength));
|
|
552
|
+
return new Response(body, {
|
|
308
553
|
status: 200,
|
|
309
554
|
headers
|
|
310
555
|
});
|
|
311
556
|
}
|
|
557
|
+
/** Whether a source's Content-Length is more than `limit` bytes. */
|
|
558
|
+
function declaresMoreThan(source, limit) {
|
|
559
|
+
return Number(source.headers.get("Content-Length") ?? NaN) > limit;
|
|
560
|
+
}
|
|
561
|
+
/** Next.js's 413 for a source larger than `images.maximumResponseBody`. */
|
|
562
|
+
function sourceTooLargeResponse() {
|
|
563
|
+
return new Response("\"url\" parameter is valid but internal response is invalid", { status: 413 });
|
|
564
|
+
}
|
|
312
565
|
/**
|
|
313
566
|
* Handle image optimization requests.
|
|
314
567
|
*
|
|
@@ -317,39 +570,80 @@ function createPassthroughImageResponse(source, config) {
|
|
|
317
570
|
* cache headers.
|
|
318
571
|
*/
|
|
319
572
|
async function handleImageOptimization(request, handlers, allowedWidths, imageConfig) {
|
|
320
|
-
const params = parseImageParams(new URL(request.url), allowedWidths, imageConfig?.qualities);
|
|
573
|
+
const params = parseImageParams(new URL(request.url), allowedWidths, imageConfig?.qualities, { localPatterns: imageConfig?.localPatterns });
|
|
321
574
|
if (!params) return badRequestResponse();
|
|
322
575
|
const { imageUrl, width, quality } = params;
|
|
323
|
-
const
|
|
576
|
+
const fetchedSource = await handlers.fetchAsset(imageUrl, request);
|
|
577
|
+
const isBuildAsset = isImageBuildAsset(fetchedSource);
|
|
578
|
+
const source = await withSniffedImageContentType(fetchedSource);
|
|
324
579
|
if (!source.ok || !source.body) return invalidImageSourceResponse();
|
|
325
580
|
const format = negotiateImageFormat(request.headers.get("Accept"));
|
|
326
581
|
const sourceContentType = source.headers.get("Content-Type");
|
|
327
|
-
if (!isSafeImageContentType(sourceContentType, imageConfig?.dangerouslyAllowSVG)) return
|
|
328
|
-
|
|
582
|
+
if (!isSafeImageContentType(sourceContentType, imageConfig?.dangerouslyAllowSVG)) return imageTypeNotAllowedResponse();
|
|
583
|
+
const sourceMediaType = imageMediaType(sourceContentType);
|
|
584
|
+
const sourceCacheControl = sourceImageCacheControl(isBuildAsset, source.headers.get("Cache-Control"), imageConfig);
|
|
585
|
+
if (BYPASS_IMAGE_CONTENT_TYPES.has(sourceMediaType)) {
|
|
586
|
+
const response = createPassthroughImageResponse(source, source.body, sourceCacheControl, imageConfig);
|
|
587
|
+
if (sourceMediaType === "image/vnd.microsoft.icon") response.headers.set("Content-Type", "image/x-icon");
|
|
588
|
+
return response;
|
|
589
|
+
}
|
|
590
|
+
const limit = imageConfig?.maximumResponseBody ?? DEFAULT_MAXIMUM_RESPONSE_BODY;
|
|
591
|
+
let reader = null;
|
|
329
592
|
if (handlers.transformImage) try {
|
|
330
|
-
|
|
593
|
+
let body = source.body;
|
|
594
|
+
if (ANIMATABLE_IMAGE_CONTENT_TYPES.has(sourceMediaType)) {
|
|
595
|
+
if (declaresMoreThan(source, limit)) {
|
|
596
|
+
source.body.cancel().catch(() => {});
|
|
597
|
+
return sourceTooLargeResponse();
|
|
598
|
+
}
|
|
599
|
+
reader = new ImageSourceReader(source.body, limit);
|
|
600
|
+
const animated = await isAnimatedImage(reader);
|
|
601
|
+
let sourceBody = reader.body();
|
|
602
|
+
if (!sourceBody) {
|
|
603
|
+
reader.cancel();
|
|
604
|
+
const refetched = await handlers.fetchAsset(imageUrl, request);
|
|
605
|
+
if (!refetched.ok || !refetched.body) return invalidImageSourceResponse();
|
|
606
|
+
reader = new ImageSourceReader(refetched.body, limit);
|
|
607
|
+
sourceBody = reader.body();
|
|
608
|
+
}
|
|
609
|
+
if (animated) return createPassthroughImageResponse(source, sourceBody, sourceCacheControl, imageConfig, reader.completeLength);
|
|
610
|
+
body = sourceBody;
|
|
611
|
+
}
|
|
612
|
+
const transformed = await handlers.transformImage(body, {
|
|
331
613
|
width,
|
|
332
614
|
format,
|
|
333
615
|
quality
|
|
334
616
|
});
|
|
335
617
|
const headers = new Headers(transformed.headers);
|
|
336
|
-
setImageResponseHeaders(headers, imageConfig);
|
|
618
|
+
setImageResponseHeaders(headers, IMAGE_CACHE_CONTROL, imageConfig);
|
|
337
619
|
if (!isSafeImageContentType(headers.get("Content-Type"), imageConfig?.dangerouslyAllowSVG)) headers.set("Content-Type", format);
|
|
338
620
|
return new Response(transformed.body, {
|
|
339
621
|
status: 200,
|
|
340
622
|
headers
|
|
341
623
|
});
|
|
342
624
|
} catch (e) {
|
|
625
|
+
if (e instanceof ImageSourceTooLargeError || reader?.tooLarge) return sourceTooLargeResponse();
|
|
343
626
|
console.error("[vinext] Image optimization error:", e);
|
|
344
627
|
}
|
|
628
|
+
const fallbackCacheControl = handlers.transformImage ? sourceImageCacheControl(isBuildAsset, null, imageConfig) : sourceCacheControl;
|
|
629
|
+
const readLength = reader?.completeLength ?? null;
|
|
345
630
|
try {
|
|
346
|
-
return createPassthroughImageResponse(source, imageConfig);
|
|
631
|
+
return createPassthroughImageResponse(source, readLength !== null && reader?.body() || source.body, fallbackCacheControl, imageConfig, readLength);
|
|
347
632
|
} catch (e) {
|
|
348
633
|
console.error("[vinext] Image fallback error, refetching source image:", e);
|
|
349
|
-
|
|
634
|
+
reader?.cancel();
|
|
635
|
+
const refetchedSource = await withSniffedImageContentType(await handlers.fetchAsset(imageUrl, request));
|
|
350
636
|
if (!refetchedSource.ok || !refetchedSource.body) return invalidImageSourceResponse();
|
|
351
|
-
if (!isSafeImageContentType(refetchedSource.headers.get("Content-Type"), imageConfig?.dangerouslyAllowSVG)) return
|
|
352
|
-
|
|
637
|
+
if (!isSafeImageContentType(refetchedSource.headers.get("Content-Type"), imageConfig?.dangerouslyAllowSVG)) return imageTypeNotAllowedResponse();
|
|
638
|
+
let body = refetchedSource.body;
|
|
639
|
+
if (reader) {
|
|
640
|
+
if (declaresMoreThan(refetchedSource, limit)) {
|
|
641
|
+
body.cancel().catch(() => {});
|
|
642
|
+
return sourceTooLargeResponse();
|
|
643
|
+
}
|
|
644
|
+
body = new ImageSourceReader(body, limit).body();
|
|
645
|
+
}
|
|
646
|
+
return createPassthroughImageResponse(refetchedSource, body, fallbackCacheControl, imageConfig);
|
|
353
647
|
}
|
|
354
648
|
}
|
|
355
649
|
const _IMAGE_OPTIMIZER_KEY = Symbol.for("vinext.imageOptimizer");
|
|
@@ -402,4 +696,4 @@ function handleConfiguredImageOptimization(request, fetchAsset, allowedWidths, i
|
|
|
402
696
|
}, allowedWidths, imageConfig);
|
|
403
697
|
}
|
|
404
698
|
//#endregion
|
|
405
|
-
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, IMAGE_RESPONSE_CACHE_STATE,
|
|
699
|
+
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, DEFAULT_LOCAL_PATTERNS, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, IMAGE_RESPONSE_CACHE_STATE, IMAGE_TYPE_NOT_ALLOWED_MESSAGE, INVALID_IMAGE_SOURCE_MESSAGE, VINEXT_IMAGE_OPTIMIZATION_PATH, getImageOptimizer, handleConfiguredImageOptimization, handleImageOptimization, invalidImageSourceResponse, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, markImageBuildAsset, markImageOptimizationSignal, negotiateImageFormat, parseImageParams, readImageOptimizationSignal, resolveDevImageRedirect, restoreImageOptimizationSignalFromTransport, serializeImageOptimizationSignalForTransport, setImageOptimizer, sniffImageContentType, sourceImageCacheControl };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/server/image-source-reader.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Streamed reads of an image source for the image optimization endpoint.
|
|
4
|
+
*
|
|
5
|
+
* Animation detection parses a GIF, PNG or WebP source as it streams in rather
|
|
6
|
+
* than buffering it, so a large source cannot exhaust a Worker isolate's
|
|
7
|
+
* memory. Only the source's first bytes are kept, to replay without a refetch.
|
|
8
|
+
*/
|
|
9
|
+
/** Thrown once a source grows past `images.maximumResponseBody`. */
|
|
10
|
+
export declare class ImageSourceTooLargeError extends Error {
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
13
|
+
export declare class ImageSourceReader {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(body: ReadableStream<Uint8Array>, limit: number);
|
|
16
|
+
/** Whether the source has grown past the limit. */
|
|
17
|
+
get tooLarge(): boolean;
|
|
18
|
+
/** The source's length once it has all been read and held, else null. */
|
|
19
|
+
get completeLength(): number | null;
|
|
20
|
+
/** Stop reading the source without waiting on its cancellation. */
|
|
21
|
+
cancel(): void;
|
|
22
|
+
/** Up to `length` bytes at `offset`, fewer where the source ends. */
|
|
23
|
+
bytes(offset: number, length: number): Promise<Uint8Array>;
|
|
24
|
+
/** The byte at `offset`, or undefined past the source's end. */
|
|
25
|
+
byte(offset: number): Promise<number | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* The whole source as a stream, replaying the bytes already read, or null
|
|
28
|
+
* once they are no longer all held. An incompletely read source replays
|
|
29
|
+
* once; the rest streams from the source, still within the limit, and is
|
|
30
|
+
* held too while the whole source fits.
|
|
31
|
+
*/
|
|
32
|
+
body(): ReadableStream<Uint8Array> | null;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|