astro 6.2.1 → 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/internal.js +16 -1
- package/dist/assets/services/sharp.js +16 -1
- 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/add/index.js +7 -4
- 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/loaders/types.d.ts +1 -1
- 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/build/plugins/plugin-css.js +7 -1
- package/dist/core/build/plugins/plugin-manifest.js +11 -1
- package/dist/core/build/static-build.js +16 -2
- package/dist/core/cache/handler.d.ts +29 -0
- package/dist/core/cache/handler.js +81 -0
- package/dist/core/compile/style.js +18 -1
- package/dist/core/config/index.d.ts +1 -1
- package/dist/core/config/index.js +4 -1
- 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/config/settings.js +2 -4
- package/dist/core/config/tsconfig.d.ts +24 -9
- package/dist/core/config/tsconfig.js +54 -45
- 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/render/params-and-props.js +1 -1
- package/dist/core/render/slots.js +9 -2
- 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/pattern.js +1 -1
- package/dist/core/routing/rewrite.js +1 -4
- 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/session/runtime.js +7 -2
- 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/prefetch/index.js +12 -7
- 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/common.js +10 -4
- package/dist/runtime/server/render/server-islands.js +2 -1
- package/dist/runtime/server/scripts.js +6 -0
- package/dist/types/public/config.d.ts +46 -12
- package/dist/types/public/content.d.ts +4 -4
- 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 -68
- package/dist/vite-plugin-astro/compile.js +2 -2
- package/dist/vite-plugin-astro/utils.d.ts +1 -0
- package/dist/vite-plugin-astro/utils.js +9 -1
- package/dist/vite-plugin-head/index.js +36 -19
- package/dist/vite-plugin-utils/index.d.ts +1 -0
- package/dist/vite-plugin-utils/index.js +3 -0
- package/package.json +13 -6
- package/dist/core/render-context.d.ts +0 -77
- package/dist/core/render-context.js +0 -826
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type { ActionAPIContext } from '../../actions/runtime/types.js';
|
|
2
|
+
import type { ComponentInstance } from '../../types/astro.js';
|
|
3
|
+
import type { Params, Props, RewritePayload } from '../../types/public/common.js';
|
|
4
|
+
import type { APIContext, AstroGlobal } from '../../types/public/context.js';
|
|
5
|
+
import type { RouteData, SSRResult } from '../../types/public/internal.js';
|
|
6
|
+
import { AstroCookies } from '../cookies/index.js';
|
|
7
|
+
import { type Pipeline } from '../render/index.js';
|
|
8
|
+
import type { ResolvedRenderOptions } from '../app/base.js';
|
|
9
|
+
/**
|
|
10
|
+
* Describes a lazily-created value that handlers can contribute to the
|
|
11
|
+
* request context. `create` is called at most once (on first `resolve`);
|
|
12
|
+
* `finalize` runs during `finalizeAll` to clean up / persist.
|
|
13
|
+
*/
|
|
14
|
+
export interface ContextProvider<T> {
|
|
15
|
+
/** Factory called lazily on the first `resolve(key)`. */
|
|
16
|
+
create: () => T;
|
|
17
|
+
/** Optional cleanup / persist callback. Receives the created value. */
|
|
18
|
+
finalize?: (value: T) => Promise<void> | void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The public contract of {@link FetchState} exposed to user-land code
|
|
22
|
+
* (custom fetch handlers, Hono middleware, etc.).
|
|
23
|
+
*
|
|
24
|
+
* Only the members listed here are part of the stable public API.
|
|
25
|
+
* Everything else on the concrete `FetchState` class is internal and
|
|
26
|
+
* may change without notice.
|
|
27
|
+
*
|
|
28
|
+
* If you add a new member to `FetchState` that should be user-visible,
|
|
29
|
+
* add it here first — the `implements` clause on the class ensures a
|
|
30
|
+
* compile-time error if the class falls out of sync.
|
|
31
|
+
*/
|
|
32
|
+
export interface AstroFetchState {
|
|
33
|
+
/** The incoming request. */
|
|
34
|
+
readonly request: Request;
|
|
35
|
+
/** Normalized URL derived from the request. */
|
|
36
|
+
readonly url: URL;
|
|
37
|
+
/** Base-stripped, decoded pathname of the request. */
|
|
38
|
+
readonly pathname: string;
|
|
39
|
+
/** The matched route for this request, if any. */
|
|
40
|
+
readonly routeData: RouteData | undefined;
|
|
41
|
+
/** Cookies for this request. */
|
|
42
|
+
readonly cookies: AstroCookies;
|
|
43
|
+
/** Request-scoped locals object, shared with user middleware. */
|
|
44
|
+
readonly locals: App.Locals;
|
|
45
|
+
/** Route params derived from routeData + pathname. */
|
|
46
|
+
readonly params: Params | undefined;
|
|
47
|
+
/** Default HTTP status for the rendered response. */
|
|
48
|
+
status: number;
|
|
49
|
+
/**
|
|
50
|
+
* Triggers a rewrite to a different route.
|
|
51
|
+
*
|
|
52
|
+
* [Astro reference](https://docs.astro.build/en/guides/routing/#rewrites)
|
|
53
|
+
*/
|
|
54
|
+
rewrite(payload: RewritePayload): Promise<Response>;
|
|
55
|
+
/**
|
|
56
|
+
* Registers a context provider under the given key. The `create`
|
|
57
|
+
* factory is called lazily on the first `resolve(key)`.
|
|
58
|
+
*/
|
|
59
|
+
provide<T>(key: string, provider: ContextProvider<T>): void;
|
|
60
|
+
/**
|
|
61
|
+
* Lazily resolves a provider registered under `key`. Returns
|
|
62
|
+
* `undefined` if no provider was registered for the key.
|
|
63
|
+
*/
|
|
64
|
+
resolve<T>(key: string): T | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Runs all registered provider `finalize` callbacks. Call this after
|
|
67
|
+
* the response is produced, typically in a `finally` block.
|
|
68
|
+
*/
|
|
69
|
+
finalizeAll(): Promise<void> | void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Retrieves the `FetchState` stashed on an `APIContext` by
|
|
73
|
+
* `FetchState.getAPIContext()`. Throws if not found — this indicates
|
|
74
|
+
* the context was not created through Astro's request pipeline.
|
|
75
|
+
*/
|
|
76
|
+
export declare function getFetchStateFromAPIContext(context: APIContext): FetchState;
|
|
77
|
+
/**
|
|
78
|
+
* Holds per-request state as it flows through the handler pipeline.
|
|
79
|
+
*
|
|
80
|
+
* **This class is user-facing** via `astro/fetch` and `astro/hono`.
|
|
81
|
+
* The {@link AstroFetchState} interface defines the stable public
|
|
82
|
+
* surface. Members not on that interface are internal and
|
|
83
|
+
* may change without notice.
|
|
84
|
+
*
|
|
85
|
+
* Performance note: fields on this class are plain properties — ES
|
|
86
|
+
* private fields (`#foo`) have a non-zero per-access cost in V8
|
|
87
|
+
* which is measurable on the hot render path, so `#` is used only
|
|
88
|
+
* for rarely-accessed memoized caches and Maps.
|
|
89
|
+
*/
|
|
90
|
+
export declare class FetchState implements AstroFetchState {
|
|
91
|
+
#private;
|
|
92
|
+
pipeline: Pipeline;
|
|
93
|
+
/**
|
|
94
|
+
* The request to render. Mutated during rewrites so subsequent renders
|
|
95
|
+
* see the rewritten URL.
|
|
96
|
+
*/
|
|
97
|
+
request: Request;
|
|
98
|
+
routeData: RouteData | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* The pathname to use for routing and rendering. Starts out as the raw,
|
|
101
|
+
* base-stripped, decoded pathname from the request. May be further
|
|
102
|
+
* normalized by `AstroHandler` after routeData is known (in dev, when
|
|
103
|
+
* the matched route has no `.html` extension, `.html` / `/index.html`
|
|
104
|
+
* suffixes are stripped).
|
|
105
|
+
*/
|
|
106
|
+
pathname: string;
|
|
107
|
+
/** Resolved render options (addCookieHeader, clientAddress, locals, etc.). */
|
|
108
|
+
readonly renderOptions: ResolvedRenderOptions;
|
|
109
|
+
/** When the request started, used to log duration. */
|
|
110
|
+
readonly timeStart: number;
|
|
111
|
+
/**
|
|
112
|
+
* The route's loaded component module. Set before middleware runs; may
|
|
113
|
+
* be swapped during in-flight rewrites from inside the middleware chain.
|
|
114
|
+
*/
|
|
115
|
+
componentInstance: ComponentInstance | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* Slot overrides supplied by the container API. `undefined` for HTTP
|
|
118
|
+
* requests — `PagesHandler` coalesces to `{}` on read so we don't
|
|
119
|
+
* allocate an empty object per request.
|
|
120
|
+
*/
|
|
121
|
+
slots: Record<string, any> | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Default HTTP status for the rendered response. Callers override
|
|
124
|
+
* before rendering runs (e.g. `AstroHandler` sets this from
|
|
125
|
+
* `BaseApp.getDefaultStatusCode`; error handlers set `404` / `500`).
|
|
126
|
+
*/
|
|
127
|
+
status: number;
|
|
128
|
+
/** Whether user middleware should be skipped for this request. */
|
|
129
|
+
skipMiddleware: boolean;
|
|
130
|
+
/** A flag that tells the render content if the rewriting was triggered. */
|
|
131
|
+
isRewriting: boolean;
|
|
132
|
+
/** A safety net in case of loops (rewrite counter). */
|
|
133
|
+
counter: number;
|
|
134
|
+
/** Cookies for this request. Created lazily on first access. */
|
|
135
|
+
cookies: AstroCookies;
|
|
136
|
+
get params(): Params | undefined;
|
|
137
|
+
set params(value: Params | undefined);
|
|
138
|
+
/** Normalized URL for this request. */
|
|
139
|
+
url: URL;
|
|
140
|
+
/** Client address for this request. */
|
|
141
|
+
clientAddress: string | undefined;
|
|
142
|
+
/** Whether this is a partial render (container API). */
|
|
143
|
+
partial: boolean | undefined;
|
|
144
|
+
/** Whether to inject CSP meta tags. */
|
|
145
|
+
shouldInjectCspMetaTags: boolean | undefined;
|
|
146
|
+
/** Request-scoped locals object, shared with user middleware. */
|
|
147
|
+
locals: App.Locals;
|
|
148
|
+
/**
|
|
149
|
+
* Memoized `props` (see `getProps`). `null` means "not yet computed"
|
|
150
|
+
* — using `null` (rather than `undefined`) keeps the hidden class
|
|
151
|
+
* stable and distinct from a valid-but-empty result.
|
|
152
|
+
*/
|
|
153
|
+
props: APIContext['props'] | null;
|
|
154
|
+
/** Memoized `ActionAPIContext` (see `getActionAPIContext`). */
|
|
155
|
+
actionApiContext: ActionAPIContext | null;
|
|
156
|
+
/** Memoized `APIContext` (see `getAPIContext`). */
|
|
157
|
+
apiContext: APIContext | null;
|
|
158
|
+
/** SSR result for the current page render. */
|
|
159
|
+
result: SSRResult | undefined;
|
|
160
|
+
/** Initial props (from container/error handler). */
|
|
161
|
+
initialProps: Props;
|
|
162
|
+
constructor(pipeline: Pipeline, request: Request, options?: ResolvedRenderOptions);
|
|
163
|
+
/**
|
|
164
|
+
* Triggers a rewrite. Delegates to the Rewrites handler.
|
|
165
|
+
*/
|
|
166
|
+
rewrite(payload: RewritePayload): Promise<Response>;
|
|
167
|
+
/**
|
|
168
|
+
* Creates the SSR result for the current page render.
|
|
169
|
+
*/
|
|
170
|
+
createResult(mod: ComponentInstance, ctx: ActionAPIContext): Promise<SSRResult>;
|
|
171
|
+
/**
|
|
172
|
+
* Creates the Astro global object for a component render.
|
|
173
|
+
*/
|
|
174
|
+
createAstro(result: SSRResult, props: Record<string, any>, slotValues: Record<string, any> | null, apiContext: ActionAPIContext): AstroGlobal;
|
|
175
|
+
/**
|
|
176
|
+
* Creates the Astro page-level partial (prototype for Astro global).
|
|
177
|
+
*/
|
|
178
|
+
createAstroPagePartial(result: SSRResult, apiContext: ActionAPIContext): Omit<AstroGlobal, 'props' | 'self' | 'slots'>;
|
|
179
|
+
getClientAddress(): string;
|
|
180
|
+
getCookies(): AstroCookies;
|
|
181
|
+
getCsp(): APIContext['csp'];
|
|
182
|
+
computeCurrentLocale(): string | undefined;
|
|
183
|
+
computePreferredLocale(): string | undefined;
|
|
184
|
+
computePreferredLocaleList(): string[] | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Lazily loads the route's component module. Returns the cached
|
|
187
|
+
* instance if already loaded. The promise is cached so concurrent
|
|
188
|
+
* callers share the same load.
|
|
189
|
+
*/
|
|
190
|
+
loadComponentInstance(): Promise<ComponentInstance>;
|
|
191
|
+
/**
|
|
192
|
+
* Registers a context provider under the given key. Handlers call
|
|
193
|
+
* this to contribute values to the request context (e.g. sessions).
|
|
194
|
+
* The `create` factory is called lazily on the first `resolve(key)`.
|
|
195
|
+
*/
|
|
196
|
+
provide<T>(key: string, provider: ContextProvider<T>): void;
|
|
197
|
+
/**
|
|
198
|
+
* Lazily resolves a provider registered under `key`. Calls
|
|
199
|
+
* `provider.create()` on first access and caches the result.
|
|
200
|
+
* Returns `undefined` if no provider was registered for the key.
|
|
201
|
+
*/
|
|
202
|
+
resolve<T>(key: string): T | undefined;
|
|
203
|
+
/**
|
|
204
|
+
* Runs all registered `finalize` callbacks. Should be called after
|
|
205
|
+
* the response is produced, typically in a `finally` block.
|
|
206
|
+
*
|
|
207
|
+
* Returns synchronously (no promise allocation) when nothing needs
|
|
208
|
+
* finalizing — important for the hot path where sessions are not used.
|
|
209
|
+
*/
|
|
210
|
+
finalizeAll(): Promise<void> | void;
|
|
211
|
+
/**
|
|
212
|
+
* Adds lazy getters to `target` for each registered provider key.
|
|
213
|
+
* Used by context creation (APIContext, Astro global) so that
|
|
214
|
+
* provider values like `session` and `cache` appear as properties
|
|
215
|
+
* without hard-coding the keys.
|
|
216
|
+
*/
|
|
217
|
+
defineProviderGetters(target: Record<string, any>): void;
|
|
218
|
+
/**
|
|
219
|
+
* Returns the resolved `props` for this render, computing them lazily
|
|
220
|
+
* from the route + component module on first access. If the
|
|
221
|
+
* `initialProps` already carries user-supplied props (e.g. the
|
|
222
|
+
* container API) those are used verbatim.
|
|
223
|
+
*/
|
|
224
|
+
getProps(): Promise<APIContext['props']>;
|
|
225
|
+
/**
|
|
226
|
+
* Returns the `ActionAPIContext` for this render, creating it lazily.
|
|
227
|
+
* Used by middleware, actions, and page dispatch.
|
|
228
|
+
*/
|
|
229
|
+
getActionAPIContext(): ActionAPIContext;
|
|
230
|
+
/**
|
|
231
|
+
* Returns the `APIContext` for this render, creating it lazily from
|
|
232
|
+
* the memoized props + action context.
|
|
233
|
+
*
|
|
234
|
+
* Callers must ensure `getProps()` has resolved at least once before
|
|
235
|
+
* calling this.
|
|
236
|
+
*/
|
|
237
|
+
getAPIContext(): APIContext;
|
|
238
|
+
/**
|
|
239
|
+
* Invalidates the cached `APIContext` so the next `getAPIContext()`
|
|
240
|
+
* call re-derives it from the (possibly mutated) state. Used
|
|
241
|
+
* after an in-flight rewrite swaps the route / request / params.
|
|
242
|
+
*/
|
|
243
|
+
invalidateContexts(): void;
|
|
244
|
+
}
|