blank project

This commit is contained in:
root
2025-10-17 20:17:33 +00:00
commit 14b2d53e8e
9366 changed files with 1515019 additions and 0 deletions

2
node_modules/astro/dist/core/app/common.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import type { SerializedSSRManifest, SSRManifest } from './types.js';
export declare function deserializeManifest(serializedManifest: SerializedSSRManifest): SSRManifest;

37
node_modules/astro/dist/core/app/common.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
import { decodeKey } from "../encryption.js";
import { NOOP_MIDDLEWARE_FN } from "../middleware/noop-middleware.js";
import { deserializeRouteData } from "../routing/manifest/serialization.js";
function deserializeManifest(serializedManifest) {
const routes = [];
for (const serializedRoute of serializedManifest.routes) {
routes.push({
...serializedRoute,
routeData: deserializeRouteData(serializedRoute.routeData)
});
const route = serializedRoute;
route.routeData = deserializeRouteData(serializedRoute.routeData);
}
const assets = new Set(serializedManifest.assets);
const componentMetadata = new Map(serializedManifest.componentMetadata);
const inlinedScripts = new Map(serializedManifest.inlinedScripts);
const clientDirectives = new Map(serializedManifest.clientDirectives);
const serverIslandNameMap = new Map(serializedManifest.serverIslandNameMap);
const key = decodeKey(serializedManifest.key);
return {
// in case user middleware exists, this no-op middleware will be reassigned (see plugin-ssr.ts)
middleware() {
return { onRequest: NOOP_MIDDLEWARE_FN };
},
...serializedManifest,
assets,
componentMetadata,
inlinedScripts,
clientDirectives,
routes,
serverIslandNameMap,
key
};
}
export {
deserializeManifest
};

View File

@@ -0,0 +1,9 @@
import type { OutgoingHttpHeaders } from 'node:http';
/**
* Takes in a nullable WebAPI Headers object and produces a NodeJS OutgoingHttpHeaders object suitable for usage
* with ServerResponse.writeHead(..) or ServerResponse.setHeader(..)
*
* @param headers WebAPI Headers object
* @returns {OutgoingHttpHeaders} NodeJS OutgoingHttpHeaders object with multiple set-cookie handled as an array of values
*/
export declare const createOutgoingHttpHeaders: (headers: Headers | undefined | null) => OutgoingHttpHeaders | undefined;

View File

@@ -0,0 +1,19 @@
const createOutgoingHttpHeaders = (headers) => {
if (!headers) {
return void 0;
}
const nodeHeaders = Object.fromEntries(headers.entries());
if (Object.keys(nodeHeaders).length === 0) {
return void 0;
}
if (headers.has("set-cookie")) {
const cookieHeaders = headers.getSetCookie();
if (cookieHeaders.length > 1) {
nodeHeaders["set-cookie"] = cookieHeaders;
}
}
return nodeHeaders;
};
export {
createOutgoingHttpHeaders
};

99
node_modules/astro/dist/core/app/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,99 @@
import { type RemotePattern } from '../../assets/utils/remotePattern.js';
import type { RoutesList } from '../../types/astro.js';
import type { RouteData, SSRManifest } from '../../types/public/internal.js';
import { getSetCookiesFromResponse } from '../cookies/index.js';
import { AstroIntegrationLogger } from '../logger/core.js';
export { deserializeManifest } from './common.js';
type ErrorPagePath = `${string}/404` | `${string}/500` | `${string}/404/` | `${string}/500/` | `${string}404.html` | `${string}500.html`;
export interface RenderOptions {
/**
* Whether to automatically add all cookies written by `Astro.cookie.set()` to the response headers.
*
* When set to `true`, they will be added to the `Set-Cookie` header as comma-separated key=value pairs. You can use the standard `response.headers.getSetCookie()` API to read them individually.
*
* When set to `false`, the cookies will only be available from `App.getSetCookieFromResponse(response)`.
*
* @default {false}
*/
addCookieHeader?: boolean;
/**
* The client IP address that will be made available as `Astro.clientAddress` in pages, and as `ctx.clientAddress` in API routes and middleware.
*
* Default: `request[Symbol.for("astro.clientAddress")]`
*/
clientAddress?: string;
/**
* The mutable object that will be made available as `Astro.locals` in pages, and as `ctx.locals` in API routes and middleware.
*/
locals?: object;
/**
* A custom fetch function for retrieving prerendered pages - 404 or 500.
*
* If not provided, Astro will fallback to its default behavior for fetching error pages.
*
* When a dynamic route is matched but ultimately results in a 404, this function will be used
* to fetch the prerendered 404 page if available. Similarly, it may be used to fetch a
* prerendered 500 error page when necessary.
*
* @param {ErrorPagePath} url - The URL of the prerendered 404 or 500 error page to fetch.
* @returns {Promise<Response>} A promise resolving to the prerendered response.
*/
prerenderedErrorPageFetch?: (url: ErrorPagePath) => Promise<Response>;
/**
* **Advanced API**: you probably do not need to use this.
*
* Default: `app.match(request)`
*/
routeData?: RouteData;
}
export interface RenderErrorOptions {
locals?: App.Locals;
routeData?: RouteData;
response?: Response;
status: 404 | 500;
/**
* Whether to skip middleware while rendering the error page. Defaults to false.
*/
skipMiddleware?: boolean;
/**
* Allows passing an error to 500.astro. It will be available through `Astro.props.error`.
*/
error?: unknown;
clientAddress: string | undefined;
prerenderedErrorPageFetch: (url: ErrorPagePath) => Promise<Response>;
}
export declare class App {
#private;
constructor(manifest: SSRManifest, streaming?: boolean);
getAdapterLogger(): AstroIntegrationLogger;
getAllowedDomains(): Partial<RemotePattern>[] | undefined;
protected get manifest(): SSRManifest;
protected set manifest(value: SSRManifest);
protected matchesAllowedDomains(forwardedHost: string, protocol?: string): boolean;
static validateForwardedHost(forwardedHost: string, allowedDomains?: Partial<RemotePattern>[], protocol?: string): boolean;
set setManifestData(newManifestData: RoutesList);
removeBase(pathname: string): string;
/**
* Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
* routes aren't returned, even if they are matched.
*
* When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
* @param request
* @param allowPrerenderedRoutes
*/
match(request: Request, allowPrerenderedRoutes?: boolean): RouteData | undefined;
render(request: Request, renderOptions?: RenderOptions): Promise<Response>;
setCookieHeaders(response: Response): Generator<string, string[], any>;
/**
* Reads all the cookies written by `Astro.cookie.set()` onto the passed response.
* For example,
* ```ts
* for (const cookie_ of App.getSetCookieFromResponse(response)) {
* const cookie: string = cookie_
* }
* ```
* @param response The response to read cookies from.
* @returns An iterator that yields key-value pairs as equal-sign-separated strings.
*/
static getSetCookieFromResponse: typeof getSetCookiesFromResponse;
}

