Files
tutortool/frontend/.svelte-kit/output/server/chunks/client.js

1449 lines
45 KiB
JavaScript

import { n as settled, r as tick$1, t as index_server_exports } from "./index-server.js";
import { b as noop, f as get_message, h as base64_decode, n as TRAILING_SLASH_PARAM, p as get_status, t as INVALIDATED_PARAM, v as text_decoder } from "./shared.js";
import { d as base } from "./environment.js";
import { S as compact, f as make_trackable, g as add_data_suffix, h as noop_span, l as decode_params, p as normalize_path, s as hash, u as decode_pathname } from "./exports.js";
import { Z as noop$1, k as writable } from "./dev.js";
import "./index-server2.js";
import "./internal.js";
import { HttpError, Redirect, SvelteKitError } from "@sveltejs/kit/internal";
import "@sveltejs/kit/internal/server";
import * as devalue from "devalue";
var STATES_KEY = "sveltekit:states";
var HISTORY_INDEX = "sveltekit:history";
var NAVIGATION_INDEX = "sveltekit:navigation";
var PRELOAD_PRIORITIES = {
tap: 1,
hover: 2,
viewport: 3,
eager: 4,
off: -1,
false: -1
};
//#endregion
//#region node_modules/@sveltejs/kit/src/runtime/client/utils.js
var origin = "";
/** @param {string | URL} url */
function resolve_url(url) {
if (url instanceof URL) return url;
let baseURI = document.baseURI;
if (!baseURI) {
const baseTags = document.getElementsByTagName("base");
baseURI = baseTags.length ? baseTags[0].href : document.URL;
}
return new URL(url, baseURI);
}
function scroll_state() {
return {
x: pageXOffset,
y: pageYOffset
};
}
({ ...PRELOAD_PRIORITIES }), PRELOAD_PRIORITIES.hover;
/** @param {any} value */
function notifiable_store(value) {
const store = writable(value);
let ready = true;
function notify() {
ready = true;
store.update((val) => val);
}
/** @param {any} new_value */
function set(new_value) {
ready = false;
store.set(new_value);
}
/** @param {(value: any) => void} run */
function subscribe(run) {
/** @type {any} */
let old_value;
return store.subscribe((new_value) => {
if (old_value === void 0 || ready && new_value !== old_value) run(old_value = new_value);
});
}
return {
notify,
set,
subscribe
};
}
var updated_listener = { v: noop };
function create_updated_store() {
const { set, subscribe } = writable(false);
return {
subscribe,
check: async () => false
};
}
/**
* Is external if
* - origin different
* - path doesn't start with base
* - uses hash router and pathname is more than base
* @param {URL} url
* @param {string} base
* @param {boolean} hash_routing
*/
function is_external_url(url, base, hash_routing) {
if (url.origin !== origin || !url.pathname.startsWith(base)) return true;
if (hash_routing) return url.pathname !== location.pathname;
return false;
}
//#endregion
//#region node_modules/@sveltejs/kit/src/runtime/client/state.svelte.js
var page;
var navigating;
var updated;
var is_legacy = noop$1.toString().includes("$$") || /function \w+\(\) \{\}/.test(noop$1.toString());
var placeholder_url = "a:";
if (is_legacy) {
page = {
data: {},
form: null,
error: null,
params: {},
route: { id: null },
state: {},
status: -1,
url: new URL(placeholder_url)
};
navigating = { current: null };
updated = { current: false };
} else {
page = new class Page {
data = {};
form = null;
error = null;
params = {};
route = { id: null };
state = {};
status = -1;
url = new URL(placeholder_url);
}();
navigating = new class Navigating {
current = null;
}();
updated = new class Updated {
current = false;
}();
updated_listener.v = () => updated.current = true;
}
/**
* @param {import('@sveltejs/kit').Page} new_page
*/
function update(new_page) {
Object.assign(page, new_page);
}
var cache = /* @__PURE__ */ new Map();
/**
* Should be called on the initial run of load functions that hydrate the page.
* Saves any requests with cache-control max-age to the cache.
* @param {URL | string} resource
* @param {RequestInit} [opts]
*/
function initial_fetch(resource, opts) {
const selector = build_selector(resource, opts);
const script = document.querySelector(selector);
if (script?.textContent) {
script.remove();
let { body, ...init } = JSON.parse(script.textContent);
const ttl = script.getAttribute("data-ttl");
if (ttl) cache.set(selector, {
body,
init,
ttl: 1e3 * Number(ttl)
});
if (script.getAttribute("data-b64") !== null) body = base64_decode(body);
return Promise.resolve(new Response(body, init));
}
return window.fetch(resource, opts);
}
/**
* Tries to get the response from the cache, if max-age allows it, else does a fetch.
* @param {URL | string} resource
* @param {string} resolved
* @param {RequestInit} [opts]
*/
function subsequent_fetch(resource, resolved, opts) {
if (cache.size > 0) {
const selector = build_selector(resource, opts);
const cached = cache.get(selector);
if (cached) {
if (performance.now() < cached.ttl && [
"default",
"force-cache",
"only-if-cached",
void 0
].includes(opts?.cache)) return new Response(cached.body, cached.init);
cache.delete(selector);
}
}
return window.fetch(resolved, opts);
}
/**
* Build the cache key for a given request
* @param {URL | RequestInfo} resource
* @param {RequestInit} [opts]
*/
function build_selector(resource, opts) {
let selector = `script[data-sveltekit-fetched][data-url=${JSON.stringify(resource instanceof Request ? resource.url : resource)}]`;
if (opts?.headers || opts?.body) {
/** @type {import('types').StrictBody[]} */
const values = [];
if (opts.headers) values.push([...new Headers(opts.headers)].join(","));
if (opts.body && (typeof opts.body === "string" || ArrayBuffer.isView(opts.body))) values.push(opts.body);
selector += `[data-hash="${hash(...values)}"]`;
}
return selector;
}
//#endregion
//#region node_modules/@sveltejs/kit/src/runtime/client/session-storage.js
/**
* Read a value from `sessionStorage`
* @param {string} key
* @param {(value: string) => any} parse
*/
/* @__NO_SIDE_EFFECTS__ */
function get(key, parse = JSON.parse) {
try {
return parse(sessionStorage[key]);
} catch {}
}
//#endregion
//#region node_modules/@sveltejs/kit/src/runtime/client/client.js
/** @import { RemoteQueryCacheEntry } from './remote-functions/query.svelte.js' */
var { onMount, tick } = index_server_exports;
/**
* Set via transformError, reset and read at the end of navigate.
* Necessary because a navigation might succeed loading but during rendering
* an error occurs, at which point the navigation result needs to be overridden with the error result.
* TODO this is all very hacky, rethink for SvelteKit 3 where we can assume Svelte 5 and do an overhaul of client.js
* @type {{ error: App.Error, status: number } | null}
*/
var rendering_error = null;
/**
* history index -> { x, y }
* @type {Record<number, { x: number; y: number }>}
*/
var scroll_positions = /* @__PURE__ */ get("sveltekit:scroll") ?? {};
/**
* navigation index -> any
* @type {Record<string, any[]>}
*/
var snapshots = /* @__PURE__ */ get("sveltekit:snapshot") ?? {};
var stores = {
url: /* @__PURE__ */ notifiable_store({}),
page: /* @__PURE__ */ notifiable_store({}),
navigating: /* @__PURE__ */ writable(null),
updated: /* @__PURE__ */ create_updated_store()
};
/** @param {number} index */
function update_scroll_positions(index) {
scroll_positions[index] = scroll_state();
}
/**
* @param {number} current_history_index
* @param {number} current_navigation_index
*/
function clear_onward_history(current_history_index, current_navigation_index) {
let i = current_history_index + 1;
while (scroll_positions[i]) {
delete scroll_positions[i];
i += 1;
}
i = current_navigation_index + 1;
while (snapshots[i]) {
delete snapshots[i];
i += 1;
}
}
/**
* Loads `href` the old-fashioned way, with a full page reload.
* Returns a `Promise` that never resolves (to prevent any
* subsequent work, e.g. history manipulation, from happening)
* @param {URL} url
* @param {boolean} [replace] If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
*/
function native_navigation(url, replace = false) {
if (replace) location.replace(url.href);
else location.href = url.href;
return new Promise(noop);
}
/**
* Checks whether a service worker is registered, and if it is,
* tries to update it.
*/
async function update_service_worker() {
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.getRegistration(base || "/");
if (registration) await registration.update();
}
}
/** @type {import('types').CSRRoute[]} All routes of the app. Only available when kit.router.resolution=client */
var routes;
/** @type {import('types').CSRPageNodeLoader} */
var default_layout_loader;
/** @type {import('types').CSRPageNodeLoader} */
var default_error_loader;
/** @type {HTMLElement} */
var target;
/** @type {import('./types.js').SvelteKitApp} */
var app;
/** @type {Array<((url: URL) => boolean)>} */
var invalidated = [];
/**
* An array of the `+layout.svelte` and `+page.svelte` component instances
* that currently live on the page — used for capturing and restoring snapshots.
* It's updated/manipulated through `bind:this` in `Root.svelte`.
* @type {import('svelte').SvelteComponent[]}
*/
var components = [];
/** @type {{id: string, token: {}, promise: Promise<import('./types.js').NavigationResult>, fork: Promise<import('svelte').Fork | null> | null} | null} */
var load_cache = null;
function discard_load_cache() {
load_cache?.fork?.then((f) => f?.discard());
load_cache = null;
}
/**
* @type {Map<string, Promise<URL>>}
* Cache for client-side rerouting, since it could contain async calls which we want to
* avoid running multiple times which would slow down navigations (e.g. else preloading
* wouldn't help because on navigation it would be called again). Since `reroute` should be
* a pure function (i.e. always return the same) value it's safe to cache across navigations.
* The server reroute calls don't need to be cached because they are called using `import(...)`
* which is cached per the JS spec.
*/
var reroute_cache = /* @__PURE__ */ new Map();
/**
* Note on before_navigate_callbacks, on_navigate_callbacks and after_navigate_callbacks:
* do not re-assign as some closures keep references to these Sets
*/
/** @type {Set<(navigation: import('@sveltejs/kit').BeforeNavigate) => void>} */
var before_navigate_callbacks = /* @__PURE__ */ new Set();
/** @type {Set<(navigation: import('@sveltejs/kit').OnNavigate) => import('types').MaybePromise<(() => void) | void>>} */
var on_navigate_callbacks = /* @__PURE__ */ new Set();
/** @type {Set<(navigation: import('@sveltejs/kit').AfterNavigate) => void>} */
var after_navigate_callbacks = /* @__PURE__ */ new Set();
/** @type {import('./types.js').NavigationState & { nav: import('@sveltejs/kit').NavigationEvent }} */
var current = {
branch: [],
error: null,
url: null,
nav: null
};
/** this being true means we SSR'd */
var hydrated = false;
var started = false;
var autoscroll = true;
var is_navigating = false;
var force_invalidation = false;
/** @type {import('svelte').SvelteComponent} */
var root;
/** @type {number} keeping track of the history index in order to prevent popstate navigation events if needed */
var current_history_index;
/** @type {number} */
var current_navigation_index;
/** @type {{}} */
var token;
/**
* A set of tokens which are associated to current preloads.
* If a preload becomes a real navigation, it's removed from the set.
* If a preload token is in the set and the preload errors, the error
* handling logic (for example reloading) is skipped.
*/
var preload_tokens = /* @__PURE__ */ new Set();
/**
* @type {Map<string, Map<string, RemoteQueryCacheEntry<any>>>}
* A map of query id -> payload -> query internals for all active queries.
*/
var query_map = /* @__PURE__ */ new Map();
function reset_invalidation() {
invalidated.length = 0;
force_invalidation = false;
}
/** @param {number} index */
function capture_snapshot(index) {
if (components.some((c) => c?.snapshot)) snapshots[index] = components.map((c) => c?.snapshot?.capture());
}
/** @param {number} index */
function restore_snapshot(index) {
snapshots[index]?.forEach((value, i) => {
components[i]?.snapshot?.restore(value);
});
}
/**
* @param {string | URL} url
* @param {{ replaceState?: boolean; noScroll?: boolean; keepFocus?: boolean; invalidateAll?: boolean; invalidate?: Array<string | URL | ((url: URL) => boolean)>; state?: Record<string, any> }} options
* @param {number} redirect_count
* @param {{}} [nav_token]
*/
async function _goto(url, options, redirect_count, nav_token) {
/** @type {string[]} */
let query_keys;
if (options.invalidateAll) discard_load_cache();
await navigate({
type: "goto",
url: resolve_url(url),
keepfocus: options.keepFocus,
noscroll: options.noScroll,
replace_state: options.replaceState,
state: options.state,
redirect_count,
nav_token,
accept: () => {
if (options.invalidateAll) {
force_invalidation = true;
query_keys = [];
query_map.forEach((entries, id) => {
for (const payload of entries.keys()) query_keys.push(id + "/" + payload);
});
}
if (options.invalidate) options.invalidate.forEach(push_invalidated);
}
});
if (options.invalidateAll) tick$1().then(tick$1).then(() => {
query_map.forEach((entries, id) => {
entries.forEach(({ resource }, payload) => {
if (query_keys?.includes(id + "/" + payload)) resource.refresh?.();
});
});
});
}
/**
* @param {import('./types.js').NavigationFinished} result
* @param {HTMLElement} target
* @param {boolean} hydrate
*/
async function initialize(result, target, hydrate) {
/** @type {import('@sveltejs/kit').NavigationEvent} */
const nav = {
params: current.params,
route: { id: current.route?.id ?? null },
url: new URL(location.href)
};
current = {
...result.state,
nav
};
const style = document.querySelector("style[data-sveltekit]");
if (style) style.remove();
Object.assign(page, result.props.page);
root = new app.root({
target,
props: {
...result.props,
stores,
components
},
hydrate,
sync: false,
transformError: void 0
});
await Promise.resolve();
restore_snapshot(current_navigation_index);
if (hydrate) {
/** @type {import('@sveltejs/kit').AfterNavigate} */
const navigation = {
from: null,
to: {
...nav,
scroll: scroll_positions[current_history_index] ?? scroll_state()
},
willUnload: false,
type: "enter",
complete: Promise.resolve()
};
after_navigate_callbacks.forEach((fn) => fn(navigation));
}
started = true;
}
/**
*
* @param {{
* url: URL;
* params: Record<string, string>;
* branch: Array<import('./types.js').BranchNode | undefined>;
* errors?: Array<import('types').CSRPageNodeLoader | undefined>;
* status: number;
* error: App.Error | null;
* route: import('types').CSRRoute | null;
* form?: Record<string, any> | null;
* }} opts
*/
async function get_navigation_result_from_branch({ url, params, branch, errors, status, error, route, form }) {
/** @type {import('types').TrailingSlash} */
let slash = "never";
if (base && (url.pathname === base || url.pathname === base + "/")) slash = "always";
else for (const node of branch) if (node?.slash !== void 0) slash = node.slash;
url.pathname = normalize_path(url.pathname, slash);
url.search = url.search;
/** @type {import('./types.js').NavigationFinished} */
const result = {
type: "loaded",
state: {
url,
params,
branch,
error,
route
},
props: {
constructors: compact(branch).map((branch_node) => branch_node.node.component),
page: clone_page(page)
}
};
if (form !== void 0) result.props.form = form;
let data = {};
let data_changed = !page;
let p = 0;
for (let i = 0; i < Math.max(branch.length, current.branch.length); i += 1) {
const node = branch[i];
const prev = current.branch[i];
if (node?.data !== prev?.data) data_changed = true;
if (!node) continue;
data = {
...data,
...node.data
};
if (data_changed) result.props[`data_${p}`] = data;
p += 1;
}
if (!current.url || url.href !== current.url.href || current.error !== error || form !== void 0 && form !== page.form || data_changed) result.props.page = {
error,
params,
route: { id: route?.id ?? null },
state: {},
status,
url: new URL(url),
form: form ?? null,
data: data_changed ? data : page.data
};
return result;
}
/**
* Call the universal load function of the given node, if it exists.
*
* @param {{
* loader: import('types').CSRPageNodeLoader;
* parent: () => Promise<Record<string, any>>;
* url: URL;
* params: Record<string, string>;
* route: { id: string | null };
* server_data_node: import('./types.js').DataNode | null;
* }} options
* @returns {Promise<import('./types.js').BranchNode>}
*/
async function load_node({ loader, parent, url, params, route, server_data_node }) {
/** @type {Record<string, any> | null} */
let data = null;
let is_tracking = true;
/** @type {import('types').Uses} */
const uses = {
dependencies: /* @__PURE__ */ new Set(),
params: /* @__PURE__ */ new Set(),
parent: false,
route: false,
url: false,
search_params: /* @__PURE__ */ new Set()
};
const node = await loader();
if (node.universal?.load) {
/** @param {string[]} deps */
function depends(...deps) {
for (const dep of deps) {
const { href } = new URL(dep, url);
uses.dependencies.add(href);
}
}
/** @type {import('@sveltejs/kit').LoadEvent} */
const load_input = {
tracing: {
enabled: false,
root: noop_span,
current: noop_span
},
route: new Proxy(route, { get: (target, key) => {
if (is_tracking) uses.route = true;
return target[key];
} }),
params: new Proxy(params, { get: (target, key) => {
if (is_tracking) uses.params.add(key);
return target[key];
} }),
data: server_data_node?.data ?? null,
url: make_trackable(url, () => {
if (is_tracking) uses.url = true;
}, (param) => {
if (is_tracking) uses.search_params.add(param);
}, app.hash),
async fetch(resource, init) {
if (resource instanceof Request) init = {
body: resource.method === "GET" || resource.method === "HEAD" ? void 0 : await resource.blob(),
cache: resource.cache,
credentials: resource.credentials,
headers: [...resource.headers].length > 0 ? resource?.headers : void 0,
integrity: resource.integrity,
keepalive: resource.keepalive,
method: resource.method,
mode: resource.mode,
redirect: resource.redirect,
referrer: resource.referrer,
referrerPolicy: resource.referrerPolicy,
signal: resource.signal,
...init
};
const { resolved, promise } = resolve_fetch_url(resource, init, url);
if (is_tracking) depends(resolved.href);
return promise;
},
setHeaders: noop,
depends,
parent() {
if (is_tracking) uses.parent = true;
return parent();
},
untrack(fn) {
is_tracking = false;
try {
return fn();
} finally {
is_tracking = true;
}
}
};
data = await node.universal.load.call(null, load_input) ?? null;
}
return {
node,
loader,
server: server_data_node,
universal: node.universal?.load ? {
type: "data",
data,
uses
} : null,
data: data ?? server_data_node?.data ?? null,
slash: node.universal?.trailingSlash ?? server_data_node?.slash
};
}
/**
* @param {Request | string | URL} input
* @param {RequestInit | undefined} init
* @param {URL} url
*/
function resolve_fetch_url(input, init, url) {
let requested = input instanceof Request ? input.url : input;
const resolved = new URL(requested, url);
if (resolved.origin === url.origin) requested = resolved.href.slice(url.origin.length);
return {
resolved,
promise: started ? subsequent_fetch(requested, resolved.href, init) : initial_fetch(requested, init)
};
}
/**
* @param {boolean} parent_changed
* @param {boolean} route_changed
* @param {boolean} url_changed
* @param {Set<string>} search_params_changed
* @param {import('types').Uses | undefined} uses
* @param {Record<string, string>} params
*/
function has_changed(parent_changed, route_changed, url_changed, search_params_changed, uses, params) {
if (force_invalidation) return true;
if (!uses) return false;
if (uses.parent && parent_changed) return true;
if (uses.route && route_changed) return true;
if (uses.url && url_changed) return true;
for (const tracked_params of uses.search_params) if (search_params_changed.has(tracked_params)) return true;
for (const param of uses.params) if (params[param] !== current.params[param]) return true;
for (const href of uses.dependencies) if (invalidated.some((fn) => fn(new URL(href)))) return true;
return false;
}
/**
* @param {import('types').ServerDataNode | import('types').ServerDataSkippedNode | null} node
* @param {import('./types.js').DataNode | null} [previous]
* @returns {import('./types.js').DataNode | null}
*/
function create_data_node(node, previous) {
if (node?.type === "data") return node;
if (node?.type === "skip") return previous ?? null;
return null;
}
/**
* @param {URL | null} old_url
* @param {URL} new_url
*/
function diff_search_params(old_url, new_url) {
if (!old_url) return new Set(new_url.searchParams.keys());
const changed = new Set([...old_url.searchParams.keys(), ...new_url.searchParams.keys()]);
for (const key of changed) {
const old_values = old_url.searchParams.getAll(key);
const new_values = new_url.searchParams.getAll(key);
if (old_values.every((value) => new_values.includes(value)) && new_values.every((value) => old_values.includes(value))) changed.delete(key);
}
return changed;
}
/**
* @param {Omit<import('./types.js').NavigationFinished['state'], 'branch'> & { error: App.Error }} opts
* @returns {import('./types.js').NavigationFinished}
*/
function preload_error({ error, url, route, params }) {
return {
type: "loaded",
state: {
error,
url,
route,
params,
branch: []
},
props: {
page: clone_page(page),
constructors: []
}
};
}
/**
* @param {import('./types.js').NavigationIntent & { preload?: {} }} intent
* @returns {Promise<import('./types.js').NavigationResult>}
*/
async function load_route({ id, invalidating, url, params, route, preload }) {
if (load_cache?.id === id) {
preload_tokens.delete(load_cache.token);
return load_cache.promise;
}
const { errors, layouts, leaf } = route;
const loaders = [...layouts, leaf];
errors.forEach((loader) => loader?.().catch(noop));
loaders.forEach((loader) => loader?.[1]().catch(noop));
/** @type {import('types').ServerNodesResponse | import('types').ServerRedirectNode | null} */
let server_data = null;
const url_changed = current.url ? id !== get_page_key(current.url) : false;
const route_changed = current.route ? route.id !== current.route.id : false;
const search_params_changed = diff_search_params(current.url, url);
let parent_invalid = false;
{
const invalid_server_nodes = loaders.map((loader, i) => {
const previous = current.branch[i];
const invalid = !!loader?.[0] && (previous?.loader !== loader[1] || has_changed(parent_invalid, route_changed, url_changed, search_params_changed, previous.server?.uses, params));
if (invalid) parent_invalid = true;
return invalid;
});
if (invalid_server_nodes.some(Boolean)) {
try {
server_data = await load_data(url, invalid_server_nodes);
} catch (error) {
const handled_error = await handle_error(error, {
url,
params,
route: { id }
});
if (preload_tokens.has(preload)) return preload_error({
error: handled_error,
url,
params,
route
});
return load_root_error_page({
status: get_status(error),
error: handled_error,
url,
route
});
}
if (server_data.type === "redirect") return server_data;
}
}
const server_data_nodes = server_data?.nodes;
let parent_changed = false;
const branch_promises = loaders.map(async (loader, i) => {
if (!loader) return;
/** @type {import('./types.js').BranchNode | undefined} */
const previous = current.branch[i];
const server_data_node = server_data_nodes?.[i];
if ((!server_data_node || server_data_node.type === "skip") && loader[1] === previous?.loader && !has_changed(parent_changed, route_changed, url_changed, search_params_changed, previous.universal?.uses, params)) return previous;
parent_changed = true;
if (server_data_node?.type === "error") throw server_data_node;
return load_node({
loader: loader[1],
url,
params,
route,
parent: async () => {
const data = {};
for (let j = 0; j < i; j += 1) Object.assign(data, (await branch_promises[j])?.data);
return data;
},
server_data_node: create_data_node(server_data_node === void 0 && loader[0] ? { type: "skip" } : server_data_node ?? null, loader[0] ? previous?.server : void 0)
});
});
for (const p of branch_promises) p.catch(noop);
/** @type {Array<import('./types.js').BranchNode | undefined>} */
const branch = [];
for (let i = 0; i < loaders.length; i += 1) if (loaders[i]) try {
branch.push(await branch_promises[i]);
} catch (err) {
if (err instanceof Redirect) return {
type: "redirect",
location: err.location
};
if (preload_tokens.has(preload)) return preload_error({
error: await handle_error(err, {
params,
url,
route: { id: route.id }
}),
url,
params,
route
});
let status = get_status(err);
/** @type {App.Error} */
let error;
if (server_data_nodes?.includes(err)) {
status = err.status ?? status;
error = err.error;
} else if (err instanceof HttpError) error = err.body;
else {
if (await stores.updated.check()) {
await update_service_worker();
return await native_navigation(url);
}
error = await handle_error(err, {
params,
url,
route: { id: route.id }
});
}
const error_load = await load_nearest_error_page(i, branch, errors);
if (error_load) return get_navigation_result_from_branch({
url,
params,
branch: branch.slice(0, error_load.idx).concat(error_load.node),
errors,
status,
error,
route
});
else return await server_fallback(url, { id: route.id }, error, status);
}
else branch.push(void 0);
return get_navigation_result_from_branch({
url,
params,
branch,
errors,
status: 200,
error: null,
route,
form: invalidating ? void 0 : null
});
}
/**
* @param {number} i Start index to backtrack from
* @param {Array<import('./types.js').BranchNode | undefined>} branch Branch to backtrack
* @param {Array<import('types').CSRPageNodeLoader | undefined>} errors All error pages for this branch
* @returns {Promise<{idx: number; node: import('./types.js').BranchNode} | undefined>}
*/
async function load_nearest_error_page(i, branch, errors) {
while (i--) if (errors[i]) {
let j = i;
while (!branch[j]) j -= 1;
try {
return {
idx: j + 1,
node: {
node: await errors[i](),
loader: errors[i],
data: {},
server: null,
universal: null
}
};
} catch {
continue;
}
}
}
/**
* @param {{
* status: number;
* error: App.Error;
* url: URL;
* route: { id: string | null }
* }} opts
* @returns {Promise<import('./types.js').NavigationFinished>}
*/
async function load_root_error_page({ status, error, url, route }) {
/** @type {Record<string, string>} */
const params = {};
/** @type {import('types').ServerDataNode | null} */
let server_data_node = null;
if (app.server_loads[0] === 0) try {
const server_data = await load_data(url, [true]);
if (server_data.type !== "data" || server_data.nodes[0] && server_data.nodes[0].type !== "data") throw 0;
server_data_node = server_data.nodes[0] ?? null;
} catch {
if (url.origin !== origin || url.pathname !== location.pathname || hydrated) await native_navigation(url);
}
try {
return get_navigation_result_from_branch({
url,
params,
branch: [await load_node({
loader: default_layout_loader,
url,
params,
route,
parent: () => Promise.resolve({}),
server_data_node: create_data_node(server_data_node)
}), {
node: await default_error_loader(),
loader: default_error_loader,
universal: null,
server: null,
data: null
}],
status,
error,
errors: [],
route: null
});
} catch (error) {
if (error instanceof Redirect) return _goto(new URL(error.location, location.href), {}, 0);
throw error;
}
}
/**
* Resolve the relative rerouted URL for a client-side navigation
* @param {URL} url
* @returns {Promise<URL | undefined>}
*/
async function get_rerouted_url(url) {
const href = url.href;
if (reroute_cache.has(href)) return reroute_cache.get(href);
let rerouted;
try {
const promise = (async () => {
let rerouted = await app.hooks.reroute({
url: new URL(url),
fetch: async (input, init) => {
return resolve_fetch_url(input, init, url).promise;
}
}) ?? url;
if (typeof rerouted === "string") {
const tmp = new URL(url);
if (app.hash) tmp.hash = rerouted;
else tmp.pathname = rerouted;
rerouted = tmp;
}
return rerouted;
})();
reroute_cache.set(href, promise);
rerouted = await promise;
} catch (e) {
reroute_cache.delete(href);
return;
}
return rerouted;
}
/**
* Resolve the full info (which route, params, etc.) for a client-side navigation from the URL,
* taking the reroute hook into account. If this isn't a client-side-navigation (or the URL is undefined),
* returns undefined.
* @param {URL | undefined} url
* @param {boolean} invalidating
* @returns {Promise<import('./types.js').NavigationIntent | undefined>}
*/
async function get_navigation_intent(url, invalidating) {
if (!url) return;
if (is_external_url(url, base, app.hash)) return;
{
const rerouted = await get_rerouted_url(url);
if (!rerouted) return;
const path = get_url_path(rerouted);
for (const route of routes) {
const params = route.exec(path);
if (params) return {
id: get_page_key(url),
invalidating,
route,
params: decode_params(params),
url
};
}
}
}
/** @param {URL} url */
function get_url_path(url) {
return decode_pathname(app.hash ? url.hash.replace(/^#/, "").replace(/[?#].+/, "") : url.pathname.slice(base.length)) || "/";
}
/** @param {URL} url */
function get_page_key(url) {
return (app.hash ? url.hash.replace(/^#/, "") : url.pathname) + url.search;
}
/**
* @param {{
* url: URL;
* type: import('@sveltejs/kit').Navigation["type"];
* intent?: import('./types.js').NavigationIntent;
* delta?: number;
* event?: PopStateEvent | MouseEvent;
* scroll?: { x: number, y: number };
* }} opts
*/
function _before_navigate({ url, type, intent, delta, event, scroll }) {
let should_block = false;
const nav = create_navigation(current, intent, url, type, scroll ?? null);
if (delta !== void 0) nav.navigation.delta = delta;
if (event !== void 0) nav.navigation.event = event;
const cancellable = {
...nav.navigation,
cancel: () => {
should_block = true;
nav.reject(/* @__PURE__ */ new Error("navigation cancelled"));
}
};
if (!is_navigating) before_navigate_callbacks.forEach((fn) => fn(cancellable));
return should_block ? null : nav;
}
/**
* @param {{
* type: import('@sveltejs/kit').NavigationType;
* url: URL;
* popped?: {
* state: Record<string, any>;
* scroll: { x: number, y: number };
* delta: number;
* };
* keepfocus?: boolean;
* noscroll?: boolean;
* replace_state?: boolean;
* state?: Record<string, any>;
* redirect_count?: number;
* nav_token?: {};
* accept?: () => void;
* block?: () => void;
* event?: Event
* }} opts
*/
async function navigate({ type, url, popped, keepfocus, noscroll, replace_state, state = {}, redirect_count = 0, nav_token = {}, accept = noop, block = noop, event }) {
const prev_token = token;
token = nav_token;
const intent = await get_navigation_intent(url, false);
const nav = type === "enter" ? create_navigation(current, intent, url, type) : _before_navigate({
url,
type,
delta: popped?.delta,
intent,
scroll: popped?.scroll,
event
});
if (!nav) {
block();
if (token === nav_token) token = prev_token;
return;
}
const previous_history_index = current_history_index;
const previous_navigation_index = current_navigation_index;
accept();
is_navigating = true;
if (started && nav.navigation.type !== "enter") stores.navigating.set(navigating.current = nav.navigation);
let navigation_result = intent && await load_route(intent);
if (!navigation_result) if (is_external_url(url, base, app.hash)) return await native_navigation(url, replace_state);
else navigation_result = await server_fallback(url, { id: null }, await handle_error(new SvelteKitError(404, "Not Found", `Not found: ${url.pathname}`), {
url,
params: {},
route: { id: null }
}), 404, replace_state);
url = intent?.url || url;
if (token !== nav_token) {
nav.reject(/* @__PURE__ */ new Error("navigation aborted"));
return false;
}
if (navigation_result.type === "redirect") {
if (redirect_count < 20) {
await navigate({
type,
url: new URL(navigation_result.location, url),
popped,
keepfocus,
noscroll,
replace_state,
state,
redirect_count: redirect_count + 1,
nav_token
});
nav.fulfil(void 0);
return;
}
navigation_result = await load_root_error_page({
status: 500,
error: await handle_error(/* @__PURE__ */ new Error("Redirect loop"), {
url,
params: {},
route: { id: null }
}),
url,
route: { id: null }
});
} else if (navigation_result.props.page.status >= 400) {
if (await stores.updated.check()) {
await update_service_worker();
await native_navigation(url, replace_state);
}
}
reset_invalidation();
update_scroll_positions(previous_history_index);
capture_snapshot(previous_navigation_index);
if (navigation_result.props.page.url.pathname !== url.pathname) url.pathname = navigation_result.props.page.url.pathname;
state = popped ? popped.state : state;
if (!popped) {
const change = replace_state ? 0 : 1;
const entry = {
[HISTORY_INDEX]: current_history_index += change,
[NAVIGATION_INDEX]: current_navigation_index += change,
[STATES_KEY]: state
};
(replace_state ? history.replaceState : history.pushState).call(history, entry, "", url);
if (!replace_state) clear_onward_history(current_history_index, current_navigation_index);
}
const load_cache_fork = intent && load_cache?.id === intent.id ? load_cache.fork : null;
if (load_cache?.fork && !load_cache_fork) discard_load_cache();
load_cache = null;
navigation_result.props.page.state = state;
/**
* @type {Promise<void> | undefined}
*/
let commit_promise;
if (started) {
const after_navigate = (await Promise.all(Array.from(on_navigate_callbacks, (fn) => fn(nav.navigation)))).filter(
/** @returns {value is () => void} */
(value) => typeof value === "function"
);
if (after_navigate.length > 0) {
function cleanup() {
after_navigate.forEach((fn) => {
after_navigate_callbacks.delete(fn);
});
}
after_navigate.push(cleanup);
after_navigate.forEach((fn) => {
after_navigate_callbacks.add(fn);
});
}
const target = nav.navigation.to;
current = {
...navigation_result.state,
nav: {
params: target.params,
route: target.route,
url: target.url
}
};
if (navigation_result.props.page) navigation_result.props.page.url = url;
const fork = load_cache_fork && await load_cache_fork;
if (fork) commit_promise = fork.commit();
else {
rendering_error = null;
root.$set(navigation_result.props);
if (rendering_error) Object.assign(navigation_result.props.page, rendering_error);
update(navigation_result.props.page);
commit_promise = settled?.();
}
} else await initialize(navigation_result, target, false);
const { activeElement } = document;
await commit_promise;
await tick$1();
await tick$1();
/** @type {Element | null | ''} */
let deep_linked = null;
if (autoscroll) {
const scroll = popped ? popped.scroll : noscroll ? scroll_state() : null;
if (scroll) scrollTo(scroll.x, scroll.y);
else if (deep_linked = url.hash && document.getElementById(get_id(url))) deep_linked.scrollIntoView();
else scrollTo(0, 0);
}
const changed_focus = document.activeElement !== activeElement && document.activeElement !== document.body;
if (!keepfocus && !changed_focus) reset_focus(url, !deep_linked);
autoscroll = true;
if (navigation_result.props.page) {
if (rendering_error) Object.assign(navigation_result.props.page, rendering_error);
Object.assign(page, navigation_result.props.page);
}
is_navigating = false;
if (type === "popstate") restore_snapshot(current_navigation_index);
nav.fulfil(void 0);
if (nav.navigation.to) nav.navigation.to.scroll = scroll_state();
after_navigate_callbacks.forEach((fn) => fn(nav.navigation));
stores.navigating.set(navigating.current = null);
}
/**
* Does a full page reload if it wouldn't result in an endless loop in the SPA case
* @param {URL} url
* @param {{ id: string | null }} route
* @param {App.Error} error
* @param {number} status
* @param {boolean} [replace_state]
* @returns {Promise<import('./types.js').NavigationFinished>}
*/
async function server_fallback(url, route, error, status, replace_state) {
if (url.origin === origin && url.pathname === location.pathname && !hydrated) return await load_root_error_page({
status,
error,
url,
route
});
return await native_navigation(url, replace_state);
}
/**
* @param {unknown} error
* @param {import('@sveltejs/kit').NavigationEvent} event
* @returns {import('types').MaybePromise<App.Error>}
*/
function handle_error(error, event) {
if (error instanceof HttpError) return error.body;
const status = get_status(error);
const message = get_message(error);
return app.hooks.handleError({
error,
event,
status,
message
}) ?? { message };
}
/**
* Allows you to navigate programmatically to a given route, with options such as keeping the current element focused.
* Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
*
* For external URLs, use `window.location = url` instead of calling `goto(url)`.
*
* @param {string | URL} url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://svelte.dev/docs/kit/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
* @param {Object} [opts] Options related to the navigation
* @param {boolean} [opts.replaceState] If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
* @param {boolean} [opts.noScroll] If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
* @param {boolean} [opts.keepFocus] If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
* @param {boolean} [opts.invalidateAll] If `true`, all `load` functions of the page will be rerun. See https://svelte.dev/docs/kit/load#rerunning-load-functions for more info on invalidation.
* @param {Array<string | URL | ((url: URL) => boolean)>} [opts.invalidate] Causes any load functions to re-run if they depend on one of the urls
* @param {App.PageState} [opts.state] An optional object that will be available as `page.state`
* @returns {Promise<void>}
*/
function goto(url, opts = {}) {
throw new Error("Cannot call goto(...) on the server");
}
/**
* @param {string | URL | ((url: URL) => boolean)} resource The invalidated URL
*/
function push_invalidated(resource) {
if (typeof resource === "function") invalidated.push(resource);
else {
const { href } = new URL(resource, location.href);
invalidated.push((url) => url.href === href);
}
}
/**
* @param {URL} url
* @param {boolean[]} invalid
* @returns {Promise<import('types').ServerNodesResponse | import('types').ServerRedirectNode>}
*/
async function load_data(url, invalid) {
const data_url = new URL(url);
data_url.pathname = add_data_suffix(url.pathname);
if (url.pathname.endsWith("/")) data_url.searchParams.append(TRAILING_SLASH_PARAM, "1");
data_url.searchParams.append(INVALIDATED_PARAM, invalid.map((i) => i ? "1" : "0").join(""));
const res = await (0, window.fetch)(data_url.href, {});
if (!res.ok) {
/** @type {string | undefined} */
let message;
if (res.headers.get("content-type")?.includes("application/json")) message = await res.json();
else if (res.status === 404) message = "Not Found";
else if (res.status === 500) message = "Internal Error";
throw new HttpError(res.status, message);
}
return new Promise(async (resolve) => {
/**
* Map of deferred promises that will be resolved by a subsequent chunk of data
* @type {Map<string, import('types').Deferred>}
*/
const deferreds = /* @__PURE__ */ new Map();
const reader = res.body.getReader();
/**
* @param {any} data
*/
function deserialize(data) {
return devalue.unflatten(data, {
...app.decoders,
Promise: (id) => {
return new Promise((fulfil, reject) => {
deferreds.set(id, {
fulfil,
reject
});
});
}
});
}
let text = "";
while (true) {
const { done, value } = await reader.read();
if (done && !text) break;
text += !value && text ? "\n" : text_decoder.decode(value, { stream: true });
while (true) {
const split = text.indexOf("\n");
if (split === -1) break;
const node = JSON.parse(text.slice(0, split));
text = text.slice(split + 1);
if (node.type === "redirect") return resolve(node);
if (node.type === "data") {
node.nodes?.forEach((node) => {
if (node?.type === "data") {
node.uses = deserialize_uses(node.uses);
node.data = deserialize(node.data);
}
});
resolve(node);
} else if (node.type === "chunk") {
const { id, data, error } = node;
const deferred = deferreds.get(id);
deferreds.delete(id);
if (error) deferred.reject(deserialize(error));
else deferred.fulfil(deserialize(data));
}
}
}
});
}
/**
* @param {any} uses
* @return {import('types').Uses}
*/
function deserialize_uses(uses) {
return {
dependencies: new Set(uses?.dependencies ?? []),
params: new Set(uses?.params ?? []),
parent: !!uses?.parent,
route: !!uses?.route,
url: !!uses?.url,
search_params: new Set(uses?.search_params ?? [])
};
}
/**
* @param {URL} url
* @param {boolean} [scroll]
*/
function reset_focus(url, scroll = true) {
const autofocus = document.querySelector("[autofocus]");
if (autofocus) autofocus.focus();
else {
const id = get_id(url);
if (id && document.getElementById(id)) {
const { x, y } = scroll_state();
setTimeout(() => {
const history_state = history.state;
location.replace(new URL(`#${id}`, location.href));
history.replaceState(history_state, "", url);
if (scroll) scrollTo(x, y);
});
} else {
const root = document.body;
const tabindex = root.getAttribute("tabindex");
root.tabIndex = -1;
root.focus({
preventScroll: true,
focusVisible: false
});
if (tabindex !== null) root.setAttribute("tabindex", tabindex);
else root.removeAttribute("tabindex");
}
const selection = getSelection();
if (selection && selection.type !== "None") {
/** @type {Range[]} */
const ranges = [];
for (let i = 0; i < selection.rangeCount; i += 1) ranges.push(selection.getRangeAt(i));
setTimeout(() => {
if (selection.rangeCount !== ranges.length) return;
for (let i = 0; i < selection.rangeCount; i += 1) {
const a = ranges[i];
const b = selection.getRangeAt(i);
if (a.commonAncestorContainer !== b.commonAncestorContainer || a.startContainer !== b.startContainer || a.endContainer !== b.endContainer || a.startOffset !== b.startOffset || a.endOffset !== b.endOffset) return;
}
selection.removeAllRanges();
});
}
}
}
/**
* @template {import('@sveltejs/kit').NavigationType} T
* @param {import('./types.js').NavigationState} current
* @param {import('./types.js').NavigationIntent | undefined} intent
* @param {URL | null} url
* @param {T} type
* @param {{ x: number, y: number } | null} [target_scroll] The scroll position for the target (for popstate navigations)
*/
function create_navigation(current, intent, url, type, target_scroll = null) {
/** @type {(value: any) => void} */
let fulfil;
/** @type {(error: any) => void} */
let reject;
const complete = new Promise((f, r) => {
fulfil = f;
reject = r;
});
complete.catch(noop);
return {
navigation: {
from: {
params: current.params,
route: { id: current.route?.id ?? null },
url: current.url,
scroll: scroll_state()
},
to: url && {
params: intent?.params ?? null,
route: { id: intent?.route?.id ?? null },
url,
scroll: target_scroll
},
willUnload: !intent,
type,
complete
},
fulfil,
reject
};
}
/**
* TODO: remove this in 3.0 when the page store is also removed
*
* We need to assign a new page object so that subscribers are correctly notified.
* However, spreading `{ ...page }` returns an empty object so we manually
* assign to each property instead.
*
* @param {import('@sveltejs/kit').Page} page
*/
function clone_page(page) {
return {
data: page.data,
error: page.error,
form: page.form,
params: page.params,
route: page.route,
state: page.state,
status: page.status,
url: page.url
};
}
/**
* @param {URL} url
* @returns {string}
*/
function get_id(url) {
let id;
if (app.hash) {
const [, , second] = url.hash.split("#", 3);
id = second ?? "";
} else id = url.hash.slice(1);
return decodeURIComponent(id);
}
//#endregion
export { updated as a, page as i, stores as n, navigating as r, goto as t };