3904 lines
125 KiB
JavaScript
3904 lines
125 KiB
JavaScript
|
|
import { B as BINARY_FORM_CONTENT_TYPE, a as BROWSER, c as create_remote_key, p as parse_remote_arg, s as stringify, d as deserialize_binary_form, T as TRAILING_SLASH_PARAM, I as INVALIDATED_PARAM } from "./chunks/shared.js";
|
||
|
|
import { json, text, error } from "@sveltejs/kit";
|
||
|
|
import { HttpError, SvelteKitError, Redirect, ActionFailure } from "@sveltejs/kit/internal";
|
||
|
|
import { with_request_store, merge_tracing, try_get_request_store } from "@sveltejs/kit/internal/server";
|
||
|
|
import { a as assets, b as base, c as app_dir, r as relative, o as override, d as reset } from "./chunks/environment.js";
|
||
|
|
import * as devalue from "devalue";
|
||
|
|
import { m as make_trackable, d as disable_search, a as decode_params, S as SCHEME, v as validate_layout_server_exports, b as validate_layout_exports, c as validate_page_server_exports, e as validate_page_exports, n as normalize_path, r as resolve, f as decode_pathname, g as validate_server_exports } from "./chunks/exports.js";
|
||
|
|
import { b as base64_encode, t as text_decoder, a as text_encoder, g as get_relative_path } from "./chunks/utils.js";
|
||
|
|
import { n as noop, s as safe_not_equal } from "./chunks/ssr.js";
|
||
|
|
import { p as public_env, r as read_implementation, o as options, s as set_private_env, a as set_public_env, g as get_hooks, b as set_read_implementation } from "./chunks/internal.js";
|
||
|
|
import { parse, serialize } from "cookie";
|
||
|
|
import * as set_cookie_parser from "set-cookie-parser";
|
||
|
|
function with_resolvers() {
|
||
|
|
let resolve2;
|
||
|
|
let reject;
|
||
|
|
const promise = new Promise((res, rej) => {
|
||
|
|
resolve2 = res;
|
||
|
|
reject = rej;
|
||
|
|
});
|
||
|
|
return { promise, resolve: resolve2, reject };
|
||
|
|
}
|
||
|
|
const NULL_BODY_STATUS = [101, 103, 204, 205, 304];
|
||
|
|
const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer;
|
||
|
|
const SVELTE_KIT_ASSETS = "/_svelte_kit_assets";
|
||
|
|
const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
||
|
|
const PAGE_METHODS = ["GET", "POST", "HEAD"];
|
||
|
|
function negotiate(accept, types) {
|
||
|
|
const parts = [];
|
||
|
|
accept.split(",").forEach((str, i) => {
|
||
|
|
const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str);
|
||
|
|
if (match) {
|
||
|
|
const [, type, subtype, q = "1"] = match;
|
||
|
|
parts.push({ type, subtype, q: +q, i });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
parts.sort((a, b) => {
|
||
|
|
if (a.q !== b.q) {
|
||
|
|
return b.q - a.q;
|
||
|
|
}
|
||
|
|
if (a.subtype === "*" !== (b.subtype === "*")) {
|
||
|
|
return a.subtype === "*" ? 1 : -1;
|
||
|
|
}
|
||
|
|
if (a.type === "*" !== (b.type === "*")) {
|
||
|
|
return a.type === "*" ? 1 : -1;
|
||
|
|
}
|
||
|
|
return a.i - b.i;
|
||
|
|
});
|
||
|
|
let accepted;
|
||
|
|
let min_priority = Infinity;
|
||
|
|
for (const mimetype of types) {
|
||
|
|
const [type, subtype] = mimetype.split("/");
|
||
|
|
const priority = parts.findIndex(
|
||
|
|
(part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*")
|
||
|
|
);
|
||
|
|
if (priority !== -1 && priority < min_priority) {
|
||
|
|
accepted = mimetype;
|
||
|
|
min_priority = priority;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return accepted;
|
||
|
|
}
|
||
|
|
function is_content_type(request, ...types) {
|
||
|
|
const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? "";
|
||
|
|
return types.includes(type.toLowerCase());
|
||
|
|
}
|
||
|
|
function is_form_content_type(request) {
|
||
|
|
return is_content_type(
|
||
|
|
request,
|
||
|
|
"application/x-www-form-urlencoded",
|
||
|
|
"multipart/form-data",
|
||
|
|
"text/plain",
|
||
|
|
BINARY_FORM_CONTENT_TYPE
|
||
|
|
);
|
||
|
|
}
|
||
|
|
function coalesce_to_error(err) {
|
||
|
|
return err instanceof Error || err && /** @type {any} */
|
||
|
|
err.name && /** @type {any} */
|
||
|
|
err.message ? (
|
||
|
|
/** @type {Error} */
|
||
|
|
err
|
||
|
|
) : new Error(JSON.stringify(err));
|
||
|
|
}
|
||
|
|
function normalize_error(error2) {
|
||
|
|
return (
|
||
|
|
/** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */
|
||
|
|
error2
|
||
|
|
);
|
||
|
|
}
|
||
|
|
function get_status(error2) {
|
||
|
|
return error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500;
|
||
|
|
}
|
||
|
|
function get_message(error2) {
|
||
|
|
return error2 instanceof SvelteKitError ? error2.text : "Internal Error";
|
||
|
|
}
|
||
|
|
const escape_html_attr_dict = {
|
||
|
|
"&": "&",
|
||
|
|
'"': """
|
||
|
|
// Svelte also escapes < because the escape function could be called inside a `noscript` there
|
||
|
|
// https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c
|
||
|
|
// However, that doesn't apply in SvelteKit
|
||
|
|
};
|
||
|
|
const escape_html_dict = {
|
||
|
|
"&": "&",
|
||
|
|
"<": "<"
|
||
|
|
};
|
||
|
|
const surrogates = (
|
||
|
|
// high surrogate without paired low surrogate
|
||
|
|
"[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]"
|
||
|
|
);
|
||
|
|
const escape_html_attr_regex = new RegExp(
|
||
|
|
`[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates,
|
||
|
|
"g"
|
||
|
|
);
|
||
|
|
const escape_html_regex = new RegExp(
|
||
|
|
`[${Object.keys(escape_html_dict).join("")}]|` + surrogates,
|
||
|
|
"g"
|
||
|
|
);
|
||
|
|
function escape_html(str, is_attr) {
|
||
|
|
const dict = is_attr ? escape_html_attr_dict : escape_html_dict;
|
||
|
|
const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => {
|
||
|
|
if (match.length === 2) {
|
||
|
|
return match;
|
||
|
|
}
|
||
|
|
return dict[match] ?? `&#${match.charCodeAt(0)};`;
|
||
|
|
});
|
||
|
|
return escaped_str;
|
||
|
|
}
|
||
|
|
function method_not_allowed(mod, method) {
|
||
|
|
return text(`${method} method not allowed`, {
|
||
|
|
status: 405,
|
||
|
|
headers: {
|
||
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
||
|
|
// "The server must generate an Allow header field in a 405 status code response"
|
||
|
|
allow: allowed_methods(mod).join(", ")
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function allowed_methods(mod) {
|
||
|
|
const allowed = ENDPOINT_METHODS.filter((method) => method in mod);
|
||
|
|
if ("GET" in mod && !("HEAD" in mod)) {
|
||
|
|
allowed.push("HEAD");
|
||
|
|
}
|
||
|
|
return allowed;
|
||
|
|
}
|
||
|
|
function get_global_name(options2) {
|
||
|
|
return `__sveltekit_${options2.version_hash}`;
|
||
|
|
}
|
||
|
|
function static_error_page(options2, status, message) {
|
||
|
|
let page = options2.templates.error({ status, message: escape_html(message) });
|
||
|
|
return text(page, {
|
||
|
|
headers: { "content-type": "text/html; charset=utf-8" },
|
||
|
|
status
|
||
|
|
});
|
||
|
|
}
|
||
|
|
async function handle_fatal_error(event, state, options2, error2) {
|
||
|
|
error2 = error2 instanceof HttpError ? error2 : coalesce_to_error(error2);
|
||
|
|
const status = get_status(error2);
|
||
|
|
const body2 = await handle_error_and_jsonify(event, state, options2, error2);
|
||
|
|
const type = negotiate(event.request.headers.get("accept") || "text/html", [
|
||
|
|
"application/json",
|
||
|
|
"text/html"
|
||
|
|
]);
|
||
|
|
if (event.isDataRequest || type === "application/json") {
|
||
|
|
return json(body2, {
|
||
|
|
status
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return static_error_page(options2, status, body2.message);
|
||
|
|
}
|
||
|
|
async function handle_error_and_jsonify(event, state, options2, error2) {
|
||
|
|
if (error2 instanceof HttpError) {
|
||
|
|
return { message: "Unknown Error", ...error2.body };
|
||
|
|
}
|
||
|
|
const status = get_status(error2);
|
||
|
|
const message = get_message(error2);
|
||
|
|
return await with_request_store(
|
||
|
|
{ event, state },
|
||
|
|
() => options2.hooks.handleError({ error: error2, event, status, message })
|
||
|
|
) ?? { message };
|
||
|
|
}
|
||
|
|
function redirect_response(status, location) {
|
||
|
|
const response = new Response(void 0, {
|
||
|
|
status,
|
||
|
|
headers: { location }
|
||
|
|
});
|
||
|
|
return response;
|
||
|
|
}
|
||
|
|
function clarify_devalue_error(event, error2) {
|
||
|
|
if (error2.path) {
|
||
|
|
return `Data returned from \`load\` while rendering ${event.route.id} is not serializable: ${error2.message} (${error2.path}). If you need to serialize/deserialize custom types, use transport hooks: https://svelte.dev/docs/kit/hooks#Universal-hooks-transport.`;
|
||
|
|
}
|
||
|
|
if (error2.path === "") {
|
||
|
|
return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`;
|
||
|
|
}
|
||
|
|
return error2.message;
|
||
|
|
}
|
||
|
|
function serialize_uses(node) {
|
||
|
|
const uses = {};
|
||
|
|
if (node.uses && node.uses.dependencies.size > 0) {
|
||
|
|
uses.dependencies = Array.from(node.uses.dependencies);
|
||
|
|
}
|
||
|
|
if (node.uses && node.uses.search_params.size > 0) {
|
||
|
|
uses.search_params = Array.from(node.uses.search_params);
|
||
|
|
}
|
||
|
|
if (node.uses && node.uses.params.size > 0) {
|
||
|
|
uses.params = Array.from(node.uses.params);
|
||
|
|
}
|
||
|
|
if (node.uses?.parent) uses.parent = 1;
|
||
|
|
if (node.uses?.route) uses.route = 1;
|
||
|
|
if (node.uses?.url) uses.url = 1;
|
||
|
|
return uses;
|
||
|
|
}
|
||
|
|
function has_prerendered_path(manifest, pathname) {
|
||
|
|
return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1));
|
||
|
|
}
|
||
|
|
function format_server_error(status, error2, event) {
|
||
|
|
const formatted_text = `
|
||
|
|
\x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`;
|
||
|
|
if (status === 404) {
|
||
|
|
return formatted_text;
|
||
|
|
}
|
||
|
|
return `${formatted_text}
|
||
|
|
${error2.stack}`;
|
||
|
|
}
|
||
|
|
function get_node_type(node_id) {
|
||
|
|
const parts = node_id?.split("/");
|
||
|
|
const filename = parts?.at(-1);
|
||
|
|
if (!filename) return "unknown";
|
||
|
|
const dot_parts = filename.split(".");
|
||
|
|
return dot_parts.slice(0, -1).join(".");
|
||
|
|
}
|
||
|
|
async function render_endpoint(event, event_state, mod, state) {
|
||
|
|
const method = (
|
||
|
|
/** @type {import('types').HttpMethod} */
|
||
|
|
event.request.method
|
||
|
|
);
|
||
|
|
let handler = mod[method] || mod.fallback;
|
||
|
|
if (method === "HEAD" && !mod.HEAD && mod.GET) {
|
||
|
|
handler = mod.GET;
|
||
|
|
}
|
||
|
|
if (!handler) {
|
||
|
|
return method_not_allowed(mod, method);
|
||
|
|
}
|
||
|
|
const prerender = mod.prerender ?? state.prerender_default;
|
||
|
|
if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
|
||
|
|
throw new Error("Cannot prerender endpoints that have mutative methods");
|
||
|
|
}
|
||
|
|
if (state.prerendering && !state.prerendering.inside_reroute && !prerender) {
|
||
|
|
if (state.depth > 0) {
|
||
|
|
throw new Error(`${event.route.id} is not prerenderable`);
|
||
|
|
} else {
|
||
|
|
return new Response(void 0, { status: 204 });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
event_state.is_endpoint_request = true;
|
||
|
|
try {
|
||
|
|
const response = await with_request_store(
|
||
|
|
{ event, state: event_state },
|
||
|
|
() => handler(
|
||
|
|
/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */
|
||
|
|
event
|
||
|
|
)
|
||
|
|
);
|
||
|
|
if (!(response instanceof Response)) {
|
||
|
|
throw new Error(
|
||
|
|
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) {
|
||
|
|
const cloned = new Response(response.clone().body, {
|
||
|
|
status: response.status,
|
||
|
|
statusText: response.statusText,
|
||
|
|
headers: new Headers(response.headers)
|
||
|
|
});
|
||
|
|
cloned.headers.set("x-sveltekit-prerender", String(prerender));
|
||
|
|
if (state.prerendering.inside_reroute && prerender) {
|
||
|
|
cloned.headers.set(
|
||
|
|
"x-sveltekit-routeid",
|
||
|
|
encodeURI(
|
||
|
|
/** @type {string} */
|
||
|
|
event.route.id
|
||
|
|
)
|
||
|
|
);
|
||
|
|
state.prerendering.dependencies.set(event.url.pathname, { response: cloned, body: null });
|
||
|
|
} else {
|
||
|
|
return cloned;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return response;
|
||
|
|
} catch (e) {
|
||
|
|
if (e instanceof Redirect) {
|
||
|
|
return new Response(void 0, {
|
||
|
|
status: e.status,
|
||
|
|
headers: { location: e.location }
|
||
|
|
});
|
||
|
|
}
|
||
|
|
throw e;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function is_endpoint_request(event) {
|
||
|
|
const { method, headers: headers2 } = event.request;
|
||
|
|
if (ENDPOINT_METHODS.includes(method) && !PAGE_METHODS.includes(method)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (method === "POST" && headers2.get("x-sveltekit-action") === "true") return false;
|
||
|
|
const accept = event.request.headers.get("accept") ?? "*/*";
|
||
|
|
return negotiate(accept, ["*", "text/html"]) !== "text/html";
|
||
|
|
}
|
||
|
|
function compact(arr) {
|
||
|
|
return arr.filter(
|
||
|
|
/** @returns {val is NonNullable<T>} */
|
||
|
|
(val) => val != null
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const DATA_SUFFIX = "/__data.json";
|
||
|
|
const HTML_DATA_SUFFIX = ".html__data.json";
|
||
|
|
function has_data_suffix(pathname) {
|
||
|
|
return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX);
|
||
|
|
}
|
||
|
|
function add_data_suffix(pathname) {
|
||
|
|
if (pathname.endsWith(".html")) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX);
|
||
|
|
return pathname.replace(/\/$/, "") + DATA_SUFFIX;
|
||
|
|
}
|
||
|
|
function strip_data_suffix(pathname) {
|
||
|
|
if (pathname.endsWith(HTML_DATA_SUFFIX)) {
|
||
|
|
return pathname.slice(0, -HTML_DATA_SUFFIX.length) + ".html";
|
||
|
|
}
|
||
|
|
return pathname.slice(0, -DATA_SUFFIX.length);
|
||
|
|
}
|
||
|
|
const ROUTE_SUFFIX = "/__route.js";
|
||
|
|
function has_resolution_suffix(pathname) {
|
||
|
|
return pathname.endsWith(ROUTE_SUFFIX);
|
||
|
|
}
|
||
|
|
function add_resolution_suffix(pathname) {
|
||
|
|
return pathname.replace(/\/$/, "") + ROUTE_SUFFIX;
|
||
|
|
}
|
||
|
|
function strip_resolution_suffix(pathname) {
|
||
|
|
return pathname.slice(0, -ROUTE_SUFFIX.length);
|
||
|
|
}
|
||
|
|
const noop_span = {
|
||
|
|
spanContext() {
|
||
|
|
return noop_span_context;
|
||
|
|
},
|
||
|
|
setAttribute() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
setAttributes() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
addEvent() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
setStatus() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
updateName() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
end() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
isRecording() {
|
||
|
|
return false;
|
||
|
|
},
|
||
|
|
recordException() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
addLink() {
|
||
|
|
return this;
|
||
|
|
},
|
||
|
|
addLinks() {
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const noop_span_context = {
|
||
|
|
traceId: "",
|
||
|
|
spanId: "",
|
||
|
|
traceFlags: 0
|
||
|
|
};
|
||
|
|
async function record_span({ name, attributes, fn }) {
|
||
|
|
{
|
||
|
|
return fn(noop_span);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function is_action_json_request(event) {
|
||
|
|
const accept = negotiate(event.request.headers.get("accept") ?? "*/*", [
|
||
|
|
"application/json",
|
||
|
|
"text/html"
|
||
|
|
]);
|
||
|
|
return accept === "application/json" && event.request.method === "POST";
|
||
|
|
}
|
||
|
|
async function handle_action_json_request(event, event_state, options2, server) {
|
||
|
|
const actions = server?.actions;
|
||
|
|
if (!actions) {
|
||
|
|
const no_actions_error = new SvelteKitError(
|
||
|
|
405,
|
||
|
|
"Method Not Allowed",
|
||
|
|
`POST method not allowed. No form actions exist for ${"this page"}`
|
||
|
|
);
|
||
|
|
return action_json(
|
||
|
|
{
|
||
|
|
type: "error",
|
||
|
|
error: await handle_error_and_jsonify(event, event_state, options2, no_actions_error)
|
||
|
|
},
|
||
|
|
{
|
||
|
|
status: no_actions_error.status,
|
||
|
|
headers: {
|
||
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
||
|
|
// "The server must generate an Allow header field in a 405 status code response"
|
||
|
|
allow: "GET"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
check_named_default_separate(actions);
|
||
|
|
try {
|
||
|
|
const data = await call_action(event, event_state, actions);
|
||
|
|
if (BROWSER) ;
|
||
|
|
if (data instanceof ActionFailure) {
|
||
|
|
return action_json({
|
||
|
|
type: "failure",
|
||
|
|
status: data.status,
|
||
|
|
// @ts-expect-error we assign a string to what is supposed to be an object. That's ok
|
||
|
|
// because we don't use the object outside, and this way we have better code navigation
|
||
|
|
// through knowing where the related interface is used.
|
||
|
|
data: stringify_action_response(
|
||
|
|
data.data,
|
||
|
|
/** @type {string} */
|
||
|
|
event.route.id,
|
||
|
|
options2.hooks.transport
|
||
|
|
)
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
return action_json({
|
||
|
|
type: "success",
|
||
|
|
status: data ? 200 : 204,
|
||
|
|
// @ts-expect-error see comment above
|
||
|
|
data: stringify_action_response(
|
||
|
|
data,
|
||
|
|
/** @type {string} */
|
||
|
|
event.route.id,
|
||
|
|
options2.hooks.transport
|
||
|
|
)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
const err = normalize_error(e);
|
||
|
|
if (err instanceof Redirect) {
|
||
|
|
return action_json_redirect(err);
|
||
|
|
}
|
||
|
|
return action_json(
|
||
|
|
{
|
||
|
|
type: "error",
|
||
|
|
error: await handle_error_and_jsonify(
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options2,
|
||
|
|
check_incorrect_fail_use(err)
|
||
|
|
)
|
||
|
|
},
|
||
|
|
{
|
||
|
|
status: get_status(err)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function check_incorrect_fail_use(error2) {
|
||
|
|
return error2 instanceof ActionFailure ? new Error('Cannot "throw fail()". Use "return fail()"') : error2;
|
||
|
|
}
|
||
|
|
function action_json_redirect(redirect) {
|
||
|
|
return action_json({
|
||
|
|
type: "redirect",
|
||
|
|
status: redirect.status,
|
||
|
|
location: redirect.location
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function action_json(data, init2) {
|
||
|
|
return json(data, init2);
|
||
|
|
}
|
||
|
|
function is_action_request(event) {
|
||
|
|
return event.request.method === "POST";
|
||
|
|
}
|
||
|
|
async function handle_action_request(event, event_state, server) {
|
||
|
|
const actions = server?.actions;
|
||
|
|
if (!actions) {
|
||
|
|
event.setHeaders({
|
||
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
||
|
|
// "The server must generate an Allow header field in a 405 status code response"
|
||
|
|
allow: "GET"
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
type: "error",
|
||
|
|
error: new SvelteKitError(
|
||
|
|
405,
|
||
|
|
"Method Not Allowed",
|
||
|
|
`POST method not allowed. No form actions exist for ${"this page"}`
|
||
|
|
)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
check_named_default_separate(actions);
|
||
|
|
try {
|
||
|
|
const data = await call_action(event, event_state, actions);
|
||
|
|
if (BROWSER) ;
|
||
|
|
if (data instanceof ActionFailure) {
|
||
|
|
return {
|
||
|
|
type: "failure",
|
||
|
|
status: data.status,
|
||
|
|
data: data.data
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
return {
|
||
|
|
type: "success",
|
||
|
|
status: 200,
|
||
|
|
// @ts-expect-error this will be removed upon serialization, so `undefined` is the same as omission
|
||
|
|
data
|
||
|
|
};
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
const err = normalize_error(e);
|
||
|
|
if (err instanceof Redirect) {
|
||
|
|
return {
|
||
|
|
type: "redirect",
|
||
|
|
status: err.status,
|
||
|
|
location: err.location
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
type: "error",
|
||
|
|
error: check_incorrect_fail_use(err)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function check_named_default_separate(actions) {
|
||
|
|
if (actions.default && Object.keys(actions).length > 1) {
|
||
|
|
throw new Error(
|
||
|
|
"When using named actions, the default action cannot be used. See the docs for more info: https://svelte.dev/docs/kit/form-actions#named-actions"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async function call_action(event, event_state, actions) {
|
||
|
|
const url = new URL(event.request.url);
|
||
|
|
let name = "default";
|
||
|
|
for (const param of url.searchParams) {
|
||
|
|
if (param[0].startsWith("/")) {
|
||
|
|
name = param[0].slice(1);
|
||
|
|
if (name === "default") {
|
||
|
|
throw new Error('Cannot use reserved action name "default"');
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const action = actions[name];
|
||
|
|
if (!action) {
|
||
|
|
throw new SvelteKitError(404, "Not Found", `No action with name '${name}' found`);
|
||
|
|
}
|
||
|
|
if (!is_form_content_type(event.request)) {
|
||
|
|
throw new SvelteKitError(
|
||
|
|
415,
|
||
|
|
"Unsupported Media Type",
|
||
|
|
`Form actions expect form-encoded data — received ${event.request.headers.get(
|
||
|
|
"content-type"
|
||
|
|
)}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return record_span({
|
||
|
|
name: "sveltekit.form_action",
|
||
|
|
attributes: {
|
||
|
|
"http.route": event.route.id || "unknown"
|
||
|
|
},
|
||
|
|
fn: async (current2) => {
|
||
|
|
const traced_event = merge_tracing(event, current2);
|
||
|
|
const result = await with_request_store(
|
||
|
|
{ event: traced_event, state: event_state },
|
||
|
|
() => action(traced_event)
|
||
|
|
);
|
||
|
|
if (result instanceof ActionFailure) {
|
||
|
|
current2.setAttributes({
|
||
|
|
"sveltekit.form_action.result.type": "failure",
|
||
|
|
"sveltekit.form_action.result.status": result.status
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function validate_action_return(data) {
|
||
|
|
if (data instanceof Redirect) {
|
||
|
|
throw new Error("Cannot `return redirect(...)` — use `redirect(...)` instead");
|
||
|
|
}
|
||
|
|
if (data instanceof HttpError) {
|
||
|
|
throw new Error("Cannot `return error(...)` — use `error(...)` or `return fail(...)` instead");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function uneval_action_response(data, route_id, transport) {
|
||
|
|
const replacer = (thing) => {
|
||
|
|
for (const key2 in transport) {
|
||
|
|
const encoded = transport[key2].encode(thing);
|
||
|
|
if (encoded) {
|
||
|
|
return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
return try_serialize(data, (value) => devalue.uneval(value, replacer), route_id);
|
||
|
|
}
|
||
|
|
function stringify_action_response(data, route_id, transport) {
|
||
|
|
const encoders = Object.fromEntries(
|
||
|
|
Object.entries(transport).map(([key2, value]) => [key2, value.encode])
|
||
|
|
);
|
||
|
|
return try_serialize(data, (value) => devalue.stringify(value, encoders), route_id);
|
||
|
|
}
|
||
|
|
function try_serialize(data, fn, route_id) {
|
||
|
|
try {
|
||
|
|
return fn(data);
|
||
|
|
} catch (e) {
|
||
|
|
const error2 = (
|
||
|
|
/** @type {any} */
|
||
|
|
e
|
||
|
|
);
|
||
|
|
if (data instanceof Response) {
|
||
|
|
throw new Error(
|
||
|
|
`Data returned from action inside ${route_id} is not serializable. Form actions need to return plain objects or fail(). E.g. return { success: true } or return fail(400, { message: "invalid" });`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if ("path" in error2) {
|
||
|
|
let message = `Data returned from action inside ${route_id} is not serializable: ${error2.message}`;
|
||
|
|
if (error2.path !== "") message += ` (data.${error2.path})`;
|
||
|
|
throw new Error(message);
|
||
|
|
}
|
||
|
|
throw error2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function create_async_iterator() {
|
||
|
|
let resolved = -1;
|
||
|
|
let returned = -1;
|
||
|
|
const deferred = [];
|
||
|
|
return {
|
||
|
|
iterate: (transform = (x) => x) => {
|
||
|
|
return {
|
||
|
|
[Symbol.asyncIterator]() {
|
||
|
|
return {
|
||
|
|
next: async () => {
|
||
|
|
const next = deferred[++returned];
|
||
|
|
if (!next) return { value: null, done: true };
|
||
|
|
const value = await next.promise;
|
||
|
|
return { value: transform(value), done: false };
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
},
|
||
|
|
add: (promise) => {
|
||
|
|
deferred.push(with_resolvers());
|
||
|
|
void promise.then((value) => {
|
||
|
|
deferred[++resolved].resolve(value);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function server_data_serializer(event, event_state, options2) {
|
||
|
|
let promise_id = 1;
|
||
|
|
let max_nodes = -1;
|
||
|
|
const iterator = create_async_iterator();
|
||
|
|
const global = get_global_name(options2);
|
||
|
|
function get_replacer(index) {
|
||
|
|
return function replacer(thing) {
|
||
|
|
if (typeof thing?.then === "function") {
|
||
|
|
const id = promise_id++;
|
||
|
|
const promise = thing.then(
|
||
|
|
/** @param {any} data */
|
||
|
|
(data) => ({ data })
|
||
|
|
).catch(
|
||
|
|
/** @param {any} error */
|
||
|
|
async (error2) => ({
|
||
|
|
error: await handle_error_and_jsonify(event, event_state, options2, error2)
|
||
|
|
})
|
||
|
|
).then(
|
||
|
|
/**
|
||
|
|
* @param {{data: any; error: any}} result
|
||
|
|
*/
|
||
|
|
async ({ data, error: error2 }) => {
|
||
|
|
let str;
|
||
|
|
try {
|
||
|
|
str = devalue.uneval(error2 ? [, error2] : [data], replacer);
|
||
|
|
} catch {
|
||
|
|
error2 = await handle_error_and_jsonify(
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options2,
|
||
|
|
new Error(`Failed to serialize promise while rendering ${event.route.id}`)
|
||
|
|
);
|
||
|
|
data = void 0;
|
||
|
|
str = devalue.uneval([, error2], replacer);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
index,
|
||
|
|
str: `${global}.resolve(${id}, ${str.includes("app.decode") ? `(app) => ${str}` : `() => ${str}`})`
|
||
|
|
};
|
||
|
|
}
|
||
|
|
);
|
||
|
|
iterator.add(promise);
|
||
|
|
return `${global}.defer(${id})`;
|
||
|
|
} else {
|
||
|
|
for (const key2 in options2.hooks.transport) {
|
||
|
|
const encoded = options2.hooks.transport[key2].encode(thing);
|
||
|
|
if (encoded) {
|
||
|
|
return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
const strings = (
|
||
|
|
/** @type {string[]} */
|
||
|
|
[]
|
||
|
|
);
|
||
|
|
return {
|
||
|
|
set_max_nodes(i) {
|
||
|
|
max_nodes = i;
|
||
|
|
},
|
||
|
|
add_node(i, node) {
|
||
|
|
try {
|
||
|
|
if (!node) {
|
||
|
|
strings[i] = "null";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const payload = { type: "data", data: node.data, uses: serialize_uses(node) };
|
||
|
|
if (node.slash) payload.slash = node.slash;
|
||
|
|
strings[i] = devalue.uneval(payload, get_replacer(i));
|
||
|
|
} catch (e) {
|
||
|
|
e.path = e.path.slice(1);
|
||
|
|
throw new Error(clarify_devalue_error(
|
||
|
|
event,
|
||
|
|
/** @type {any} */
|
||
|
|
e
|
||
|
|
));
|
||
|
|
}
|
||
|
|
},
|
||
|
|
get_data(csp) {
|
||
|
|
const open = `<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ""}>`;
|
||
|
|
const close = `<\/script>
|
||
|
|
`;
|
||
|
|
return {
|
||
|
|
data: `[${compact(max_nodes > -1 ? strings.slice(0, max_nodes) : strings).join(",")}]`,
|
||
|
|
chunks: promise_id > 1 ? iterator.iterate(({ index, str }) => {
|
||
|
|
if (max_nodes > -1 && index >= max_nodes) {
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
return open + str + close;
|
||
|
|
}) : null
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function server_data_serializer_json(event, event_state, options2) {
|
||
|
|
let promise_id = 1;
|
||
|
|
const iterator = create_async_iterator();
|
||
|
|
const reducers = {
|
||
|
|
...Object.fromEntries(
|
||
|
|
Object.entries(options2.hooks.transport).map(([key2, value]) => [key2, value.encode])
|
||
|
|
),
|
||
|
|
/** @param {any} thing */
|
||
|
|
Promise: (thing) => {
|
||
|
|
if (typeof thing?.then !== "function") {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const id = promise_id++;
|
||
|
|
let key2 = "data";
|
||
|
|
const promise = thing.catch(
|
||
|
|
/** @param {any} e */
|
||
|
|
async (e) => {
|
||
|
|
key2 = "error";
|
||
|
|
return handle_error_and_jsonify(
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options2,
|
||
|
|
/** @type {any} */
|
||
|
|
e
|
||
|
|
);
|
||
|
|
}
|
||
|
|
).then(
|
||
|
|
/** @param {any} value */
|
||
|
|
async (value) => {
|
||
|
|
let str;
|
||
|
|
try {
|
||
|
|
str = devalue.stringify(value, reducers);
|
||
|
|
} catch {
|
||
|
|
const error2 = await handle_error_and_jsonify(
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options2,
|
||
|
|
new Error(`Failed to serialize promise while rendering ${event.route.id}`)
|
||
|
|
);
|
||
|
|
key2 = "error";
|
||
|
|
str = devalue.stringify(error2, reducers);
|
||
|
|
}
|
||
|
|
return `{"type":"chunk","id":${id},"${key2}":${str}}
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
);
|
||
|
|
iterator.add(promise);
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const strings = (
|
||
|
|
/** @type {string[]} */
|
||
|
|
[]
|
||
|
|
);
|
||
|
|
return {
|
||
|
|
add_node(i, node) {
|
||
|
|
try {
|
||
|
|
if (!node) {
|
||
|
|
strings[i] = "null";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (node.type === "error" || node.type === "skip") {
|
||
|
|
strings[i] = JSON.stringify(node);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
strings[i] = `{"type":"data","data":${devalue.stringify(node.data, reducers)},"uses":${JSON.stringify(
|
||
|
|
serialize_uses(node)
|
||
|
|
)}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ""}}`;
|
||
|
|
} catch (e) {
|
||
|
|
e.path = "data" + e.path;
|
||
|
|
throw new Error(clarify_devalue_error(
|
||
|
|
event,
|
||
|
|
/** @type {any} */
|
||
|
|
e
|
||
|
|
));
|
||
|
|
}
|
||
|
|
},
|
||
|
|
get_data() {
|
||
|
|
return {
|
||
|
|
data: `{"type":"data","nodes":[${strings.join(",")}]}
|
||
|
|
`,
|
||
|
|
chunks: promise_id > 1 ? iterator.iterate() : null
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
async function load_server_data({ event, event_state, state, node, parent }) {
|
||
|
|
if (!node?.server) return null;
|
||
|
|
let is_tracking = true;
|
||
|
|
const uses = {
|
||
|
|
dependencies: /* @__PURE__ */ new Set(),
|
||
|
|
params: /* @__PURE__ */ new Set(),
|
||
|
|
parent: false,
|
||
|
|
route: false,
|
||
|
|
url: false,
|
||
|
|
search_params: /* @__PURE__ */ new Set()
|
||
|
|
};
|
||
|
|
const load = node.server.load;
|
||
|
|
const slash = node.server.trailingSlash;
|
||
|
|
if (!load) {
|
||
|
|
return { type: "data", data: null, uses, slash };
|
||
|
|
}
|
||
|
|
const url = make_trackable(
|
||
|
|
event.url,
|
||
|
|
() => {
|
||
|
|
if (is_tracking) {
|
||
|
|
uses.url = true;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
(param) => {
|
||
|
|
if (is_tracking) {
|
||
|
|
uses.search_params.add(param);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
if (state.prerendering) {
|
||
|
|
disable_search(url);
|
||
|
|
}
|
||
|
|
const result = await record_span({
|
||
|
|
name: "sveltekit.load",
|
||
|
|
attributes: {
|
||
|
|
"sveltekit.load.node_id": node.server_id || "unknown",
|
||
|
|
"sveltekit.load.node_type": get_node_type(node.server_id),
|
||
|
|
"http.route": event.route.id || "unknown"
|
||
|
|
},
|
||
|
|
fn: async (current2) => {
|
||
|
|
const traced_event = merge_tracing(event, current2);
|
||
|
|
const result2 = await with_request_store(
|
||
|
|
{ event: traced_event, state: event_state },
|
||
|
|
() => load.call(null, {
|
||
|
|
...traced_event,
|
||
|
|
fetch: (info, init2) => {
|
||
|
|
new URL(info instanceof Request ? info.url : info, event.url);
|
||
|
|
return event.fetch(info, init2);
|
||
|
|
},
|
||
|
|
/** @param {string[]} deps */
|
||
|
|
depends: (...deps) => {
|
||
|
|
for (const dep of deps) {
|
||
|
|
const { href } = new URL(dep, event.url);
|
||
|
|
uses.dependencies.add(href);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
params: new Proxy(event.params, {
|
||
|
|
get: (target, key2) => {
|
||
|
|
if (is_tracking) {
|
||
|
|
uses.params.add(key2);
|
||
|
|
}
|
||
|
|
return target[
|
||
|
|
/** @type {string} */
|
||
|
|
key2
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
parent: async () => {
|
||
|
|
if (is_tracking) {
|
||
|
|
uses.parent = true;
|
||
|
|
}
|
||
|
|
return parent();
|
||
|
|
},
|
||
|
|
route: new Proxy(event.route, {
|
||
|
|
get: (target, key2) => {
|
||
|
|
if (is_tracking) {
|
||
|
|
uses.route = true;
|
||
|
|
}
|
||
|
|
return target[
|
||
|
|
/** @type {'id'} */
|
||
|
|
key2
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
url,
|
||
|
|
untrack(fn) {
|
||
|
|
is_tracking = false;
|
||
|
|
try {
|
||
|
|
return fn();
|
||
|
|
} finally {
|
||
|
|
is_tracking = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
return result2;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
type: "data",
|
||
|
|
data: result ?? null,
|
||
|
|
uses,
|
||
|
|
slash
|
||
|
|
};
|
||
|
|
}
|
||
|
|
async function load_data({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
fetched,
|
||
|
|
node,
|
||
|
|
parent,
|
||
|
|
server_data_promise,
|
||
|
|
state,
|
||
|
|
resolve_opts,
|
||
|
|
csr
|
||
|
|
}) {
|
||
|
|
const server_data_node = await server_data_promise;
|
||
|
|
const load = node?.universal?.load;
|
||
|
|
if (!load) {
|
||
|
|
return server_data_node?.data ?? null;
|
||
|
|
}
|
||
|
|
const result = await record_span({
|
||
|
|
name: "sveltekit.load",
|
||
|
|
attributes: {
|
||
|
|
"sveltekit.load.node_id": node.universal_id || "unknown",
|
||
|
|
"sveltekit.load.node_type": get_node_type(node.universal_id),
|
||
|
|
"http.route": event.route.id || "unknown"
|
||
|
|
},
|
||
|
|
fn: async (current2) => {
|
||
|
|
const traced_event = merge_tracing(event, current2);
|
||
|
|
return await with_request_store(
|
||
|
|
{ event: traced_event, state: event_state },
|
||
|
|
() => load.call(null, {
|
||
|
|
url: event.url,
|
||
|
|
params: event.params,
|
||
|
|
data: server_data_node?.data ?? null,
|
||
|
|
route: event.route,
|
||
|
|
fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts),
|
||
|
|
setHeaders: event.setHeaders,
|
||
|
|
depends: () => {
|
||
|
|
},
|
||
|
|
parent,
|
||
|
|
untrack: (fn) => fn(),
|
||
|
|
tracing: traced_event.tracing
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return result ?? null;
|
||
|
|
}
|
||
|
|
function create_universal_fetch(event, state, fetched, csr, resolve_opts) {
|
||
|
|
const universal_fetch = async (input, init2) => {
|
||
|
|
const cloned_body = input instanceof Request && input.body ? input.clone().body : null;
|
||
|
|
const cloned_headers = input instanceof Request && [...input.headers].length ? new Headers(input.headers) : init2?.headers;
|
||
|
|
let response = await event.fetch(input, init2);
|
||
|
|
const url = new URL(input instanceof Request ? input.url : input, event.url);
|
||
|
|
const same_origin = url.origin === event.url.origin;
|
||
|
|
let dependency;
|
||
|
|
if (same_origin) {
|
||
|
|
if (state.prerendering) {
|
||
|
|
dependency = { response, body: null };
|
||
|
|
state.prerendering.dependencies.set(url.pathname, dependency);
|
||
|
|
}
|
||
|
|
} else if (url.protocol === "https:" || url.protocol === "http:") {
|
||
|
|
const mode = input instanceof Request ? input.mode : init2?.mode ?? "cors";
|
||
|
|
if (mode === "no-cors") {
|
||
|
|
response = new Response("", {
|
||
|
|
status: response.status,
|
||
|
|
statusText: response.statusText,
|
||
|
|
headers: response.headers
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
const acao = response.headers.get("access-control-allow-origin");
|
||
|
|
if (!acao || acao !== event.url.origin && acao !== "*") {
|
||
|
|
throw new Error(
|
||
|
|
`CORS error: ${acao ? "Incorrect" : "No"} 'Access-Control-Allow-Origin' header is present on the requested resource`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let teed_body;
|
||
|
|
const proxy = new Proxy(response, {
|
||
|
|
get(response2, key2, receiver) {
|
||
|
|
async function push_fetched(body2, is_b64) {
|
||
|
|
const status_number = Number(response2.status);
|
||
|
|
if (isNaN(status_number)) {
|
||
|
|
throw new Error(
|
||
|
|
`response.status is not a number. value: "${response2.status}" type: ${typeof response2.status}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
fetched.push({
|
||
|
|
url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
|
||
|
|
method: event.request.method,
|
||
|
|
request_body: (
|
||
|
|
/** @type {string | ArrayBufferView | undefined} */
|
||
|
|
input instanceof Request && cloned_body ? await stream_to_string(cloned_body) : init2?.body
|
||
|
|
),
|
||
|
|
request_headers: cloned_headers,
|
||
|
|
response_body: body2,
|
||
|
|
response: response2,
|
||
|
|
is_b64
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (key2 === "body") {
|
||
|
|
if (response2.body === null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (teed_body) {
|
||
|
|
return teed_body;
|
||
|
|
}
|
||
|
|
const [a, b] = response2.body.tee();
|
||
|
|
void (async () => {
|
||
|
|
let result = new Uint8Array();
|
||
|
|
for await (const chunk of a) {
|
||
|
|
const combined = new Uint8Array(result.length + chunk.length);
|
||
|
|
combined.set(result, 0);
|
||
|
|
combined.set(chunk, result.length);
|
||
|
|
result = combined;
|
||
|
|
}
|
||
|
|
if (dependency) {
|
||
|
|
dependency.body = new Uint8Array(result);
|
||
|
|
}
|
||
|
|
void push_fetched(base64_encode(result), true);
|
||
|
|
})();
|
||
|
|
return teed_body = b;
|
||
|
|
}
|
||
|
|
if (key2 === "arrayBuffer") {
|
||
|
|
return async () => {
|
||
|
|
const buffer = await response2.arrayBuffer();
|
||
|
|
const bytes = new Uint8Array(buffer);
|
||
|
|
if (dependency) {
|
||
|
|
dependency.body = bytes;
|
||
|
|
}
|
||
|
|
if (buffer instanceof ArrayBuffer) {
|
||
|
|
await push_fetched(base64_encode(bytes), true);
|
||
|
|
}
|
||
|
|
return buffer;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
async function text2() {
|
||
|
|
const body2 = await response2.text();
|
||
|
|
if (body2 === "" && NULL_BODY_STATUS.includes(response2.status)) {
|
||
|
|
await push_fetched(void 0, false);
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
if (!body2 || typeof body2 === "string") {
|
||
|
|
await push_fetched(body2, false);
|
||
|
|
}
|
||
|
|
if (dependency) {
|
||
|
|
dependency.body = body2;
|
||
|
|
}
|
||
|
|
return body2;
|
||
|
|
}
|
||
|
|
if (key2 === "text") {
|
||
|
|
return text2;
|
||
|
|
}
|
||
|
|
if (key2 === "json") {
|
||
|
|
return async () => {
|
||
|
|
const body2 = await text2();
|
||
|
|
return body2 ? JSON.parse(body2) : void 0;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
const value = Reflect.get(response2, key2, response2);
|
||
|
|
if (value instanceof Function) {
|
||
|
|
return Object.defineProperties(
|
||
|
|
/**
|
||
|
|
* @this {any}
|
||
|
|
*/
|
||
|
|
function() {
|
||
|
|
return Reflect.apply(value, this === receiver ? response2 : this, arguments);
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: { value: value.name },
|
||
|
|
length: { value: value.length }
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (csr) {
|
||
|
|
const get = response.headers.get;
|
||
|
|
response.headers.get = (key2) => {
|
||
|
|
const lower = key2.toLowerCase();
|
||
|
|
const value = get.call(response.headers, lower);
|
||
|
|
if (value && !lower.startsWith("x-sveltekit-")) {
|
||
|
|
const included = resolve_opts.filterSerializedResponseHeaders(lower, value);
|
||
|
|
if (!included) {
|
||
|
|
throw new Error(
|
||
|
|
`Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://svelte.dev/docs/kit/hooks#Server-hooks-handle (at ${event.route.id})`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return proxy;
|
||
|
|
};
|
||
|
|
return (input, init2) => {
|
||
|
|
const response = universal_fetch(input, init2);
|
||
|
|
response.catch(() => {
|
||
|
|
});
|
||
|
|
return response;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
async function stream_to_string(stream) {
|
||
|
|
let result = "";
|
||
|
|
const reader = stream.getReader();
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
result += text_decoder.decode(value);
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
const subscriber_queue = [];
|
||
|
|
function readable(value, start) {
|
||
|
|
return {
|
||
|
|
subscribe: writable(value, start).subscribe
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function writable(value, start = noop) {
|
||
|
|
let stop;
|
||
|
|
const subscribers = /* @__PURE__ */ new Set();
|
||
|
|
function set(new_value) {
|
||
|
|
if (safe_not_equal(value, new_value)) {
|
||
|
|
value = new_value;
|
||
|
|
if (stop) {
|
||
|
|
const run_queue = !subscriber_queue.length;
|
||
|
|
for (const subscriber of subscribers) {
|
||
|
|
subscriber[1]();
|
||
|
|
subscriber_queue.push(subscriber, value);
|
||
|
|
}
|
||
|
|
if (run_queue) {
|
||
|
|
for (let i = 0; i < subscriber_queue.length; i += 2) {
|
||
|
|
subscriber_queue[i][0](subscriber_queue[i + 1]);
|
||
|
|
}
|
||
|
|
subscriber_queue.length = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function update(fn) {
|
||
|
|
set(fn(value));
|
||
|
|
}
|
||
|
|
function subscribe(run, invalidate = noop) {
|
||
|
|
const subscriber = [run, invalidate];
|
||
|
|
subscribers.add(subscriber);
|
||
|
|
if (subscribers.size === 1) {
|
||
|
|
stop = start(set, update) || noop;
|
||
|
|
}
|
||
|
|
run(value);
|
||
|
|
return () => {
|
||
|
|
subscribers.delete(subscriber);
|
||
|
|
if (subscribers.size === 0 && stop) {
|
||
|
|
stop();
|
||
|
|
stop = null;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return { set, update, subscribe };
|
||
|
|
}
|
||
|
|
function hash(...values) {
|
||
|
|
let hash2 = 5381;
|
||
|
|
for (const value of values) {
|
||
|
|
if (typeof value === "string") {
|
||
|
|
let i = value.length;
|
||
|
|
while (i) hash2 = hash2 * 33 ^ value.charCodeAt(--i);
|
||
|
|
} else if (ArrayBuffer.isView(value)) {
|
||
|
|
const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
||
|
|
let i = buffer.length;
|
||
|
|
while (i) hash2 = hash2 * 33 ^ buffer[--i];
|
||
|
|
} else {
|
||
|
|
throw new TypeError("value must be a string or TypedArray");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return (hash2 >>> 0).toString(36);
|
||
|
|
}
|
||
|
|
const replacements = {
|
||
|
|
"<": "\\u003C",
|
||
|
|
"\u2028": "\\u2028",
|
||
|
|
"\u2029": "\\u2029"
|
||
|
|
};
|
||
|
|
const pattern = new RegExp(`[${Object.keys(replacements).join("")}]`, "g");
|
||
|
|
function serialize_data(fetched, filter, prerendering = false) {
|
||
|
|
const headers2 = {};
|
||
|
|
let cache_control = null;
|
||
|
|
let age = null;
|
||
|
|
let varyAny = false;
|
||
|
|
for (const [key2, value] of fetched.response.headers) {
|
||
|
|
if (filter(key2, value)) {
|
||
|
|
headers2[key2] = value;
|
||
|
|
}
|
||
|
|
if (key2 === "cache-control") cache_control = value;
|
||
|
|
else if (key2 === "age") age = value;
|
||
|
|
else if (key2 === "vary" && value.trim() === "*") varyAny = true;
|
||
|
|
}
|
||
|
|
const payload = {
|
||
|
|
status: fetched.response.status,
|
||
|
|
statusText: fetched.response.statusText,
|
||
|
|
headers: headers2,
|
||
|
|
body: fetched.response_body
|
||
|
|
};
|
||
|
|
const safe_payload = JSON.stringify(payload).replace(pattern, (match) => replacements[match]);
|
||
|
|
const attrs = [
|
||
|
|
'type="application/json"',
|
||
|
|
"data-sveltekit-fetched",
|
||
|
|
`data-url="${escape_html(fetched.url, true)}"`
|
||
|
|
];
|
||
|
|
if (fetched.is_b64) {
|
||
|
|
attrs.push("data-b64");
|
||
|
|
}
|
||
|
|
if (fetched.request_headers || fetched.request_body) {
|
||
|
|
const values = [];
|
||
|
|
if (fetched.request_headers) {
|
||
|
|
values.push([...new Headers(fetched.request_headers)].join(","));
|
||
|
|
}
|
||
|
|
if (fetched.request_body) {
|
||
|
|
values.push(fetched.request_body);
|
||
|
|
}
|
||
|
|
attrs.push(`data-hash="${hash(...values)}"`);
|
||
|
|
}
|
||
|
|
if (!prerendering && fetched.method === "GET" && cache_control && !varyAny) {
|
||
|
|
const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control);
|
||
|
|
if (match) {
|
||
|
|
const ttl = +match[1] - +(age ?? "0");
|
||
|
|
attrs.push(`data-ttl="${ttl}"`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return `<script ${attrs.join(" ")}>${safe_payload}<\/script>`;
|
||
|
|
}
|
||
|
|
const s = JSON.stringify;
|
||
|
|
function sha256(data) {
|
||
|
|
if (!key[0]) precompute();
|
||
|
|
const out = init.slice(0);
|
||
|
|
const array2 = encode(data);
|
||
|
|
for (let i = 0; i < array2.length; i += 16) {
|
||
|
|
const w = array2.subarray(i, i + 16);
|
||
|
|
let tmp;
|
||
|
|
let a;
|
||
|
|
let b;
|
||
|
|
let out0 = out[0];
|
||
|
|
let out1 = out[1];
|
||
|
|
let out2 = out[2];
|
||
|
|
let out3 = out[3];
|
||
|
|
let out4 = out[4];
|
||
|
|
let out5 = out[5];
|
||
|
|
let out6 = out[6];
|
||
|
|
let out7 = out[7];
|
||
|
|
for (let i2 = 0; i2 < 64; i2++) {
|
||
|
|
if (i2 < 16) {
|
||
|
|
tmp = w[i2];
|
||
|
|
} else {
|
||
|
|
a = w[i2 + 1 & 15];
|
||
|
|
b = w[i2 + 14 & 15];
|
||
|
|
tmp = w[i2 & 15] = (a >>> 7 ^ a >>> 18 ^ a >>> 3 ^ a << 25 ^ a << 14) + (b >>> 17 ^ b >>> 19 ^ b >>> 10 ^ b << 15 ^ b << 13) + w[i2 & 15] + w[i2 + 9 & 15] | 0;
|
||
|
|
}
|
||
|
|
tmp = tmp + out7 + (out4 >>> 6 ^ out4 >>> 11 ^ out4 >>> 25 ^ out4 << 26 ^ out4 << 21 ^ out4 << 7) + (out6 ^ out4 & (out5 ^ out6)) + key[i2];
|
||
|
|
out7 = out6;
|
||
|
|
out6 = out5;
|
||
|
|
out5 = out4;
|
||
|
|
out4 = out3 + tmp | 0;
|
||
|
|
out3 = out2;
|
||
|
|
out2 = out1;
|
||
|
|
out1 = out0;
|
||
|
|
out0 = tmp + (out1 & out2 ^ out3 & (out1 ^ out2)) + (out1 >>> 2 ^ out1 >>> 13 ^ out1 >>> 22 ^ out1 << 30 ^ out1 << 19 ^ out1 << 10) | 0;
|
||
|
|
}
|
||
|
|
out[0] = out[0] + out0 | 0;
|
||
|
|
out[1] = out[1] + out1 | 0;
|
||
|
|
out[2] = out[2] + out2 | 0;
|
||
|
|
out[3] = out[3] + out3 | 0;
|
||
|
|
out[4] = out[4] + out4 | 0;
|
||
|
|
out[5] = out[5] + out5 | 0;
|
||
|
|
out[6] = out[6] + out6 | 0;
|
||
|
|
out[7] = out[7] + out7 | 0;
|
||
|
|
}
|
||
|
|
const bytes = new Uint8Array(out.buffer);
|
||
|
|
reverse_endianness(bytes);
|
||
|
|
return btoa(String.fromCharCode(...bytes));
|
||
|
|
}
|
||
|
|
const init = new Uint32Array(8);
|
||
|
|
const key = new Uint32Array(64);
|
||
|
|
function precompute() {
|
||
|
|
function frac(x) {
|
||
|
|
return (x - Math.floor(x)) * 4294967296;
|
||
|
|
}
|
||
|
|
let prime = 2;
|
||
|
|
for (let i = 0; i < 64; prime++) {
|
||
|
|
let is_prime = true;
|
||
|
|
for (let factor = 2; factor * factor <= prime; factor++) {
|
||
|
|
if (prime % factor === 0) {
|
||
|
|
is_prime = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (is_prime) {
|
||
|
|
if (i < 8) {
|
||
|
|
init[i] = frac(prime ** (1 / 2));
|
||
|
|
}
|
||
|
|
key[i] = frac(prime ** (1 / 3));
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function reverse_endianness(bytes) {
|
||
|
|
for (let i = 0; i < bytes.length; i += 4) {
|
||
|
|
const a = bytes[i + 0];
|
||
|
|
const b = bytes[i + 1];
|
||
|
|
const c = bytes[i + 2];
|
||
|
|
const d = bytes[i + 3];
|
||
|
|
bytes[i + 0] = d;
|
||
|
|
bytes[i + 1] = c;
|
||
|
|
bytes[i + 2] = b;
|
||
|
|
bytes[i + 3] = a;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function encode(str) {
|
||
|
|
const encoded = text_encoder.encode(str);
|
||
|
|
const length = encoded.length * 8;
|
||
|
|
const size = 512 * Math.ceil((length + 65) / 512);
|
||
|
|
const bytes = new Uint8Array(size / 8);
|
||
|
|
bytes.set(encoded);
|
||
|
|
bytes[encoded.length] = 128;
|
||
|
|
reverse_endianness(bytes);
|
||
|
|
const words = new Uint32Array(bytes.buffer);
|
||
|
|
words[words.length - 2] = Math.floor(length / 4294967296);
|
||
|
|
words[words.length - 1] = length;
|
||
|
|
return words;
|
||
|
|
}
|
||
|
|
const array = new Uint8Array(16);
|
||
|
|
function generate_nonce() {
|
||
|
|
crypto.getRandomValues(array);
|
||
|
|
return btoa(String.fromCharCode(...array));
|
||
|
|
}
|
||
|
|
const quoted = /* @__PURE__ */ new Set([
|
||
|
|
"self",
|
||
|
|
"unsafe-eval",
|
||
|
|
"unsafe-hashes",
|
||
|
|
"unsafe-inline",
|
||
|
|
"none",
|
||
|
|
"strict-dynamic",
|
||
|
|
"report-sample",
|
||
|
|
"wasm-unsafe-eval",
|
||
|
|
"script"
|
||
|
|
]);
|
||
|
|
const crypto_pattern = /^(nonce|sha\d\d\d)-/;
|
||
|
|
class BaseProvider {
|
||
|
|
/** @type {boolean} */
|
||
|
|
#use_hashes;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#script_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#script_src_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#script_src_elem_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#style_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#style_src_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#style_src_attr_needs_csp;
|
||
|
|
/** @type {boolean} */
|
||
|
|
#style_src_elem_needs_csp;
|
||
|
|
/** @type {import('types').CspDirectives} */
|
||
|
|
#directives;
|
||
|
|
/** @type {import('types').Csp.Source[]} */
|
||
|
|
#script_src;
|
||
|
|
/** @type {import('types').Csp.Source[]} */
|
||
|
|
#script_src_elem;
|
||
|
|
/** @type {import('types').Csp.Source[]} */
|
||
|
|
#style_src;
|
||
|
|
/** @type {import('types').Csp.Source[]} */
|
||
|
|
#style_src_attr;
|
||
|
|
/** @type {import('types').Csp.Source[]} */
|
||
|
|
#style_src_elem;
|
||
|
|
/** @type {string} */
|
||
|
|
#nonce;
|
||
|
|
/**
|
||
|
|
* @param {boolean} use_hashes
|
||
|
|
* @param {import('types').CspDirectives} directives
|
||
|
|
* @param {string} nonce
|
||
|
|
*/
|
||
|
|
constructor(use_hashes, directives, nonce) {
|
||
|
|
this.#use_hashes = use_hashes;
|
||
|
|
this.#directives = directives;
|
||
|
|
const d = this.#directives;
|
||
|
|
this.#script_src = [];
|
||
|
|
this.#script_src_elem = [];
|
||
|
|
this.#style_src = [];
|
||
|
|
this.#style_src_attr = [];
|
||
|
|
this.#style_src_elem = [];
|
||
|
|
const effective_script_src = d["script-src"] || d["default-src"];
|
||
|
|
const script_src_elem = d["script-src-elem"];
|
||
|
|
const effective_style_src = d["style-src"] || d["default-src"];
|
||
|
|
const style_src_attr = d["style-src-attr"];
|
||
|
|
const style_src_elem = d["style-src-elem"];
|
||
|
|
const needs_csp = (directive) => !!directive && !directive.some((value) => value === "unsafe-inline");
|
||
|
|
this.#script_src_needs_csp = needs_csp(effective_script_src);
|
||
|
|
this.#script_src_elem_needs_csp = needs_csp(script_src_elem);
|
||
|
|
this.#style_src_needs_csp = needs_csp(effective_style_src);
|
||
|
|
this.#style_src_attr_needs_csp = needs_csp(style_src_attr);
|
||
|
|
this.#style_src_elem_needs_csp = needs_csp(style_src_elem);
|
||
|
|
this.#script_needs_csp = this.#script_src_needs_csp || this.#script_src_elem_needs_csp;
|
||
|
|
this.#style_needs_csp = this.#style_src_needs_csp || this.#style_src_attr_needs_csp || this.#style_src_elem_needs_csp;
|
||
|
|
this.script_needs_nonce = this.#script_needs_csp && !this.#use_hashes;
|
||
|
|
this.style_needs_nonce = this.#style_needs_csp && !this.#use_hashes;
|
||
|
|
this.#nonce = nonce;
|
||
|
|
}
|
||
|
|
/** @param {string} content */
|
||
|
|
add_script(content) {
|
||
|
|
if (!this.#script_needs_csp) return;
|
||
|
|
const source = this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
|
||
|
|
if (this.#script_src_needs_csp) {
|
||
|
|
this.#script_src.push(source);
|
||
|
|
}
|
||
|
|
if (this.#script_src_elem_needs_csp) {
|
||
|
|
this.#script_src_elem.push(source);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/** @param {string} content */
|
||
|
|
add_style(content) {
|
||
|
|
if (!this.#style_needs_csp) return;
|
||
|
|
const source = this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
|
||
|
|
if (this.#style_src_needs_csp) {
|
||
|
|
this.#style_src.push(source);
|
||
|
|
}
|
||
|
|
if (this.#style_src_attr_needs_csp) {
|
||
|
|
this.#style_src_attr.push(source);
|
||
|
|
}
|
||
|
|
if (this.#style_src_elem_needs_csp) {
|
||
|
|
const sha256_empty_comment_hash = "sha256-9OlNO0DNEeaVzHL4RZwCLsBHA8WBQ8toBp/4F5XV2nc=";
|
||
|
|
const d = this.#directives;
|
||
|
|
if (d["style-src-elem"] && !d["style-src-elem"].includes(sha256_empty_comment_hash) && !this.#style_src_elem.includes(sha256_empty_comment_hash)) {
|
||
|
|
this.#style_src_elem.push(sha256_empty_comment_hash);
|
||
|
|
}
|
||
|
|
if (source !== sha256_empty_comment_hash) {
|
||
|
|
this.#style_src_elem.push(source);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @param {boolean} [is_meta]
|
||
|
|
*/
|
||
|
|
get_header(is_meta = false) {
|
||
|
|
const header = [];
|
||
|
|
const directives = { ...this.#directives };
|
||
|
|
if (this.#style_src.length > 0) {
|
||
|
|
directives["style-src"] = [
|
||
|
|
...directives["style-src"] || directives["default-src"] || [],
|
||
|
|
...this.#style_src
|
||
|
|
];
|
||
|
|
}
|
||
|
|
if (this.#style_src_attr.length > 0) {
|
||
|
|
directives["style-src-attr"] = [
|
||
|
|
...directives["style-src-attr"] || [],
|
||
|
|
...this.#style_src_attr
|
||
|
|
];
|
||
|
|
}
|
||
|
|
if (this.#style_src_elem.length > 0) {
|
||
|
|
directives["style-src-elem"] = [
|
||
|
|
...directives["style-src-elem"] || [],
|
||
|
|
...this.#style_src_elem
|
||
|
|
];
|
||
|
|
}
|
||
|
|
if (this.#script_src.length > 0) {
|
||
|
|
directives["script-src"] = [
|
||
|
|
...directives["script-src"] || directives["default-src"] || [],
|
||
|
|
...this.#script_src
|
||
|
|
];
|
||
|
|
}
|
||
|
|
if (this.#script_src_elem.length > 0) {
|
||
|
|
directives["script-src-elem"] = [
|
||
|
|
...directives["script-src-elem"] || [],
|
||
|
|
...this.#script_src_elem
|
||
|
|
];
|
||
|
|
}
|
||
|
|
for (const key2 in directives) {
|
||
|
|
if (is_meta && (key2 === "frame-ancestors" || key2 === "report-uri" || key2 === "sandbox")) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
const value = (
|
||
|
|
/** @type {string[] | true} */
|
||
|
|
directives[key2]
|
||
|
|
);
|
||
|
|
if (!value) continue;
|
||
|
|
const directive = [key2];
|
||
|
|
if (Array.isArray(value)) {
|
||
|
|
value.forEach((value2) => {
|
||
|
|
if (quoted.has(value2) || crypto_pattern.test(value2)) {
|
||
|
|
directive.push(`'${value2}'`);
|
||
|
|
} else {
|
||
|
|
directive.push(value2);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
header.push(directive.join(" "));
|
||
|
|
}
|
||
|
|
return header.join("; ");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
class CspProvider extends BaseProvider {
|
||
|
|
get_meta() {
|
||
|
|
const content = this.get_header(true);
|
||
|
|
if (!content) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
return `<meta http-equiv="content-security-policy" content="${escape_html(content, true)}">`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
class CspReportOnlyProvider extends BaseProvider {
|
||
|
|
/**
|
||
|
|
* @param {boolean} use_hashes
|
||
|
|
* @param {import('types').CspDirectives} directives
|
||
|
|
* @param {string} nonce
|
||
|
|
*/
|
||
|
|
constructor(use_hashes, directives, nonce) {
|
||
|
|
super(use_hashes, directives, nonce);
|
||
|
|
if (Object.values(directives).filter((v) => !!v).length > 0) {
|
||
|
|
const has_report_to = directives["report-to"]?.length ?? 0 > 0;
|
||
|
|
const has_report_uri = directives["report-uri"]?.length ?? 0 > 0;
|
||
|
|
if (!has_report_to && !has_report_uri) {
|
||
|
|
throw Error(
|
||
|
|
"`content-security-policy-report-only` must be specified with either the `report-to` or `report-uri` directives, or both"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
class Csp {
|
||
|
|
/** @readonly */
|
||
|
|
nonce = generate_nonce();
|
||
|
|
/** @type {CspProvider} */
|
||
|
|
csp_provider;
|
||
|
|
/** @type {CspReportOnlyProvider} */
|
||
|
|
report_only_provider;
|
||
|
|
/**
|
||
|
|
* @param {import('./types.js').CspConfig} config
|
||
|
|
* @param {import('./types.js').CspOpts} opts
|
||
|
|
*/
|
||
|
|
constructor({ mode, directives, reportOnly }, { prerender }) {
|
||
|
|
const use_hashes = mode === "hash" || mode === "auto" && prerender;
|
||
|
|
this.csp_provider = new CspProvider(use_hashes, directives, this.nonce);
|
||
|
|
this.report_only_provider = new CspReportOnlyProvider(use_hashes, reportOnly, this.nonce);
|
||
|
|
}
|
||
|
|
get script_needs_nonce() {
|
||
|
|
return this.csp_provider.script_needs_nonce || this.report_only_provider.script_needs_nonce;
|
||
|
|
}
|
||
|
|
get style_needs_nonce() {
|
||
|
|
return this.csp_provider.style_needs_nonce || this.report_only_provider.style_needs_nonce;
|
||
|
|
}
|
||
|
|
/** @param {string} content */
|
||
|
|
add_script(content) {
|
||
|
|
this.csp_provider.add_script(content);
|
||
|
|
this.report_only_provider.add_script(content);
|
||
|
|
}
|
||
|
|
/** @param {string} content */
|
||
|
|
add_style(content) {
|
||
|
|
this.csp_provider.add_style(content);
|
||
|
|
this.report_only_provider.add_style(content);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function exec(match, params, matchers) {
|
||
|
|
const result = {};
|
||
|
|
const values = match.slice(1);
|
||
|
|
const values_needing_match = values.filter((value) => value !== void 0);
|
||
|
|
let buffered = 0;
|
||
|
|
for (let i = 0; i < params.length; i += 1) {
|
||
|
|
const param = params[i];
|
||
|
|
let value = values[i - buffered];
|
||
|
|
if (param.chained && param.rest && buffered) {
|
||
|
|
value = values.slice(i - buffered, i + 1).filter((s2) => s2).join("/");
|
||
|
|
buffered = 0;
|
||
|
|
}
|
||
|
|
if (value === void 0) {
|
||
|
|
if (param.rest) result[param.name] = "";
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (!param.matcher || matchers[param.matcher](value)) {
|
||
|
|
result[param.name] = value;
|
||
|
|
const next_param = params[i + 1];
|
||
|
|
const next_value = values[i + 1];
|
||
|
|
if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
|
||
|
|
buffered = 0;
|
||
|
|
}
|
||
|
|
if (!next_param && !next_value && Object.keys(result).length === values_needing_match.length) {
|
||
|
|
buffered = 0;
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (param.optional && param.chained) {
|
||
|
|
buffered++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (buffered) return;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
function generate_route_object(route, url, manifest) {
|
||
|
|
const { errors, layouts, leaf } = route;
|
||
|
|
const nodes = [...errors, ...layouts.map((l) => l?.[1]), leaf[1]].filter((n) => typeof n === "number").map((n) => `'${n}': () => ${create_client_import(manifest._.client.nodes?.[n], url)}`).join(",\n ");
|
||
|
|
return [
|
||
|
|
`{
|
||
|
|
id: ${s(route.id)}`,
|
||
|
|
`errors: ${s(route.errors)}`,
|
||
|
|
`layouts: ${s(route.layouts)}`,
|
||
|
|
`leaf: ${s(route.leaf)}`,
|
||
|
|
`nodes: {
|
||
|
|
${nodes}
|
||
|
|
}
|
||
|
|
}`
|
||
|
|
].join(",\n ");
|
||
|
|
}
|
||
|
|
function create_client_import(import_path, url) {
|
||
|
|
if (!import_path) return "Promise.resolve({})";
|
||
|
|
if (import_path[0] === "/") {
|
||
|
|
return `import('${import_path}')`;
|
||
|
|
}
|
||
|
|
if (assets !== "") {
|
||
|
|
return `import('${assets}/${import_path}')`;
|
||
|
|
}
|
||
|
|
let path = get_relative_path(url.pathname, `${base}/${import_path}`);
|
||
|
|
if (path[0] !== ".") path = `./${path}`;
|
||
|
|
return `import('${path}')`;
|
||
|
|
}
|
||
|
|
async function resolve_route(resolved_path, url, manifest) {
|
||
|
|
if (!manifest._.client.routes) {
|
||
|
|
return text("Server-side route resolution disabled", { status: 400 });
|
||
|
|
}
|
||
|
|
let route = null;
|
||
|
|
let params = {};
|
||
|
|
const matchers = await manifest._.matchers();
|
||
|
|
for (const candidate of manifest._.client.routes) {
|
||
|
|
const match = candidate.pattern.exec(resolved_path);
|
||
|
|
if (!match) continue;
|
||
|
|
const matched = exec(match, candidate.params, matchers);
|
||
|
|
if (matched) {
|
||
|
|
route = candidate;
|
||
|
|
params = decode_params(matched);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return create_server_routing_response(route, params, url, manifest).response;
|
||
|
|
}
|
||
|
|
function create_server_routing_response(route, params, url, manifest) {
|
||
|
|
const headers2 = new Headers({
|
||
|
|
"content-type": "application/javascript; charset=utf-8"
|
||
|
|
});
|
||
|
|
if (route) {
|
||
|
|
const csr_route = generate_route_object(route, url, manifest);
|
||
|
|
const body2 = `${create_css_import(route, url, manifest)}
|
||
|
|
export const route = ${csr_route}; export const params = ${JSON.stringify(params)};`;
|
||
|
|
return { response: text(body2, { headers: headers2 }), body: body2 };
|
||
|
|
} else {
|
||
|
|
return { response: text("", { headers: headers2 }), body: "" };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function create_css_import(route, url, manifest) {
|
||
|
|
const { errors, layouts, leaf } = route;
|
||
|
|
let css = "";
|
||
|
|
for (const node of [...errors, ...layouts.map((l) => l?.[1]), leaf[1]]) {
|
||
|
|
if (typeof node !== "number") continue;
|
||
|
|
const node_css = manifest._.client.css?.[node];
|
||
|
|
for (const css_path of node_css ?? []) {
|
||
|
|
css += `'${assets || base}/${css_path}',`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!css) return "";
|
||
|
|
return `${create_client_import(
|
||
|
|
/** @type {string} */
|
||
|
|
manifest._.client.start,
|
||
|
|
url
|
||
|
|
)}.then(x => x.load_css([${css}]));`;
|
||
|
|
}
|
||
|
|
const updated = {
|
||
|
|
...readable(false),
|
||
|
|
check: () => false
|
||
|
|
};
|
||
|
|
async function render_response({
|
||
|
|
branch,
|
||
|
|
fetched,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
page_config,
|
||
|
|
status,
|
||
|
|
error: error2 = null,
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
resolve_opts,
|
||
|
|
action_result,
|
||
|
|
data_serializer
|
||
|
|
}) {
|
||
|
|
if (state.prerendering) {
|
||
|
|
if (options2.csp.mode === "nonce") {
|
||
|
|
throw new Error('Cannot use prerendering if config.kit.csp.mode === "nonce"');
|
||
|
|
}
|
||
|
|
if (options2.app_template_contains_nonce) {
|
||
|
|
throw new Error("Cannot use prerendering if page template contains %sveltekit.nonce%");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const { client } = manifest._;
|
||
|
|
const modulepreloads = new Set(client.imports);
|
||
|
|
const stylesheets = new Set(client.stylesheets);
|
||
|
|
const fonts = new Set(client.fonts);
|
||
|
|
const link_headers = /* @__PURE__ */ new Set();
|
||
|
|
const link_tags = /* @__PURE__ */ new Set();
|
||
|
|
const inline_styles = /* @__PURE__ */ new Map();
|
||
|
|
let rendered;
|
||
|
|
const form_value = action_result?.type === "success" || action_result?.type === "failure" ? action_result.data ?? null : null;
|
||
|
|
let base$1 = base;
|
||
|
|
let assets$1 = assets;
|
||
|
|
let base_expression = s(base);
|
||
|
|
{
|
||
|
|
if (!state.prerendering?.fallback) {
|
||
|
|
const segments = event.url.pathname.slice(base.length).split("/").slice(2);
|
||
|
|
base$1 = segments.map(() => "..").join("/") || ".";
|
||
|
|
base_expression = `new URL(${s(base$1)}, location).pathname.slice(0, -1)`;
|
||
|
|
if (!assets || assets[0] === "/" && assets !== SVELTE_KIT_ASSETS) {
|
||
|
|
assets$1 = base$1;
|
||
|
|
}
|
||
|
|
} else if (options2.hash_routing) {
|
||
|
|
base_expression = "new URL('.', location).pathname.slice(0, -1)";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (page_config.ssr) {
|
||
|
|
const props = {
|
||
|
|
stores: {
|
||
|
|
page: writable(null),
|
||
|
|
navigating: writable(null),
|
||
|
|
updated
|
||
|
|
},
|
||
|
|
constructors: await Promise.all(
|
||
|
|
branch.map(({ node }) => {
|
||
|
|
if (!node.component) {
|
||
|
|
throw new Error(`Missing +page.svelte component for route ${event.route.id}`);
|
||
|
|
}
|
||
|
|
return node.component();
|
||
|
|
})
|
||
|
|
),
|
||
|
|
form: form_value
|
||
|
|
};
|
||
|
|
let data2 = {};
|
||
|
|
for (let i = 0; i < branch.length; i += 1) {
|
||
|
|
data2 = { ...data2, ...branch[i].data };
|
||
|
|
props[`data_${i}`] = data2;
|
||
|
|
}
|
||
|
|
props.page = {
|
||
|
|
error: error2,
|
||
|
|
params: (
|
||
|
|
/** @type {Record<string, any>} */
|
||
|
|
event.params
|
||
|
|
),
|
||
|
|
route: event.route,
|
||
|
|
status,
|
||
|
|
url: event.url,
|
||
|
|
data: data2,
|
||
|
|
form: form_value,
|
||
|
|
state: {}
|
||
|
|
};
|
||
|
|
const render_opts = {
|
||
|
|
context: /* @__PURE__ */ new Map([
|
||
|
|
[
|
||
|
|
"__request__",
|
||
|
|
{
|
||
|
|
page: props.page
|
||
|
|
}
|
||
|
|
]
|
||
|
|
])
|
||
|
|
};
|
||
|
|
const fetch2 = globalThis.fetch;
|
||
|
|
try {
|
||
|
|
if (BROWSER) ;
|
||
|
|
rendered = await with_request_store({ event, state: event_state }, async () => {
|
||
|
|
if (relative) override({ base: base$1, assets: assets$1 });
|
||
|
|
const maybe_promise = options2.root.render(props, render_opts);
|
||
|
|
const rendered2 = options2.async && "then" in maybe_promise ? (
|
||
|
|
/** @type {ReturnType<typeof options.root.render> & Promise<any>} */
|
||
|
|
maybe_promise.then((r) => r)
|
||
|
|
) : maybe_promise;
|
||
|
|
if (options2.async) {
|
||
|
|
reset();
|
||
|
|
}
|
||
|
|
const { head: head2, html: html2, css } = options2.async ? await rendered2 : rendered2;
|
||
|
|
return { head: head2, html: html2, css };
|
||
|
|
});
|
||
|
|
} finally {
|
||
|
|
reset();
|
||
|
|
}
|
||
|
|
for (const { node } of branch) {
|
||
|
|
for (const url of node.imports) modulepreloads.add(url);
|
||
|
|
for (const url of node.stylesheets) stylesheets.add(url);
|
||
|
|
for (const url of node.fonts) fonts.add(url);
|
||
|
|
if (node.inline_styles && !client.inline) {
|
||
|
|
Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
rendered = { head: "", html: "", css: { code: "", map: null } };
|
||
|
|
}
|
||
|
|
let head = "";
|
||
|
|
let body2 = rendered.html;
|
||
|
|
const csp = new Csp(options2.csp, {
|
||
|
|
prerender: !!state.prerendering
|
||
|
|
});
|
||
|
|
const prefixed = (path) => {
|
||
|
|
if (path.startsWith("/")) {
|
||
|
|
return base + path;
|
||
|
|
}
|
||
|
|
return `${assets$1}/${path}`;
|
||
|
|
};
|
||
|
|
const style = client.inline ? client.inline?.style : Array.from(inline_styles.values()).join("\n");
|
||
|
|
if (style) {
|
||
|
|
const attributes = [];
|
||
|
|
if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
|
||
|
|
csp.add_style(style);
|
||
|
|
head += `
|
||
|
|
<style${attributes.join("")}>${style}</style>`;
|
||
|
|
}
|
||
|
|
for (const dep of stylesheets) {
|
||
|
|
const path = prefixed(dep);
|
||
|
|
const attributes = ['rel="stylesheet"'];
|
||
|
|
if (inline_styles.has(dep)) {
|
||
|
|
attributes.push("disabled", 'media="(max-width: 0)"');
|
||
|
|
} else {
|
||
|
|
if (resolve_opts.preload({ type: "css", path })) {
|
||
|
|
link_headers.add(`<${encodeURI(path)}>; rel="preload"; as="style"; nopush`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
head += `
|
||
|
|
<link href="${path}" ${attributes.join(" ")}>`;
|
||
|
|
}
|
||
|
|
for (const dep of fonts) {
|
||
|
|
const path = prefixed(dep);
|
||
|
|
if (resolve_opts.preload({ type: "font", path })) {
|
||
|
|
const ext = dep.slice(dep.lastIndexOf(".") + 1);
|
||
|
|
link_tags.add(`<link rel="preload" as="font" type="font/${ext}" href="${path}" crossorigin>`);
|
||
|
|
link_headers.add(
|
||
|
|
`<${encodeURI(path)}>; rel="preload"; as="font"; type="font/${ext}"; crossorigin; nopush`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const global = get_global_name(options2);
|
||
|
|
const { data, chunks } = data_serializer.get_data(csp);
|
||
|
|
if (page_config.ssr && page_config.csr) {
|
||
|
|
body2 += `
|
||
|
|
${fetched.map(
|
||
|
|
(item) => serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
|
||
|
|
).join("\n ")}`;
|
||
|
|
}
|
||
|
|
if (page_config.csr) {
|
||
|
|
const route = manifest._.client.routes?.find((r) => r.id === event.route.id) ?? null;
|
||
|
|
if (client.uses_env_dynamic_public && state.prerendering) {
|
||
|
|
modulepreloads.add(`${app_dir}/env.js`);
|
||
|
|
}
|
||
|
|
if (!client.inline) {
|
||
|
|
const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
|
||
|
|
(path) => resolve_opts.preload({ type: "js", path })
|
||
|
|
);
|
||
|
|
for (const path of included_modulepreloads) {
|
||
|
|
link_headers.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
|
||
|
|
if (options2.preload_strategy !== "modulepreload") {
|
||
|
|
head += `
|
||
|
|
<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
|
||
|
|
} else {
|
||
|
|
link_tags.add(`<link rel="modulepreload" href="${path}">`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (state.prerendering && link_tags.size > 0) {
|
||
|
|
head += Array.from(link_tags).map((tag) => `
|
||
|
|
${tag}`).join("");
|
||
|
|
}
|
||
|
|
if (manifest._.client.routes && state.prerendering && !state.prerendering.fallback) {
|
||
|
|
const pathname = add_resolution_suffix(event.url.pathname);
|
||
|
|
state.prerendering.dependencies.set(
|
||
|
|
pathname,
|
||
|
|
create_server_routing_response(route, event.params, new URL(pathname, event.url), manifest)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const blocks = [];
|
||
|
|
const load_env_eagerly = client.uses_env_dynamic_public && state.prerendering;
|
||
|
|
const properties = [`base: ${base_expression}`];
|
||
|
|
if (assets) {
|
||
|
|
properties.push(`assets: ${s(assets)}`);
|
||
|
|
}
|
||
|
|
if (client.uses_env_dynamic_public) {
|
||
|
|
properties.push(`env: ${load_env_eagerly ? "null" : s(public_env)}`);
|
||
|
|
}
|
||
|
|
if (chunks) {
|
||
|
|
blocks.push("const deferred = new Map();");
|
||
|
|
properties.push(`defer: (id) => new Promise((fulfil, reject) => {
|
||
|
|
deferred.set(id, { fulfil, reject });
|
||
|
|
})`);
|
||
|
|
let app_declaration = "";
|
||
|
|
if (Object.keys(options2.hooks.transport).length > 0) {
|
||
|
|
if (client.inline) {
|
||
|
|
app_declaration = `const app = __sveltekit_${options2.version_hash}.app.app;`;
|
||
|
|
} else if (client.app) {
|
||
|
|
app_declaration = `const app = await import(${s(prefixed(client.app))});`;
|
||
|
|
} else {
|
||
|
|
app_declaration = `const { app } = await import(${s(prefixed(client.start))});`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const prelude = app_declaration ? `${app_declaration}
|
||
|
|
const [data, error] = fn(app);` : `const [data, error] = fn();`;
|
||
|
|
properties.push(`resolve: async (id, fn) => {
|
||
|
|
${prelude}
|
||
|
|
|
||
|
|
const try_to_resolve = () => {
|
||
|
|
if (!deferred.has(id)) {
|
||
|
|
setTimeout(try_to_resolve, 0);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const { fulfil, reject } = deferred.get(id);
|
||
|
|
deferred.delete(id);
|
||
|
|
if (error) reject(error);
|
||
|
|
else fulfil(data);
|
||
|
|
}
|
||
|
|
try_to_resolve();
|
||
|
|
}`);
|
||
|
|
}
|
||
|
|
blocks.push(`${global} = {
|
||
|
|
${properties.join(",\n ")}
|
||
|
|
};`);
|
||
|
|
const args = ["element"];
|
||
|
|
blocks.push("const element = document.currentScript.parentElement;");
|
||
|
|
if (page_config.ssr) {
|
||
|
|
const serialized = { form: "null", error: "null" };
|
||
|
|
if (form_value) {
|
||
|
|
serialized.form = uneval_action_response(
|
||
|
|
form_value,
|
||
|
|
/** @type {string} */
|
||
|
|
event.route.id,
|
||
|
|
options2.hooks.transport
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (error2) {
|
||
|
|
serialized.error = devalue.uneval(error2);
|
||
|
|
}
|
||
|
|
const hydrate = [
|
||
|
|
`node_ids: [${branch.map(({ node }) => node.index).join(", ")}]`,
|
||
|
|
`data: ${data}`,
|
||
|
|
`form: ${serialized.form}`,
|
||
|
|
`error: ${serialized.error}`
|
||
|
|
];
|
||
|
|
if (status !== 200) {
|
||
|
|
hydrate.push(`status: ${status}`);
|
||
|
|
}
|
||
|
|
if (manifest._.client.routes) {
|
||
|
|
if (route) {
|
||
|
|
const stringified = generate_route_object(route, event.url, manifest).replaceAll(
|
||
|
|
"\n",
|
||
|
|
"\n "
|
||
|
|
);
|
||
|
|
hydrate.push(`params: ${devalue.uneval(event.params)}`, `server_route: ${stringified}`);
|
||
|
|
}
|
||
|
|
} else if (options2.embedded) {
|
||
|
|
hydrate.push(`params: ${devalue.uneval(event.params)}`, `route: ${s(event.route)}`);
|
||
|
|
}
|
||
|
|
const indent = " ".repeat(load_env_eagerly ? 7 : 6);
|
||
|
|
args.push(`{
|
||
|
|
${indent} ${hydrate.join(`,
|
||
|
|
${indent} `)}
|
||
|
|
${indent}}`);
|
||
|
|
}
|
||
|
|
const { remote_data: remote_cache } = event_state;
|
||
|
|
let serialized_remote_data = "";
|
||
|
|
if (remote_cache) {
|
||
|
|
const remote = {};
|
||
|
|
for (const [info, cache] of remote_cache) {
|
||
|
|
if (!info.id) continue;
|
||
|
|
for (const key2 in cache) {
|
||
|
|
remote[create_remote_key(info.id, key2)] = await cache[key2];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const replacer = (thing) => {
|
||
|
|
for (const key2 in options2.hooks.transport) {
|
||
|
|
const encoded = options2.hooks.transport[key2].encode(thing);
|
||
|
|
if (encoded) {
|
||
|
|
return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
serialized_remote_data = `${global}.data = ${devalue.uneval(remote, replacer)};
|
||
|
|
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
const boot = client.inline ? `${client.inline.script}
|
||
|
|
|
||
|
|
${serialized_remote_data}${global}.app.start(${args.join(", ")});` : client.app ? `Promise.all([
|
||
|
|
import(${s(prefixed(client.start))}),
|
||
|
|
import(${s(prefixed(client.app))})
|
||
|
|
]).then(([kit, app]) => {
|
||
|
|
${serialized_remote_data}kit.start(app, ${args.join(", ")});
|
||
|
|
});` : `import(${s(prefixed(client.start))}).then((app) => {
|
||
|
|
${serialized_remote_data}app.start(${args.join(", ")})
|
||
|
|
});`;
|
||
|
|
if (load_env_eagerly) {
|
||
|
|
blocks.push(`import(${s(`${base$1}/${app_dir}/env.js`)}).then(({ env }) => {
|
||
|
|
${global}.env = env;
|
||
|
|
|
||
|
|
${boot.replace(/\n/g, "\n ")}
|
||
|
|
});`);
|
||
|
|
} else {
|
||
|
|
blocks.push(boot);
|
||
|
|
}
|
||
|
|
if (options2.service_worker) {
|
||
|
|
let opts = "";
|
||
|
|
if (options2.service_worker_options != null) {
|
||
|
|
const service_worker_options = { ...options2.service_worker_options };
|
||
|
|
opts = `, ${s(service_worker_options)}`;
|
||
|
|
}
|
||
|
|
blocks.push(`if ('serviceWorker' in navigator) {
|
||
|
|
addEventListener('load', function () {
|
||
|
|
navigator.serviceWorker.register('${prefixed("service-worker.js")}'${opts});
|
||
|
|
});
|
||
|
|
}`);
|
||
|
|
}
|
||
|
|
const init_app = `
|
||
|
|
{
|
||
|
|
${blocks.join("\n\n ")}
|
||
|
|
}
|
||
|
|
`;
|
||
|
|
csp.add_script(init_app);
|
||
|
|
body2 += `
|
||
|
|
<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ""}>${init_app}<\/script>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
const headers2 = new Headers({
|
||
|
|
"x-sveltekit-page": "true",
|
||
|
|
"content-type": "text/html"
|
||
|
|
});
|
||
|
|
if (state.prerendering) {
|
||
|
|
const http_equiv = [];
|
||
|
|
const csp_headers = csp.csp_provider.get_meta();
|
||
|
|
if (csp_headers) {
|
||
|
|
http_equiv.push(csp_headers);
|
||
|
|
}
|
||
|
|
if (state.prerendering.cache) {
|
||
|
|
http_equiv.push(`<meta http-equiv="cache-control" content="${state.prerendering.cache}">`);
|
||
|
|
}
|
||
|
|
if (http_equiv.length > 0) {
|
||
|
|
head = http_equiv.join("\n") + head;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
const csp_header = csp.csp_provider.get_header();
|
||
|
|
if (csp_header) {
|
||
|
|
headers2.set("content-security-policy", csp_header);
|
||
|
|
}
|
||
|
|
const report_only_header = csp.report_only_provider.get_header();
|
||
|
|
if (report_only_header) {
|
||
|
|
headers2.set("content-security-policy-report-only", report_only_header);
|
||
|
|
}
|
||
|
|
if (link_headers.size) {
|
||
|
|
headers2.set("link", Array.from(link_headers).join(", "));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
head += rendered.head;
|
||
|
|
const html = options2.templates.app({
|
||
|
|
head,
|
||
|
|
body: body2,
|
||
|
|
assets: assets$1,
|
||
|
|
nonce: (
|
||
|
|
/** @type {string} */
|
||
|
|
csp.nonce
|
||
|
|
),
|
||
|
|
env: public_env
|
||
|
|
});
|
||
|
|
const transformed = await resolve_opts.transformPageChunk({
|
||
|
|
html,
|
||
|
|
done: true
|
||
|
|
}) || "";
|
||
|
|
if (!chunks) {
|
||
|
|
headers2.set("etag", `"${hash(transformed)}"`);
|
||
|
|
}
|
||
|
|
return !chunks ? text(transformed, {
|
||
|
|
status,
|
||
|
|
headers: headers2
|
||
|
|
}) : new Response(
|
||
|
|
new ReadableStream({
|
||
|
|
async start(controller) {
|
||
|
|
controller.enqueue(text_encoder.encode(transformed + "\n"));
|
||
|
|
for await (const chunk of chunks) {
|
||
|
|
if (chunk.length) controller.enqueue(text_encoder.encode(chunk));
|
||
|
|
}
|
||
|
|
controller.close();
|
||
|
|
},
|
||
|
|
type: "bytes"
|
||
|
|
}),
|
||
|
|
{
|
||
|
|
headers: headers2
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
class PageNodes {
|
||
|
|
data;
|
||
|
|
/**
|
||
|
|
* @param {Array<import('types').SSRNode | undefined>} nodes
|
||
|
|
*/
|
||
|
|
constructor(nodes) {
|
||
|
|
this.data = nodes;
|
||
|
|
}
|
||
|
|
layouts() {
|
||
|
|
return this.data.slice(0, -1);
|
||
|
|
}
|
||
|
|
page() {
|
||
|
|
return this.data.at(-1);
|
||
|
|
}
|
||
|
|
validate() {
|
||
|
|
for (const layout of this.layouts()) {
|
||
|
|
if (layout) {
|
||
|
|
validate_layout_server_exports(
|
||
|
|
layout.server,
|
||
|
|
/** @type {string} */
|
||
|
|
layout.server_id
|
||
|
|
);
|
||
|
|
validate_layout_exports(
|
||
|
|
layout.universal,
|
||
|
|
/** @type {string} */
|
||
|
|
layout.universal_id
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const page = this.page();
|
||
|
|
if (page) {
|
||
|
|
validate_page_server_exports(
|
||
|
|
page.server,
|
||
|
|
/** @type {string} */
|
||
|
|
page.server_id
|
||
|
|
);
|
||
|
|
validate_page_exports(
|
||
|
|
page.universal,
|
||
|
|
/** @type {string} */
|
||
|
|
page.universal_id
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
|
||
|
|
* @param {Option} option
|
||
|
|
* @returns {Value | undefined}
|
||
|
|
*/
|
||
|
|
#get_option(option) {
|
||
|
|
return this.data.reduce(
|
||
|
|
(value, node) => {
|
||
|
|
return node?.universal?.[option] ?? node?.server?.[option] ?? value;
|
||
|
|
},
|
||
|
|
/** @type {Value | undefined} */
|
||
|
|
void 0
|
||
|
|
);
|
||
|
|
}
|
||
|
|
csr() {
|
||
|
|
return this.#get_option("csr") ?? true;
|
||
|
|
}
|
||
|
|
ssr() {
|
||
|
|
return this.#get_option("ssr") ?? true;
|
||
|
|
}
|
||
|
|
prerender() {
|
||
|
|
return this.#get_option("prerender") ?? false;
|
||
|
|
}
|
||
|
|
trailing_slash() {
|
||
|
|
return this.#get_option("trailingSlash") ?? "never";
|
||
|
|
}
|
||
|
|
get_config() {
|
||
|
|
let current2 = {};
|
||
|
|
for (const node of this.data) {
|
||
|
|
if (!node?.universal?.config && !node?.server?.config) continue;
|
||
|
|
current2 = {
|
||
|
|
...current2,
|
||
|
|
// TODO: should we override the server config value with the universal value similar to other page options?
|
||
|
|
...node?.universal?.config,
|
||
|
|
...node?.server?.config
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return Object.keys(current2).length ? current2 : void 0;
|
||
|
|
}
|
||
|
|
should_prerender_data() {
|
||
|
|
return this.data.some(
|
||
|
|
// prerender in case of trailingSlash because the client retrieves that value from the server
|
||
|
|
(node) => node?.server?.load || node?.server?.trailingSlash !== void 0
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async function respond_with_error({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
status,
|
||
|
|
error: error2,
|
||
|
|
resolve_opts
|
||
|
|
}) {
|
||
|
|
if (event.request.headers.get("x-sveltekit-error")) {
|
||
|
|
return static_error_page(
|
||
|
|
options2,
|
||
|
|
status,
|
||
|
|
/** @type {Error} */
|
||
|
|
error2.message
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const fetched = [];
|
||
|
|
try {
|
||
|
|
const branch = [];
|
||
|
|
const default_layout = await manifest._.nodes[0]();
|
||
|
|
const nodes = new PageNodes([default_layout]);
|
||
|
|
const ssr = nodes.ssr();
|
||
|
|
const csr = nodes.csr();
|
||
|
|
const data_serializer = server_data_serializer(event, event_state, options2);
|
||
|
|
if (ssr) {
|
||
|
|
state.error = true;
|
||
|
|
const server_data_promise = load_server_data({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
state,
|
||
|
|
node: default_layout,
|
||
|
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||
|
|
parent: async () => ({})
|
||
|
|
});
|
||
|
|
const server_data = await server_data_promise;
|
||
|
|
data_serializer.add_node(0, server_data);
|
||
|
|
const data = await load_data({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
fetched,
|
||
|
|
node: default_layout,
|
||
|
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||
|
|
parent: async () => ({}),
|
||
|
|
resolve_opts,
|
||
|
|
server_data_promise,
|
||
|
|
state,
|
||
|
|
csr
|
||
|
|
});
|
||
|
|
branch.push(
|
||
|
|
{
|
||
|
|
node: default_layout,
|
||
|
|
server_data,
|
||
|
|
data
|
||
|
|
},
|
||
|
|
{
|
||
|
|
node: await manifest._.nodes[1](),
|
||
|
|
// 1 is always the root error
|
||
|
|
data: null,
|
||
|
|
server_data: null
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return await render_response({
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
page_config: {
|
||
|
|
ssr,
|
||
|
|
csr
|
||
|
|
},
|
||
|
|
status,
|
||
|
|
error: await handle_error_and_jsonify(event, event_state, options2, error2),
|
||
|
|
branch,
|
||
|
|
fetched,
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
resolve_opts,
|
||
|
|
data_serializer
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
if (e instanceof Redirect) {
|
||
|
|
return redirect_response(e.status, e.location);
|
||
|
|
}
|
||
|
|
return static_error_page(
|
||
|
|
options2,
|
||
|
|
get_status(e),
|
||
|
|
(await handle_error_and_jsonify(event, event_state, options2, e)).message
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async function handle_remote_call(event, state, options2, manifest, id) {
|
||
|
|
return record_span({
|
||
|
|
name: "sveltekit.remote.call",
|
||
|
|
attributes: {},
|
||
|
|
fn: (current2) => {
|
||
|
|
const traced_event = merge_tracing(event, current2);
|
||
|
|
return with_request_store(
|
||
|
|
{ event: traced_event, state },
|
||
|
|
() => handle_remote_call_internal(traced_event, state, options2, manifest, id)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
async function handle_remote_call_internal(event, state, options2, manifest, id) {
|
||
|
|
const [hash2, name, additional_args] = id.split("/");
|
||
|
|
const remotes = manifest._.remotes;
|
||
|
|
if (!remotes[hash2]) error(404);
|
||
|
|
const module = await remotes[hash2]();
|
||
|
|
const fn = module.default[name];
|
||
|
|
if (!fn) error(404);
|
||
|
|
const info = fn.__;
|
||
|
|
const transport = options2.hooks.transport;
|
||
|
|
event.tracing.current.setAttributes({
|
||
|
|
"sveltekit.remote.call.type": info.type,
|
||
|
|
"sveltekit.remote.call.name": info.name
|
||
|
|
});
|
||
|
|
let form_client_refreshes;
|
||
|
|
try {
|
||
|
|
if (info.type === "query_batch") {
|
||
|
|
if (event.request.method !== "POST") {
|
||
|
|
throw new SvelteKitError(
|
||
|
|
405,
|
||
|
|
"Method Not Allowed",
|
||
|
|
`\`query.batch\` functions must be invoked via POST request, not ${event.request.method}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const { payloads } = await event.request.json();
|
||
|
|
const args = payloads.map((payload2) => parse_remote_arg(payload2, transport));
|
||
|
|
const get_result = await with_request_store({ event, state }, () => info.run(args));
|
||
|
|
const results = await Promise.all(
|
||
|
|
args.map(async (arg, i) => {
|
||
|
|
try {
|
||
|
|
return { type: "result", data: get_result(arg, i) };
|
||
|
|
} catch (error2) {
|
||
|
|
return {
|
||
|
|
type: "error",
|
||
|
|
error: await handle_error_and_jsonify(event, state, options2, error2),
|
||
|
|
status: error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500
|
||
|
|
};
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "result",
|
||
|
|
result: stringify(results, transport)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (info.type === "form") {
|
||
|
|
if (event.request.method !== "POST") {
|
||
|
|
throw new SvelteKitError(
|
||
|
|
405,
|
||
|
|
"Method Not Allowed",
|
||
|
|
`\`form\` functions must be invoked via POST request, not ${event.request.method}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!is_form_content_type(event.request)) {
|
||
|
|
throw new SvelteKitError(
|
||
|
|
415,
|
||
|
|
"Unsupported Media Type",
|
||
|
|
`\`form\` functions expect form-encoded data — received ${event.request.headers.get(
|
||
|
|
"content-type"
|
||
|
|
)}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const { data: data2, meta, form_data } = await deserialize_binary_form(event.request);
|
||
|
|
if (additional_args && !("id" in data2)) {
|
||
|
|
data2.id = JSON.parse(decodeURIComponent(additional_args));
|
||
|
|
}
|
||
|
|
const fn2 = info.fn;
|
||
|
|
const result = await with_request_store({ event, state }, () => fn2(data2, meta, form_data));
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "result",
|
||
|
|
result: stringify(result, transport),
|
||
|
|
refreshes: result.issues ? void 0 : await serialize_refreshes(meta.remote_refreshes)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (info.type === "command") {
|
||
|
|
const { payload: payload2, refreshes } = await event.request.json();
|
||
|
|
const arg = parse_remote_arg(payload2, transport);
|
||
|
|
const data2 = await with_request_store({ event, state }, () => fn(arg));
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "result",
|
||
|
|
result: stringify(data2, transport),
|
||
|
|
refreshes: await serialize_refreshes(refreshes)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const payload = info.type === "prerender" ? additional_args : (
|
||
|
|
/** @type {string} */
|
||
|
|
// new URL(...) necessary because we're hiding the URL from the user in the event object
|
||
|
|
new URL(event.request.url).searchParams.get("payload")
|
||
|
|
);
|
||
|
|
const data = await with_request_store(
|
||
|
|
{ event, state },
|
||
|
|
() => fn(parse_remote_arg(payload, transport))
|
||
|
|
);
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "result",
|
||
|
|
result: stringify(data, transport)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
} catch (error2) {
|
||
|
|
if (error2 instanceof Redirect) {
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "redirect",
|
||
|
|
location: error2.location,
|
||
|
|
refreshes: await serialize_refreshes(form_client_refreshes)
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const status = error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500;
|
||
|
|
return json(
|
||
|
|
/** @type {RemoteFunctionResponse} */
|
||
|
|
{
|
||
|
|
type: "error",
|
||
|
|
error: await handle_error_and_jsonify(event, state, options2, error2),
|
||
|
|
status
|
||
|
|
},
|
||
|
|
{
|
||
|
|
// By setting a non-200 during prerendering we fail the prerender process (unless handleHttpError handles it).
|
||
|
|
// Errors at runtime will be passed to the client and are handled there
|
||
|
|
status: state.prerendering ? status : void 0,
|
||
|
|
headers: {
|
||
|
|
"cache-control": "private, no-store"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
async function serialize_refreshes(client_refreshes) {
|
||
|
|
const refreshes = state.refreshes ?? {};
|
||
|
|
if (client_refreshes) {
|
||
|
|
for (const key2 of client_refreshes) {
|
||
|
|
if (refreshes[key2] !== void 0) continue;
|
||
|
|
const [hash3, name2, payload] = key2.split("/");
|
||
|
|
const loader = manifest._.remotes[hash3];
|
||
|
|
const fn2 = (await loader?.())?.default?.[name2];
|
||
|
|
if (!fn2) error(400, "Bad Request");
|
||
|
|
refreshes[key2] = with_request_store(
|
||
|
|
{ event, state },
|
||
|
|
() => fn2(parse_remote_arg(payload, transport))
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (Object.keys(refreshes).length === 0) {
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
return stringify(
|
||
|
|
Object.fromEntries(
|
||
|
|
await Promise.all(
|
||
|
|
Object.entries(refreshes).map(async ([key2, promise]) => [key2, await promise])
|
||
|
|
)
|
||
|
|
),
|
||
|
|
transport
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async function handle_remote_form_post(event, state, manifest, id) {
|
||
|
|
return record_span({
|
||
|
|
name: "sveltekit.remote.form.post",
|
||
|
|
attributes: {},
|
||
|
|
fn: (current2) => {
|
||
|
|
const traced_event = merge_tracing(event, current2);
|
||
|
|
return with_request_store(
|
||
|
|
{ event: traced_event, state },
|
||
|
|
() => handle_remote_form_post_internal(traced_event, state, manifest, id)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
async function handle_remote_form_post_internal(event, state, manifest, id) {
|
||
|
|
const [hash2, name, action_id] = id.split("/");
|
||
|
|
const remotes = manifest._.remotes;
|
||
|
|
const module = await remotes[hash2]?.();
|
||
|
|
let form = (
|
||
|
|
/** @type {RemoteForm<any, any>} */
|
||
|
|
module?.default[name]
|
||
|
|
);
|
||
|
|
if (!form) {
|
||
|
|
event.setHeaders({
|
||
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
||
|
|
// "The server must generate an Allow header field in a 405 status code response"
|
||
|
|
allow: "GET"
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
type: "error",
|
||
|
|
error: new SvelteKitError(
|
||
|
|
405,
|
||
|
|
"Method Not Allowed",
|
||
|
|
`POST method not allowed. No form actions exist for ${"this page"}`
|
||
|
|
)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
if (action_id) {
|
||
|
|
form = with_request_store({ event, state }, () => form.for(JSON.parse(action_id)));
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
const fn = (
|
||
|
|
/** @type {RemoteInfo & { type: 'form' }} */
|
||
|
|
/** @type {any} */
|
||
|
|
form.__.fn
|
||
|
|
);
|
||
|
|
const { data, meta, form_data } = await deserialize_binary_form(event.request);
|
||
|
|
if (action_id && !("id" in data)) {
|
||
|
|
data.id = JSON.parse(decodeURIComponent(action_id));
|
||
|
|
}
|
||
|
|
await with_request_store({ event, state }, () => fn(data, meta, form_data));
|
||
|
|
return {
|
||
|
|
type: "success",
|
||
|
|
status: 200
|
||
|
|
};
|
||
|
|
} catch (e) {
|
||
|
|
const err = normalize_error(e);
|
||
|
|
if (err instanceof Redirect) {
|
||
|
|
return {
|
||
|
|
type: "redirect",
|
||
|
|
status: err.status,
|
||
|
|
location: err.location
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
type: "error",
|
||
|
|
error: check_incorrect_fail_use(err)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function get_remote_id(url) {
|
||
|
|
return url.pathname.startsWith(`${base}/${app_dir}/remote/`) && url.pathname.replace(`${base}/${app_dir}/remote/`, "");
|
||
|
|
}
|
||
|
|
function get_remote_action(url) {
|
||
|
|
return url.searchParams.get("/remote");
|
||
|
|
}
|
||
|
|
const MAX_DEPTH = 10;
|
||
|
|
async function render_page(event, event_state, page, options2, manifest, state, nodes, resolve_opts) {
|
||
|
|
if (state.depth > MAX_DEPTH) {
|
||
|
|
return text(`Not found: ${event.url.pathname}`, {
|
||
|
|
status: 404
|
||
|
|
// TODO in some cases this should be 500. not sure how to differentiate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (is_action_json_request(event)) {
|
||
|
|
const node = await manifest._.nodes[page.leaf]();
|
||
|
|
return handle_action_json_request(event, event_state, options2, node?.server);
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
const leaf_node = (
|
||
|
|
/** @type {import('types').SSRNode} */
|
||
|
|
nodes.page()
|
||
|
|
);
|
||
|
|
let status = 200;
|
||
|
|
let action_result = void 0;
|
||
|
|
if (is_action_request(event)) {
|
||
|
|
const remote_id = get_remote_action(event.url);
|
||
|
|
if (remote_id) {
|
||
|
|
action_result = await handle_remote_form_post(event, event_state, manifest, remote_id);
|
||
|
|
} else {
|
||
|
|
action_result = await handle_action_request(event, event_state, leaf_node.server);
|
||
|
|
}
|
||
|
|
if (action_result?.type === "redirect") {
|
||
|
|
return redirect_response(action_result.status, action_result.location);
|
||
|
|
}
|
||
|
|
if (action_result?.type === "error") {
|
||
|
|
status = get_status(action_result.error);
|
||
|
|
}
|
||
|
|
if (action_result?.type === "failure") {
|
||
|
|
status = action_result.status;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const should_prerender = nodes.prerender();
|
||
|
|
if (should_prerender) {
|
||
|
|
const mod = leaf_node.server;
|
||
|
|
if (mod?.actions) {
|
||
|
|
throw new Error("Cannot prerender pages with actions");
|
||
|
|
}
|
||
|
|
} else if (state.prerendering) {
|
||
|
|
return new Response(void 0, {
|
||
|
|
status: 204
|
||
|
|
});
|
||
|
|
}
|
||
|
|
state.prerender_default = should_prerender;
|
||
|
|
const should_prerender_data = nodes.should_prerender_data();
|
||
|
|
const data_pathname = add_data_suffix(event.url.pathname);
|
||
|
|
const fetched = [];
|
||
|
|
const ssr = nodes.ssr();
|
||
|
|
const csr = nodes.csr();
|
||
|
|
if (ssr === false && !(state.prerendering && should_prerender_data)) {
|
||
|
|
if (BROWSER && action_result && !event.request.headers.has("x-sveltekit-action")) ;
|
||
|
|
return await render_response({
|
||
|
|
branch: [],
|
||
|
|
fetched,
|
||
|
|
page_config: {
|
||
|
|
ssr: false,
|
||
|
|
csr
|
||
|
|
},
|
||
|
|
status,
|
||
|
|
error: null,
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
resolve_opts,
|
||
|
|
data_serializer: server_data_serializer(event, event_state, options2)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
const branch = [];
|
||
|
|
let load_error = null;
|
||
|
|
const data_serializer = server_data_serializer(event, event_state, options2);
|
||
|
|
const data_serializer_json = state.prerendering && should_prerender_data ? server_data_serializer_json(event, event_state, options2) : null;
|
||
|
|
const server_promises = nodes.data.map((node, i) => {
|
||
|
|
if (load_error) {
|
||
|
|
throw load_error;
|
||
|
|
}
|
||
|
|
return Promise.resolve().then(async () => {
|
||
|
|
try {
|
||
|
|
if (node === leaf_node && action_result?.type === "error") {
|
||
|
|
throw action_result.error;
|
||
|
|
}
|
||
|
|
const server_data = await load_server_data({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
state,
|
||
|
|
node,
|
||
|
|
parent: async () => {
|
||
|
|
const data = {};
|
||
|
|
for (let j = 0; j < i; j += 1) {
|
||
|
|
const parent = await server_promises[j];
|
||
|
|
if (parent) Object.assign(data, parent.data);
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (node) {
|
||
|
|
data_serializer.add_node(i, server_data);
|
||
|
|
}
|
||
|
|
data_serializer_json?.add_node(i, server_data);
|
||
|
|
return server_data;
|
||
|
|
} catch (e) {
|
||
|
|
load_error = /** @type {Error} */
|
||
|
|
e;
|
||
|
|
throw load_error;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
const load_promises = nodes.data.map((node, i) => {
|
||
|
|
if (load_error) throw load_error;
|
||
|
|
return Promise.resolve().then(async () => {
|
||
|
|
try {
|
||
|
|
return await load_data({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
fetched,
|
||
|
|
node,
|
||
|
|
parent: async () => {
|
||
|
|
const data = {};
|
||
|
|
for (let j = 0; j < i; j += 1) {
|
||
|
|
Object.assign(data, await load_promises[j]);
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
},
|
||
|
|
resolve_opts,
|
||
|
|
server_data_promise: server_promises[i],
|
||
|
|
state,
|
||
|
|
csr
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
load_error = /** @type {Error} */
|
||
|
|
e;
|
||
|
|
throw load_error;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
for (const p of server_promises) p.catch(() => {
|
||
|
|
});
|
||
|
|
for (const p of load_promises) p.catch(() => {
|
||
|
|
});
|
||
|
|
for (let i = 0; i < nodes.data.length; i += 1) {
|
||
|
|
const node = nodes.data[i];
|
||
|
|
if (node) {
|
||
|
|
try {
|
||
|
|
const server_data = await server_promises[i];
|
||
|
|
const data = await load_promises[i];
|
||
|
|
branch.push({ node, server_data, data });
|
||
|
|
} catch (e) {
|
||
|
|
const err = normalize_error(e);
|
||
|
|
if (err instanceof Redirect) {
|
||
|
|
if (state.prerendering && should_prerender_data) {
|
||
|
|
const body2 = JSON.stringify({
|
||
|
|
type: "redirect",
|
||
|
|
location: err.location
|
||
|
|
});
|
||
|
|
state.prerendering.dependencies.set(data_pathname, {
|
||
|
|
response: text(body2),
|
||
|
|
body: body2
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return redirect_response(err.status, err.location);
|
||
|
|
}
|
||
|
|
const status2 = get_status(err);
|
||
|
|
const error2 = await handle_error_and_jsonify(event, event_state, options2, err);
|
||
|
|
while (i--) {
|
||
|
|
if (page.errors[i]) {
|
||
|
|
const index = (
|
||
|
|
/** @type {number} */
|
||
|
|
page.errors[i]
|
||
|
|
);
|
||
|
|
const node2 = await manifest._.nodes[index]();
|
||
|
|
let j = i;
|
||
|
|
while (!branch[j]) j -= 1;
|
||
|
|
data_serializer.set_max_nodes(j + 1);
|
||
|
|
const layouts = compact(branch.slice(0, j + 1));
|
||
|
|
const nodes2 = new PageNodes(layouts.map((layout) => layout.node));
|
||
|
|
return await render_response({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
resolve_opts,
|
||
|
|
page_config: {
|
||
|
|
ssr: nodes2.ssr(),
|
||
|
|
csr: nodes2.csr()
|
||
|
|
},
|
||
|
|
status: status2,
|
||
|
|
error: error2,
|
||
|
|
branch: layouts.concat({
|
||
|
|
node: node2,
|
||
|
|
data: null,
|
||
|
|
server_data: null
|
||
|
|
}),
|
||
|
|
fetched,
|
||
|
|
data_serializer
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return static_error_page(options2, status2, error2.message);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
branch.push(null);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (state.prerendering && data_serializer_json) {
|
||
|
|
let { data, chunks } = data_serializer_json.get_data();
|
||
|
|
if (chunks) {
|
||
|
|
for await (const chunk of chunks) {
|
||
|
|
data += chunk;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
state.prerendering.dependencies.set(data_pathname, {
|
||
|
|
response: text(data),
|
||
|
|
body: data
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return await render_response({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
resolve_opts,
|
||
|
|
page_config: {
|
||
|
|
csr,
|
||
|
|
ssr
|
||
|
|
},
|
||
|
|
status,
|
||
|
|
error: null,
|
||
|
|
branch: ssr === false ? [] : compact(branch),
|
||
|
|
action_result,
|
||
|
|
fetched,
|
||
|
|
data_serializer: ssr === false ? server_data_serializer(event, event_state, options2) : data_serializer
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
return await respond_with_error({
|
||
|
|
event,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
status: 500,
|
||
|
|
error: e,
|
||
|
|
resolve_opts
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function once(fn) {
|
||
|
|
let done = false;
|
||
|
|
let result;
|
||
|
|
return () => {
|
||
|
|
if (done) return result;
|
||
|
|
done = true;
|
||
|
|
return result = fn();
|
||
|
|
};
|
||
|
|
}
|
||
|
|
async function render_data(event, event_state, route, options2, manifest, state, invalidated_data_nodes, trailing_slash) {
|
||
|
|
if (!route.page) {
|
||
|
|
return new Response(void 0, {
|
||
|
|
status: 404
|
||
|
|
});
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
const node_ids = [...route.page.layouts, route.page.leaf];
|
||
|
|
const invalidated = invalidated_data_nodes ?? node_ids.map(() => true);
|
||
|
|
let aborted = false;
|
||
|
|
const url = new URL(event.url);
|
||
|
|
url.pathname = normalize_path(url.pathname, trailing_slash);
|
||
|
|
const new_event = { ...event, url };
|
||
|
|
const functions = node_ids.map((n, i) => {
|
||
|
|
return once(async () => {
|
||
|
|
try {
|
||
|
|
if (aborted) {
|
||
|
|
return (
|
||
|
|
/** @type {import('types').ServerDataSkippedNode} */
|
||
|
|
{
|
||
|
|
type: "skip"
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const node = n == void 0 ? n : await manifest._.nodes[n]();
|
||
|
|
return load_server_data({
|
||
|
|
event: new_event,
|
||
|
|
event_state,
|
||
|
|
state,
|
||
|
|
node,
|
||
|
|
parent: async () => {
|
||
|
|
const data2 = {};
|
||
|
|
for (let j = 0; j < i; j += 1) {
|
||
|
|
const parent = (
|
||
|
|
/** @type {import('types').ServerDataNode | null} */
|
||
|
|
await functions[j]()
|
||
|
|
);
|
||
|
|
if (parent) {
|
||
|
|
Object.assign(data2, parent.data);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return data2;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
aborted = true;
|
||
|
|
throw e;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
const promises = functions.map(async (fn, i) => {
|
||
|
|
if (!invalidated[i]) {
|
||
|
|
return (
|
||
|
|
/** @type {import('types').ServerDataSkippedNode} */
|
||
|
|
{
|
||
|
|
type: "skip"
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return fn();
|
||
|
|
});
|
||
|
|
let length = promises.length;
|
||
|
|
const nodes = await Promise.all(
|
||
|
|
promises.map(
|
||
|
|
(p, i) => p.catch(async (error2) => {
|
||
|
|
if (error2 instanceof Redirect) {
|
||
|
|
throw error2;
|
||
|
|
}
|
||
|
|
length = Math.min(length, i + 1);
|
||
|
|
return (
|
||
|
|
/** @type {import('types').ServerErrorNode} */
|
||
|
|
{
|
||
|
|
type: "error",
|
||
|
|
error: await handle_error_and_jsonify(event, event_state, options2, error2),
|
||
|
|
status: error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : void 0
|
||
|
|
}
|
||
|
|
);
|
||
|
|
})
|
||
|
|
)
|
||
|
|
);
|
||
|
|
const data_serializer = server_data_serializer_json(event, event_state, options2);
|
||
|
|
for (let i = 0; i < nodes.length; i++) data_serializer.add_node(i, nodes[i]);
|
||
|
|
const { data, chunks } = data_serializer.get_data();
|
||
|
|
if (!chunks) {
|
||
|
|
return json_response(data);
|
||
|
|
}
|
||
|
|
return new Response(
|
||
|
|
new ReadableStream({
|
||
|
|
async start(controller) {
|
||
|
|
controller.enqueue(text_encoder.encode(data));
|
||
|
|
for await (const chunk of chunks) {
|
||
|
|
controller.enqueue(text_encoder.encode(chunk));
|
||
|
|
}
|
||
|
|
controller.close();
|
||
|
|
},
|
||
|
|
type: "bytes"
|
||
|
|
}),
|
||
|
|
{
|
||
|
|
headers: {
|
||
|
|
// we use a proprietary content type to prevent buffering.
|
||
|
|
// the `text` prefix makes it inspectable
|
||
|
|
"content-type": "text/sveltekit-data",
|
||
|
|
"cache-control": "private, no-store"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
const error2 = normalize_error(e);
|
||
|
|
if (error2 instanceof Redirect) {
|
||
|
|
return redirect_json_response(error2);
|
||
|
|
} else {
|
||
|
|
return json_response(await handle_error_and_jsonify(event, event_state, options2, error2), 500);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function json_response(json2, status = 200) {
|
||
|
|
return text(typeof json2 === "string" ? json2 : JSON.stringify(json2), {
|
||
|
|
status,
|
||
|
|
headers: {
|
||
|
|
"content-type": "application/json",
|
||
|
|
"cache-control": "private, no-store"
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function redirect_json_response(redirect) {
|
||
|
|
return json_response(
|
||
|
|
/** @type {import('types').ServerRedirectNode} */
|
||
|
|
{
|
||
|
|
type: "redirect",
|
||
|
|
location: redirect.location
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const INVALID_COOKIE_CHARACTER_REGEX = /[\x00-\x1F\x7F()<>@,;:"/[\]?={} \t]/;
|
||
|
|
function validate_options(options2) {
|
||
|
|
if (options2?.path === void 0) {
|
||
|
|
throw new Error("You must specify a `path` when setting, deleting or serializing cookies");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function generate_cookie_key(domain, path, name) {
|
||
|
|
return `${domain || ""}${path}?${encodeURIComponent(name)}`;
|
||
|
|
}
|
||
|
|
function get_cookies(request, url) {
|
||
|
|
const header = request.headers.get("cookie") ?? "";
|
||
|
|
const initial_cookies = parse(header, { decode: (value) => value });
|
||
|
|
let normalized_url;
|
||
|
|
const new_cookies = /* @__PURE__ */ new Map();
|
||
|
|
const defaults = {
|
||
|
|
httpOnly: true,
|
||
|
|
sameSite: "lax",
|
||
|
|
secure: url.hostname === "localhost" && url.protocol === "http:" ? false : true
|
||
|
|
};
|
||
|
|
const cookies = {
|
||
|
|
// The JSDoc param annotations appearing below for get, set and delete
|
||
|
|
// are necessary to expose the `cookie` library types to
|
||
|
|
// typescript users. `@type {import('@sveltejs/kit').Cookies}` above is not
|
||
|
|
// sufficient to do so.
|
||
|
|
/**
|
||
|
|
* @param {string} name
|
||
|
|
* @param {import('cookie').CookieParseOptions} [opts]
|
||
|
|
*/
|
||
|
|
get(name, opts) {
|
||
|
|
const best_match = Array.from(new_cookies.values()).filter((c) => {
|
||
|
|
return c.name === name && domain_matches(url.hostname, c.options.domain) && path_matches(url.pathname, c.options.path);
|
||
|
|
}).sort((a, b) => b.options.path.length - a.options.path.length)[0];
|
||
|
|
if (best_match) {
|
||
|
|
return best_match.options.maxAge === 0 ? void 0 : best_match.value;
|
||
|
|
}
|
||
|
|
const req_cookies = parse(header, { decode: opts?.decode });
|
||
|
|
const cookie = req_cookies[name];
|
||
|
|
return cookie;
|
||
|
|
},
|
||
|
|
/**
|
||
|
|
* @param {import('cookie').CookieParseOptions} [opts]
|
||
|
|
*/
|
||
|
|
getAll(opts) {
|
||
|
|
const cookies2 = parse(header, { decode: opts?.decode });
|
||
|
|
const lookup = /* @__PURE__ */ new Map();
|
||
|
|
for (const c of new_cookies.values()) {
|
||
|
|
if (domain_matches(url.hostname, c.options.domain) && path_matches(url.pathname, c.options.path)) {
|
||
|
|
const existing = lookup.get(c.name);
|
||
|
|
if (!existing || c.options.path.length > existing.options.path.length) {
|
||
|
|
lookup.set(c.name, c);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (const c of lookup.values()) {
|
||
|
|
cookies2[c.name] = c.value;
|
||
|
|
}
|
||
|
|
return Object.entries(cookies2).map(([name, value]) => ({ name, value }));
|
||
|
|
},
|
||
|
|
/**
|
||
|
|
* @param {string} name
|
||
|
|
* @param {string} value
|
||
|
|
* @param {import('./page/types.js').Cookie['options']} options
|
||
|
|
*/
|
||
|
|
set(name, value, options2) {
|
||
|
|
const illegal_characters = name.match(INVALID_COOKIE_CHARACTER_REGEX);
|
||
|
|
if (illegal_characters) {
|
||
|
|
console.warn(
|
||
|
|
`The cookie name "${name}" will be invalid in SvelteKit 3.0 as it contains ${illegal_characters.join(
|
||
|
|
" and "
|
||
|
|
)}. See RFC 2616 for more details https://datatracker.ietf.org/doc/html/rfc2616#section-2.2`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
validate_options(options2);
|
||
|
|
set_internal(name, value, { ...defaults, ...options2 });
|
||
|
|
},
|
||
|
|
/**
|
||
|
|
* @param {string} name
|
||
|
|
* @param {import('./page/types.js').Cookie['options']} options
|
||
|
|
*/
|
||
|
|
delete(name, options2) {
|
||
|
|
validate_options(options2);
|
||
|
|
cookies.set(name, "", { ...options2, maxAge: 0 });
|
||
|
|
},
|
||
|
|
/**
|
||
|
|
* @param {string} name
|
||
|
|
* @param {string} value
|
||
|
|
* @param {import('./page/types.js').Cookie['options']} options
|
||
|
|
*/
|
||
|
|
serialize(name, value, options2) {
|
||
|
|
validate_options(options2);
|
||
|
|
let path = options2.path;
|
||
|
|
if (!options2.domain || options2.domain === url.hostname) {
|
||
|
|
if (!normalized_url) {
|
||
|
|
throw new Error("Cannot serialize cookies until after the route is determined");
|
||
|
|
}
|
||
|
|
path = resolve(normalized_url, path);
|
||
|
|
}
|
||
|
|
return serialize(name, value, { ...defaults, ...options2, path });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function get_cookie_header(destination, header2) {
|
||
|
|
const combined_cookies = {
|
||
|
|
// cookies sent by the user agent have lowest precedence
|
||
|
|
...initial_cookies
|
||
|
|
};
|
||
|
|
for (const cookie of new_cookies.values()) {
|
||
|
|
if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
|
||
|
|
if (!path_matches(destination.pathname, cookie.options.path)) continue;
|
||
|
|
const encoder = cookie.options.encode || encodeURIComponent;
|
||
|
|
combined_cookies[cookie.name] = encoder(cookie.value);
|
||
|
|
}
|
||
|
|
if (header2) {
|
||
|
|
const parsed = parse(header2, { decode: (value) => value });
|
||
|
|
for (const name in parsed) {
|
||
|
|
combined_cookies[name] = parsed[name];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Object.entries(combined_cookies).map(([name, value]) => `${name}=${value}`).join("; ");
|
||
|
|
}
|
||
|
|
const internal_queue = [];
|
||
|
|
function set_internal(name, value, options2) {
|
||
|
|
if (!normalized_url) {
|
||
|
|
internal_queue.push(() => set_internal(name, value, options2));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
let path = options2.path;
|
||
|
|
if (!options2.domain || options2.domain === url.hostname) {
|
||
|
|
path = resolve(normalized_url, path);
|
||
|
|
}
|
||
|
|
const cookie_key = generate_cookie_key(options2.domain, path, name);
|
||
|
|
const cookie = { name, value, options: { ...options2, path } };
|
||
|
|
new_cookies.set(cookie_key, cookie);
|
||
|
|
}
|
||
|
|
function set_trailing_slash(trailing_slash) {
|
||
|
|
normalized_url = normalize_path(url.pathname, trailing_slash);
|
||
|
|
internal_queue.forEach((fn) => fn());
|
||
|
|
}
|
||
|
|
return { cookies, new_cookies, get_cookie_header, set_internal, set_trailing_slash };
|
||
|
|
}
|
||
|
|
function domain_matches(hostname, constraint) {
|
||
|
|
if (!constraint) return true;
|
||
|
|
const normalized = constraint[0] === "." ? constraint.slice(1) : constraint;
|
||
|
|
if (hostname === normalized) return true;
|
||
|
|
return hostname.endsWith("." + normalized);
|
||
|
|
}
|
||
|
|
function path_matches(path, constraint) {
|
||
|
|
if (!constraint) return true;
|
||
|
|
const normalized = constraint.endsWith("/") ? constraint.slice(0, -1) : constraint;
|
||
|
|
if (path === normalized) return true;
|
||
|
|
return path.startsWith(normalized + "/");
|
||
|
|
}
|
||
|
|
function add_cookies_to_headers(headers2, cookies) {
|
||
|
|
for (const new_cookie of cookies) {
|
||
|
|
const { name, value, options: options2 } = new_cookie;
|
||
|
|
headers2.append("set-cookie", serialize(name, value, options2));
|
||
|
|
if (options2.path.endsWith(".html")) {
|
||
|
|
const path = add_data_suffix(options2.path);
|
||
|
|
headers2.append("set-cookie", serialize(name, value, { ...options2, path }));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function create_fetch({ event, options: options2, manifest, state, get_cookie_header, set_internal }) {
|
||
|
|
const server_fetch = async (info, init2) => {
|
||
|
|
const original_request = normalize_fetch_input(info, init2, event.url);
|
||
|
|
let mode = (info instanceof Request ? info.mode : init2?.mode) ?? "cors";
|
||
|
|
let credentials = (info instanceof Request ? info.credentials : init2?.credentials) ?? "same-origin";
|
||
|
|
return options2.hooks.handleFetch({
|
||
|
|
event,
|
||
|
|
request: original_request,
|
||
|
|
fetch: async (info2, init3) => {
|
||
|
|
const request = normalize_fetch_input(info2, init3, event.url);
|
||
|
|
const url = new URL(request.url);
|
||
|
|
if (!request.headers.has("origin")) {
|
||
|
|
request.headers.set("origin", event.url.origin);
|
||
|
|
}
|
||
|
|
if (info2 !== original_request) {
|
||
|
|
mode = (info2 instanceof Request ? info2.mode : init3?.mode) ?? "cors";
|
||
|
|
credentials = (info2 instanceof Request ? info2.credentials : init3?.credentials) ?? "same-origin";
|
||
|
|
}
|
||
|
|
if ((request.method === "GET" || request.method === "HEAD") && (mode === "no-cors" && url.origin !== event.url.origin || url.origin === event.url.origin)) {
|
||
|
|
request.headers.delete("origin");
|
||
|
|
}
|
||
|
|
if (url.origin !== event.url.origin) {
|
||
|
|
if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && credentials !== "omit") {
|
||
|
|
const cookie = get_cookie_header(url, request.headers.get("cookie"));
|
||
|
|
if (cookie) request.headers.set("cookie", cookie);
|
||
|
|
}
|
||
|
|
return fetch(request);
|
||
|
|
}
|
||
|
|
const prefix = assets || base;
|
||
|
|
const decoded = decodeURIComponent(url.pathname);
|
||
|
|
const filename = (decoded.startsWith(prefix) ? decoded.slice(prefix.length) : decoded).slice(1);
|
||
|
|
const filename_html = `${filename}/index.html`;
|
||
|
|
const is_asset = manifest.assets.has(filename) || filename in manifest._.server_assets;
|
||
|
|
const is_asset_html = manifest.assets.has(filename_html) || filename_html in manifest._.server_assets;
|
||
|
|
if (is_asset || is_asset_html) {
|
||
|
|
const file = is_asset ? filename : filename_html;
|
||
|
|
if (state.read) {
|
||
|
|
const type = is_asset ? manifest.mimeTypes[filename.slice(filename.lastIndexOf("."))] : "text/html";
|
||
|
|
return new Response(state.read(file), {
|
||
|
|
headers: type ? { "content-type": type } : {}
|
||
|
|
});
|
||
|
|
} else if (read_implementation && file in manifest._.server_assets) {
|
||
|
|
const length = manifest._.server_assets[file];
|
||
|
|
const type = manifest.mimeTypes[file.slice(file.lastIndexOf("."))];
|
||
|
|
return new Response(read_implementation(file), {
|
||
|
|
headers: {
|
||
|
|
"Content-Length": "" + length,
|
||
|
|
"Content-Type": type
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return await fetch(request);
|
||
|
|
}
|
||
|
|
if (has_prerendered_path(manifest, base + decoded)) {
|
||
|
|
return await fetch(request);
|
||
|
|
}
|
||
|
|
if (credentials !== "omit") {
|
||
|
|
const cookie = get_cookie_header(url, request.headers.get("cookie"));
|
||
|
|
if (cookie) {
|
||
|
|
request.headers.set("cookie", cookie);
|
||
|
|
}
|
||
|
|
const authorization = event.request.headers.get("authorization");
|
||
|
|
if (authorization && !request.headers.has("authorization")) {
|
||
|
|
request.headers.set("authorization", authorization);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!request.headers.has("accept")) {
|
||
|
|
request.headers.set("accept", "*/*");
|
||
|
|
}
|
||
|
|
if (!request.headers.has("accept-language")) {
|
||
|
|
request.headers.set(
|
||
|
|
"accept-language",
|
||
|
|
/** @type {string} */
|
||
|
|
event.request.headers.get("accept-language")
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const response = await internal_fetch(request, options2, manifest, state);
|
||
|
|
const set_cookie = response.headers.get("set-cookie");
|
||
|
|
if (set_cookie) {
|
||
|
|
for (const str of set_cookie_parser.splitCookiesString(set_cookie)) {
|
||
|
|
const { name, value, ...options3 } = set_cookie_parser.parseString(str, {
|
||
|
|
decodeValues: false
|
||
|
|
});
|
||
|
|
const path = options3.path ?? (url.pathname.split("/").slice(0, -1).join("/") || "/");
|
||
|
|
set_internal(name, value, {
|
||
|
|
path,
|
||
|
|
encode: (value2) => value2,
|
||
|
|
.../** @type {import('cookie').CookieSerializeOptions} */
|
||
|
|
options3
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return response;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
return (input, init2) => {
|
||
|
|
const response = server_fetch(input, init2);
|
||
|
|
response.catch(() => {
|
||
|
|
});
|
||
|
|
return response;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function normalize_fetch_input(info, init2, url) {
|
||
|
|
if (info instanceof Request) {
|
||
|
|
return info;
|
||
|
|
}
|
||
|
|
return new Request(typeof info === "string" ? new URL(info, url) : info, init2);
|
||
|
|
}
|
||
|
|
async function internal_fetch(request, options2, manifest, state) {
|
||
|
|
if (request.signal) {
|
||
|
|
if (request.signal.aborted) {
|
||
|
|
throw new DOMException("The operation was aborted.", "AbortError");
|
||
|
|
}
|
||
|
|
let remove_abort_listener = () => {
|
||
|
|
};
|
||
|
|
const abort_promise = new Promise((_, reject) => {
|
||
|
|
const on_abort = () => {
|
||
|
|
reject(new DOMException("The operation was aborted.", "AbortError"));
|
||
|
|
};
|
||
|
|
request.signal.addEventListener("abort", on_abort, { once: true });
|
||
|
|
remove_abort_listener = () => request.signal.removeEventListener("abort", on_abort);
|
||
|
|
});
|
||
|
|
const result = await Promise.race([
|
||
|
|
respond(request, options2, manifest, {
|
||
|
|
...state,
|
||
|
|
depth: state.depth + 1
|
||
|
|
}),
|
||
|
|
abort_promise
|
||
|
|
]);
|
||
|
|
remove_abort_listener();
|
||
|
|
return result;
|
||
|
|
} else {
|
||
|
|
return await respond(request, options2, manifest, {
|
||
|
|
...state,
|
||
|
|
depth: state.depth + 1
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let body;
|
||
|
|
let etag;
|
||
|
|
let headers;
|
||
|
|
function get_public_env(request) {
|
||
|
|
body ??= `export const env=${JSON.stringify(public_env)}`;
|
||
|
|
etag ??= `W/${Date.now()}`;
|
||
|
|
headers ??= new Headers({
|
||
|
|
"content-type": "application/javascript; charset=utf-8",
|
||
|
|
etag
|
||
|
|
});
|
||
|
|
if (request.headers.get("if-none-match") === etag) {
|
||
|
|
return new Response(void 0, { status: 304, headers });
|
||
|
|
}
|
||
|
|
return new Response(body, { headers });
|
||
|
|
}
|
||
|
|
const default_transform = ({ html }) => html;
|
||
|
|
const default_filter = () => false;
|
||
|
|
const default_preload = ({ type }) => type === "js" || type === "css";
|
||
|
|
const page_methods = /* @__PURE__ */ new Set(["GET", "HEAD", "POST"]);
|
||
|
|
const allowed_page_methods = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
||
|
|
let warned_on_devtools_json_request = false;
|
||
|
|
const respond = propagate_context(internal_respond);
|
||
|
|
async function internal_respond(request, options2, manifest, state) {
|
||
|
|
const url = new URL(request.url);
|
||
|
|
const is_route_resolution_request = has_resolution_suffix(url.pathname);
|
||
|
|
const is_data_request = has_data_suffix(url.pathname);
|
||
|
|
const remote_id = get_remote_id(url);
|
||
|
|
{
|
||
|
|
const request_origin = request.headers.get("origin");
|
||
|
|
if (remote_id) {
|
||
|
|
if (request.method !== "GET" && request_origin !== url.origin) {
|
||
|
|
const message = "Cross-site remote requests are forbidden";
|
||
|
|
return json({ message }, { status: 403 });
|
||
|
|
}
|
||
|
|
} else if (options2.csrf_check_origin) {
|
||
|
|
const forbidden = is_form_content_type(request) && (request.method === "POST" || request.method === "PUT" || request.method === "PATCH" || request.method === "DELETE") && request_origin !== url.origin && (!request_origin || !options2.csrf_trusted_origins.includes(request_origin));
|
||
|
|
if (forbidden) {
|
||
|
|
const message = `Cross-site ${request.method} form submissions are forbidden`;
|
||
|
|
const opts = { status: 403 };
|
||
|
|
if (request.headers.get("accept") === "application/json") {
|
||
|
|
return json({ message }, opts);
|
||
|
|
}
|
||
|
|
return text(message, opts);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (options2.hash_routing && url.pathname !== base + "/" && url.pathname !== "/[fallback]") {
|
||
|
|
return text("Not found", { status: 404 });
|
||
|
|
}
|
||
|
|
let invalidated_data_nodes;
|
||
|
|
if (is_route_resolution_request) {
|
||
|
|
url.pathname = strip_resolution_suffix(url.pathname);
|
||
|
|
} else if (is_data_request) {
|
||
|
|
url.pathname = strip_data_suffix(url.pathname) + (url.searchParams.get(TRAILING_SLASH_PARAM) === "1" ? "/" : "") || "/";
|
||
|
|
url.searchParams.delete(TRAILING_SLASH_PARAM);
|
||
|
|
invalidated_data_nodes = url.searchParams.get(INVALIDATED_PARAM)?.split("").map((node) => node === "1");
|
||
|
|
url.searchParams.delete(INVALIDATED_PARAM);
|
||
|
|
} else if (remote_id) {
|
||
|
|
url.pathname = request.headers.get("x-sveltekit-pathname") ?? base;
|
||
|
|
url.search = request.headers.get("x-sveltekit-search") ?? "";
|
||
|
|
}
|
||
|
|
const headers2 = {};
|
||
|
|
const { cookies, new_cookies, get_cookie_header, set_internal, set_trailing_slash } = get_cookies(
|
||
|
|
request,
|
||
|
|
url
|
||
|
|
);
|
||
|
|
const event_state = {
|
||
|
|
prerendering: state.prerendering,
|
||
|
|
transport: options2.hooks.transport,
|
||
|
|
handleValidationError: options2.hooks.handleValidationError,
|
||
|
|
tracing: {
|
||
|
|
record_span
|
||
|
|
},
|
||
|
|
is_in_remote_function: false
|
||
|
|
};
|
||
|
|
const event = {
|
||
|
|
cookies,
|
||
|
|
// @ts-expect-error `fetch` needs to be created after the `event` itself
|
||
|
|
fetch: null,
|
||
|
|
getClientAddress: state.getClientAddress || (() => {
|
||
|
|
throw new Error(
|
||
|
|
`${"@sveltejs/adapter-node"} does not specify getClientAddress. Please raise an issue`
|
||
|
|
);
|
||
|
|
}),
|
||
|
|
locals: {},
|
||
|
|
params: {},
|
||
|
|
platform: state.platform,
|
||
|
|
request,
|
||
|
|
route: { id: null },
|
||
|
|
setHeaders: (new_headers) => {
|
||
|
|
for (const key2 in new_headers) {
|
||
|
|
const lower = key2.toLowerCase();
|
||
|
|
const value = new_headers[key2];
|
||
|
|
if (lower === "set-cookie") {
|
||
|
|
throw new Error(
|
||
|
|
"Use `event.cookies.set(name, value, options)` instead of `event.setHeaders` to set cookies"
|
||
|
|
);
|
||
|
|
} else if (lower in headers2) {
|
||
|
|
if (lower === "server-timing") {
|
||
|
|
headers2[lower] += ", " + value;
|
||
|
|
} else {
|
||
|
|
throw new Error(`"${key2}" header is already set`);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
headers2[lower] = value;
|
||
|
|
if (state.prerendering && lower === "cache-control") {
|
||
|
|
state.prerendering.cache = /** @type {string} */
|
||
|
|
value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
url,
|
||
|
|
isDataRequest: is_data_request,
|
||
|
|
isSubRequest: state.depth > 0,
|
||
|
|
isRemoteRequest: !!remote_id
|
||
|
|
};
|
||
|
|
event.fetch = create_fetch({
|
||
|
|
event,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
get_cookie_header,
|
||
|
|
set_internal
|
||
|
|
});
|
||
|
|
if (state.emulator?.platform) {
|
||
|
|
event.platform = await state.emulator.platform({
|
||
|
|
config: {},
|
||
|
|
prerender: !!state.prerendering?.fallback
|
||
|
|
});
|
||
|
|
}
|
||
|
|
let resolved_path = url.pathname;
|
||
|
|
if (!remote_id) {
|
||
|
|
const prerendering_reroute_state = state.prerendering?.inside_reroute;
|
||
|
|
try {
|
||
|
|
if (state.prerendering) state.prerendering.inside_reroute = true;
|
||
|
|
resolved_path = await options2.hooks.reroute({ url: new URL(url), fetch: event.fetch }) ?? url.pathname;
|
||
|
|
} catch {
|
||
|
|
return text("Internal Server Error", {
|
||
|
|
status: 500
|
||
|
|
});
|
||
|
|
} finally {
|
||
|
|
if (state.prerendering) state.prerendering.inside_reroute = prerendering_reroute_state;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
resolved_path = decode_pathname(resolved_path);
|
||
|
|
} catch {
|
||
|
|
return text("Malformed URI", { status: 400 });
|
||
|
|
}
|
||
|
|
if (resolved_path !== url.pathname && !state.prerendering?.fallback && has_prerendered_path(manifest, resolved_path)) {
|
||
|
|
const url2 = new URL(request.url);
|
||
|
|
url2.pathname = is_data_request ? add_data_suffix(resolved_path) : is_route_resolution_request ? add_resolution_suffix(resolved_path) : resolved_path;
|
||
|
|
const response = await fetch(url2, request);
|
||
|
|
const headers22 = new Headers(response.headers);
|
||
|
|
if (headers22.has("content-encoding")) {
|
||
|
|
headers22.delete("content-encoding");
|
||
|
|
headers22.delete("content-length");
|
||
|
|
}
|
||
|
|
return new Response(response.body, {
|
||
|
|
headers: headers22,
|
||
|
|
status: response.status,
|
||
|
|
statusText: response.statusText
|
||
|
|
});
|
||
|
|
}
|
||
|
|
let route = null;
|
||
|
|
if (base && !state.prerendering?.fallback) {
|
||
|
|
if (!resolved_path.startsWith(base)) {
|
||
|
|
return text("Not found", { status: 404 });
|
||
|
|
}
|
||
|
|
resolved_path = resolved_path.slice(base.length) || "/";
|
||
|
|
}
|
||
|
|
if (is_route_resolution_request) {
|
||
|
|
return resolve_route(resolved_path, new URL(request.url), manifest);
|
||
|
|
}
|
||
|
|
if (resolved_path === `/${app_dir}/env.js`) {
|
||
|
|
return get_public_env(request);
|
||
|
|
}
|
||
|
|
if (!remote_id && resolved_path.startsWith(`/${app_dir}`)) {
|
||
|
|
const headers22 = new Headers();
|
||
|
|
headers22.set("cache-control", "public, max-age=0, must-revalidate");
|
||
|
|
return text("Not found", { status: 404, headers: headers22 });
|
||
|
|
}
|
||
|
|
if (!state.prerendering?.fallback) {
|
||
|
|
const matchers = await manifest._.matchers();
|
||
|
|
for (const candidate of manifest._.routes) {
|
||
|
|
const match = candidate.pattern.exec(resolved_path);
|
||
|
|
if (!match) continue;
|
||
|
|
const matched = exec(match, candidate.params, matchers);
|
||
|
|
if (matched) {
|
||
|
|
route = candidate;
|
||
|
|
event.route = { id: route.id };
|
||
|
|
event.params = decode_params(matched);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let resolve_opts = {
|
||
|
|
transformPageChunk: default_transform,
|
||
|
|
filterSerializedResponseHeaders: default_filter,
|
||
|
|
preload: default_preload
|
||
|
|
};
|
||
|
|
let trailing_slash = "never";
|
||
|
|
try {
|
||
|
|
const page_nodes = route?.page ? new PageNodes(await load_page_nodes(route.page, manifest)) : void 0;
|
||
|
|
if (route && !remote_id) {
|
||
|
|
if (url.pathname === base || url.pathname === base + "/") {
|
||
|
|
trailing_slash = "always";
|
||
|
|
} else if (page_nodes) {
|
||
|
|
if (BROWSER) ;
|
||
|
|
trailing_slash = page_nodes.trailing_slash();
|
||
|
|
} else if (route.endpoint) {
|
||
|
|
const node = await route.endpoint();
|
||
|
|
trailing_slash = node.trailingSlash ?? "never";
|
||
|
|
if (BROWSER) ;
|
||
|
|
}
|
||
|
|
if (!is_data_request) {
|
||
|
|
const normalized = normalize_path(url.pathname, trailing_slash);
|
||
|
|
if (normalized !== url.pathname && !state.prerendering?.fallback) {
|
||
|
|
return new Response(void 0, {
|
||
|
|
status: 308,
|
||
|
|
headers: {
|
||
|
|
"x-sveltekit-normalize": "1",
|
||
|
|
location: (
|
||
|
|
// ensure paths starting with '//' are not treated as protocol-relative
|
||
|
|
(normalized.startsWith("//") ? url.origin + normalized : normalized) + (url.search === "?" ? "" : url.search)
|
||
|
|
)
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (state.before_handle || state.emulator?.platform) {
|
||
|
|
let config = {};
|
||
|
|
let prerender = false;
|
||
|
|
if (route.endpoint) {
|
||
|
|
const node = await route.endpoint();
|
||
|
|
config = node.config ?? config;
|
||
|
|
prerender = node.prerender ?? prerender;
|
||
|
|
} else if (page_nodes) {
|
||
|
|
config = page_nodes.get_config() ?? config;
|
||
|
|
prerender = page_nodes.prerender();
|
||
|
|
}
|
||
|
|
if (state.before_handle) {
|
||
|
|
state.before_handle(event, config, prerender);
|
||
|
|
}
|
||
|
|
if (state.emulator?.platform) {
|
||
|
|
event.platform = await state.emulator.platform({ config, prerender });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
set_trailing_slash(trailing_slash);
|
||
|
|
if (state.prerendering && !state.prerendering.fallback && !state.prerendering.inside_reroute) {
|
||
|
|
disable_search(url);
|
||
|
|
}
|
||
|
|
const response = await record_span({
|
||
|
|
name: "sveltekit.handle.root",
|
||
|
|
attributes: {
|
||
|
|
"http.route": event.route.id || "unknown",
|
||
|
|
"http.method": event.request.method,
|
||
|
|
"http.url": event.url.href,
|
||
|
|
"sveltekit.is_data_request": is_data_request,
|
||
|
|
"sveltekit.is_sub_request": event.isSubRequest
|
||
|
|
},
|
||
|
|
fn: async (root_span) => {
|
||
|
|
const traced_event = {
|
||
|
|
...event,
|
||
|
|
tracing: {
|
||
|
|
enabled: false,
|
||
|
|
root: root_span,
|
||
|
|
current: root_span
|
||
|
|
}
|
||
|
|
};
|
||
|
|
return await with_request_store(
|
||
|
|
{ event: traced_event, state: event_state },
|
||
|
|
() => options2.hooks.handle({
|
||
|
|
event: traced_event,
|
||
|
|
resolve: (event2, opts) => {
|
||
|
|
return record_span({
|
||
|
|
name: "sveltekit.resolve",
|
||
|
|
attributes: {
|
||
|
|
"http.route": event2.route.id || "unknown"
|
||
|
|
},
|
||
|
|
fn: (resolve_span) => {
|
||
|
|
return with_request_store(
|
||
|
|
null,
|
||
|
|
() => resolve2(merge_tracing(event2, resolve_span), page_nodes, opts).then(
|
||
|
|
(response2) => {
|
||
|
|
for (const key2 in headers2) {
|
||
|
|
const value = headers2[key2];
|
||
|
|
response2.headers.set(
|
||
|
|
key2,
|
||
|
|
/** @type {string} */
|
||
|
|
value
|
||
|
|
);
|
||
|
|
}
|
||
|
|
add_cookies_to_headers(response2.headers, new_cookies.values());
|
||
|
|
if (state.prerendering && event2.route.id !== null) {
|
||
|
|
response2.headers.set("x-sveltekit-routeid", encodeURI(event2.route.id));
|
||
|
|
}
|
||
|
|
resolve_span.setAttributes({
|
||
|
|
"http.response.status_code": response2.status,
|
||
|
|
"http.response.body.size": response2.headers.get("content-length") || "unknown"
|
||
|
|
});
|
||
|
|
return response2;
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (response.status === 200 && response.headers.has("etag")) {
|
||
|
|
let if_none_match_value = request.headers.get("if-none-match");
|
||
|
|
if (if_none_match_value?.startsWith('W/"')) {
|
||
|
|
if_none_match_value = if_none_match_value.substring(2);
|
||
|
|
}
|
||
|
|
const etag2 = (
|
||
|
|
/** @type {string} */
|
||
|
|
response.headers.get("etag")
|
||
|
|
);
|
||
|
|
if (if_none_match_value === etag2) {
|
||
|
|
const headers22 = new Headers({ etag: etag2 });
|
||
|
|
for (const key2 of [
|
||
|
|
"cache-control",
|
||
|
|
"content-location",
|
||
|
|
"date",
|
||
|
|
"expires",
|
||
|
|
"vary",
|
||
|
|
"set-cookie"
|
||
|
|
]) {
|
||
|
|
const value = response.headers.get(key2);
|
||
|
|
if (value) headers22.set(key2, value);
|
||
|
|
}
|
||
|
|
return new Response(void 0, {
|
||
|
|
status: 304,
|
||
|
|
headers: headers22
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (is_data_request && response.status >= 300 && response.status <= 308) {
|
||
|
|
const location = response.headers.get("location");
|
||
|
|
if (location) {
|
||
|
|
return redirect_json_response(new Redirect(
|
||
|
|
/** @type {any} */
|
||
|
|
response.status,
|
||
|
|
location
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return response;
|
||
|
|
} catch (e) {
|
||
|
|
if (e instanceof Redirect) {
|
||
|
|
const response = is_data_request || remote_id ? redirect_json_response(e) : route?.page && is_action_json_request(event) ? action_json_redirect(e) : redirect_response(e.status, e.location);
|
||
|
|
add_cookies_to_headers(response.headers, new_cookies.values());
|
||
|
|
return response;
|
||
|
|
}
|
||
|
|
return await handle_fatal_error(event, event_state, options2, e);
|
||
|
|
}
|
||
|
|
async function resolve2(event2, page_nodes, opts) {
|
||
|
|
try {
|
||
|
|
if (opts) {
|
||
|
|
resolve_opts = {
|
||
|
|
transformPageChunk: opts.transformPageChunk || default_transform,
|
||
|
|
filterSerializedResponseHeaders: opts.filterSerializedResponseHeaders || default_filter,
|
||
|
|
preload: opts.preload || default_preload
|
||
|
|
};
|
||
|
|
}
|
||
|
|
if (options2.hash_routing || state.prerendering?.fallback) {
|
||
|
|
return await render_response({
|
||
|
|
event: event2,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
page_config: { ssr: false, csr: true },
|
||
|
|
status: 200,
|
||
|
|
error: null,
|
||
|
|
branch: [],
|
||
|
|
fetched: [],
|
||
|
|
resolve_opts,
|
||
|
|
data_serializer: server_data_serializer(event2, event_state, options2)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (remote_id) {
|
||
|
|
return await handle_remote_call(event2, event_state, options2, manifest, remote_id);
|
||
|
|
}
|
||
|
|
if (route) {
|
||
|
|
const method = (
|
||
|
|
/** @type {import('types').HttpMethod} */
|
||
|
|
event2.request.method
|
||
|
|
);
|
||
|
|
let response2;
|
||
|
|
if (is_data_request) {
|
||
|
|
response2 = await render_data(
|
||
|
|
event2,
|
||
|
|
event_state,
|
||
|
|
route,
|
||
|
|
options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
invalidated_data_nodes,
|
||
|
|
trailing_slash
|
||
|
|
);
|
||
|
|
} else if (route.endpoint && (!route.page || is_endpoint_request(event2))) {
|
||
|
|
response2 = await render_endpoint(event2, event_state, await route.endpoint(), state);
|
||
|
|
} else if (route.page) {
|
||
|
|
if (!page_nodes) {
|
||
|
|
throw new Error("page_nodes not found. This should never happen");
|
||
|
|
} else if (page_methods.has(method)) {
|
||
|
|
response2 = await render_page(
|
||
|
|
event2,
|
||
|
|
event_state,
|
||
|
|
route.page,
|
||
|
|
options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
page_nodes,
|
||
|
|
resolve_opts
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
const allowed_methods2 = new Set(allowed_page_methods);
|
||
|
|
const node = await manifest._.nodes[route.page.leaf]();
|
||
|
|
if (node?.server?.actions) {
|
||
|
|
allowed_methods2.add("POST");
|
||
|
|
}
|
||
|
|
if (method === "OPTIONS") {
|
||
|
|
response2 = new Response(null, {
|
||
|
|
status: 204,
|
||
|
|
headers: {
|
||
|
|
allow: Array.from(allowed_methods2.values()).join(", ")
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
const mod = [...allowed_methods2].reduce(
|
||
|
|
(acc, curr) => {
|
||
|
|
acc[curr] = true;
|
||
|
|
return acc;
|
||
|
|
},
|
||
|
|
/** @type {Record<string, any>} */
|
||
|
|
{}
|
||
|
|
);
|
||
|
|
response2 = method_not_allowed(mod, method);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
throw new Error("Route is neither page nor endpoint. This should never happen");
|
||
|
|
}
|
||
|
|
if (request.method === "GET" && route.page && route.endpoint) {
|
||
|
|
const vary = response2.headers.get("vary")?.split(",")?.map((v) => v.trim().toLowerCase());
|
||
|
|
if (!(vary?.includes("accept") || vary?.includes("*"))) {
|
||
|
|
response2 = new Response(response2.body, {
|
||
|
|
status: response2.status,
|
||
|
|
statusText: response2.statusText,
|
||
|
|
headers: new Headers(response2.headers)
|
||
|
|
});
|
||
|
|
response2.headers.append("Vary", "Accept");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return response2;
|
||
|
|
}
|
||
|
|
if (state.error && event2.isSubRequest) {
|
||
|
|
const headers22 = new Headers(request.headers);
|
||
|
|
headers22.set("x-sveltekit-error", "true");
|
||
|
|
return await fetch(request, { headers: headers22 });
|
||
|
|
}
|
||
|
|
if (state.error) {
|
||
|
|
return text("Internal Server Error", {
|
||
|
|
status: 500
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (state.depth === 0) {
|
||
|
|
if (BROWSER && event2.url.pathname === "/.well-known/appspecific/com.chrome.devtools.json") ;
|
||
|
|
return await respond_with_error({
|
||
|
|
event: event2,
|
||
|
|
event_state,
|
||
|
|
options: options2,
|
||
|
|
manifest,
|
||
|
|
state,
|
||
|
|
status: 404,
|
||
|
|
error: new SvelteKitError(404, "Not Found", `Not found: ${event2.url.pathname}`),
|
||
|
|
resolve_opts
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (state.prerendering) {
|
||
|
|
return text("not found", { status: 404 });
|
||
|
|
}
|
||
|
|
const response = await fetch(request);
|
||
|
|
return new Response(response.body, response);
|
||
|
|
} catch (e) {
|
||
|
|
return await handle_fatal_error(event2, event_state, options2, e);
|
||
|
|
} finally {
|
||
|
|
event2.cookies.set = () => {
|
||
|
|
throw new Error("Cannot use `cookies.set(...)` after the response has been generated");
|
||
|
|
};
|
||
|
|
event2.setHeaders = () => {
|
||
|
|
throw new Error("Cannot use `setHeaders(...)` after the response has been generated");
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function load_page_nodes(page, manifest) {
|
||
|
|
return Promise.all([
|
||
|
|
// we use == here rather than === because [undefined] serializes as "[null]"
|
||
|
|
...page.layouts.map((n) => n == void 0 ? n : manifest._.nodes[n]()),
|
||
|
|
manifest._.nodes[page.leaf]()
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
function propagate_context(fn) {
|
||
|
|
return async (req, ...rest) => {
|
||
|
|
{
|
||
|
|
return fn(req, ...rest);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function filter_env(env, allowed, disallowed) {
|
||
|
|
return Object.fromEntries(
|
||
|
|
Object.entries(env).filter(
|
||
|
|
([k]) => k.startsWith(allowed) && (disallowed === "" || !k.startsWith(disallowed))
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
function set_app(value) {
|
||
|
|
}
|
||
|
|
let init_promise;
|
||
|
|
let current = null;
|
||
|
|
class Server {
|
||
|
|
/** @type {import('types').SSROptions} */
|
||
|
|
#options;
|
||
|
|
/** @type {import('@sveltejs/kit').SSRManifest} */
|
||
|
|
#manifest;
|
||
|
|
/** @param {import('@sveltejs/kit').SSRManifest} manifest */
|
||
|
|
constructor(manifest) {
|
||
|
|
this.#options = options;
|
||
|
|
this.#manifest = manifest;
|
||
|
|
if (IN_WEBCONTAINER) {
|
||
|
|
const respond2 = this.respond.bind(this);
|
||
|
|
this.respond = async (...args) => {
|
||
|
|
const { promise, resolve: resolve2 } = (
|
||
|
|
/** @type {PromiseWithResolvers<void>} */
|
||
|
|
with_resolvers()
|
||
|
|
);
|
||
|
|
const previous = current;
|
||
|
|
current = promise;
|
||
|
|
await previous;
|
||
|
|
return respond2(...args).finally(resolve2);
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @param {import('@sveltejs/kit').ServerInitOptions} opts
|
||
|
|
*/
|
||
|
|
async init({ env, read }) {
|
||
|
|
const { env_public_prefix, env_private_prefix } = this.#options;
|
||
|
|
set_private_env(filter_env(env, env_private_prefix, env_public_prefix));
|
||
|
|
set_public_env(filter_env(env, env_public_prefix, env_private_prefix));
|
||
|
|
if (read) {
|
||
|
|
const wrapped_read = (file) => {
|
||
|
|
const result = read(file);
|
||
|
|
if (result instanceof ReadableStream) {
|
||
|
|
return result;
|
||
|
|
} else {
|
||
|
|
return new ReadableStream({
|
||
|
|
async start(controller) {
|
||
|
|
try {
|
||
|
|
const stream = await Promise.resolve(result);
|
||
|
|
if (!stream) {
|
||
|
|
controller.close();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const reader = stream.getReader();
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) break;
|
||
|
|
controller.enqueue(value);
|
||
|
|
}
|
||
|
|
controller.close();
|
||
|
|
} catch (error2) {
|
||
|
|
controller.error(error2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
set_read_implementation(wrapped_read);
|
||
|
|
}
|
||
|
|
await (init_promise ??= (async () => {
|
||
|
|
try {
|
||
|
|
const module = await get_hooks();
|
||
|
|
this.#options.hooks = {
|
||
|
|
handle: module.handle || (({ event, resolve: resolve2 }) => resolve2(event)),
|
||
|
|
handleError: module.handleError || (({ status, error: error2, event }) => {
|
||
|
|
const error_message = format_server_error(
|
||
|
|
status,
|
||
|
|
/** @type {Error} */
|
||
|
|
error2,
|
||
|
|
event
|
||
|
|
);
|
||
|
|
console.error(error_message);
|
||
|
|
}),
|
||
|
|
handleFetch: module.handleFetch || (({ request, fetch: fetch2 }) => fetch2(request)),
|
||
|
|
handleValidationError: module.handleValidationError || (({ issues }) => {
|
||
|
|
console.error("Remote function schema validation failed:", issues);
|
||
|
|
return { message: "Bad Request" };
|
||
|
|
}),
|
||
|
|
reroute: module.reroute || (() => {
|
||
|
|
}),
|
||
|
|
transport: module.transport || {}
|
||
|
|
};
|
||
|
|
set_app({
|
||
|
|
decoders: module.transport ? Object.fromEntries(Object.entries(module.transport).map(([k, v]) => [k, v.decode])) : {}
|
||
|
|
});
|
||
|
|
if (module.init) {
|
||
|
|
await module.init();
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
{
|
||
|
|
throw e;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})());
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @param {Request} request
|
||
|
|
* @param {import('types').RequestOptions} options
|
||
|
|
*/
|
||
|
|
async respond(request, options2) {
|
||
|
|
return respond(request, this.#options, this.#manifest, {
|
||
|
|
...options2,
|
||
|
|
error: false,
|
||
|
|
depth: 0
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
export {
|
||
|
|
Server
|
||
|
|
};
|