494
node_modules/astro/dist/core/app/index.js generated vendored Normal file
View File

@@ -0,0 +1,494 @@
import {
collapseDuplicateTrailingSlashes,
hasFileExtension,
isInternalPath
} from "@astrojs/internal-helpers/path";
import { matchPattern } from "../../assets/utils/remotePattern.js";
import { normalizeTheLocale } from "../../i18n/index.js";
import {
clientAddressSymbol,
DEFAULT_404_COMPONENT,
REROUTABLE_STATUS_CODES,
REROUTE_DIRECTIVE_HEADER,
responseSentSymbol
} from "../constants.js";
import { getSetCookiesFromResponse } from "../cookies/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { consoleLogDestination } from "../logger/console.js";
import { AstroIntegrationLogger, Logger } from "../logger/core.js";
import { NOOP_MIDDLEWARE_FN } from "../middleware/noop-middleware.js";
import {
appendForwardSlash,
joinPaths,
prependForwardSlash,
removeTrailingForwardSlash
} from "../path.js";
import { createAssetLink } from "../render/ssr-element.js";
import { RenderContext } from "../render-context.js";
import { redirectTemplate } from "../routing/3xx.js";
import { ensure404Route } from "../routing/astro-designed-error-pages.js";
import { createDefaultRoutes } from "../routing/default.js";
import { matchRoute } from "../routing/match.js";
import { PERSIST_SYMBOL } from "../session.js";
import { AppPipeline } from "./pipeline.js";
import { deserializeManifest } from "./common.js";
class App {
#manifest;
#manifestData;
#logger = new Logger({
dest: consoleLogDestination,
level: "info"
});
#baseWithoutTrailingSlash;
#pipeline;
#adapterLogger;
constructor(manifest, streaming = true) {
this.#manifest = manifest;
this.#manifestData = {
routes: manifest.routes.map((route) => route.routeData)
};
ensure404Route(this.#manifestData);
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
this.#pipeline = this.#createPipeline(streaming);
this.#adapterLogger = new AstroIntegrationLogger(
this.#logger.options,
this.#manifest.adapterName
);
}
getAdapterLogger() {
return this.#adapterLogger;
}
getAllowedDomains() {
return this.#manifest.allowedDomains;
}
get manifest() {
return this.#manifest;
}
set manifest(value) {
this.#manifest = value;
}
matchesAllowedDomains(forwardedHost, protocol) {
return App.validateForwardedHost(forwardedHost, this.#manifest.allowedDomains, protocol);
}
static validateForwardedHost(forwardedHost, allowedDomains, protocol) {
if (!allowedDomains || allowedDomains.length === 0) {
return false;
}
try {
const testUrl = new URL(`${protocol || "https"}://${forwardedHost}`);
return allowedDomains.some((pattern) => {
return matchPattern(testUrl, pattern);
});
} catch {
return false;
}
}
/**
* Creates a pipeline by reading the stored manifest
*
* @param streaming
* @private
*/
#createPipeline(streaming = false) {
return AppPipeline.create({
logger: this.#logger,
manifest: this.#manifest,
runtimeMode: "production",
renderers: this.#manifest.renderers,
defaultRoutes: createDefaultRoutes(this.#manifest),
resolve: async (specifier) => {
if (!(specifier in this.#manifest.entryModules)) {
throw new Error(`Unable to resolve [${specifier}]`);
}
const bundlePath = this.#manifest.entryModules[specifier];
if (bundlePath.startsWith("data:") || bundlePath.length === 0) {
return bundlePath;
} else {
return createAssetLink(bundlePath, this.#manifest.base, this.#manifest.assetsPrefix);
}
},
serverLike: true,
streaming
});
}
set setManifestData(newManifestData) {
this.#manifestData = newManifestData;
}
removeBase(pathname) {
if (pathname.startsWith(this.#manifest.base)) {
return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
}
return pathname;
}
/**
* It removes the base from the request URL, prepends it with a forward slash and attempts to decoded it.
*
* If the decoding fails, it logs the error and return the pathname as is.
* @param request
* @private
*/
#getPathnameFromRequest(request) {
const url = new URL(request.url);
const pathname = prependForwardSlash(this.removeBase(url.pathname));
try {
return decodeURI(pathname);
} catch (e) {
this.getAdapterLogger().error(e.toString());
return pathname;
}
}
/**
* Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
* routes aren't returned, even if they are matched.
*
* When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
* @param request
* @param allowPrerenderedRoutes
*/
match(request, allowPrerenderedRoutes = false) {
const url = new URL(request.url);
if (this.#manifest.assets.has(url.pathname)) return void 0;
let pathname = this.#computePathnameFromDomain(request);
if (!pathname) {
pathname = prependForwardSlash(this.removeBase(url.pathname));
}
let routeData = matchRoute(decodeURI(pathname), this.#manifestData);
if (!routeData) return void 0;
if (allowPrerenderedRoutes) {
return routeData;
} else if (routeData.prerender) {
return void 0;
}
return routeData;
}
#computePathnameFromDomain(request) {
let pathname = void 0;
const url = new URL(request.url);
if (this.#manifest.i18n && (this.#manifest.i18n.strategy === "domains-prefix-always" || this.#manifest.i18n.strategy === "domains-prefix-other-locales" || this.#manifest.i18n.strategy === "domains-prefix-always-no-redirect")) {
let forwardedHost = request.headers.get("X-Forwarded-Host");
let protocol = request.headers.get("X-Forwarded-Proto");
if (protocol) {
protocol = protocol + ":";
} else {
protocol = url.protocol;
}
if (forwardedHost && !this.matchesAllowedDomains(forwardedHost, protocol?.replace(":", ""))) {
forwardedHost = null;
}
let host = forwardedHost;
if (!host) {
host = request.headers.get("Host");
}
if (host && protocol) {
host = host.split(":")[0];
try {
let locale;
const hostAsUrl = new URL(`${protocol}//${host}`);
for (const [domainKey, localeValue] of Object.entries(
this.#manifest.i18n.domainLookupTable
)) {
const domainKeyAsUrl = new URL(domainKey);
if (hostAsUrl.host === domainKeyAsUrl.host && hostAsUrl.protocol === domainKeyAsUrl.protocol) {
locale = localeValue;
break;
}
}
if (locale) {
pathname = prependForwardSlash(
joinPaths(normalizeTheLocale(locale), this.removeBase(url.pathname))
);
if (url.pathname.endsWith("/")) {
pathname = appendForwardSlash(pathname);
}
}
} catch (e) {
this.#logger.error(
"router",
`Astro tried to parse ${protocol}//${host} as an URL, but it threw a parsing error. Check the X-Forwarded-Host and X-Forwarded-Proto headers.`
);
this.#logger.error("router", `Error: ${e}`);
}
}
}
return pathname;
}
#redirectTrailingSlash(pathname) {
const { trailingSlash } = this.#manifest;
if (pathname === "/" || isInternalPath(pathname)) {
return pathname;
}
const path = collapseDuplicateTrailingSlashes(pathname, trailingSlash !== "never");
if (path !== pathname) {
return path;
}
if (trailingSlash === "ignore") {
return pathname;
}
if (trailingSlash === "always" && !hasFileExtension(pathname)) {
return appendForwardSlash(pathname);
}
if (trailingSlash === "never") {
return removeTrailingForwardSlash(pathname);
}
return pathname;
}
async render(request, renderOptions) {
let routeData;
let locals;
let clientAddress;
let addCookieHeader;
const url = new URL(request.url);
const redirect = this.#redirectTrailingSlash(url.pathname);
const prerenderedErrorPageFetch = renderOptions?.prerenderedErrorPageFetch ?? fetch;
if (redirect !== url.pathname) {
const status = request.method === "GET" ? 301 : 308;
return new Response(
redirectTemplate({
status,
relativeLocation: url.pathname,
absoluteLocation: redirect,
from: request.url
}),
{
status,
headers: {
location: redirect + url.search
}
}
);
}
addCookieHeader = renderOptions?.addCookieHeader;
clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
routeData = renderOptions?.routeData;
locals = renderOptions?.locals;
if (routeData) {
this.#logger.debug(
"router",
"The adapter " + this.#manifest.adapterName + " provided a custom RouteData for ",
request.url
);
this.#logger.debug("router", "RouteData:\n" + routeData);
}
if (locals) {
if (typeof locals !== "object") {
const error = new AstroError(AstroErrorData.LocalsNotAnObject);
this.#logger.error(null, error.stack);
return this.#renderError(request, {
status: 500,
error,
clientAddress,
prerenderedErrorPageFetch
});
}
}
if (!routeData) {
routeData = this.match(request);
this.#logger.debug("router", "Astro matched the following route for " + request.url);
this.#logger.debug("router", "RouteData:\n" + routeData);
}
if (!routeData) {
routeData = this.#manifestData.routes.find(
(route) => route.component === "404.astro" || route.component === DEFAULT_404_COMPONENT
);
}
if (!routeData) {
this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
return this.#renderError(request, {
locals,
status: 404,
clientAddress,
prerenderedErrorPageFetch
});
}
const pathname = this.#getPathnameFromRequest(request);
const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
let response;
let session;
try {
const mod = await this.#pipeline.getModuleForRoute(routeData);
const renderContext = await RenderContext.create({
pipeline: this.#pipeline,
locals,
pathname,
request,
routeData,
status: defaultStatus,
clientAddress
});
session = renderContext.session;
response = await renderContext.render(await mod.page());
} catch (err) {
this.#logger.error(null, err.stack || err.message || String(err));
return this.#renderError(request, {
locals,
status: 500,
error: err,
clientAddress,
prerenderedErrorPageFetch
});
} finally {
await session?.[PERSIST_SYMBOL]();
}
if (REROUTABLE_STATUS_CODES.includes(response.status) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
return this.#renderError(request, {
locals,
response,
status: response.status,
// We don't have an error to report here. Passing null means we pass nothing intentionally
// while undefined means there's no error
error: response.status === 500 ? null : void 0,
clientAddress,
prerenderedErrorPageFetch
});
}
if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
response.headers.delete(REROUTE_DIRECTIVE_HEADER);
}
if (addCookieHeader) {
for (const setCookieHeaderValue of App.getSetCookieFromResponse(response)) {
response.headers.append("set-cookie", setCookieHeaderValue);
}
}
Reflect.set(response, responseSentSymbol, true);
return response;
}
setCookieHeaders(response) {
return getSetCookiesFromResponse(response);
}
/**
* Reads all the cookies written by `Astro.cookie.set()` onto the passed response.
* For example,
* ```ts
* for (const cookie_ of App.getSetCookieFromResponse(response)) {
* const cookie: string = cookie_
* }
* ```
* @param response The response to read cookies from.
* @returns An iterator that yields key-value pairs as equal-sign-separated strings.
*/
static getSetCookieFromResponse = getSetCookiesFromResponse;
/**
* If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
* This also handles pre-rendered /404 or /500 routes
*/
async #renderError(request, {
locals,
status,
response: originalResponse,
skipMiddleware = false,
error,
clientAddress,
prerenderedErrorPageFetch
}) {
const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
const url = new URL(request.url);
if (errorRouteData) {
if (errorRouteData.prerender) {
const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
const statusURL = new URL(
`${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
url
);
if (statusURL.toString() !== request.url) {
const response2 = await prerenderedErrorPageFetch(statusURL.toString());
const override = { status, removeContentEncodingHeaders: true };
return this.#mergeResponses(response2, originalResponse, override);
}
}
const mod = await this.#pipeline.getModuleForRoute(errorRouteData);
let session;
try {
const renderContext = await RenderContext.create({
locals,
pipeline: this.#pipeline,
middleware: skipMiddleware ? NOOP_MIDDLEWARE_FN : void 0,
pathname: this.#getPathnameFromRequest(request),
request,
routeData: errorRouteData,
status,
props: { error },
clientAddress
});
session = renderContext.session;
const response2 = await renderContext.render(await mod.page());
return this.#mergeResponses(response2, originalResponse);
} catch {
if (skipMiddleware === false) {
return this.#renderError(request, {
locals,
status,
response: originalResponse,
skipMiddleware: true,
clientAddress,
prerenderedErrorPageFetch
});
}
} finally {
await session?.[PERSIST_SYMBOL]();
}
}
const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
Reflect.set(response, responseSentSymbol, true);
return response;
}
#mergeResponses(newResponse, originalResponse, override) {
let newResponseHeaders = newResponse.headers;
if (override?.removeContentEncodingHeaders) {
newResponseHeaders = new Headers(newResponseHeaders);
newResponseHeaders.delete("Content-Encoding");
newResponseHeaders.delete("Content-Length");
}
if (!originalResponse) {
if (override !== void 0) {
return new Response(newResponse.body, {
status: override.status,
statusText: newResponse.statusText,
headers: newResponseHeaders
});
}
return newResponse;
}
const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
try {
originalResponse.headers.delete("Content-type");
} catch {
}
const mergedHeaders = new Map([
...Array.from(newResponseHeaders),
...Array.from(originalResponse.headers)
]);
const newHeaders = new Headers();
for (const [name, value] of mergedHeaders) {
newHeaders.set(name, value);
}
return new Response(newResponse.body, {
status,
statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
// If you're looking at here for possible bugs, it means that it's not a bug.
// With the middleware, users can meddle with headers, and we should pass to the 404/500.
// If users see something weird, it's because they are setting some headers they should not.
//
// Although, we don't want it to replace the content-type, because the error page must return `text/html`
headers: newHeaders
});
}
#getDefaultStatusCode(routeData, pathname) {
if (!routeData.pattern.test(pathname)) {
for (const fallbackRoute of routeData.fallbackRoutes) {
if (fallbackRoute.pattern.test(pathname)) {
return 302;
}
}
}
const route = removeTrailingForwardSlash(routeData.route);
if (route.endsWith("/404")) return 404;
if (route.endsWith("/500")) return 500;
return 200;
}
}
export {
App,
deserializeManifest
};

7
node_modules/astro/dist/core/app/middlewares.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import type { MiddlewareHandler } from '../../types/public/common.js';
/**
* Returns a middleware function in charge to check the `origin` header.
*
* @private
*/
export declare function createOriginCheckMiddleware(): MiddlewareHandler;

48
node_modules/astro/dist/core/app/middlewares.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import { defineMiddleware } from "../middleware/index.js";
const FORM_CONTENT_TYPES = [
"application/x-www-form-urlencoded",
"multipart/form-data",
"text/plain"
];
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
function createOriginCheckMiddleware() {
return defineMiddleware((context, next) => {
const { request, url, isPrerendered } = context;
if (isPrerendered) {
return next();
}
if (SAFE_METHODS.includes(request.method)) {
return next();
}
const isSameOrigin = request.headers.get("origin") === url.origin;
const hasContentType = request.headers.has("content-type");
if (hasContentType) {
const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
if (formLikeHeader && !isSameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403
});
}
} else {
if (!isSameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403
});
}
}
return next();
});
}
function hasFormLikeHeader(contentType) {
if (contentType) {
for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
return true;
}
}
}
return false;
}
export {
createOriginCheckMiddleware
};

60
node_modules/astro/dist/core/app/node.d.ts generated vendored Normal file
View File

@@ -0,0 +1,60 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { RemotePattern } from '../../types/public/config.js';
import type { RouteData } from '../../types/public/internal.js';
import type { RenderOptions } from './index.js';
import { App } from './index.js';
import type { NodeAppHeadersJson, SSRManifest } from './types.js';
export { apply as applyPolyfills } from '../polyfill.js';
/**
* Allow the request body to be explicitly overridden. For example, this
* is used by the Express JSON middleware.
*/
interface NodeRequest extends IncomingMessage {
body?: unknown;
}
export declare class NodeApp extends App {
headersMap: NodeAppHeadersJson | undefined;
setHeadersMap(headers: NodeAppHeadersJson): void;
match(req: NodeRequest | Request, allowPrerenderedRoutes?: boolean): RouteData | undefined;
render(request: NodeRequest | Request, options?: RenderOptions): Promise<Response>;
/**
* @deprecated Instead of passing `RouteData` and locals individually, pass an object with `routeData` and `locals` properties.
* See https://github.com/withastro/astro/pull/9199 for more information.
*/
render(request: NodeRequest | Request, routeData?: RouteData, locals?: object): Promise<Response>;
/**
* Converts a NodeJS IncomingMessage into a web standard Request.
* ```js
* import { NodeApp } from 'astro/app/node';
* import { createServer } from 'node:http';
*
* const server = createServer(async (req, res) => {
* const request = NodeApp.createRequest(req);
* const response = await app.render(request);
* await NodeApp.writeResponse(response, res);
* })
* ```
*/
static createRequest(req: NodeRequest, { skipBody, allowedDomains, }?: {
skipBody?: boolean;
allowedDomains?: Partial<RemotePattern>[];
}): Request;
/**
* Streams a web-standard Response into a NodeJS Server Response.
* ```js
* import { NodeApp } from 'astro/app/node';
* import { createServer } from 'node:http';
*
* const server = createServer(async (req, res) => {
* const request = NodeApp.createRequest(req);
* const response = await app.render(request);
* await NodeApp.writeResponse(response, res);
* })
* ```
* @param source WhatWG Response
* @param destination NodeJS ServerResponse
*/
static writeResponse(source: Response, destination: ServerResponse): Promise<ServerResponse<IncomingMessage> | undefined>;
}
export declare function loadManifest(rootFolder: URL): Promise<SSRManifest>;
export declare function loadApp(rootFolder: URL): Promise<NodeApp>;

264
node_modules/astro/dist/core/app/node.js generated vendored Normal file
View File

@@ -0,0 +1,264 @@
import fs from "node:fs";
import { Http2ServerResponse } from "node:http2";
import { clientAddressSymbol, nodeRequestAbortControllerCleanupSymbol } from "../constants.js";
import { deserializeManifest } from "./common.js";
import { createOutgoingHttpHeaders } from "./createOutgoingHttpHeaders.js";
import { App } from "./index.js";
import { apply } from "../polyfill.js";
class NodeApp extends App {
headersMap = void 0;
setHeadersMap(headers) {
this.headersMap = headers;
}
match(req, allowPrerenderedRoutes = false) {
if (!(req instanceof Request)) {
req = NodeApp.createRequest(req, {
skipBody: true,
allowedDomains: this.manifest.allowedDomains
});
}
return super.match(req, allowPrerenderedRoutes);
}
render(req, routeDataOrOptions, maybeLocals) {
if (!(req instanceof Request)) {
req = NodeApp.createRequest(req, {
allowedDomains: this.manifest.allowedDomains
});
}
return super.render(req, routeDataOrOptions, maybeLocals);
}
/**
* Converts a NodeJS IncomingMessage into a web standard Request.
* ```js
* import { NodeApp } from 'astro/app/node';
* import { createServer } from 'node:http';
*
* const server = createServer(async (req, res) => {
* const request = NodeApp.createRequest(req);
* const response = await app.render(request);
* await NodeApp.writeResponse(response, res);
* })
* ```
*/
static createRequest(req, {
skipBody = false,
allowedDomains = []
} = {}) {
const controller = new AbortController();
const isEncrypted = "encrypted" in req.socket && req.socket.encrypted;
const getFirstForwardedValue = (multiValueHeader) => {
return multiValueHeader?.toString()?.split(",").map((e) => e.trim())?.[0];
};
const forwardedProtocol = getFirstForwardedValue(req.headers["x-forwarded-proto"]);
const providedProtocol = isEncrypted ? "https" : "http";
const protocol = forwardedProtocol ?? providedProtocol;
let forwardedHostname = getFirstForwardedValue(req.headers["x-forwarded-host"]);
const providedHostname = req.headers.host ?? req.headers[":authority"];
if (forwardedHostname && !App.validateForwardedHost(
forwardedHostname,
allowedDomains,
forwardedProtocol ?? providedProtocol
)) {
forwardedHostname = void 0;
}
const hostname = forwardedHostname ?? providedHostname;
const port = getFirstForwardedValue(req.headers["x-forwarded-port"]);
let url;
try {
const hostnamePort = getHostnamePort(hostname, port);
url = new URL(`${protocol}://${hostnamePort}${req.url}`);
} catch {
const hostnamePort = getHostnamePort(providedHostname, port);
url = new URL(`${providedProtocol}://${hostnamePort}`);
}
const options = {
method: req.method || "GET",
headers: makeRequestHeaders(req),
signal: controller.signal
};
const bodyAllowed = options.method !== "HEAD" && options.method !== "GET" && skipBody === false;
if (bodyAllowed) {
Object.assign(options, makeRequestBody(req));
}
const request = new Request(url, options);
const socket = getRequestSocket(req);
if (socket && typeof socket.on === "function") {
const existingCleanup = getAbortControllerCleanup(req);
if (existingCleanup) {
existingCleanup();
}
let cleanedUp = false;
const removeSocketListener = () => {
if (typeof socket.off === "function") {
socket.off("close", onSocketClose);
} else if (typeof socket.removeListener === "function") {
socket.removeListener("close", onSocketClose);
}
};
const cleanup = () => {
if (cleanedUp) return;
cleanedUp = true;
removeSocketListener();
controller.signal.removeEventListener("abort", cleanup);
Reflect.deleteProperty(req, nodeRequestAbortControllerCleanupSymbol);
};
const onSocketClose = () => {
cleanup();
if (!controller.signal.aborted) {
controller.abort();
}
};
socket.on("close", onSocketClose);
controller.signal.addEventListener("abort", cleanup, { once: true });
Reflect.set(req, nodeRequestAbortControllerCleanupSymbol, cleanup);
if (socket.destroyed) {
onSocketClose();
}
}
const forwardedClientIp = getFirstForwardedValue(req.headers["x-forwarded-for"]);
const clientIp = forwardedClientIp || req.socket?.remoteAddress;
if (clientIp) {
Reflect.set(request, clientAddressSymbol, clientIp);
}
return request;
}
/**
* Streams a web-standard Response into a NodeJS Server Response.
* ```js
* import { NodeApp } from 'astro/app/node';
* import { createServer } from 'node:http';
*
* const server = createServer(async (req, res) => {
* const request = NodeApp.createRequest(req);
* const response = await app.render(request);
* await NodeApp.writeResponse(response, res);
* })
* ```
* @param source WhatWG Response
* @param destination NodeJS ServerResponse
*/
static async writeResponse(source, destination) {
const { status, headers, body, statusText } = source;
if (!(destination instanceof Http2ServerResponse)) {
destination.statusMessage = statusText;
}
destination.writeHead(status, createOutgoingHttpHeaders(headers));
const cleanupAbortFromDestination = getAbortControllerCleanup(
destination.req ?? void 0
);
if (cleanupAbortFromDestination) {
const runCleanup = () => {
cleanupAbortFromDestination();
if (typeof destination.off === "function") {
destination.off("finish", runCleanup);
destination.off("close", runCleanup);
} else {
destination.removeListener?.("finish", runCleanup);
destination.removeListener?.("close", runCleanup);
}
};
destination.on("finish", runCleanup);
destination.on("close", runCleanup);
}
if (!body) return destination.end();
try {
const reader = body.getReader();
destination.on("close", () => {
reader.cancel().catch((err) => {
console.error(
`There was an uncaught error in the middle of the stream while rendering ${destination.req.url}.`,
err
);
});
});
let result = await reader.read();
while (!result.done) {
destination.write(result.value);
result = await reader.read();
}
destination.end();
} catch (err) {
destination.write("Internal server error", () => {
err instanceof Error ? destination.destroy(err) : destination.destroy();
});
}
}
}
function getHostnamePort(hostname, port) {
const portInHostname = typeof hostname === "string" && /:\d+$/.test(hostname);
const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ""}`;
return hostnamePort;
}
function makeRequestHeaders(req) {
const headers = new Headers();
for (const [name, value] of Object.entries(req.headers)) {
if (value === void 0) {
continue;
}
if (Array.isArray(value)) {
for (const item of value) {
headers.append(name, item);
}
} else {
headers.append(name, value);
}
}
return headers;
}
function makeRequestBody(req) {
if (req.body !== void 0) {
if (typeof req.body === "string" && req.body.length > 0) {
return { body: Buffer.from(req.body) };
}
if (typeof req.body === "object" && req.body !== null && Object.keys(req.body).length > 0) {
return { body: Buffer.from(JSON.stringify(req.body)) };
}
if (typeof req.body === "object" && req.body !== null && typeof req.body[Symbol.asyncIterator] !== "undefined") {
return asyncIterableToBodyProps(req.body);
}
}
return asyncIterableToBodyProps(req);
}
function asyncIterableToBodyProps(iterable) {
return {
// Node uses undici for the Request implementation. Undici accepts
// a non-standard async iterable for the body.
// @ts-expect-error
body: iterable,
// The duplex property is required when using a ReadableStream or async
// iterable for the body. The type definitions do not include the duplex
// property because they are not up-to-date.
duplex: "half"
};
}
function getAbortControllerCleanup(req) {
if (!req) return void 0;
const cleanup = Reflect.get(req, nodeRequestAbortControllerCleanupSymbol);
return typeof cleanup === "function" ? cleanup : void 0;
}
function getRequestSocket(req) {
if (req.socket && typeof req.socket.on === "function") {
return req.socket;
}
const http2Socket = req.stream?.session?.socket;
if (http2Socket && typeof http2Socket.on === "function") {
return http2Socket;
}
return void 0;
}
async function loadManifest(rootFolder) {
const manifestFile = new URL("./manifest.json", rootFolder);
const rawManifest = await fs.promises.readFile(manifestFile, "utf-8");
const serializedManifest = JSON.parse(rawManifest);
return deserializeManifest(serializedManifest);
}
async function loadApp(rootFolder) {
const manifest = await loadManifest(rootFolder);
return new NodeApp(manifest);
}
export {
NodeApp,
apply as applyPolyfills,
loadApp,
loadManifest
};

13
node_modules/astro/dist/core/app/pipeline.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import type { ComponentInstance } from '../../types/astro.js';
import type { RewritePayload } from '../../types/public/common.js';
import type { RouteData, SSRResult } from '../../types/public/internal.js';
import { Pipeline, type TryRewriteResult } from '../base-pipeline.js';
import type { SinglePageBuiltModule } from '../build/types.js';
export declare class AppPipeline extends Pipeline {
static create({ logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, defaultRoutes, }: Pick<AppPipeline, 'logger' | 'manifest' | 'runtimeMode' | 'renderers' | 'resolve' | 'serverLike' | 'streaming' | 'defaultRoutes'>): AppPipeline;
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
componentMetadata(): void;
getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
tryRewrite(payload: RewritePayload, request: Request): Promise<TryRewriteResult>;
getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule>;
}

105
node_modules/astro/dist/core/app/pipeline.js generated vendored Normal file
View File

@@ -0,0 +1,105 @@
import { Pipeline } from "../base-pipeline.js";
import { RedirectSinglePageBuiltModule } from "../redirects/component.js";
import { createModuleScriptElement, createStylesheetElementSet } from "../render/ssr-element.js";
import { findRouteToRewrite } from "../routing/rewrite.js";
class AppPipeline extends Pipeline {
static create({
logger,
manifest,
runtimeMode,
renderers,
resolve,
serverLike,
streaming,
defaultRoutes
}) {
const pipeline = new AppPipeline(
logger,
manifest,
runtimeMode,
renderers,
resolve,
serverLike,
streaming,
void 0,
void 0,
void 0,
void 0,
void 0,
void 0,
void 0,
void 0,
defaultRoutes
);
return pipeline;
}
headElements(routeData) {
const routeInfo = this.manifest.routes.find((route) => route.routeData === routeData);
const links = /* @__PURE__ */ new Set();
const scripts = /* @__PURE__ */ new Set();
const styles = createStylesheetElementSet(routeInfo?.styles ?? []);
for (const script of routeInfo?.scripts ?? []) {
if ("stage" in script) {
if (script.stage === "head-inline") {
scripts.add({
props: {},
children: script.children
});
}
} else {
scripts.add(createModuleScriptElement(script));
}
}
return { links, styles, scripts };
}
componentMetadata() {
}
async getComponentByRoute(routeData) {
const module = await this.getModuleForRoute(routeData);
return module.page();
}
async tryRewrite(payload, request) {
const { newUrl, pathname, routeData } = findRouteToRewrite({
payload,
request,
routes: this.manifest?.routes.map((r) => r.routeData),
trailingSlash: this.manifest.trailingSlash,
buildFormat: this.manifest.buildFormat,
base: this.manifest.base,
outDir: this.serverLike ? this.manifest.buildClientDir : this.manifest.outDir
});
const componentInstance = await this.getComponentByRoute(routeData);
return { newUrl, pathname, componentInstance, routeData };
}
async getModuleForRoute(route) {
for (const defaultRoute of this.defaultRoutes) {
if (route.component === defaultRoute.component) {
return {
page: () => Promise.resolve(defaultRoute.instance),
renderers: []
};
}
}
if (route.type === "redirect") {
return RedirectSinglePageBuiltModule;
} else {
if (this.manifest.pageMap) {
const importComponentInstance = this.manifest.pageMap.get(route.component);
if (!importComponentInstance) {
throw new Error(
`Unexpectedly unable to find a component instance for route ${route.route}`
);
}
return await importComponentInstance();
} else if (this.manifest.pageModule) {
return this.manifest.pageModule;
}
throw new Error(
"Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue."
);
}
}
}
export {
AppPipeline
};

121
node_modules/astro/dist/core/app/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,121 @@
import type { ZodType } from 'zod';
import type { ActionAccept, ActionClient } from '../../actions/runtime/server.js';
import type { RoutingStrategies } from '../../i18n/utils.js';
import type { ComponentInstance, SerializedRouteData } from '../../types/astro.js';
import type { AstroMiddlewareInstance } from '../../types/public/common.js';
import type { AstroConfig, CspAlgorithm, Locales, RemotePattern, ResolvedSessionConfig } from '../../types/public/config.js';
import type { RouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult } from '../../types/public/internal.js';
import type { SinglePageBuiltModule } from '../build/types.js';
import type { CspDirective } from '../csp/config.js';
type ComponentPath = string;
export type StylesheetAsset = {
type: 'inline';
content: string;
} | {
type: 'external';
src: string;
};
export interface RouteInfo {
routeData: RouteData;
file: string;
links: string[];
scripts: ({
children: string;
stage: string;
} | {
type: 'inline' | 'external';
value: string;
})[];
styles: StylesheetAsset[];
}
export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
routeData: SerializedRouteData;
};
type ImportComponentInstance = () => Promise<SinglePageBuiltModule>;
export type AssetsPrefix = string | ({
fallback: string;
} & Record<string, string>) | undefined;
export type SSRManifest = {
hrefRoot: string;
adapterName: string;
routes: RouteInfo[];
site?: string;
base: string;
/**
* The base of the assets generated **by the user**. For example, scripts created by the user falls under this category.
*
* The value of this field comes from `vite.base`. We aren't usually this tight to vite in our code base, so probably
* this should be refactored somehow.
*/
userAssetsBase: string | undefined;
trailingSlash: AstroConfig['trailingSlash'];
buildFormat: NonNullable<AstroConfig['build']>['format'];
compressHTML: boolean;
assetsPrefix?: AssetsPrefix;
renderers: SSRLoadedRenderer[];
/**
* Map of directive name (e.g. `load`) to the directive script code
*/
clientDirectives: Map<string, string>;
entryModules: Record<string, string>;
inlinedScripts: Map<string, string>;
assets: Set<string>;
componentMetadata: SSRResult['componentMetadata'];
pageModule?: SinglePageBuiltModule;
pageMap?: Map<ComponentPath, ImportComponentInstance>;
serverIslandMap?: Map<string, () => Promise<ComponentInstance>>;
serverIslandNameMap?: Map<string, string>;
key: Promise<CryptoKey>;
i18n: SSRManifestI18n | undefined;
middleware?: () => Promise<AstroMiddlewareInstance> | AstroMiddlewareInstance;
actions?: () => Promise<SSRActions> | SSRActions;
checkOrigin: boolean;
allowedDomains?: Partial<RemotePattern>[];
sessionConfig?: ResolvedSessionConfig<any>;
cacheDir: string | URL;
srcDir: string | URL;
outDir: string | URL;
publicDir: string | URL;
buildClientDir: string | URL;
buildServerDir: string | URL;
csp: SSRManifestCSP | undefined;
};
export type SSRActions = {
server: Record<string, ActionClient<unknown, ActionAccept, ZodType>>;
};
export type SSRManifestI18n = {
fallback: Record<string, string> | undefined;
fallbackType: 'redirect' | 'rewrite';
strategy: RoutingStrategies;
locales: Locales;
defaultLocale: string;
domainLookupTable: Record<string, string>;
};
export type SSRManifestCSP = {
cspDestination: 'adapter' | 'meta' | 'header' | undefined;
algorithm: CspAlgorithm;
scriptHashes: string[];
scriptResources: string[];
isStrictDynamic: boolean;
styleHashes: string[];
styleResources: string[];
directives: CspDirective[];
};
/** Public type exposed through the `astro:build:ssr` integration hook */
export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives' | 'serverIslandNameMap' | 'key'> & {
routes: SerializedRouteInfo[];
assets: string[];
componentMetadata: [string, SSRComponentMetadata][];
inlinedScripts: [string, string][];
clientDirectives: [string, string][];
serverIslandNameMap: [string, string][];
key: string;
};
export type NodeAppHeadersJson = {
pathname: string;
headers: {
key: string;
value: string;
}[];
}[];
export {};

0
node_modules/astro/dist/core/app/types.js generated vendored Normal file
View File