astro 6.2.2 → 6.3.0
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/actions/handler.d.ts +32 -0
- package/dist/actions/handler.js +45 -0
- package/dist/actions/runtime/server.js +1 -1
- package/dist/assets/build/generate.js +1 -1
- package/dist/assets/build/remote.d.ts +3 -2
- package/dist/assets/build/remote.js +16 -9
- package/dist/assets/endpoint/generic.js +4 -7
- package/dist/assets/endpoint/shared.js +7 -2
- package/dist/assets/index.d.ts +1 -0
- package/dist/assets/index.js +2 -0
- package/dist/assets/services/sharp.js +7 -0
- package/dist/assets/utils/index.d.ts +1 -0
- package/dist/assets/utils/index.js +2 -0
- package/dist/assets/utils/redirectValidation.d.ts +48 -0
- package/dist/assets/utils/redirectValidation.js +48 -0
- package/dist/assets/utils/remoteProbe.js +25 -2
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/container/index.js +18 -14
- package/dist/content/content-layer.js +3 -4
- package/dist/content/server-listeners.js +0 -4
- package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
- package/dist/core/app/base.d.ts +33 -15
- package/dist/core/app/base.js +120 -324
- package/dist/core/app/dev/app.d.ts +3 -2
- package/dist/core/app/dev/app.js +4 -60
- package/dist/core/app/entrypoints/virtual/dev.js +2 -0
- package/dist/core/app/entrypoints/virtual/prod.js +4 -1
- package/dist/core/app/prepare-response.d.ts +11 -0
- package/dist/core/app/prepare-response.js +18 -0
- package/dist/core/app/render-options.d.ts +11 -0
- package/dist/core/app/render-options.js +11 -0
- package/dist/core/base-pipeline.d.ts +38 -1
- package/dist/core/base-pipeline.js +50 -7
- package/dist/core/build/app.d.ts +3 -4
- package/dist/core/build/app.js +3 -17
- package/dist/core/cache/handler.d.ts +29 -0
- package/dist/core/cache/handler.js +81 -0
- package/dist/core/config/schemas/base.d.ts +4 -0
- package/dist/core/config/schemas/base.js +4 -0
- package/dist/core/config/schemas/relative.d.ts +6 -0
- package/dist/core/constants.d.ts +27 -1
- package/dist/core/constants.js +14 -1
- package/dist/core/cookies/cookies.d.ts +7 -2
- package/dist/core/cookies/cookies.js +11 -4
- package/dist/core/cookies/response.d.ts +1 -1
- package/dist/core/cookies/response.js +1 -2
- package/dist/core/create-vite.js +15 -0
- package/dist/core/csp/runtime.js +6 -4
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/build-handler.d.ts +17 -0
- package/dist/core/errors/build-handler.js +22 -0
- package/dist/core/errors/default-handler.d.ts +14 -0
- package/dist/core/errors/default-handler.js +144 -0
- package/dist/core/errors/dev-handler.d.ts +21 -0
- package/dist/core/errors/dev-handler.js +82 -0
- package/dist/core/errors/handler.d.ts +9 -0
- package/dist/core/errors/handler.js +0 -0
- package/dist/core/fetch/default-handler.d.ts +17 -0
- package/dist/core/fetch/default-handler.js +45 -0
- package/dist/core/fetch/fetch-state.d.ts +244 -0
- package/dist/core/fetch/fetch-state.js +779 -0
- package/dist/core/fetch/index.d.ts +61 -0
- package/dist/core/fetch/index.js +121 -0
- package/dist/core/fetch/types.d.ts +6 -0
- package/dist/core/fetch/types.js +0 -0
- package/dist/core/fetch/vite-plugin.d.ts +5 -0
- package/dist/core/fetch/vite-plugin.js +69 -0
- package/dist/core/hono/index.d.ts +21 -0
- package/dist/core/hono/index.js +98 -0
- package/dist/core/i18n/handler.d.ts +18 -0
- package/dist/core/i18n/handler.js +119 -0
- package/dist/core/logger/core.d.ts +8 -0
- package/dist/core/logger/core.js +16 -0
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/middleware/astro-middleware.d.ts +27 -0
- package/dist/core/middleware/astro-middleware.js +53 -0
- package/dist/core/pages/handler.d.ts +20 -0
- package/dist/core/pages/handler.js +74 -0
- package/dist/core/redirects/render.d.ts +2 -2
- package/dist/core/redirects/render.js +7 -8
- package/dist/core/rewrites/handler.d.ts +37 -0
- package/dist/core/rewrites/handler.js +67 -0
- package/dist/core/routing/3xx.js +8 -4
- package/dist/core/routing/handler.d.ts +17 -0
- package/dist/core/routing/handler.js +172 -0
- package/dist/core/routing/match.d.ts +0 -7
- package/dist/core/routing/match.js +0 -5
- package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
- package/dist/core/routing/trailing-slash-handler.js +67 -0
- package/dist/core/session/drivers.d.ts +1 -1
- package/dist/core/session/handler.d.ts +11 -0
- package/dist/core/session/handler.js +33 -0
- package/dist/core/util/normalized-url.d.ts +10 -0
- package/dist/core/util/normalized-url.js +21 -0
- package/dist/i18n/middleware.d.ts +10 -0
- package/dist/i18n/middleware.js +4 -88
- package/dist/i18n/utils.js +2 -2
- package/dist/runtime/server/astro-island.js +57 -20
- package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
- package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
- package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
- package/dist/runtime/server/astro-island.prebuilt.js +1 -1
- package/dist/runtime/server/render/server-islands.js +2 -1
- package/dist/types/public/config.d.ts +46 -12
- package/dist/types/public/internal.d.ts +1 -1
- package/dist/vite-plugin-app/app.d.ts +4 -5
- package/dist/vite-plugin-app/app.js +20 -65
- package/package.json +11 -5
- package/dist/core/render-context.d.ts +0 -77
- package/dist/core/render-context.js +0 -826
package/dist/core/constants.d.ts
CHANGED
|
@@ -35,6 +35,17 @@ export declare const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
|
|
|
35
35
|
* The name for the header used to help i18n middleware, which only needs to act on "page" and "fallback" route types.
|
|
36
36
|
*/
|
|
37
37
|
export declare const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
|
|
38
|
+
/**
|
|
39
|
+
* Internal headers that should be stripped from the response before
|
|
40
|
+
* sending it to the user agent. Add new internal headers here so
|
|
41
|
+
* `prepareResponse` removes them automatically.
|
|
42
|
+
*/
|
|
43
|
+
export declare const INTERNAL_RESPONSE_HEADERS: readonly ["X-Astro-Reroute", "X-Astro-Rewrite", "X-Astro-Noop", "X-Astro-Route-Type"];
|
|
44
|
+
/**
|
|
45
|
+
* Set by internal handlers (e.g. PagesHandler) to signal that a
|
|
46
|
+
* response should be replaced with the corresponding error page.
|
|
47
|
+
*/
|
|
48
|
+
export declare const ASTRO_ERROR_HEADER = "X-Astro-Error";
|
|
38
49
|
/**
|
|
39
50
|
* The value of the `component` field of the default 404 page, which is used when there is no user-provided 404.astro page.
|
|
40
51
|
*/
|
|
@@ -55,7 +66,7 @@ export declare const REROUTABLE_STATUS_CODES: number[];
|
|
|
55
66
|
export declare const clientAddressSymbol: unique symbol;
|
|
56
67
|
/**
|
|
57
68
|
* The symbol used as a field on the request object to store the object to be made available to Astro APIs as `locals`.
|
|
58
|
-
* Use judiciously, as locals are now stored within `
|
|
69
|
+
* Use judiciously, as locals are now stored within `FetchState` by default. Tacking it onto request is no longer necessary.
|
|
59
70
|
*/
|
|
60
71
|
export declare const clientLocalsSymbol: unique symbol;
|
|
61
72
|
/**
|
|
@@ -66,6 +77,21 @@ export declare const originPathnameSymbol: unique symbol;
|
|
|
66
77
|
* Use this symbol to set and retrieve the pipeline.
|
|
67
78
|
*/
|
|
68
79
|
export declare const pipelineSymbol: unique symbol;
|
|
80
|
+
/**
|
|
81
|
+
* Use this symbol to stash the active `FetchState` on an `APIContext`
|
|
82
|
+
* (or `ActionAPIContext`). Consumed by internal shims that need access
|
|
83
|
+
* to per-request state without appearing in the public context shape
|
|
84
|
+
* — e.g. the manual-strategy i18n middleware wrapper in
|
|
85
|
+
* `src/i18n/middleware.ts`.
|
|
86
|
+
*/
|
|
87
|
+
export declare const fetchStateSymbol: unique symbol;
|
|
88
|
+
/**
|
|
89
|
+
* Use this symbol to stash the `BaseApp` on an incoming `Request` at the
|
|
90
|
+
* top of the pipeline. Fetch handlers loaded from `virtual:astro:fetchable`
|
|
91
|
+
* (including `DefaultFetchHandler`) read it to find the app associated
|
|
92
|
+
* with the current request without needing App passed to their constructor.
|
|
93
|
+
*/
|
|
94
|
+
export declare const appSymbol: unique symbol;
|
|
69
95
|
/**
|
|
70
96
|
* Use this symbol to opt into handling prerender routes in Astro core dev middleware.
|
|
71
97
|
*/
|
package/dist/core/constants.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
const ASTRO_VERSION = "6.
|
|
1
|
+
const ASTRO_VERSION = "6.3.0";
|
|
2
2
|
const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
|
|
3
3
|
const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
|
|
4
4
|
const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
|
|
5
5
|
const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
|
|
6
6
|
const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
|
|
7
7
|
const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
|
|
8
|
+
const INTERNAL_RESPONSE_HEADERS = [
|
|
9
|
+
REROUTE_DIRECTIVE_HEADER,
|
|
10
|
+
REWRITE_DIRECTIVE_HEADER_KEY,
|
|
11
|
+
NOOP_MIDDLEWARE_HEADER,
|
|
12
|
+
ROUTE_TYPE_HEADER
|
|
13
|
+
];
|
|
14
|
+
const ASTRO_ERROR_HEADER = "X-Astro-Error";
|
|
8
15
|
const DEFAULT_404_COMPONENT = "astro-default-404.astro";
|
|
9
16
|
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308, 300, 304];
|
|
10
17
|
const REROUTABLE_STATUS_CODES = [404, 500];
|
|
@@ -12,6 +19,8 @@ const clientAddressSymbol = /* @__PURE__ */ Symbol.for("astro.clientAddress");
|
|
|
12
19
|
const clientLocalsSymbol = /* @__PURE__ */ Symbol.for("astro.locals");
|
|
13
20
|
const originPathnameSymbol = /* @__PURE__ */ Symbol.for("astro.originPathname");
|
|
14
21
|
const pipelineSymbol = /* @__PURE__ */ Symbol.for("astro.pipeline");
|
|
22
|
+
const fetchStateSymbol = /* @__PURE__ */ Symbol.for("astro.fetchState");
|
|
23
|
+
const appSymbol = /* @__PURE__ */ Symbol.for("astro.app");
|
|
15
24
|
const devPrerenderMiddlewareSymbol = /* @__PURE__ */ Symbol.for("astro.devPrerenderMiddleware");
|
|
16
25
|
const nodeRequestAbortControllerCleanupSymbol = /* @__PURE__ */ Symbol.for(
|
|
17
26
|
"astro.nodeRequestAbortControllerCleanup"
|
|
@@ -43,10 +52,12 @@ const ASTRO_VITE_ENVIRONMENT_NAMES = {
|
|
|
43
52
|
prerender: "prerender"
|
|
44
53
|
};
|
|
45
54
|
export {
|
|
55
|
+
ASTRO_ERROR_HEADER,
|
|
46
56
|
ASTRO_GENERATOR,
|
|
47
57
|
ASTRO_VERSION,
|
|
48
58
|
ASTRO_VITE_ENVIRONMENT_NAMES,
|
|
49
59
|
DEFAULT_404_COMPONENT,
|
|
60
|
+
INTERNAL_RESPONSE_HEADERS,
|
|
50
61
|
MIDDLEWARE_PATH_SEGMENT_NAME,
|
|
51
62
|
NOOP_MIDDLEWARE_HEADER,
|
|
52
63
|
REDIRECT_STATUS_CODES,
|
|
@@ -56,9 +67,11 @@ export {
|
|
|
56
67
|
REWRITE_DIRECTIVE_HEADER_VALUE,
|
|
57
68
|
ROUTE_TYPE_HEADER,
|
|
58
69
|
SUPPORTED_MARKDOWN_FILE_EXTENSIONS,
|
|
70
|
+
appSymbol,
|
|
59
71
|
clientAddressSymbol,
|
|
60
72
|
clientLocalsSymbol,
|
|
61
73
|
devPrerenderMiddlewareSymbol,
|
|
74
|
+
fetchStateSymbol,
|
|
62
75
|
nodeRequestAbortControllerCleanupSymbol,
|
|
63
76
|
originPathnameSymbol,
|
|
64
77
|
pipelineSymbol,
|
|
@@ -72,8 +72,13 @@ declare class AstroCookies implements AstroCookiesInterface {
|
|
|
72
72
|
*/
|
|
73
73
|
headers(): Generator<string, void, unknown>;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
75
|
+
* Marks the cookies as consumed and returns the header values.
|
|
76
|
+
* After consumption, any subsequent `set()` calls will warn.
|
|
77
|
+
*/
|
|
78
|
+
consume(): Generator<string, void, unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated Use the instance method `cookies.consume()` instead.
|
|
81
|
+
* Kept for backward compatibility with adapters.
|
|
77
82
|
*/
|
|
78
83
|
static consume(cookies: AstroCookies): Generator<string, void, unknown>;
|
|
79
84
|
}
|
|
@@ -173,12 +173,19 @@ class AstroCookies {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
176
|
+
* Marks the cookies as consumed and returns the header values.
|
|
177
|
+
* After consumption, any subsequent `set()` calls will warn.
|
|
178
|
+
*/
|
|
179
|
+
consume() {
|
|
180
|
+
this.#consumed = true;
|
|
181
|
+
return this.headers();
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* @deprecated Use the instance method `cookies.consume()` instead.
|
|
185
|
+
* Kept for backward compatibility with adapters.
|
|
178
186
|
*/
|
|
179
187
|
static consume(cookies) {
|
|
180
|
-
cookies
|
|
181
|
-
return cookies.headers();
|
|
188
|
+
return cookies.consume();
|
|
182
189
|
}
|
|
183
190
|
#ensureParsed() {
|
|
184
191
|
if (!this.#requestValues) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AstroCookies } from './cookies.js';
|
|
1
|
+
import type { AstroCookies } from './cookies.js';
|
|
2
2
|
export declare function attachCookiesToResponse(response: Response, cookies: AstroCookies): void;
|
|
3
3
|
export declare function getCookiesFromResponse(response: Response): AstroCookies | undefined;
|
|
4
4
|
export declare function getSetCookiesFromResponse(response: Response): Generator<string, string[]>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AstroCookies } from "./cookies.js";
|
|
2
1
|
const astroCookiesSymbol = /* @__PURE__ */ Symbol.for("astro.cookies");
|
|
3
2
|
function attachCookiesToResponse(response, cookies) {
|
|
4
3
|
Reflect.set(response, astroCookiesSymbol, cookies);
|
|
@@ -16,7 +15,7 @@ function* getSetCookiesFromResponse(response) {
|
|
|
16
15
|
if (!cookies) {
|
|
17
16
|
return [];
|
|
18
17
|
}
|
|
19
|
-
for (const headerValue of
|
|
18
|
+
for (const headerValue of cookies.consume()) {
|
|
20
19
|
yield headerValue;
|
|
21
20
|
}
|
|
22
21
|
return [];
|
package/dist/core/create-vite.js
CHANGED
|
@@ -23,6 +23,7 @@ import astroDevToolbar from "../toolbar/vite-plugin-dev-toolbar.js";
|
|
|
23
23
|
import astroTransitions from "../transitions/vite-plugin-transitions.js";
|
|
24
24
|
import { vitePluginAdapterConfig } from "../vite-plugin-adapter-config/index.js";
|
|
25
25
|
import { vitePluginApp } from "../vite-plugin-app/index.js";
|
|
26
|
+
import { vitePluginFetchable } from "./fetch/vite-plugin.js";
|
|
26
27
|
import astroVitePlugin from "../vite-plugin-astro/index.js";
|
|
27
28
|
import { vitePluginAstroServer } from "../vite-plugin-astro-server/index.js";
|
|
28
29
|
import configAliasVitePlugin from "../vite-plugin-config-alias/index.js";
|
|
@@ -118,6 +119,19 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
118
119
|
customLogger: createViteLogger(logger, settings.config.vite.logLevel),
|
|
119
120
|
appType: "custom",
|
|
120
121
|
plugins: [
|
|
122
|
+
// Raise the watcher's maxListeners limit before any other plugin's
|
|
123
|
+
// configureServer hook can add listeners. Astro registers 12+ change
|
|
124
|
+
// listeners across its built-in Vite plugins, easily exceeding
|
|
125
|
+
// Node's default limit of 10.
|
|
126
|
+
{
|
|
127
|
+
name: "astro:watcher-max-listeners",
|
|
128
|
+
configureServer(server) {
|
|
129
|
+
const current = server.watcher.getMaxListeners();
|
|
130
|
+
if (current !== 0 && current < 50) {
|
|
131
|
+
server.watcher.setMaxListeners(50);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
121
135
|
serializedManifestPlugin({ settings, command, sync }),
|
|
122
136
|
vitePluginRenderers({
|
|
123
137
|
settings,
|
|
@@ -138,6 +152,7 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
138
152
|
// The server plugin is for dev only and having it run during the build causes
|
|
139
153
|
// the build to run very slow as the filewatcher is triggered often.
|
|
140
154
|
vitePluginApp(),
|
|
155
|
+
vitePluginFetchable({ settings }),
|
|
141
156
|
command === "dev" && vitePluginAstroServer({ settings, logger }),
|
|
142
157
|
command === "dev" && vitePluginAstroServerClient(),
|
|
143
158
|
astroDevCssPlugin({ routesList, command }),
|
package/dist/core/csp/runtime.js
CHANGED
|
@@ -8,25 +8,27 @@ function deduplicateDirectiveValues(existingDirective, newDirective) {
|
|
|
8
8
|
return `${directiveName} ${finalDirectives.join(" ")}`;
|
|
9
9
|
}
|
|
10
10
|
function pushDirective(directives, newDirective) {
|
|
11
|
-
let deduplicated = false;
|
|
12
11
|
if (directives.length === 0) {
|
|
13
12
|
return [newDirective];
|
|
14
13
|
}
|
|
15
14
|
const finalDirectives = [];
|
|
15
|
+
let matched = false;
|
|
16
16
|
for (const directive of directives) {
|
|
17
|
-
if (
|
|
17
|
+
if (matched) {
|
|
18
18
|
finalDirectives.push(directive);
|
|
19
19
|
continue;
|
|
20
20
|
}
|
|
21
21
|
const result = deduplicateDirectiveValues(directive, newDirective);
|
|
22
22
|
if (result) {
|
|
23
23
|
finalDirectives.push(result);
|
|
24
|
-
|
|
24
|
+
matched = true;
|
|
25
25
|
} else {
|
|
26
26
|
finalDirectives.push(directive);
|
|
27
|
-
finalDirectives.push(newDirective);
|
|
28
27
|
}
|
|
29
28
|
}
|
|
29
|
+
if (!matched) {
|
|
30
|
+
finalDirectives.push(newDirective);
|
|
31
|
+
}
|
|
30
32
|
return finalDirectives;
|
|
31
33
|
}
|
|
32
34
|
export {
|
package/dist/core/dev/dev.js
CHANGED
|
@@ -37,7 +37,7 @@ async function dev(inlineConfig) {
|
|
|
37
37
|
await telemetry.record([]);
|
|
38
38
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
39
39
|
const logger = restart.container.logger;
|
|
40
|
-
const currentVersion = "6.
|
|
40
|
+
const currentVersion = "6.3.0";
|
|
41
41
|
const isPrerelease = currentVersion.includes("-");
|
|
42
42
|
if (!isPrerelease) {
|
|
43
43
|
try {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BaseApp, RenderErrorOptions } from '../app/base.js';
|
|
2
|
+
import type { Pipeline } from '../base-pipeline.js';
|
|
3
|
+
import type { ErrorHandler } from './handler.js';
|
|
4
|
+
/**
|
|
5
|
+
* The error handler used during static build / prerendering.
|
|
6
|
+
*
|
|
7
|
+
* - For 500 errors, returns the original response if present, otherwise
|
|
8
|
+
* throws so the build surfaces the underlying error to the developer.
|
|
9
|
+
* - For other errors (e.g. 404), delegates to `DefaultErrorHandler` with
|
|
10
|
+
* `prerenderedErrorPageFetch` cleared (the build pipeline can't fetch
|
|
11
|
+
* prerendered pages the way production SSR can).
|
|
12
|
+
*/
|
|
13
|
+
export declare class BuildErrorHandler implements ErrorHandler {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(app: BaseApp<Pipeline>);
|
|
16
|
+
renderError(request: Request, options: RenderErrorOptions): Promise<Response>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DefaultErrorHandler } from "./default-handler.js";
|
|
2
|
+
class BuildErrorHandler {
|
|
3
|
+
#default;
|
|
4
|
+
constructor(app) {
|
|
5
|
+
this.#default = new DefaultErrorHandler(app);
|
|
6
|
+
}
|
|
7
|
+
async renderError(request, options) {
|
|
8
|
+
if (options.status === 500) {
|
|
9
|
+
if (options.response) {
|
|
10
|
+
return options.response;
|
|
11
|
+
}
|
|
12
|
+
throw options.error;
|
|
13
|
+
}
|
|
14
|
+
return this.#default.renderError(request, {
|
|
15
|
+
...options,
|
|
16
|
+
prerenderedErrorPageFetch: void 0
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
BuildErrorHandler
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BaseApp, RenderErrorOptions } from '../app/base.js';
|
|
2
|
+
import type { Pipeline } from '../base-pipeline.js';
|
|
3
|
+
import type { ErrorHandler } from './handler.js';
|
|
4
|
+
/**
|
|
5
|
+
* The default error handler used in production SSR. Attempts to render the
|
|
6
|
+
* matching error route (404.astro / 500.astro), falling back to a plain
|
|
7
|
+
* response with the given status. Handles prerendered error pages via
|
|
8
|
+
* `prerenderedErrorPageFetch`.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DefaultErrorHandler implements ErrorHandler {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(app: BaseApp<Pipeline>);
|
|
13
|
+
renderError(request: Request, { status, response: originalResponse, skipMiddleware, error, pathname, ...resolvedRenderOptions }: RenderErrorOptions): Promise<Response>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { FetchState } from "../fetch/fetch-state.js";
|
|
2
|
+
import { prepareResponse } from "../app/prepare-response.js";
|
|
3
|
+
import { attachCookiesToResponse } from "../cookies/index.js";
|
|
4
|
+
import { getCookiesFromResponse } from "../cookies/response.js";
|
|
5
|
+
import { AstroMiddleware } from "../middleware/astro-middleware.js";
|
|
6
|
+
import { PagesHandler } from "../pages/handler.js";
|
|
7
|
+
import { matchRoute } from "../routing/match.js";
|
|
8
|
+
import { provideSession } from "../session/handler.js";
|
|
9
|
+
class DefaultErrorHandler {
|
|
10
|
+
#app;
|
|
11
|
+
#astroMiddleware;
|
|
12
|
+
#pagesHandler;
|
|
13
|
+
constructor(app) {
|
|
14
|
+
this.#app = app;
|
|
15
|
+
this.#astroMiddleware = new AstroMiddleware(app.pipeline);
|
|
16
|
+
this.#pagesHandler = new PagesHandler(app.pipeline);
|
|
17
|
+
}
|
|
18
|
+
async renderError(request, {
|
|
19
|
+
status,
|
|
20
|
+
response: originalResponse,
|
|
21
|
+
skipMiddleware = false,
|
|
22
|
+
error,
|
|
23
|
+
pathname,
|
|
24
|
+
...resolvedRenderOptions
|
|
25
|
+
}) {
|
|
26
|
+
const app = this.#app;
|
|
27
|
+
const resolvedPathname = pathname ?? new FetchState(app.pipeline, request).pathname;
|
|
28
|
+
const errorRoutePath = `/${status}${app.manifest.trailingSlash === "always" ? "/" : ""}`;
|
|
29
|
+
const errorRouteData = matchRoute(errorRoutePath, app.manifestData);
|
|
30
|
+
const url = new URL(request.url);
|
|
31
|
+
if (errorRouteData) {
|
|
32
|
+
if (errorRouteData.prerender) {
|
|
33
|
+
const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
|
|
34
|
+
const statusURL = new URL(`${app.baseWithoutTrailingSlash}/${status}${maybeDotHtml}`, url);
|
|
35
|
+
if (statusURL.toString() !== request.url && resolvedRenderOptions.prerenderedErrorPageFetch) {
|
|
36
|
+
const response2 = await resolvedRenderOptions.prerenderedErrorPageFetch(
|
|
37
|
+
statusURL.toString()
|
|
38
|
+
);
|
|
39
|
+
const override = { status, removeContentEncodingHeaders: true };
|
|
40
|
+
const newResponse = mergeResponses(response2, originalResponse, override);
|
|
41
|
+
prepareResponse(newResponse, resolvedRenderOptions);
|
|
42
|
+
return newResponse;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const mod = await app.pipeline.getComponentByRoute(errorRouteData);
|
|
46
|
+
const errorState = new FetchState(app.pipeline, request);
|
|
47
|
+
errorState.skipMiddleware = skipMiddleware;
|
|
48
|
+
errorState.clientAddress = resolvedRenderOptions.clientAddress;
|
|
49
|
+
errorState.routeData = errorRouteData;
|
|
50
|
+
errorState.pathname = resolvedPathname;
|
|
51
|
+
errorState.status = status;
|
|
52
|
+
errorState.componentInstance = mod;
|
|
53
|
+
errorState.locals = resolvedRenderOptions.locals ?? {};
|
|
54
|
+
errorState.initialProps = { error };
|
|
55
|
+
try {
|
|
56
|
+
await provideSession(errorState);
|
|
57
|
+
const response2 = await this.#astroMiddleware.handle(
|
|
58
|
+
errorState,
|
|
59
|
+
this.#pagesHandler.handle.bind(this.#pagesHandler)
|
|
60
|
+
);
|
|
61
|
+
const newResponse = mergeResponses(response2, originalResponse);
|
|
62
|
+
prepareResponse(newResponse, resolvedRenderOptions);
|
|
63
|
+
return newResponse;
|
|
64
|
+
} catch {
|
|
65
|
+
if (skipMiddleware === false) {
|
|
66
|
+
return this.renderError(request, {
|
|
67
|
+
...resolvedRenderOptions,
|
|
68
|
+
status,
|
|
69
|
+
response: originalResponse,
|
|
70
|
+
skipMiddleware: true,
|
|
71
|
+
pathname: resolvedPathname
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
} finally {
|
|
75
|
+
await errorState.finalizeAll();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const response = mergeResponses(new Response(null, { status }), originalResponse);
|
|
79
|
+
prepareResponse(response, resolvedRenderOptions);
|
|
80
|
+
return response;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function mergeResponses(newResponse, originalResponse, override) {
|
|
84
|
+
let newResponseHeaders = newResponse.headers;
|
|
85
|
+
if (override?.removeContentEncodingHeaders) {
|
|
86
|
+
newResponseHeaders = new Headers(newResponseHeaders);
|
|
87
|
+
newResponseHeaders.delete("Content-Encoding");
|
|
88
|
+
newResponseHeaders.delete("Content-Length");
|
|
89
|
+
}
|
|
90
|
+
if (!originalResponse) {
|
|
91
|
+
if (override !== void 0) {
|
|
92
|
+
return new Response(newResponse.body, {
|
|
93
|
+
status: override.status,
|
|
94
|
+
statusText: newResponse.statusText,
|
|
95
|
+
headers: newResponseHeaders
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return newResponse;
|
|
99
|
+
}
|
|
100
|
+
const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
|
|
101
|
+
try {
|
|
102
|
+
originalResponse.headers.delete("Content-type");
|
|
103
|
+
originalResponse.headers.delete("Content-Length");
|
|
104
|
+
originalResponse.headers.delete("Transfer-Encoding");
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
const newHeaders = new Headers();
|
|
108
|
+
const seen = /* @__PURE__ */ new Set();
|
|
109
|
+
for (const [name, value] of originalResponse.headers) {
|
|
110
|
+
newHeaders.append(name, value);
|
|
111
|
+
seen.add(name.toLowerCase());
|
|
112
|
+
}
|
|
113
|
+
for (const [name, value] of newResponseHeaders) {
|
|
114
|
+
if (!seen.has(name.toLowerCase())) {
|
|
115
|
+
newHeaders.append(name, value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const mergedResponse = new Response(newResponse.body, {
|
|
119
|
+
status,
|
|
120
|
+
statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
|
|
121
|
+
// If you're looking at here for possible bugs, it means that it's not a bug.
|
|
122
|
+
// With the middleware, users can meddle with headers, and we should pass to the 404/500.
|
|
123
|
+
// If users see something weird, it's because they are setting some headers they should not.
|
|
124
|
+
//
|
|
125
|
+
// Although, we don't want it to replace the content-type, because the error page must return `text/html`
|
|
126
|
+
headers: newHeaders
|
|
127
|
+
});
|
|
128
|
+
const originalCookies = getCookiesFromResponse(originalResponse);
|
|
129
|
+
const newCookies = getCookiesFromResponse(newResponse);
|
|
130
|
+
if (originalCookies) {
|
|
131
|
+
if (newCookies) {
|
|
132
|
+
for (const cookieValue of newCookies.consume()) {
|
|
133
|
+
originalResponse.headers.append("set-cookie", cookieValue);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
attachCookiesToResponse(mergedResponse, originalCookies);
|
|
137
|
+
} else if (newCookies) {
|
|
138
|
+
attachCookiesToResponse(mergedResponse, newCookies);
|
|
139
|
+
}
|
|
140
|
+
return mergedResponse;
|
|
141
|
+
}
|
|
142
|
+
export {
|
|
143
|
+
DefaultErrorHandler
|
|
144
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BaseApp, RenderErrorOptions } from '../app/base.js';
|
|
2
|
+
import type { Pipeline } from '../base-pipeline.js';
|
|
3
|
+
import type { ErrorHandler } from './handler.js';
|
|
4
|
+
export interface DevErrorHandlerOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether to inject CSP meta tags into the rendered error page response.
|
|
7
|
+
* The Vite dev server injects them; the non-runnable dev pipeline does not.
|
|
8
|
+
*/
|
|
9
|
+
shouldInjectCspMetaTags: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The dev-server error handler. Renders custom 404/500 routes if the user
|
|
13
|
+
* has them, otherwise throws so Vite's dev overlay is shown. Shared between
|
|
14
|
+
* the Vite dev server (`AstroServerApp`) and the non-runnable dev pipeline
|
|
15
|
+
* (`DevApp`); only `shouldInjectCspMetaTags` differs between them.
|
|
16
|
+
*/
|
|
17
|
+
export declare class DevErrorHandler implements ErrorHandler {
|
|
18
|
+
#private;
|
|
19
|
+
constructor(app: BaseApp<Pipeline>, options: DevErrorHandlerOptions);
|
|
20
|
+
renderError(request: Request, { skipMiddleware, error, status, response: _response, pathname, ...resolvedRenderOptions }: RenderErrorOptions): Promise<Response>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { FetchState } from "../fetch/fetch-state.js";
|
|
2
|
+
import { AstroMiddleware } from "../middleware/astro-middleware.js";
|
|
3
|
+
import { PagesHandler } from "../pages/handler.js";
|
|
4
|
+
import { getCustom404Route, getCustom500Route } from "../routing/helpers.js";
|
|
5
|
+
import { isAstroError } from "./index.js";
|
|
6
|
+
import { MiddlewareNoDataOrNextCalled, MiddlewareNotAResponse } from "./errors-data.js";
|
|
7
|
+
class DevErrorHandler {
|
|
8
|
+
#app;
|
|
9
|
+
#shouldInjectCspMetaTags;
|
|
10
|
+
#astroMiddleware;
|
|
11
|
+
#pagesHandler;
|
|
12
|
+
constructor(app, options) {
|
|
13
|
+
this.#app = app;
|
|
14
|
+
this.#shouldInjectCspMetaTags = options.shouldInjectCspMetaTags;
|
|
15
|
+
this.#astroMiddleware = new AstroMiddleware(app.pipeline);
|
|
16
|
+
this.#pagesHandler = new PagesHandler(app.pipeline);
|
|
17
|
+
}
|
|
18
|
+
async renderError(request, {
|
|
19
|
+
skipMiddleware = false,
|
|
20
|
+
error,
|
|
21
|
+
status,
|
|
22
|
+
response: _response,
|
|
23
|
+
pathname,
|
|
24
|
+
...resolvedRenderOptions
|
|
25
|
+
}) {
|
|
26
|
+
if (isAstroError(error) && [MiddlewareNoDataOrNextCalled.name, MiddlewareNotAResponse.name].includes(error.name)) {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
const app = this.#app;
|
|
30
|
+
const shouldInjectCspMetaTags = this.#shouldInjectCspMetaTags;
|
|
31
|
+
const resolvedPathname = pathname ?? new FetchState(app.pipeline, request).pathname;
|
|
32
|
+
const renderRoute = async (routeData) => {
|
|
33
|
+
try {
|
|
34
|
+
const preloadedComponent = await app.pipeline.getComponentByRoute(routeData);
|
|
35
|
+
const errorState = new FetchState(app.pipeline, request);
|
|
36
|
+
errorState.skipMiddleware = skipMiddleware;
|
|
37
|
+
errorState.clientAddress = resolvedRenderOptions.clientAddress;
|
|
38
|
+
errorState.shouldInjectCspMetaTags = shouldInjectCspMetaTags ? !!app.manifest.csp : false;
|
|
39
|
+
errorState.routeData = routeData;
|
|
40
|
+
errorState.pathname = resolvedPathname;
|
|
41
|
+
errorState.status = status;
|
|
42
|
+
errorState.componentInstance = preloadedComponent;
|
|
43
|
+
errorState.locals = resolvedRenderOptions.locals ?? {};
|
|
44
|
+
errorState.initialProps = { error };
|
|
45
|
+
const response = await this.#astroMiddleware.handle(
|
|
46
|
+
errorState,
|
|
47
|
+
this.#pagesHandler.handle.bind(this.#pagesHandler)
|
|
48
|
+
);
|
|
49
|
+
if (error) {
|
|
50
|
+
app.logger.error("router", error.stack || error.message);
|
|
51
|
+
}
|
|
52
|
+
return response;
|
|
53
|
+
} catch (_err) {
|
|
54
|
+
if (skipMiddleware === false) {
|
|
55
|
+
return this.renderError(request, {
|
|
56
|
+
...resolvedRenderOptions,
|
|
57
|
+
status: 500,
|
|
58
|
+
skipMiddleware: true,
|
|
59
|
+
error: _err,
|
|
60
|
+
pathname: resolvedPathname
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
throw _err;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (status === 404) {
|
|
67
|
+
const custom404 = getCustom404Route(app.manifestData);
|
|
68
|
+
if (custom404) {
|
|
69
|
+
return renderRoute(custom404);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const custom500 = getCustom500Route(app.manifestData);
|
|
73
|
+
if (!custom500) {
|
|
74
|
+
throw error;
|
|
75
|
+
} else {
|
|
76
|
+
return renderRoute(custom500);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
DevErrorHandler
|
|
82
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RenderErrorOptions } from '../app/base.js';
|
|
2
|
+
/**
|
|
3
|
+
* A strategy for rendering error responses (404, 500, etc.). Each execution
|
|
4
|
+
* environment (prod SSR, build/prerender, dev server) supplies its own
|
|
5
|
+
* implementation rather than overriding a method on the app.
|
|
6
|
+
*/
|
|
7
|
+
export interface ErrorHandler {
|
|
8
|
+
renderError(request: Request, options: RenderErrorOptions): Promise<Response>;
|
|
9
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BaseApp, ResolvedRenderOptions } from '../app/base.js';
|
|
2
|
+
import type { Pipeline } from '../base-pipeline.js';
|
|
3
|
+
import type { FetchHandler } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The default request handler for `BaseApp`. Builds the per-request
|
|
6
|
+
* `FetchState` and delegates to an `AstroHandler`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class DefaultFetchHandler {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(app?: BaseApp<Pipeline>);
|
|
11
|
+
/**
|
|
12
|
+
* Fast path: called directly by `BaseApp.render()` with pre-resolved
|
|
13
|
+
* options, avoiding the `Reflect.set/get` round-trip through the request.
|
|
14
|
+
*/
|
|
15
|
+
renderWithOptions(request: Request, options: ResolvedRenderOptions): Promise<Response>;
|
|
16
|
+
fetch: FetchHandler;
|
|
17
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { FetchState } from "./fetch-state.js";
|
|
2
|
+
import { appSymbol } from "../constants.js";
|
|
3
|
+
import { AstroHandler } from "../routing/handler.js";
|
|
4
|
+
class DefaultFetchHandler {
|
|
5
|
+
#app;
|
|
6
|
+
#handler;
|
|
7
|
+
constructor(app) {
|
|
8
|
+
this.#app = app ?? null;
|
|
9
|
+
this.#handler = app ? new AstroHandler(app) : null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fast path: called directly by `BaseApp.render()` with pre-resolved
|
|
13
|
+
* options, avoiding the `Reflect.set/get` round-trip through the request.
|
|
14
|
+
*/
|
|
15
|
+
renderWithOptions(request, options) {
|
|
16
|
+
if (!this.#app) {
|
|
17
|
+
const app = Reflect.get(request, appSymbol);
|
|
18
|
+
if (!app) {
|
|
19
|
+
throw new Error("No fetch handler provided.");
|
|
20
|
+
}
|
|
21
|
+
this.#app = app;
|
|
22
|
+
this.#handler = new AstroHandler(app);
|
|
23
|
+
}
|
|
24
|
+
const state = new FetchState(this.#app.pipeline, request, options);
|
|
25
|
+
return this.#handler.handle(state);
|
|
26
|
+
}
|
|
27
|
+
fetch = (request) => {
|
|
28
|
+
if (!this.#app) {
|
|
29
|
+
const app = Reflect.get(request, appSymbol);
|
|
30
|
+
if (!app) {
|
|
31
|
+
throw new Error("No fetch handler provided.");
|
|
32
|
+
}
|
|
33
|
+
this.#app = app;
|
|
34
|
+
this.#handler = new AstroHandler(app);
|
|
35
|
+
}
|
|
36
|
+
const state = new FetchState(this.#app.pipeline, request);
|
|
37
|
+
if (!this.#handler) {
|
|
38
|
+
throw new Error("No fetch handler provided.");
|
|
39
|
+
}
|
|
40
|
+
return this.#handler.handle(state);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
DefaultFetchHandler
|
|
45
|
+
};
|