Socket
Socket
Sign inDemoInstall

@sveltejs/kit

Package Overview
Dependencies
Maintainers
4
Versions
780
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sveltejs/kit - npm Package Compare versions

Comparing version 1.15.0 to 1.22.1

src/exports/public.d.ts

34

package.json
{
"name": "@sveltejs/kit",
"version": "1.15.0",
"version": "1.22.1",
"description": "The fastest way to build Svelte apps",
"repository": {

@@ -13,6 +14,6 @@ "type": "git",

"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^2.4.1",
"@types/cookie": "^0.5.1",
"cookie": "^0.5.0",
"devalue": "^4.3.0",
"devalue": "^4.3.1",
"esm-env": "^1.0.0",

@@ -23,9 +24,8 @@ "kleur": "^4.1.5",

"sade": "^1.8.1",
"set-cookie-parser": "^2.5.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^2.0.2",
"tiny-glob": "^0.2.9",
"undici": "5.21.0"
"undici": "~5.22.0"
},
"devDependencies": {
"@playwright/test": "^1.29.2",
"@playwright/test": "1.30.0",
"@types/connect": "^3.4.35",

@@ -37,12 +37,13 @@ "@types/marked": "^4.0.7",

"@types/set-cookie-parser": "^2.4.2",
"dts-buddy": "^0.0.10",
"marked": "^4.2.3",
"rollup": "^3.7.0",
"svelte": "^3.56.0",
"svelte-preprocess": "^5.0.0",
"svelte": "^4.0.3",
"svelte-preprocess": "^5.0.4",
"typescript": "^4.9.4",
"uvu": "^0.5.6",
"vite": "^4.2.0"
"vite": "^4.3.6",
"vitest": "^0.32.2"
},
"peerDependencies": {
"svelte": "^3.54.0",
"svelte": "^3.54.0 || ^4.0.0-next.0",
"vite": "^4.0.0"

@@ -69,11 +70,15 @@ },

"./node": {
"types": "./types/index.d.ts",
"import": "./src/exports/node/index.js"
},
"./node/polyfills": {
"types": "./types/index.d.ts",
"import": "./src/exports/node/polyfills.js"
},
"./hooks": {
"types": "./types/index.d.ts",
"import": "./src/exports/hooks/index.js"
},
"./vite": {
"types": "./types/index.d.ts",
"import": "./src/exports/vite/index.js"

@@ -95,5 +100,6 @@ }

"test:cross-platform:build": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
"test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
"postinstall": "node postinstall.js"
"test:unit": "vitest --config kit.vitest.config.js run",
"postinstall": "node postinstall.js",
"generate:version": "node scripts/generate-version.js"
}
}
import fs from 'node:fs';
import path from 'node:path';
import glob from 'tiny-glob/sync.js';
import { load_config } from './src/core/config/index.js';
import { list_files } from './src/core/utils.js';
import * as sync from './src/core/sync/sync.js';

@@ -21,3 +21,3 @@

for (const directory of packages) {
directories.push(...glob(directory, { cwd }).map((dir) => path.resolve(cwd, dir)));
directories.push(...list_files(directory).map((dir) => path.resolve(cwd, dir)));
}

@@ -41,4 +41,4 @@ } else {

} catch (error) {
console.log('Error while trying to sync SvelteKit config');
console.log(error.stack);
console.error('Error while trying to sync SvelteKit config');
console.error(error);
}

@@ -48,3 +48,3 @@ }

} catch (error) {
console.error(error.stack);
console.error(error);
}

@@ -8,1 +8,13 @@ /**

export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
export const ENDPOINT_METHODS = new Set([
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS',
'HEAD'
]);
export const PAGE_METHODS = new Set(['GET', 'POST', 'HEAD']);
import { existsSync, statSync, createReadStream, createWriteStream } from 'node:fs';
import { join } from 'node:path/posix';
import { extname, join, resolve } from 'node:path';
import { pipeline } from 'node:stream';
import { promisify } from 'node:util';
import zlib from 'node:zlib';
import glob from 'tiny-glob';
import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';

@@ -13,4 +12,6 @@ import { generate_manifest } from '../generate_manifest/index.js';

import { write } from '../sync/utils.js';
import { list_files } from '../utils.js';
const pipe = promisify(pipeline);
const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm'];

@@ -28,3 +29,3 @@ /**

* }} opts
* @returns {import('types').Builder}
* @returns {import('@sveltejs/kit').Builder}
*/

@@ -40,3 +41,3 @@ export function create_builder({

}) {
/** @type {Map<import('types').RouteDefinition, import('types').RouteData>} */
/** @type {Map<import('@sveltejs/kit').RouteDefinition, import('types').RouteData>} */
const lookup = new Map();

@@ -53,3 +54,3 @@

/** @type {import('types').RouteDefinition} */
/** @type {import('@sveltejs/kit').RouteDefinition} */
const facade = {

@@ -90,11 +91,8 @@ id: route.id,

const files = await glob('**/*.{html,js,mjs,json,css,svg,xml,wasm}', {
cwd: directory,
dot: true,
absolute: true,
filesOnly: true
});
const files = list_files(directory, (file) => extensions.includes(extname(file))).map(
(file) => resolve(directory, file)
);
await Promise.all(
files.map((file) => Promise.all([compress_file(file, 'gz'), compress_file(file, 'br')]))
files.flatMap((file) => [compress_file(file, 'gz'), compress_file(file, 'br')])
);

@@ -170,3 +168,3 @@ },

? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
: route_data
: route_data.filter((route) => prerender_map.get(route.id) !== true)
});

@@ -173,0 +171,0 @@ },

@@ -76,3 +76,3 @@ import fs from 'node:fs';

/**
* @param {import('types').Config} config
* @param {import('@sveltejs/kit').Config} config
* @returns {import('types').ValidatedConfig}

@@ -99,3 +99,3 @@ */

/**
* @param {import('types').Config} config
* @param {import('@sveltejs/kit').Config} config
* @returns {import('types').ValidatedConfig}

@@ -102,0 +102,0 @@ */

@@ -114,2 +114,7 @@ import { join } from 'node:path';

dangerZone: object({
// TODO 2.0: Remove this
trackServerFetches: boolean(false)
}),
embedded: boolean(false),

@@ -119,3 +124,4 @@

dir: string(process.cwd()),
publicPrefix: string('PUBLIC_')
publicPrefix: string('PUBLIC_'),
privatePrefix: string('')
}),

@@ -206,14 +212,44 @@

handleHttpError: validate('fail', (input, keypath) => {
if (typeof input === 'function') return input;
if (['fail', 'warn', 'ignore'].includes(input)) return input;
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
}),
handleHttpError: validate(
(/** @type {any} */ { message }) => {
throw new Error(
message +
'\nTo suppress or handle this error, implement `handleHttpError` in https://kit.svelte.dev/docs/configuration#prerender'
);
},
(input, keypath) => {
if (typeof input === 'function') return input;
if (['fail', 'warn', 'ignore'].includes(input)) return input;
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
}
),
handleMissingId: validate('fail', (input, keypath) => {
if (typeof input === 'function') return input;
if (['fail', 'warn', 'ignore'].includes(input)) return input;
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
}),
handleMissingId: validate(
(/** @type {any} */ { message }) => {
throw new Error(
message +
'\nTo suppress or handle this error, implement `handleMissingId` in https://kit.svelte.dev/docs/configuration#prerender'
);
},
(input, keypath) => {
if (typeof input === 'function') return input;
if (['fail', 'warn', 'ignore'].includes(input)) return input;
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
}
),
handleEntryGeneratorMismatch: validate(
(/** @type {any} */ { message }) => {
throw new Error(
message +
'\nTo suppress or handle this error, implement `handleEntryGeneratorMismatch` in https://kit.svelte.dev/docs/configuration#prerender'
);
},
(input, keypath) => {
if (typeof input === 'function') return input;
if (['fail', 'warn', 'ignore'].includes(input)) return input;
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
}
),
origin: validate('http://sveltekit-prerender', (input, keypath) => {

@@ -220,0 +256,0 @@ assert_string(input, keypath);

@@ -66,6 +66,9 @@ import { GENERATED_COMMENT } from '../constants.js';

* @param {import('types').Env} env
* @param {string} prefix
* @param {{
* public_prefix: string;
* private_prefix: string;
* }} prefixes
* @returns {string}
*/
export function create_dynamic_types(id, env, prefix) {
export function create_dynamic_types(id, env, { public_prefix, private_prefix }) {
const properties = Object.keys(env[id])

@@ -75,9 +78,15 @@ .filter((k) => valid_identifier.test(k))

const prefixed = `[key: \`${prefix}\${string}\`]`;
const public_prefixed = `[key: \`${public_prefix}\${string}\`]`;
const private_prefixed = `[key: \`${private_prefix}\${string}\`]`;
if (id === 'private') {
properties.push(`${prefixed}: undefined;`);
properties.push(`[key: string]: string | undefined;`);
if (public_prefix) {
properties.push(`${public_prefixed}: undefined;`);
}
properties.push(`${private_prefixed}: string | undefined;`);
} else {
properties.push(`${prefixed}: string | undefined;`);
if (private_prefix) {
properties.push(`${private_prefixed}: undefined;`);
}
properties.push(`${public_prefixed}: string | undefined;`);
}

@@ -84,0 +93,0 @@

@@ -46,15 +46,4 @@ import { s } from '../../utils/misc.js';

/** @typedef {{ index: number, path: string }} LookupEntry */
/** @type {Map<import('types').PageNode, LookupEntry>} */
const bundled_nodes = new Map();
build_data.manifest_data.nodes.forEach((node, i) => {
bundled_nodes.set(node, {
path: join_relative(relative_path, `/nodes/${i}.js`),
index: i
});
});
/** @type {(path: string) => string} */
const loader = (path) => `() => import('${path}')`;
const loader = (path) => `__memo(() => import('${path}'))`;

@@ -70,12 +59,8 @@ const assets = build_data.manifest_data.assets.map((asset) => asset.file);

function get_nodes(indexes) {
let string = indexes.map((n) => reindexed.get(n) ?? '').join(',');
const string = indexes.map((n) => reindexed.get(n) ?? '').join(',');
if (indexes.at(-1) === undefined) {
// since JavaScript ignores trailing commas, we need to insert a dummy
// comma so that the array has the correct length if the last item
// is undefined
string += ',';
}
return `[${string}]`;
// since JavaScript ignores trailing commas, we need to insert a dummy
// comma so that the array has the correct length if the last item
// is undefined
return `[${string},]`;
}

@@ -85,4 +70,4 @@

// String representation of
/** @type {import('types').SSRManifest} */
return dedent`
/** @template {import('@sveltejs/kit').SSRManifest} T */
const manifest_expr = dedent`
{

@@ -100,2 +85,4 @@ appDir: ${s(build_data.app_dir)},

${routes.map(route => {
if (!route.page && !route.endpoint) return;
route.params.forEach(param => {

@@ -105,4 +92,2 @@ if (param.matcher) matchers.add(param.matcher);

if (!route.page && !route.endpoint) return;
return dedent`

@@ -120,3 +105,6 @@ {

matchers: async () => {
${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n')}
${Array.from(
matchers,
type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`
).join('\n')}
return { ${Array.from(matchers).join(', ')} };

@@ -127,2 +115,15 @@ }

`;
// Memoize the loaders to prevent Node from doing unnecessary work
// on every dynamic import call
return dedent`
(() => {
function __memo(fn) {
let value;
return () => value ??= (value = fn());
}
return ${manifest_expr}
})()
`;
}

@@ -5,3 +5,5 @@ import { join } from 'node:path';

import {
validate_common_exports,
validate_layout_exports,
validate_layout_server_exports,
validate_page_exports,
validate_page_server_exports,

@@ -14,2 +16,5 @@ validate_server_exports

import { installPolyfills } from '../../exports/node/polyfills.js';
import { resolvePath } from '../../exports/index.js';
import { ENDPOINT_METHODS } from '../../constants.js';
import { filter_private_env, filter_public_env } from '../../utils/env.js';

@@ -25,3 +30,3 @@ export default forked(import.meta.url, analyse);

async function analyse({ manifest_path, env }) {
/** @type {import('types').SSRManifest} */
/** @type {import('@sveltejs/kit').SSRManifest} */
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;

@@ -46,6 +51,5 @@

// set env, in case it's used in initialisation
const entries = Object.entries(env);
const prefix = config.env.publicPrefix;
internal.set_private_env(Object.fromEntries(entries.filter(([k]) => !k.startsWith(prefix))));
internal.set_public_env(Object.fromEntries(entries.filter(([k]) => k.startsWith(prefix))));
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
internal.set_private_env(filter_private_env(env, { public_prefix, private_prefix }));
internal.set_public_env(filter_public_env(env, { public_prefix, private_prefix }));

@@ -79,2 +83,4 @@ /** @type {import('types').ServerMetadata} */

let config = undefined;
/** @type {import('types').PrerenderEntryGenerator | undefined} */
let entries = undefined;

@@ -95,10 +101,10 @@ if (route.endpoint) {

if (mod.GET) api_methods.push('GET');
if (mod.POST) api_methods.push('POST');
if (mod.PUT) api_methods.push('PUT');
if (mod.PATCH) api_methods.push('PATCH');
if (mod.DELETE) api_methods.push('DELETE');
if (mod.OPTIONS) api_methods.push('OPTIONS');
Object.values(mod).forEach((/** @type {import('types').HttpMethod} */ method) => {
if (mod[method] && ENDPOINT_METHODS.has(method)) {
api_methods.push(method);
}
});
config = mod.config;
entries = mod.entries;
}

@@ -118,4 +124,4 @@

if (layout) {
validate_common_exports(layout.server, layout.server_id);
validate_common_exports(layout.universal, layout.universal_id);
validate_layout_server_exports(layout.server, layout.server_id);
validate_layout_exports(layout.universal, layout.universal_id);
}

@@ -129,3 +135,3 @@ }

validate_page_server_exports(page.server, page.server_id);
validate_common_exports(page.universal, page.universal_id);
validate_page_exports(page.universal, page.universal_id);
}

@@ -136,2 +142,3 @@

config = get_config(nodes);
entries ??= get_option(nodes, 'entries');
}

@@ -148,3 +155,5 @@

},
prerender
prerender,
entries:
entries && (await entries()).map((entry_object) => resolvePath(route.id, entry_object))
});

@@ -151,0 +160,0 @@ }

@@ -16,2 +16,16 @@ import { resolve } from '../../utils/url.js';

const CRAWLABLE_META_NAME_ATTRS = new Set([
'og:url',
'og:image',
'og:image:url',
'og:image:secure_url',
'og:video',
'og:video:url',
'og:video:secure_url',
'og:audio',
'og:audio:url',
'og:audio:secure_url',
'twitter:image'
]);
/**

@@ -85,2 +99,5 @@ * @param {string} html

/** @type {Record<string, string>} */
const attributes = {};
if (tag === 'SCRIPT' || tag === 'STYLE') {

@@ -100,5 +117,2 @@ while (i < html.length) {

let href = '';
let rel = '';
while (i < html.length) {

@@ -165,40 +179,3 @@ const start = i;

value = decode(value);
if (name === 'href') {
if (tag === 'BASE') {
base = resolve(base, value);
} else {
href = resolve(base, value);
}
} else if (name === 'id') {
ids.push(value);
} else if (name === 'name') {
if (tag === 'A') ids.push(value);
} else if (name === 'rel') {
rel = value;
} else if (name === 'src') {
if (value) hrefs.push(resolve(base, value));
} else if (name === 'srcset') {
const candidates = [];
let insideURL = true;
value = value.trim();
for (let i = 0; i < value.length; i++) {
if (
value[i] === ',' &&
(!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))
) {
candidates.push(value.slice(0, i));
value = value.substring(i + 1).trim();
i = 0;
insideURL = true;
} else if (WHITESPACE.test(value[i])) {
insideURL = false;
}
}
candidates.push(value);
for (const candidate of candidates) {
const src = candidate.split(WHITESPACE)[0];
if (src) hrefs.push(resolve(base, src));
}
}
attributes[name] = value;
} else {

@@ -212,5 +189,53 @@ i -= 1;

if (href && !/\bexternal\b/i.test(rel)) {
hrefs.push(resolve(base, href));
const { href, id, name, property, rel, src, srcset, content } = attributes;
if (href) {
if (tag === 'BASE') {
base = resolve(base, href);
} else if (!rel || !/\bexternal\b/i.test(rel)) {
hrefs.push(resolve(base, href));
}
}
if (id) {
ids.push(id);
}
if (name && tag === 'A') {
ids.push(name);
}
if (src) {
hrefs.push(resolve(base, src));
}
if (srcset) {
let value = srcset;
const candidates = [];
let insideURL = true;
value = value.trim();
for (let i = 0; i < value.length; i++) {
if (value[i] === ',' && (!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))) {
candidates.push(value.slice(0, i));
value = value.substring(i + 1).trim();
i = 0;
insideURL = true;
} else if (WHITESPACE.test(value[i])) {
insideURL = false;
}
}
candidates.push(value);
for (const candidate of candidates) {
const src = candidate.split(WHITESPACE)[0];
if (src) hrefs.push(resolve(base, src));
}
}
if (tag === 'META' && content) {
const attr = name ?? property;
if (attr && CRAWLABLE_META_NAME_ATTRS.has(attr)) {
hrefs.push(resolve(base, content));
}
}
}

@@ -217,0 +242,0 @@ }

@@ -30,3 +30,3 @@ import { readFileSync } from 'node:fs';

/** @type {import('types').SSRManifest} */
/** @type {import('@sveltejs/kit').SSRManifest} */
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;

@@ -33,0 +33,0 @@

@@ -1,2 +0,2 @@

import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';

@@ -15,2 +15,3 @@ import { pathToFileURL } from 'node:url';

import { forked } from '../../utils/fork.js';
import * as devalue from 'devalue';

@@ -29,3 +30,3 @@ export default forked(import.meta.url, prerender);

async function prerender({ out, manifest_path, metadata, verbose, env }) {
/** @type {import('types').SSRManifest} */
/** @type {import('@sveltejs/kit').SSRManifest} */
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;

@@ -132,2 +133,10 @@

const handle_entry_generator_mismatch = normalise_error_handler(
log,
config.prerender.handleEntryGeneratorMismatch,
({ generatedFromId, entry, matchedId }) => {
return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId} - see the \`handleEntryGeneratorMismatch\` option in https://kit.svelte.dev/docs/configuration#prerender for more info.`;
}
);
const q = queue(config.prerender.concurrency);

@@ -170,4 +179,5 @@

* @param {string} [encoded]
* @param {string} [generated_from_id]
*/
function enqueue(referrer, decoded, encoded) {
function enqueue(referrer, decoded, encoded, generated_from_id) {
if (seen.has(decoded)) return;

@@ -179,3 +189,3 @@ seen.add(decoded);

return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer));
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id));
}

@@ -187,4 +197,5 @@

* @param {string?} referrer
* @param {string} [generated_from_id]
*/
async function visit(decoded, encoded, referrer) {
async function visit(decoded, encoded, referrer, generated_from_id) {
if (!decoded.startsWith(config.paths.base)) {

@@ -215,2 +226,16 @@ handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' });

const encoded_id = response.headers.get('x-sveltekit-routeid');
const decoded_id = encoded_id && decode_uri(encoded_id);
if (
decoded_id !== null &&
generated_from_id !== undefined &&
decoded_id !== generated_from_id
) {
handle_entry_generator_mismatch({
generatedFromId: generated_from_id,
entry: decoded,
matchedId: decoded_id
});
}
const body = Buffer.from(await response.arrayBuffer());

@@ -326,3 +351,7 @@

dest,
`<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
`<script>location.href=${devalue.uneval(
location
)};</script><meta http-equiv="refresh" content=${escape_html_attr(
`0;url=${location}`
)}>`
);

@@ -349,4 +378,19 @@

if (response.status === 200) {
mkdirp(dirname(dest));
if (existsSync(dest) && statSync(dest).isDirectory()) {
throw new Error(
`Cannot save ${decoded} as it is already a directory. See https://kit.svelte.dev/docs/page-options#prerender-route-conflicts for more information`
);
}
const dir = dirname(dest);
if (existsSync(dir) && !statSync(dir).isDirectory()) {
const parent = decoded.split('/').slice(0, -1).join('/');
throw new Error(
`Cannot save ${decoded} as ${parent} is already a file. See https://kit.svelte.dev/docs/page-options#prerender-route-conflicts for more information`
);
}
mkdirp(dir);
log.info(`${response.status} ${decoded}`);

@@ -375,5 +419,14 @@ writeFileSync(dest, body);

/** @type {Array<{ id: string, entries: Array<string>}>} */
const route_level_entries = [];
for (const [id, { entries }] of metadata.routes.entries()) {
if (entries) {
route_level_entries.push({ id, entries });
}
}
if (
config.prerender.entries.length > 1 ||
config.prerender.entries[0] !== '*' ||
route_level_entries.length > 0 ||
prerender_map.size > 0

@@ -399,2 +452,8 @@ ) {

for (const { id, entries } of route_level_entries) {
for (const entry of entries) {
enqueue(null, config.paths.base + entry, undefined, id);
}
}
await q.done();

@@ -401,0 +460,0 @@

import fs from 'node:fs';
import path from 'node:path';
import mime from 'mime';
import { runtime_directory } from '../../utils.js';
import { list_files, runtime_directory } from '../../utils.js';
import { posixify } from '../../../utils/filesystem.js';

@@ -230,2 +230,14 @@ import { parse_route_id } from '../../../utils/routing.js';

/**
* @param {string} type
* @param {string} existing_file
*/
function duplicate_files_error(type, existing_file) {
return new Error(
`Multiple ${type} files found in ${routes_base}${route.id} : ${path.basename(
existing_file
)} and ${file.name}`
);
}
if (item.kind === 'component') {

@@ -238,7 +250,17 @@ if (item.is_error) {

} else if (item.is_layout) {
if (!route.layout) route.layout = { depth, child_pages: [] };
if (!route.layout) {
route.layout = { depth, child_pages: [] };
} else if (route.layout.component) {
throw duplicate_files_error('layout component', route.layout.component);
}
route.layout.component = project_relative;
if (item.uses_layout !== undefined) route.layout.parent_id = item.uses_layout;
} else {
if (!route.leaf) route.leaf = { depth };
if (!route.leaf) {
route.leaf = { depth };
} else if (route.leaf.component) {
throw duplicate_files_error('page component', route.leaf.component);
}
route.leaf.component = project_relative;

@@ -248,8 +270,28 @@ if (item.uses_layout !== undefined) route.leaf.parent_id = item.uses_layout;

} else if (item.is_layout) {
if (!route.layout) route.layout = { depth, child_pages: [] };
if (!route.layout) {
route.layout = { depth, child_pages: [] };
} else if (route.layout[item.kind]) {
throw duplicate_files_error(
item.kind + ' layout module',
/** @type {string} */ (route.layout[item.kind])
);
}
route.layout[item.kind] = project_relative;
} else if (item.is_page) {
if (!route.leaf) route.leaf = { depth };
if (!route.leaf) {
route.leaf = { depth };
} else if (route.leaf[item.kind]) {
throw duplicate_files_error(
item.kind + ' page module',
/** @type {string} */ (route.leaf[item.kind])
);
}
route.leaf[item.kind] = project_relative;
} else {
if (route.endpoint) {
throw duplicate_files_error('endpoint', route.endpoint.file);
}
route.endpoint = {

@@ -420,3 +462,3 @@ file: project_relative

const kind = !!(match[1] || match[4] || match[7]) ? 'server' : 'universal';
const kind = match[1] || match[4] || match[7] ? 'server' : 'universal';

@@ -433,24 +475,2 @@ return {

/** @param {string} dir */
function list_files(dir) {
/** @type {string[]} */
const files = [];
/** @param {string} current */
function walk(current) {
for (const file of fs.readdirSync(path.resolve(dir, current))) {
const child = path.posix.join(current, file);
if (fs.statSync(path.resolve(dir, child)).isDirectory()) {
walk(child);
} else {
files.push(child);
}
}
}
if (fs.existsSync(dir)) walk('');
return files;
}
/**

@@ -479,3 +499,3 @@ * @param {string} needle

// find all permutations created by optional parameters
const split = normalized.split(/<\?(.+?)\>/g);
const split = normalized.split(/<\?(.+?)>/g);

@@ -482,0 +502,0 @@ let permutations = [/** @type {string} */ (split[0])];

@@ -11,3 +11,3 @@ import fs from 'node:fs';

// inside either `packages/kit` or `kit.svelte.dev`
const descriptions_dir = fileURLToPath(new URL('../../../types/synthetic', import.meta.url));
const descriptions_dir = fileURLToPath(new URL('../../../src/types/synthetic', import.meta.url));

@@ -26,5 +26,8 @@ /** @param {string} filename */

* @param {import('types').Env} env
* @param {string} prefix
* @param {{
* public_prefix: string;
* private_prefix: string;
* }} prefixes
*/
const template = (env, prefix) => `
const template = (env, prefixes) => `
${GENERATED_COMMENT}

@@ -41,6 +44,6 @@

${read_description('$env+dynamic+private.md')}
${create_dynamic_types('private', env, prefix)}
${create_dynamic_types('private', env, prefixes)}
${read_description('$env+dynamic+public.md')}
${create_dynamic_types('public', env, prefix)}
${create_dynamic_types('public', env, prefixes)}
`;

@@ -57,7 +60,8 @@

const env = get_env(config.env, mode);
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
write_if_changed(
path.join(config.outDir, 'ambient.d.ts'),
template(env, config.env.publicPrefix)
template(env, { public_prefix, private_prefix })
);
}

@@ -24,3 +24,3 @@ import { relative_path, resolve_entry } from '../../utils/filesystem.js';

`import * as universal from ${s(relative_path(`${output}/nodes`, node.universal))};`,
`export { universal };`
'export { universal };'
);

@@ -27,0 +27,0 @@ }

@@ -37,4 +37,6 @@ import fs from 'node:fs';

csrf_check_origin: ${s(config.kit.csrf.checkOrigin)},
track_server_fetches: ${s(config.kit.dangerZone.trackServerFetches)},
embedded: ${config.kit.embedded},
env_public_prefix: '${config.kit.env.publicPrefix}',
env_private_prefix: '${config.kit.env.privatePrefix}',
hooks: null, // added lazily, via \`get_hooks\`

@@ -78,3 +80,3 @@ preload_strategy: ${s(config.kit.output.preloadStrategy)},

export function write_server(config, output) {
// TODO the casting shouldn't be necessary — investigate
// TODO the casting shouldn't be necessary — investigate
const hooks_file = /** @type {string} */ (resolve_entry(config.kit.files.hooks.server));

@@ -81,0 +83,0 @@

@@ -55,3 +55,3 @@ import fs from 'node:fs';

const has_meta_data = fs.existsSync(meta_data_file);
let meta_data = has_meta_data
const meta_data = has_meta_data
? /** @type {Record<string, string[]>} */ (JSON.parse(fs.readFileSync(meta_data_file, 'utf-8')))

@@ -181,3 +181,3 @@ : {};

// now generate new types
const imports = [`import type * as Kit from '@sveltejs/kit';`];
const imports = ["import type * as Kit from '@sveltejs/kit';"];

@@ -199,2 +199,8 @@ /** @type {string[]} */

if (route.params.length > 0) {
exports.push(
'export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;'
);
}
declarations.push(`type RouteId = '${route.id}';`);

@@ -206,20 +212,20 @@

// If T extends the empty object, void is also allowed as a return type
`type MaybeWithVoid<T> = {} extends T ? T | void : T;`,
'type MaybeWithVoid<T> = {} extends T ? T | void : T;',
// Returns the key of the object whose values are required.
`export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];`,
'export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];',
// Helper type to get the correct output type for load functions. It should be passed the parent type to check what types from App.PageData are still required.
// If none, void is also allowed as a return type.
`type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>`,
'type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>',
// null & {} == null, we need to prevent that in some situations
`type EnsureDefined<T> = T extends null | undefined ? {} : T;`,
'type EnsureDefined<T> = T extends null | undefined ? {} : T;',
// Takes a union type and returns a union type where each type also has all properties
// of all possible types (typed as undefined), making accessing them more ergonomic
`type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;`,
'type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;',
// Re-export `Snapshot` from @sveltejs/kit — in future we could use this to infer <T> from the return type of `snapshot.capture`
`export type Snapshot<T = any> = Kit.Snapshot<T>;`
'export type Snapshot<T = any> = Kit.Snapshot<T>;'
);

@@ -256,6 +262,6 @@ }

exports.push(
`export type Action<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Action<RouteParams, OutputData, RouteId>`
'export type Action<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Action<RouteParams, OutputData, RouteId>'
);
exports.push(
`export type Actions<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Actions<RouteParams, OutputData, RouteId>`
'export type Actions<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Actions<RouteParams, OutputData, RouteId>'
);

@@ -328,7 +334,7 @@ }

if (route.endpoint) {
exports.push(`export type RequestHandler = Kit.RequestHandler<RouteParams, RouteId>;`);
exports.push('export type RequestHandler = Kit.RequestHandler<RouteParams, RouteId>;');
}
if (route.leaf?.server || route.layout?.server || route.endpoint) {
exports.push(`export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;`);
exports.push('export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;');
}

@@ -389,3 +395,3 @@

node.universal || (!is_page && all_pages_have_load)
? `Partial<App.PageData> & Record<string, any> | void`
? 'Partial<App.PageData> & Record<string, any> | void'
: `OutputDataShape<${parent_type}>`;

@@ -408,11 +414,11 @@ exports.push(

exports.push(
`type ExcludeActionFailure<T> = T extends Kit.ActionFailure<any> ? never : T extends void ? never : T;`,
`type ActionsSuccess<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: ExcludeActionFailure<Awaited<ReturnType<T[Key]>>>; }[keyof T];`,
`type ExtractActionFailure<T> = T extends Kit.ActionFailure<infer X> ? X extends void ? never : X : never;`,
`type ActionsFailure<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: Exclude<ExtractActionFailure<Awaited<ReturnType<T[Key]>>>, void>; }[keyof T];`,
'type ExcludeActionFailure<T> = T extends Kit.ActionFailure<any> ? never : T extends void ? never : T;',
'type ActionsSuccess<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: ExcludeActionFailure<Awaited<ReturnType<T[Key]>>>; }[keyof T];',
'type ExtractActionFailure<T> = T extends Kit.ActionFailure<infer X> ? X extends void ? never : X : never;',
'type ActionsFailure<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: Exclude<ExtractActionFailure<Awaited<ReturnType<T[Key]>>>, void>; }[keyof T];',
`type ActionsExport = typeof import('${from}').actions`,
`export type SubmitFunction = Kit.SubmitFunction<Expand<ActionsSuccess<ActionsExport>>, Expand<ActionsFailure<ActionsExport>>>`
'export type SubmitFunction = Kit.SubmitFunction<Expand<ActionsSuccess<ActionsExport>>, Expand<ActionsFailure<ActionsExport>>>'
);
type = `Expand<Kit.AwaitedActions<ActionsExport>> | null`;
type = 'Expand<Kit.AwaitedActions<ActionsExport>> | null';
}

@@ -445,3 +451,3 @@ exports.push(`export type ActionData = ${type};`);

!is_page && all_pages_have_load
? `Partial<App.PageData> & Record<string, any> | void`
? 'Partial<App.PageData> & Record<string, any> | void'
: `OutputDataShape<${parent_type}>`;

@@ -694,3 +700,3 @@ exports.push(

let a = declaration.type.pos;
let b = declaration.type.end;
const b = declaration.type.end;
while (/\s/.test(content[a])) a += 1;

@@ -767,3 +773,3 @@

let a = declaration.type.pos;
let b = declaration.type.end;
const b = declaration.type.end;
while (/\s/.test(content[a])) a += 1;

@@ -796,3 +802,3 @@

arg.name.end,
`: import('./$types').RequestEvent` + (add_parens ? ')' : '')
": import('./$types').RequestEvent" + (add_parens ? ')' : '')
);

@@ -799,0 +805,0 @@ }

@@ -0,1 +1,2 @@

import fs from 'node:fs';
import path from 'node:path';

@@ -59,1 +60,28 @@ import { fileURLToPath } from 'node:url';

}
/**
* @param {string} dir
* @param {(file: string) => boolean} [filter]
*/
export function list_files(dir, filter) {
/** @type {string[]} */
const files = [];
/** @param {string} current */
function walk(current) {
for (const file of fs.readdirSync(path.resolve(dir, current))) {
const child = path.posix.join(current, file);
if (fs.statSync(path.resolve(dir, child)).isDirectory()) {
walk(child);
} else {
if (!filter || filter(child)) {
files.push(child);
}
}
}
}
if (fs.existsSync(dir)) walk('');
return files;
}
/**
* @param {...import('types').Handle} handlers
* @returns {import('types').Handle}
* A helper function for sequencing multiple `handle` calls in a middleware-like manner.
* The behavior for the `handle` options is as follows:
* - `transformPageChunk` is applied in reverse order and merged
* - `preload` is applied in forward order, the first option "wins" and no `preload` options after it are called
* - `filterSerializedResponseHeaders` behaves the same as `preload`
*
* ```js
* /// file: src/hooks.server.js
* import { sequence } from '@sveltejs/kit/hooks';
*
* /// type: import('@sveltejs/kit').Handle
* async function first({ event, resolve }) {
* console.log('first pre-processing');
* const result = await resolve(event, {
* transformPageChunk: ({ html }) => {
* // transforms are applied in reverse order
* console.log('first transform');
* return html;
* },
* preload: () => {
* // this one wins as it's the first defined in the chain
* console.log('first preload');
* }
* });
* console.log('first post-processing');
* return result;
* }
*
* /// type: import('@sveltejs/kit').Handle
* async function second({ event, resolve }) {
* console.log('second pre-processing');
* const result = await resolve(event, {
* transformPageChunk: ({ html }) => {
* console.log('second transform');
* return html;
* },
* preload: () => {
* console.log('second preload');
* },
* filterSerializedResponseHeaders: () => {
* // this one wins as it's the first defined in the chain
* console.log('second filterSerializedResponseHeaders');
* }
* });
* console.log('second post-processing');
* return result;
* }
*
* export const handle = sequence(first, second);
* ```
*
* The example above would print:
*
* ```
* first pre-processing
* first preload
* second pre-processing
* second filterSerializedResponseHeaders
* second transform
* first transform
* second post-processing
* first post-processing
* ```
*
* @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions
* @returns {import('@sveltejs/kit').Handle}
*/

@@ -14,4 +78,4 @@ export function sequence(...handlers) {

* @param {number} i
* @param {import('types').RequestEvent} event
* @param {import('types').ResolveOptions | undefined} parent_options
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('@sveltejs/kit').ResolveOptions | undefined} parent_options
* @returns {import('types').MaybePromise<Response>}

@@ -25,3 +89,3 @@ */

resolve: (event, options) => {
/** @param {{ html: string, done: boolean }} opts */
/** @type {import('@sveltejs/kit').ResolveOptions['transformPageChunk']} */
const transformPageChunk = async ({ html, done }) => {

@@ -39,5 +103,17 @@ if (options?.transformPageChunk) {

/** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */
const filterSerializedResponseHeaders =
parent_options?.filterSerializedResponseHeaders ??
options?.filterSerializedResponseHeaders;
/** @type {import('@sveltejs/kit').ResolveOptions['preload']} */
const preload = parent_options?.preload ?? options?.preload;
return i < length - 1
? apply_handle(i + 1, event, { transformPageChunk })
: resolve(event, { transformPageChunk });
? apply_handle(i + 1, event, {
transformPageChunk,
filterSerializedResponseHeaders,
preload
})
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
}

@@ -44,0 +120,0 @@ });

import { HttpError, Redirect, ActionFailure } from '../runtime/control.js';
import { BROWSER, DEV } from 'esm-env';
import { get_route_segments } from '../utils/routing.js';
// For some reason we need to type the params as well here,
// JSdoc doesn't seem to like @type with function overloads
export { VERSION } from '../version.js';
/**
* @type {import('@sveltejs/kit').error}
* @overload
* @param {number} status
* @param {any} message
* @param {App.Error} body
* @return {HttpError}
*/
export function error(status, message) {
/**
* @overload
* @param {number} status
* @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} [body]
* @return {HttpError}
*/
/**
* Creates an `HttpError` object with an HTTP status code and an optional message.
* This object, if thrown during request handling, will cause SvelteKit to
* return an error response without invoking `handleError`.
* Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
*/
export function error(status, body) {
if ((!BROWSER || DEV) && (isNaN(status) || status < 400 || status > 599)) {

@@ -16,6 +34,11 @@ throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);

return new HttpError(status, message);
return new HttpError(status, body);
}
/** @type {import('@sveltejs/kit').redirect} */
/**
* Create a `Redirect` object. If thrown during request handling, SvelteKit will return a redirect response.
* Make sure you're not catching the thrown redirect, which would prevent SvelteKit from handling it.
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages). Must be in the range 300-308.
* @param {string} location The location to redirect to.
*/
export function redirect(status, location) {

@@ -29,3 +52,7 @@ if ((!BROWSER || DEV) && (isNaN(status) || status < 300 || status > 308)) {

/** @type {import('@sveltejs/kit').json} */
/**
* Create a JSON `Response` object from the supplied data.
* @param {any} data The value that will be serialized as JSON.
* @param {ResponseInit} [init] Options such as `status` and `headers` that will be added to the response. `Content-Type: application/json` and `Content-Length` headers will be added automatically.
*/
export function json(data, init) {

@@ -56,7 +83,16 @@ // TODO deprecate this in favour of `Response.json` when it's

/** @type {import('@sveltejs/kit').text} */
/**
* Create a `Response` object from the supplied body.
* @param {string} body The value that will be used as-is.
* @param {ResponseInit} [init] Options such as `status` and `headers` that will be added to the response. A `Content-Length` header will be added automatically.
*/
export function text(body, init) {
const headers = new Headers(init?.headers);
if (!headers.has('content-length')) {
headers.set('content-length', encoder.encode(body).byteLength.toString());
const encoded = encoder.encode(body);
headers.set('content-length', encoded.byteLength.toString());
return new Response(encoded, {
...init,
headers
});
}

@@ -71,5 +107,7 @@

/**
* Generates an `ActionFailure` object.
* @param {number} status
* @param {Record<string, any> | undefined} [data]
* Create an `ActionFailure` object.
* @template {Record<string, unknown> | undefined} [T=undefined]
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* @param {T} [data] Data associated with the failure (e.g. validation errors)
* @returns {ActionFailure<T>}
*/

@@ -79,1 +117,47 @@ export function fail(status, data) {

}
const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
/**
* Populate a route ID with params to resolve a pathname.
* @example
* ```js
* resolvePath(
* `/blog/[slug]/[...somethingElse]`,
* {
* slug: 'hello-world',
* somethingElse: 'something/else'
* }
* ); // `/blog/hello-world/something/else`
* ```
* @param {string} id
* @param {Record<string, string | undefined>} params
* @returns {string}
*/
export function resolvePath(id, params) {
const segments = get_route_segments(id);
return (
'/' +
segments
.map((segment) =>
segment.replace(basic_param_pattern, (_, optional, rest, name) => {
const param_value = params[name];
// This is nested so TS correctly narrows the type
if (!param_value) {
if (optional) return '';
if (rest && param_value !== undefined) return '';
throw new Error(`Missing parameter '${name}' in route ${id}`);
}
if (param_value.startsWith('/') || param_value.endsWith('/'))
throw new Error(
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
);
return param_value;
})
)
.filter(Boolean)
.join('/')
);
}

@@ -95,3 +95,10 @@ import * as set_cookie_parser from 'set-cookie-parser';

/** @type {import('@sveltejs/kit/node').getRequest} */
/**
* @param {{
* request: import('http').IncomingMessage;
* base: string;
* bodySizeLimit?: number;
* }} options
* @returns {Promise<Request>}
*/
export async function getRequest({ request, base, bodySizeLimit }) {

@@ -107,15 +114,27 @@ return new Request(base + request.url, {

/** @type {import('@sveltejs/kit/node').setResponse} */
/**
* @param {import('http').ServerResponse} res
* @param {Response} response
* @returns {Promise<void>}
*/
export async function setResponse(res, response) {
const headers = Object.fromEntries(response.headers);
if (response.headers.has('set-cookie')) {
const header = /** @type {string} */ (response.headers.get('set-cookie'));
const split = set_cookie_parser.splitCookiesString(header);
// @ts-expect-error
headers['set-cookie'] = split;
for (const [key, value] of response.headers) {
try {
res.setHeader(
key,
key === 'set-cookie'
? set_cookie_parser.splitCookiesString(
// This is absurd but necessary, TODO: investigate why
/** @type {string}*/ (response.headers.get(key))
)
: value
);
} catch (error) {
res.getHeaderNames().forEach((name) => res.removeHeader(name));
res.writeHead(500).end(String(error));
return;
}
}
res.writeHead(response.status, headers);
res.writeHead(response.status);

@@ -128,7 +147,6 @@ if (!response.body) {

if (response.body.locked) {
res.write(
res.end(
'Fatal error: Response body is locked. ' +
`This can happen when the response was already read (for example through 'response.json()' or 'response.text()').`
"This can happen when the response was already read (for example through 'response.json()' or 'response.text()')."
);
res.end();
return;

@@ -135,0 +153,0 @@ }

@@ -25,2 +25,10 @@ import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web';

// TODO: remove this once we only support Node 18.11+ (the version multipart/form-data was added)
/**
* Make various web APIs available as globals:
* - `crypto`
* - `fetch`
* - `Headers`
* - `Request`
* - `Response`
*/
export function installPolyfills() {

@@ -27,0 +35,0 @@ for (const name in globals) {

@@ -5,2 +5,3 @@ import fs from 'node:fs';

import { s } from '../../../utils/misc.js';
import { normalizePath } from 'vite';

@@ -52,13 +53,7 @@ /**

if (node.component && client_manifest) {
const entry = find_deps(client_manifest, node.component, true);
imported.push(...entry.imports);
stylesheets.push(...entry.stylesheets);
fonts.push(...entry.fonts);
exports.push(
`export const component = async () => (await import('../${
'let component_cache;',
`export const component = async () => component_cache ??= (await import('../${
resolve_symlinks(server_manifest, node.component).chunk.file
}')).default;`,
`export const file = '${entry.file}';` // TODO what is this?
}')).default;`
);

@@ -68,12 +63,4 @@ }

if (node.universal) {
if (client_manifest) {
const entry = find_deps(client_manifest, node.universal, true);
imported.push(...entry.imports);
stylesheets.push(...entry.stylesheets);
fonts.push(...entry.fonts);
}
imports.push(`import * as universal from '../${server_manifest[node.universal].file}';`);
exports.push(`export { universal };`);
exports.push('export { universal };');
exports.push(`export const universal_id = ${s(node.universal)};`);

@@ -84,6 +71,18 @@ }

imports.push(`import * as server from '../${server_manifest[node.server].file}';`);
exports.push(`export { server };`);
exports.push('export { server };');
exports.push(`export const server_id = ${s(node.server)};`);
}
if (client_manifest && (node.universal || node.component)) {
const entry = find_deps(
client_manifest,
`${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`,
true
);
imported.push(...entry.imports);
stylesheets.push(...entry.stylesheets);
fonts.push(...entry.fonts);
}
exports.push(

@@ -90,0 +89,0 @@ `export const imports = ${s(imported)};`,

@@ -38,3 +38,3 @@ import fs from 'node:fs';

// which is guaranteed to be `<base>/service-worker.js`
const base = `location.pathname.split('/').slice(0, -1).join('/')`;
const base = "location.pathname.split('/').slice(0, -1).join('/')";

@@ -83,4 +83,5 @@ fs.writeFileSync(

},
configFile: false,
define: vite_config.define,
configFile: false,
publicDir: false,
resolve: {

@@ -87,0 +88,0 @@ alias: [...get_config_aliases(kit), { find: '$service-worker', replacement: service_worker }]

import fs from 'node:fs';
import path from 'node:path';
import { normalizePath } from 'vite';

@@ -75,3 +76,3 @@ /**

while (!manifest[file]) {
const next = path.relative('.', fs.realpathSync(file));
const next = normalizePath(path.relative('.', fs.realpathSync(file)));
if (next === file) throw new Error(`Could not find file "${file}" in Vite manifest`);

@@ -93,12 +94,1 @@ file = next;

}
const method_names = new Set(['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH', 'OPTIONS']);
// If we'd written this in TypeScript, it could be easy...
/**
* @param {string} str
* @returns {str is import('types').HttpMethod}
*/
export function is_http_method(str) {
return method_names.has(str);
}

@@ -47,3 +47,3 @@ import fs from 'node:fs';

let manifest_data;
/** @type {import('types').SSRManifest} */
/** @type {import('@sveltejs/kit').SSRManifest} */
let manifest;

@@ -119,14 +119,7 @@

client: {
start: {
file: `${runtime_base}/client/start.js`,
imports: [],
stylesheets: [],
fonts: []
},
app: {
file: `${svelte_config.kit.outDir}/generated/client/app.js`,
imports: [],
stylesheets: [],
fonts: []
}
start: `${runtime_base}/client/start.js`,
app: `${to_fs(svelte_config.kit.outDir)}/generated/client/app.js`,
imports: [],
stylesheets: [],
fonts: []
},

@@ -150,3 +143,3 @@ nodes: manifest_data.nodes.map((node, index) => {

result.component = async () => {
const { module_node, module, url } = await resolve(
const { module_node, module } = await resolve(
/** @type {string} */ (node.component)

@@ -156,3 +149,2 @@ );

module_nodes.push(module_node);
result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for?

@@ -191,15 +183,14 @@ return module.default;

for (const dep of deps) {
const url = new URL(dep.url, 'http://localhost/');
const url = new URL(dep.url, 'dummy:/');
const query = url.searchParams;
if (
isCSSRequest(dep.file) ||
(query.has('svelte') && query.get('type') === 'style')
(isCSSRequest(dep.file) ||
(query.has('svelte') && query.get('type') === 'style')) &&
!(query.has('raw') || query.has('url') || query.has('inline'))
) {
// setting `?inline` to load CSS modules as css string
query.set('inline', '');
try {
const mod = await loud_ssr_load_module(
`${url.pathname}${url.search}${url.hash}`
query.set('inline', '');
const mod = await vite.ssrLoadModule(
`${decodeURI(url.pathname)}${url.search}${url.hash}`
);

@@ -243,3 +234,3 @@ styles[dep.url] = mod.default;

matchers: async () => {
/** @type {Record<string, import('types').ParamMatcher>} */
/** @type {Record<string, import('@sveltejs/kit').ParamMatcher>} */
const matchers = {};

@@ -364,3 +355,3 @@

// causing `instanceof` checks to fail
const control_module_node = await import(`../../../runtime/control.js`);
const control_module_node = await import('../../../runtime/control.js');
const control_module_vite = await vite.ssrLoadModule(`${runtime_base}/control.js`);

@@ -367,0 +358,0 @@

@@ -104,3 +104,3 @@ import fs from 'node:fs';

`\n${colors.bold().red(path.relative('.', filename))}\n` +
`\`<slot />\` missing — inner content will not be rendered`;
'`<slot />` missing — inner content will not be rendered';

@@ -115,3 +115,6 @@ if (!warned.has(message)) {

/** @return {Promise<import('vite').Plugin[]>} */
/**
* Returns the SvelteKit Vite plugins.
* @returns {Promise<import('vite').Plugin[]>}
*/
export async function sveltekit() {

@@ -193,2 +196,5 @@ const svelte_config = await load_config();

const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
/** @type {import('vite').Plugin} */

@@ -237,5 +243,7 @@ const plugin_setup = {

server: {
cors: { preflightContinue: true },
fs: {
allow: [...allow]
},
sourcemapIgnoreList,
watch: {

@@ -246,4 +254,3 @@ ignored: [

]
},
cors: { preflightContinue: true }
}
},

@@ -346,3 +353,3 @@ preview: {

? `globalThis.__sveltekit_${version_hash}`
: `globalThis.__sveltekit_dev`;
: 'globalThis.__sveltekit_dev';

@@ -393,3 +400,3 @@ if (options?.ssr === false && process.env.TEST !== 'true') {

// we use this alias so that we won't collide with user aliases
case '\0__sveltekit/paths':
case '\0__sveltekit/paths': {
const { assets, base } = svelte_config.kit.paths;

@@ -430,4 +437,5 @@

`;
}
case '\0__sveltekit/environment':
case '\0__sveltekit/environment': {
const { version } = svelte_config.kit;

@@ -443,2 +451,3 @@

`;
}
}

@@ -534,22 +543,7 @@ }

/**
* @param {string | undefined} file
*/
function add_input(file) {
if (!file) return;
const resolved = path.resolve(file);
const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
const name = relative.startsWith('..')
? path.basename(file).replace(/^\+/, '')
: relative.replace(/(\\|\/)\+/g, '-').replace(/[\\/]/g, '-');
input[`entry/${name}`] = resolved;
}
for (const node of manifest_data.nodes) {
add_input(node.component);
add_input(node.universal);
}
manifest_data.nodes.forEach((node, i) => {
if (node.component || node.universal) {
input[`nodes/${i}`] = `${kit.outDir}/generated/client-optimized/nodes/${i}.js`;
}
});
}

@@ -560,6 +554,16 @@

// We could always use a relative asset base path here, but it's better for performance not to.
// E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
// That's larger and takes longer to run and also causes an HTML diff between SSR and client
// causing us to do a more expensive hydration check.
const client_base = kit.paths.relative || kit.paths.assets ? './' : kit.paths.base || '/';
new_config = {
base: ssr ? assets_base(kit) : './',
base: ssr ? assets_base(kit) : client_base,
build: {
copyPublicDir: !ssr,
cssCodeSplit: true,
cssMinify: initial_config.build?.minify == null ? true : !!initial_config.build.minify,
// don't use the default name to avoid collisions with 'static/manifest.json'
manifest: 'vite-manifest.json',
outDir: `${out}/${ssr ? 'server' : 'client'}`,

@@ -573,3 +577,4 @@ rollupOptions: {

assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
hoistTransitiveImports: false
hoistTransitiveImports: false,
sourcemapIgnoreList
},

@@ -579,6 +584,3 @@ preserveEntrySignatures: 'strict'

ssrEmitAssets: true,
copyPublicDir: !ssr,
target: ssr ? 'node16.14' : undefined,
// don't use the default name to avoid collisions with 'static/manifest.json'
manifest: 'vite-manifest.json'
target: ssr ? 'node16.14' : undefined
},

@@ -678,3 +680,3 @@ publicDir: kit.files.assets,

manifest_data,
service_worker: !!service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
client: null,

@@ -737,13 +739,13 @@ server_manifest

const deps_of = /** @param {string} f */ (f) =>
find_deps(client_manifest, posixify(path.relative('.', f)), false);
const start = deps_of(`${runtime_directory}/client/start.js`);
const app = deps_of(`${kit.outDir}/generated/client-optimized/app.js`);
build_data.client = {
start: find_deps(
client_manifest,
posixify(path.relative('.', `${runtime_directory}/client/start.js`)),
false
),
app: find_deps(
client_manifest,
posixify(path.relative('.', `${kit.outDir}/generated/client-optimized/app.js`)),
false
)
start: start.file,
app: app.file,
imports: [...start.imports, ...app.imports],
stylesheets: [...start.stylesheets, ...app.stylesheets],
fonts: [...start.fonts, ...app.fonts]
};

@@ -750,0 +752,0 @@

@@ -5,2 +5,3 @@ import path from 'node:path';

import { negotiate } from '../../utils/http.js';
import { filter_private_env, filter_public_env } from '../../utils/env.js';

@@ -60,7 +61,8 @@ /**

export function get_env(env_config, mode) {
const entries = Object.entries(loadEnv(mode, env_config.dir, ''));
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = env_config;
const env = loadEnv(mode, env_config.dir, '');
return {
public: Object.fromEntries(entries.filter(([k]) => k.startsWith(env_config.publicPrefix))),
private: Object.fromEntries(entries.filter(([k]) => !k.startsWith(env_config.publicPrefix)))
public: filter_public_env(env, { public_prefix, private_prefix }),
private: filter_private_env(env, { public_prefix, private_prefix })
};

@@ -67,0 +69,0 @@ }

import { BROWSER, DEV } from 'esm-env';
export { building, version } from '__sveltekit/environment';
/**
* @type {import('$app/environment').browser}
* `true` if the app is running in the browser.
*/

@@ -9,6 +10,4 @@ export const browser = BROWSER;

/**
* @type {import('$app/environment').dev}
* Whether the dev server is running. This is not guaranteed to correspond to `NODE_ENV` or `MODE`.
*/
export const dev = DEV;
export { building, version } from '__sveltekit/environment';
import * as devalue from 'devalue';
import { BROWSER, DEV } from 'esm-env';
import { client } from '../client/singletons.js';
import { invalidateAll } from './navigation.js';
import { BROWSER, DEV } from 'esm-env';
/**
* @param {string} name
* This action updates the `form` property of the current page with the given data and updates `$page.status`.
* In case of an error, it redirects to the nearest error page.
* @template {Record<string, unknown> | undefined} Success
* @template {Record<string, unknown> | undefined} Failure
* @param {import('@sveltejs/kit').ActionResult<Success, Failure>} result
* @returns {Promise<void>}
*/
function guard(name) {
return () => {
throw new Error(`Cannot call ${name}(...) on the server`);
};
export function applyAction(result) {
if (BROWSER) {
return client.apply_action(result);
} else {
throw new Error('Cannot call applyAction(...) on the server');
}
}
/** @type {import('$app/forms').applyAction} */
export const applyAction = BROWSER ? client.apply_action : guard('applyAction');
/** @type {import('$app/forms').deserialize} */
/**
* Use this function to deserialize the response from a form submission.
* Usage:
*
* ```js
* import { deserialize } from '$app/forms';
*
* async function handleSubmit(event) {
* const response = await fetch('/form?/action', {
* method: 'POST',
* body: new FormData(event.target)
* });
*
* const result = deserialize(await response.text());
* // ...
* }
* ```
* @template {Record<string, unknown> | undefined} Success
* @template {Record<string, unknown> | undefined} Failure
* @param {string} result
* @returns {import('@sveltejs/kit').ActionResult<Success, Failure>}
*/
export function deserialize(result) {

@@ -27,9 +52,51 @@ const parsed = JSON.parse(result);

/** @type {import('$app/forms').enhance} */
export function enhance(form, submit = () => {}) {
if (
DEV &&
/** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form)).method !==
'post'
) {
/**
* @param {string} old_name
* @param {string} new_name
* @param {string} call_location
* @returns void
*/
function warn_on_access(old_name, new_name, call_location) {
if (!DEV) return;
// TODO 2.0: Remove this code
console.warn(
`\`${old_name}\` has been deprecated in favor of \`${new_name}\`. \`${old_name}\` will be removed in a future version. (Called from ${call_location})`
);
}
/**
* Shallow clone an element, so that we can access e.g. `form.action` without worrying
* that someone has added an `<input name="action">` (https://github.com/sveltejs/kit/issues/7593)
* @template {HTMLElement} T
* @param {T} element
* @returns {T}
*/
function clone(element) {
return /** @type {T} */ (HTMLElement.prototype.cloneNode.call(element));
}
/**
* This action enhances a `<form>` element that otherwise would work without JavaScript.
*
* The `submit` function is called upon submission with the given FormData and the `action` that should be triggered.
* If `cancel` is called, the form will not be submitted.
* You can use the abort `controller` to cancel the submission in case another one starts.
* If a function is returned, that function is called with the response from the server.
* If nothing is returned, the fallback will be used.
*
* If this function or its return value isn't set, it
* - falls back to updating the `form` prop with the returned data if the action is one same page as the form
* - updates `$page.status`
* - resets the `<form>` element and invalidates all data in case of successful submission with no redirect response
* - redirects in case of a redirect response
* - redirects to the nearest error page in case of an unexpected error
*
* If you provide a custom function with a callback and want to use the default behavior, invoke `update` in your callback.
* @template {Record<string, unknown> | undefined} Success
* @template {Record<string, unknown> | undefined} Failure
* @param {HTMLFormElement} form_element The form element
* @param {import('@sveltejs/kit').SubmitFunction<Success, Failure>} submit Submit callback
*/
export function enhance(form_element, submit = () => {}) {
if (DEV && clone(form_element).method !== 'post') {
throw new Error('use:enhance can only be used on <form> fields with method="POST"');

@@ -41,3 +108,3 @@ }

* action: URL;
* result: import('types').ActionResult;
* result: import('@sveltejs/kit').ActionResult;
* reset?: boolean

@@ -50,3 +117,3 @@ * }} opts

// We call reset from the prototype to avoid DOM clobbering
HTMLFormElement.prototype.reset.call(form);
HTMLFormElement.prototype.reset.call(form_element);
}

@@ -69,2 +136,7 @@ await invalidateAll();

async function handle_submit(event) {
const method = event.submitter?.hasAttribute('formmethod')
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formMethod
: clone(form_element).method;
if (method !== 'post') return;
event.preventDefault();

@@ -74,13 +146,24 @@

// We can't do submitter.formAction directly because that property is always set
// We do cloneNode for avoid DOM clobbering - https://github.com/sveltejs/kit/issues/7593
event.submitter?.hasAttribute('formaction')
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
: /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form)).action
: clone(form_element).action
);
const data = new FormData(form);
const form_data = new FormData(form_element);
if (DEV && clone(form_element).enctype !== 'multipart/form-data') {
for (const value of form_data.values()) {
if (value instanceof File) {
// TODO 2.0: Upgrade to `throw Error`
console.warn(
'Your form contains <input type="file"> fields, but is missing the `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819. This will be upgraded to an error in v2.0.'
);
break;
}
}
}
const submitter_name = event.submitter?.getAttribute('name');
if (submitter_name) {
data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
form_data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
}

@@ -93,2 +176,3 @@

// TODO 2.0: Remove `data` and `form`
const callback =

@@ -99,4 +183,12 @@ (await submit({

controller,
data,
form,
get data() {
warn_on_access('data', 'formData', 'use:enhance submit function');
return form_data;
},
formData: form_data,
get form() {
warn_on_access('form', 'formElement', 'use:enhance submit function');
return form_element;
},
formElement: form_element,
submitter: event.submitter

@@ -106,3 +198,3 @@ })) ?? fallback_callback;

/** @type {import('types').ActionResult} */
/** @type {import('@sveltejs/kit').ActionResult} */
let result;

@@ -118,3 +210,3 @@

cache: 'no-store',
body: data,
body: form_data,
signal: controller.signal

@@ -132,4 +224,12 @@ });

action,
data,
form,
get data() {
warn_on_access('data', 'formData', 'callback returned from use:enhance submit function');
return form_data;
},
formData: form_data,
get form() {
warn_on_access('form', 'formElement', 'callback returned from use:enhance submit function');
return form_element;
},
formElement: form_element,
update: (opts) => fallback_callback({ action, result, reset: opts?.reset }),

@@ -142,3 +242,3 @@ // @ts-expect-error generic constraints stuff we don't care about

// @ts-expect-error
HTMLFormElement.prototype.addEventListener.call(form, 'submit', handle_submit);
HTMLFormElement.prototype.addEventListener.call(form_element, 'submit', handle_submit);

@@ -148,5 +248,5 @@ return {

// @ts-expect-error
HTMLFormElement.prototype.removeEventListener.call(form, 'submit', handle_submit);
HTMLFormElement.prototype.removeEventListener.call(form_element, 'submit', handle_submit);
}
};
}

@@ -1,22 +0,112 @@

import { BROWSER } from 'esm-env';
import { client } from '../client/singletons.js';
import { client_method } from '../client/singletons.js';
/**
* @param {string} name
* If called when the page is being updated following a navigation (in `onMount` or `afterNavigate` or an action, for example), this disables SvelteKit's built-in scroll handling.
* This is generally discouraged, since it breaks user expectations.
* @returns {void}
*/
function guard(name) {
return () => {
throw new Error(`Cannot call ${name}(...) on the server`);
};
}
export const disableScrollHandling = /* @__PURE__ */ client_method('disable_scroll_handling');
export const disableScrollHandling = BROWSER
? client.disable_scroll_handling
: guard('disableScrollHandling');
export const goto = BROWSER ? client.goto : guard('goto');
export const invalidate = BROWSER ? client.invalidate : guard('invalidate');
export const invalidateAll = BROWSER ? client.invalidateAll : guard('invalidateAll');
export const preloadData = BROWSER ? client.preload_data : guard('preloadData');
export const preloadCode = BROWSER ? client.preload_code : guard('preloadCode');
export const beforeNavigate = BROWSER ? client.before_navigate : () => {};
export const afterNavigate = BROWSER ? client.after_navigate : () => {};
/**
* 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)`.
*
* @type {(url: string | URL, opts?: {
* replaceState?: boolean;
* noScroll?: boolean;
* keepFocus?: boolean;
* invalidateAll?: boolean;
* state?: any
* }) => Promise<void>}
* @param {string | URL} url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://kit.svelte.dev/docs/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} [invalidateAll] If `true`, all `load` functions of the page will be rerun. See https://kit.svelte.dev/docs/load#rerunning-load-functions for more info on invalidation.
* @param {any} [opts.state] The state of the new/updated history entry
* @returns {Promise<void>}
*/
export const goto = /* @__PURE__ */ client_method('goto');
/**
* Causes any `load` functions belonging to the currently active page to re-run if they depend on the `url` in question, via `fetch` or `depends`. Returns a `Promise` that resolves when the page is subsequently updated.
*
* If the argument is given as a `string` or `URL`, it must resolve to the same URL that was passed to `fetch` or `depends` (including query parameters).
* To create a custom identifier, use a string beginning with `[a-z]+:` (e.g. `custom:state`) — this is a valid URL.
*
* The `function` argument can be used define a custom predicate. It receives the full `URL` and causes `load` to rerun if `true` is returned.
* This can be useful if you want to invalidate based on a pattern instead of a exact match.
*
* ```ts
* // Example: Match '/path' regardless of the query parameters
* import { invalidate } from '$app/navigation';
*
* invalidate((url) => url.pathname === '/path');
* ```
* @type {(url: string | URL | ((url: URL) => boolean)) => Promise<void>}
* @param {string | URL | ((url: URL) => boolean)} url The invalidated URL
* @returns {Promise<void>}
*/
export const invalidate = /* @__PURE__ */ client_method('invalidate');
/**
* Causes all `load` functions belonging to the currently active page to re-run. Returns a `Promise` that resolves when the page is subsequently updated.
* @type {() => Promise<void>}
* @returns {Promise<void>}
*/
export const invalidateAll = /* @__PURE__ */ client_method('invalidate_all');
/**
* Programmatically preloads the given page, which means
* 1. ensuring that the code for the page is loaded, and
* 2. calling the page's load function with the appropriate options.
*
* This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `<a>` element with `data-sveltekit-preload-data`.
* If the next navigation is to `href`, the values returned from load will be used, making navigation instantaneous.
* Returns a Promise that resolves when the preload is complete.
*
* @type {(href: string) => Promise<void>}
* @param {string} href Page to preload
* @returns {Promise<void>}
*/
export const preloadData = /* @__PURE__ */ client_method('preload_data');
/**
* Programmatically imports the code for routes that haven't yet been fetched.
* Typically, you might call this to speed up subsequent navigation.
*
* You can specify routes by any matching pathname such as `/about` (to match `src/routes/about/+page.svelte`) or `/blog/*` (to match `src/routes/blog/[slug]/+page.svelte`).
*
* Unlike `preloadData`, this won't call `load` functions.
* Returns a Promise that resolves when the modules have been imported.
*
* @type {(...urls: string[]) => Promise<void>}
* @param {...string[]} urls
* @returns {Promise<void>}
*/
export const preloadCode = /* @__PURE__ */ client_method('preload_code');
/**
* A navigation interceptor that triggers before we navigate to a new URL, whether by clicking a link, calling `goto(...)`, or using the browser back/forward controls.
* Calling `cancel()` will prevent the navigation from completing. If the navigation would have directly unloaded the current page, calling `cancel` will trigger the native
* browser unload confirmation dialog. In these cases, `navigation.willUnload` is `true`.
*
* When a navigation isn't client side, `navigation.to.route.id` will be `null`.
*
* `beforeNavigate` must be called during a component initialization. It remains active as long as the component is mounted.
* @type {(callback: (navigation: import('@sveltejs/kit').BeforeNavigate) => void) => void}
* @param {(navigation: import('@sveltejs/kit').BeforeNavigate) => void} callback
* @returns {void}
*/
export const beforeNavigate = /* @__PURE__ */ client_method('before_navigate');
/**
* A lifecycle function that runs the supplied `callback` when the current component mounts, and also whenever we navigate to a new URL.
*
* `afterNavigate` must be called during a component initialization. It remains active as long as the component is mounted.
* @type {(callback: (navigation: import('@sveltejs/kit').AfterNavigate) => void) => void}
* @param {(navigation: import('@sveltejs/kit').AfterNavigate) => void} callback
* @returns {void}
*/
export const afterNavigate = /* @__PURE__ */ client_method('after_navigate');

@@ -6,3 +6,4 @@ import { getContext } from 'svelte';

/**
* @type {import('$app/stores').getStores}
* A function that returns all of the contextual stores. On the server, this must be called during component initialization.
* Only use this if you need to defer store subscription until after the component has mounted, for some reason.
*/

@@ -13,8 +14,11 @@ export const getStores = () => {

return {
/** @type {typeof page} */
page: {
subscribe: stores.page.subscribe
},
/** @type {typeof navigating} */
navigating: {
subscribe: stores.navigating.subscribe
},
/** @type {typeof updated} */
updated: stores.updated

@@ -24,5 +28,10 @@ };

/** @type {typeof import('$app/stores').page} */
/**
* A readable store whose value contains page data.
*
* On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
*
* @type {import('svelte/store').Readable<import('@sveltejs/kit').Page>}
*/
export const page = {
/** @param {(value: any) => void} fn */
subscribe(fn) {

@@ -34,3 +43,10 @@ const store = __SVELTEKIT_DEV__ ? get_store('page') : getStores().page;

/** @type {typeof import('$app/stores').navigating} */
/**
* A readable store.
* When navigating starts, its value is a `Navigation` object with `from`, `to`, `type` and (if `type === 'popstate'`) `delta` properties.
* When navigating finishes, its value reverts to `null`.
*
* On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
* @type {import('svelte/store').Readable<import('@sveltejs/kit').Navigation | null>}
*/
export const navigating = {

@@ -43,3 +59,8 @@ subscribe(fn) {

/** @type {typeof import('$app/stores').updated} */
/**
* A readable store whose initial value is `false`. If [`version.pollInterval`](https://kit.svelte.dev/docs/configuration#version) is a non-zero value, SvelteKit will poll for new versions of the app and update the store value to `true` when it detects one. `updated.check()` will force an immediate check, regardless of polling.
*
* On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
* @type {import('svelte/store').Readable<boolean> & { check(): Promise<boolean> }}
*/
export const updated = {

@@ -58,4 +79,4 @@ subscribe(fn) {

browser
? `Cannot check updated store before subscribing`
: `Can only check updated store in browser`
? 'Cannot check updated store before subscribing'
: 'Can only check updated store in browser'
);

@@ -62,0 +83,0 @@ }

import { DEV } from 'esm-env';
import { onMount, tick } from 'svelte';
import {
add_data_suffix,
decode_params,
decode_pathname,
make_trackable,
decode_pathname,
decode_params,
normalize_path,
add_data_suffix
normalize_path
} from '../../utils/url.js';
import {
initial_fetch,
lock_fetch,
native_fetch,
subsequent_fetch,
unlock_fetch
} from './fetcher.js';
import { parse } from './parse.js';
import * as storage from './session-storage.js';
import {
find_anchor,

@@ -18,21 +27,12 @@ get_base_uri,

} from './utils.js';
import * as storage from './session-storage.js';
import {
lock_fetch,
unlock_fetch,
initial_fetch,
subsequent_fetch,
native_fetch
} from './fetcher.js';
import { parse } from './parse.js';
import { base } from '__sveltekit/paths';
import * as devalue from 'devalue';
import { compact } from '../../utils/array.js';
import { validate_page_exports } from '../../utils/exports.js';
import { unwrap_promises } from '../../utils/promises.js';
import { HttpError, Redirect } from '../control.js';
import { INVALIDATED_PARAM, validate_depends } from '../shared.js';
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY, SNAPSHOT_KEY } from './constants.js';
import { stores } from './singletons.js';
import { unwrap_promises } from '../../utils/promises.js';
import * as devalue from 'devalue';
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY, SNAPSHOT_KEY } from './constants.js';
import { validate_common_exports } from '../../utils/exports.js';
import { compact } from '../../utils/array.js';
import { validate_depends } from '../shared.js';

@@ -90,6 +90,6 @@ let errored = false;

const callbacks = {
/** @type {Array<(navigation: import('types').BeforeNavigate) => void>} */
/** @type {Array<(navigation: import('@sveltejs/kit').BeforeNavigate) => void>} */
before_navigate: [],
/** @type {Array<(navigation: import('types').AfterNavigate) => void>} */
/** @type {Array<(navigation: import('@sveltejs/kit').AfterNavigate) => void>} */
after_navigate: []

@@ -143,3 +143,3 @@ };

/** @type {import('types').Page} */
/** @type {import('@sveltejs/kit').Page} */
let page;

@@ -159,2 +159,3 @@

await pending_invalidate;
if (!pending_invalidate) return;
pending_invalidate = null;

@@ -169,3 +170,17 @@

load_cache = null;
await update(intent, url, []);
const nav_token = (token = {});
const navigation_result = intent && (await load_route(intent));
if (nav_token !== token) return;
if (navigation_result) {
if (navigation_result.type === 'redirect') {
return goto(new URL(navigation_result.location, url).href, {}, [url.pathname], nav_token);
} else {
if (navigation_result.props.page !== undefined) {
page = navigation_result.props.page;
}
root.$set(navigation_result.props);
}
}
}

@@ -264,182 +279,5 @@

/**
* Returns `true` if update completes, `false` if it is aborted
* @param {import('./types').NavigationIntent | undefined} intent
* @param {URL} url
* @param {string[]} redirect_chain
* @param {number} [previous_history_index]
* @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean, details: { replaceState: boolean, state: any } | null}} [opts]
* @param {{}} [nav_token] To distinguish between different navigation events and determine the latest. Needed for example for redirects to keep the original token
* @param {() => void} [callback]
*/
async function update(
intent,
url,
redirect_chain,
previous_history_index,
opts,
nav_token = {},
callback
) {
token = nav_token;
let navigation_result = intent && (await load_route(intent));
if (!navigation_result) {
if (is_external_url(url, base)) {
return await native_navigation(url);
}
navigation_result = await server_fallback(
url,
{ id: null },
await handle_error(new Error(`Not found: ${url.pathname}`), {
url,
params: {},
route: { id: null }
}),
404
);
}
// if this is an internal navigation intent, use the normalized
// URL for the rest of the function
url = intent?.url || url;
// abort if user navigated during update
if (token !== nav_token) return false;
if (navigation_result.type === 'redirect') {
if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
navigation_result = await load_root_error_page({
status: 500,
error: await handle_error(new Error('Redirect loop'), {
url,
params: {},
route: { id: null }
}),
url,
route: { id: null }
});
} else {
goto(
new URL(navigation_result.location, url).href,
{},
[...redirect_chain, url.pathname],
nav_token
);
return false;
}
} else if (/** @type {number} */ (navigation_result.props.page?.status) >= 400) {
const updated = await stores.updated.check();
if (updated) {
await native_navigation(url);
}
}
// reset invalidation only after a finished navigation. If there are redirects or
// additional invalidations, they should get the same invalidation treatment
invalidated.length = 0;
force_invalidation = false;
updating = true;
// `previous_history_index` will be undefined for invalidation
if (previous_history_index) {
update_scroll_positions(previous_history_index);
capture_snapshot(previous_history_index);
}
// ensure the url pathname matches the page's trailing slash option
if (
navigation_result.props.page?.url &&
navigation_result.props.page.url.pathname !== url.pathname
) {
url.pathname = navigation_result.props.page?.url.pathname;
}
if (opts && opts.details) {
const { details } = opts;
const change = details.replaceState ? 0 : 1;
details.state[INDEX_KEY] = current_history_index += change;
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', url);
if (!details.replaceState) {
// if we navigated back, then pushed a new state, we can
// release memory by pruning the scroll/snapshot lookup
let i = current_history_index + 1;
while (snapshots[i] || scroll_positions[i]) {
delete snapshots[i];
delete scroll_positions[i];
i += 1;
}
}
}
// reset preload synchronously after the history state has been set to avoid race conditions
load_cache = null;
if (started) {
current = navigation_result.state;
// reset url before updating page store
if (navigation_result.props.page) {
navigation_result.props.page.url = url;
}
root.$set(navigation_result.props);
} else {
initialize(navigation_result);
}
// opts must be passed if we're navigating
if (opts) {
const { scroll, keepfocus } = opts;
const { activeElement } = document;
// need to render the DOM before we can scroll to the rendered elements and do focus management
await tick();
// we reset scroll before dealing with focus, to avoid a flash of unscrolled content
if (autoscroll) {
const deep_linked =
url.hash && document.getElementById(decodeURIComponent(url.hash.slice(1)));
if (scroll) {
scrollTo(scroll.x, scroll.y);
} else if (deep_linked) {
// Here we use `scrollIntoView` on the element instead of `scrollTo`
// because it natively supports the `scroll-margin` and `scroll-behavior`
// CSS properties.
deep_linked.scrollIntoView();
} else {
scrollTo(0, 0);
}
}
const changed_focus =
// reset focus only if any manual focus management didn't override it
document.activeElement !== activeElement &&
// also refocus when activeElement is body already because the
// focus event might not have been fired on it yet
document.activeElement !== document.body;
if (!keepfocus && !changed_focus) {
await reset_focus();
}
} else {
await tick();
}
autoscroll = true;
if (navigation_result.props.page) {
page = navigation_result.props.page;
}
if (callback) callback();
updating = false;
}
/** @param {import('./types').NavigationFinished} result */
function initialize(result) {
if (DEV && document.querySelector('vite-error-overlay')) return;
if (DEV && result.state.error && document.querySelector('vite-error-overlay')) return;

@@ -451,3 +289,3 @@ current = result.state;

page = /** @type {import('types').Page} */ (result.props.page);
page = /** @type {import('@sveltejs/kit').Page} */ (result.props.page);

@@ -462,3 +300,3 @@ root = new app.root({

/** @type {import('types').AfterNavigate} */
/** @type {import('@sveltejs/kit').AfterNavigate} */
const navigation = {

@@ -506,2 +344,3 @@ from: null,

url.pathname = normalize_path(url.pathname, slash);
// eslint-disable-next-line
url.search = url.search; // turn `/?` into `/`

@@ -606,3 +445,3 @@

if (DEV) {
validate_common_exports(node.universal);
validate_page_exports(node.universal);
}

@@ -621,3 +460,3 @@

/** @type {import('types').LoadEvent} */
/** @type {import('@sveltejs/kit').LoadEvent} */
const load_input = {

@@ -1077,3 +916,3 @@ route: {

* url: URL;
* type: import('types').NavigationType;
* type: import('@sveltejs/kit').NavigationType;
* intent?: import('./types').NavigationIntent;

@@ -1086,3 +925,3 @@ * delta?: number;

/** @type {import('types').Navigation} */
/** @type {import('@sveltejs/kit').Navigation} */
const navigation = {

@@ -1132,3 +971,3 @@ from: {

* } | null;
* type: import('types').NavigationType;
* type: import('@sveltejs/kit').NavigationType;
* delta?: number;

@@ -1148,3 +987,3 @@ * nav_token?: {};

delta,
nav_token,
nav_token = {},
accepted,

@@ -1172,21 +1011,157 @@ blocked

await update(
intent,
url,
redirect_chain,
previous_history_index,
{
scroll,
keepfocus,
details
},
nav_token,
() => {
navigating = false;
callbacks.after_navigate.forEach((fn) =>
fn(/** @type {import('types').AfterNavigate} */ (navigation))
token = nav_token;
let navigation_result = intent && (await load_route(intent));
if (!navigation_result) {
if (is_external_url(url, base)) {
return await native_navigation(url);
}
navigation_result = await server_fallback(
url,
{ id: null },
await handle_error(new Error(`Not found: ${url.pathname}`), {
url,
params: {},
route: { id: null }
}),
404
);
}
// if this is an internal navigation intent, use the normalized
// URL for the rest of the function
url = intent?.url || url;
// abort if user navigated during update
if (token !== nav_token) return false;
if (navigation_result.type === 'redirect') {
if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
navigation_result = await load_root_error_page({
status: 500,
error: await handle_error(new Error('Redirect loop'), {
url,
params: {},
route: { id: null }
}),
url,
route: { id: null }
});
} else {
goto(
new URL(navigation_result.location, url).href,
{},
[...redirect_chain, url.pathname],
nav_token
);
stores.navigating.set(null);
return false;
}
} else if (/** @type {number} */ (navigation_result.props.page?.status) >= 400) {
const updated = await stores.updated.check();
if (updated) {
await native_navigation(url);
}
}
// reset invalidation only after a finished navigation. If there are redirects or
// additional invalidations, they should get the same invalidation treatment
invalidated.length = 0;
force_invalidation = false;
updating = true;
update_scroll_positions(previous_history_index);
capture_snapshot(previous_history_index);
// ensure the url pathname matches the page's trailing slash option
if (
navigation_result.props.page?.url &&
navigation_result.props.page.url.pathname !== url.pathname
) {
url.pathname = navigation_result.props.page?.url.pathname;
}
if (details) {
const change = details.replaceState ? 0 : 1;
details.state[INDEX_KEY] = current_history_index += change;
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', url);
if (!details.replaceState) {
// if we navigated back, then pushed a new state, we can
// release memory by pruning the scroll/snapshot lookup
let i = current_history_index + 1;
while (snapshots[i] || scroll_positions[i]) {
delete snapshots[i];
delete scroll_positions[i];
i += 1;
}
}
}
// reset preload synchronously after the history state has been set to avoid race conditions
load_cache = null;
if (started) {
current = navigation_result.state;
// reset url before updating page store
if (navigation_result.props.page) {
navigation_result.props.page.url = url;
}
root.$set(navigation_result.props);
} else {
initialize(navigation_result);
}
const { activeElement } = document;
// need to render the DOM before we can scroll to the rendered elements and do focus management
await tick();
// we reset scroll before dealing with focus, to avoid a flash of unscrolled content
if (autoscroll) {
const deep_linked =
url.hash && document.getElementById(decodeURIComponent(url.hash.slice(1)));
if (scroll) {
scrollTo(scroll.x, scroll.y);
} else if (deep_linked) {
// Here we use `scrollIntoView` on the element instead of `scrollTo`
// because it natively supports the `scroll-margin` and `scroll-behavior`
// CSS properties.
deep_linked.scrollIntoView();
} else {
scrollTo(0, 0);
}
}
const changed_focus =
// reset focus only if any manual focus management didn't override it
document.activeElement !== activeElement &&
// also refocus when activeElement is body already because the
// focus event might not have been fired on it yet
document.activeElement !== document.body;
if (!keepfocus && !changed_focus) {
reset_focus();
}
autoscroll = true;
if (navigation_result.props.page) {
page = navigation_result.props.page;
}
navigating = false;
if (type === 'popstate') {
restore_snapshot(current_history_index);
}
callbacks.after_navigate.forEach((fn) =>
fn(/** @type {import('@sveltejs/kit').AfterNavigate} */ (navigation))
);
stores.navigating.set(null);
updating = false;
}

@@ -1219,3 +1194,3 @@

debugger;
debugger; // eslint-disable-line
}

@@ -1286,4 +1261,4 @@

const { url, external } = get_link_info(a, base);
if (external) return;
const { url, external, download } = get_link_info(a, base);
if (external || download) return;

@@ -1321,4 +1296,4 @@ const options = get_router_options(a);

for (const a of container.querySelectorAll('a')) {
const { url, external } = get_link_info(a, base);
if (external) continue;
const { url, external, download } = get_link_info(a, base);
if (external || download) continue;

@@ -1344,3 +1319,3 @@ const options = get_router_options(a);

* @param {unknown} error
* @param {import('types').NavigationEvent} event
* @param {import('@sveltejs/kit').NavigationEvent} event
* @returns {import('types').MaybePromise<App.Error>}

@@ -1412,3 +1387,3 @@ */

invalidateAll: () => {
invalidate_all: () => {
force_invalidation = true;

@@ -1495,3 +1470,3 @@ return invalidate();

// it's due to an external or full-page-reload link, for which we don't want to call the hook again.
/** @type {import('types').BeforeNavigate} */
/** @type {import('@sveltejs/kit').BeforeNavigate} */
const navigation = {

@@ -1542,3 +1517,3 @@ from: {

const { url, external, target } = get_link_info(a, base);
const { url, external, target, download } = get_link_info(a, base);
if (!url) return;

@@ -1571,2 +1546,4 @@

if (download) return;
// Ignore the following but fire beforeNavigate

@@ -1590,5 +1567,13 @@ if (external || options.reload) {

if (hash !== undefined && nonhash === location.href.split('#')[0]) {
// If we are trying to navigate to the same hash, we should only
// attempt to scroll to that element and avoid any history changes.
// Otherwise, this can cause Firefox to incorrectly assign a null
// history state value without any signal that we can detect.
if (current.url.hash === url.hash) {
event.preventDefault();
a.ownerDocument.getElementById(hash)?.scrollIntoView();
return;
}
// set this flag to distinguish between navigations triggered by
// clicking a hash link and those triggered by popstate
// TODO why not update history here directly?
hash_navigating = true;

@@ -1598,7 +1583,9 @@

current.url = url;
stores.page.set({ ...page, url });
stores.page.notify();
update_url(url);
return;
if (!options.replace_state) return;
// hashchange event shouldn't occur if the router is replacing state.
hash_navigating = false;
event.preventDefault();
}

@@ -1694,3 +1681,2 @@

const delta = event.state[INDEX_KEY] - current_history_index;
let blocked = false;

@@ -1708,3 +1694,2 @@ await navigate({

history.go(-delta);
blocked = true;
},

@@ -1714,5 +1699,9 @@ type: 'popstate',

});
if (!blocked) {
restore_snapshot(current_history_index);
} else {
// since popstate event is also emitted when an anchor referencing the same
// document is clicked, we have to check that the router isn't already handling
// the navigation. otherwise we would be updating the page store twice.
if (!hash_navigating) {
const url = new URL(location.href);
update_url(url);
}

@@ -1739,3 +1728,3 @@ }

for (const link of document.querySelectorAll('link')) {
if (link.rel === 'icon') link.href = link.href;
if (link.rel === 'icon') link.href = link.href; // eslint-disable-line
}

@@ -1752,2 +1741,11 @@

});
/**
* @param {URL} url
*/
function update_url(url) {
current.url = url;
stores.page.set({ ...page, url });
stores.page.notify();
}
},

@@ -1801,10 +1799,26 @@

/** @type {Array<import('./types').BranchNode | undefined>} */
const branch = await Promise.all(branch_promises);
const parsed_route = routes.find(({ id }) => id === route.id);
// server-side will have compacted the branch, reinstate empty slots
// so that error boundaries can be lined up correctly
if (parsed_route) {
const layouts = parsed_route.layouts;
for (let i = 0; i < layouts.length; i++) {
if (!layouts[i]) {
branch.splice(i, 0, undefined);
}
}
}
result = await get_navigation_result_from_branch({
url,
params,
branch: await Promise.all(branch_promises),
branch,
status,
error,
form,
route: routes.find(({ id }) => id === route.id) ?? null
route: parsed_route ?? null
});

@@ -1840,9 +1854,6 @@ } catch (error) {

data_url.pathname = add_data_suffix(url.pathname);
if (DEV && url.searchParams.has('x-sveltekit-invalidated')) {
throw new Error('Cannot used reserved query parameter "x-sveltekit-invalidated"');
if (DEV && url.searchParams.has(INVALIDATED_PARAM)) {
throw new Error(`Cannot used reserved query parameter "${INVALIDATED_PARAM}"`);
}
data_url.searchParams.append(
'x-sveltekit-invalidated',
invalid.map((x) => (x ? '1' : '')).join('_')
);
data_url.searchParams.append(INVALIDATED_PARAM, invalid.map((i) => (i ? '1' : '0')).join(''));

@@ -1857,2 +1868,4 @@ const res = await native_fetch(data_url.href);

// TODO: fix eslint error
// eslint-disable-next-line
return new Promise(async (resolve) => {

@@ -1960,3 +1973,4 @@ /**

root.tabIndex = -1;
root.focus({ preventScroll: true });
// @ts-expect-error
root.focus({ preventScroll: true, focusVisible: false });

@@ -1970,8 +1984,40 @@ // restore `tabindex` as to prevent `root` from stealing input from elements

return new Promise((resolve) => {
// capture current selection, so we can compare the state after
// snapshot restoration and afterNavigate callbacks have run
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);
// we need to do a deep comparison rather than just `a !== b` because
// Safari behaves differently to other browsers
if (
a.commonAncestorContainer !== b.commonAncestorContainer ||
a.startContainer !== b.startContainer ||
a.endContainer !== b.endContainer ||
a.startOffset !== b.startOffset ||
a.endOffset !== b.endOffset
) {
return;
}
}
// if the selection hasn't changed (as a result of an element being (auto)focused,
// or a programmatic selection, we reset everything as part of the navigation)
// fixes https://github.com/sveltejs/kit/issues/8439
resolve(getSelection()?.removeAllRanges());
selection.removeAllRanges();
});
});
}
}

@@ -1978,0 +2024,0 @@ }

@@ -32,10 +32,8 @@ import { DEV } from 'esm-env';

const stack_array = /** @type {string} */ (new Error().stack).split('\n');
// We need to do some Firefox-specific cutoff because it (impressively) maintains the stack
// across events and for example traces a `fetch` call triggered from a button back
// to the creation of the event listener and the element creation itself,
// We need to do a cutoff because Safari and Firefox maintain the stack
// across events and for example traces a `fetch` call triggered from a button
// back to the creation of the event listener and the element creation itself,
// where at some point client.js will show up, leading to false positives.
const firefox_cutoff = stack_array.findIndex((a) => a.includes('*listen@'));
const stack = stack_array
.slice(0, firefox_cutoff !== -1 ? firefox_cutoff : undefined)
.join('\n');
const cutoff = stack_array.findIndex((a) => a.includes('load@') || a.includes('at load'));
const stack = stack_array.slice(0, cutoff + 2).join('\n');

@@ -42,0 +40,0 @@ const heuristic = can_inspect_stack_trace

import { writable } from 'svelte/store';
import { create_updated_store, notifiable_store } from './utils.js';
import { BROWSER } from 'esm-env';

@@ -16,7 +17,38 @@ /** @type {import('./types').Client} */

/**
* @template {keyof typeof client} T
* @param {T} key
* @returns {typeof client[T]}
*/
export function client_method(key) {
if (!BROWSER) {
if (key === 'before_navigate' || key === 'after_navigate') {
// @ts-expect-error doesn't recognize that both keys here return void so expects a async function
return () => {};
} else {
/** @type {Record<string, string>} */
const name_lookup = {
disable_scroll_handling: 'disableScrollHandling',
preload_data: 'preloadData',
preload_code: 'preloadCode',
invalidate_all: 'invalidateAll'
};
return () => {
throw new Error(`Cannot call ${name_lookup[key] ?? key}(...) on the server`);
};
}
} else {
// @ts-expect-error
return (...args) => client[key](...args);
}
}
export const stores = {
url: notifiable_store({}),
page: notifiable_store({}),
navigating: writable(/** @type {import('types').Navigation | null} */ (null)),
updated: create_updated_store()
url: /* @__PURE__ */ notifiable_store({}),
page: /* @__PURE__ */ notifiable_store({}),
navigating: /* @__PURE__ */ writable(
/** @type {import('@sveltejs/kit').Navigation | null} */ (null)
),
updated: /* @__PURE__ */ create_updated_store()
};

@@ -13,3 +13,3 @@ import { DEV } from 'esm-env';

console.warn(
`Placing %sveltekit.body% directly inside <body> is not recommended, as your app may break for users who have certain browser extensions installed.\n\nConsider wrapping it in an element:\n\n<div style="display: contents">\n %sveltekit.body%\n</div>`
'Placing %sveltekit.body% directly inside <body> is not recommended, as your app may break for users who have certain browser extensions installed.\n\nConsider wrapping it in an element:\n\n<div style="display: contents">\n %sveltekit.body%\n</div>'
);

@@ -16,0 +16,0 @@ }

@@ -1,2 +0,2 @@

import { applyAction } from '$app/forms';
import { applyAction } from '../app/forms';
import {

@@ -10,14 +10,6 @@ afterNavigate,

preloadData
} from '$app/navigation';
} from '../app/navigation';
import { SvelteComponent } from 'svelte';
import {
ClientHooks,
CSRPageNode,
CSRPageNodeLoader,
CSRRoute,
Page,
ParamMatcher,
TrailingSlash,
Uses
} from 'types';
import { ClientHooks, CSRPageNode, CSRPageNodeLoader, CSRRoute, TrailingSlash, Uses } from 'types';
import { Page, ParamMatcher } from '@sveltejs/kit';

@@ -58,3 +50,3 @@ export interface SvelteKitApp {

invalidate: typeof invalidate;
invalidateAll: typeof invalidateAll;
invalidate_all: typeof invalidateAll;
preload_code: typeof preloadCode;

@@ -61,0 +53,0 @@ preload_data: typeof preloadData;

@@ -35,6 +35,6 @@ import { BROWSER, DEV } from 'esm-env';

'preload-data': ['', 'off', 'tap', 'hover'],
keepfocus: ['', 'off'],
noscroll: ['', 'off'],
reload: ['', 'off'],
replacestate: ['', 'off']
keepfocus: ['', 'true', 'off', 'false'],
noscroll: ['', 'true', 'off', 'false'],
reload: ['', 'true', 'off', 'false'],
replacestate: ['', 'true', 'off', 'false']
});

@@ -137,6 +137,7 @@

is_external_url(url, base) ||
(a.getAttribute('rel') || '').split(/\s+/).includes('external') ||
a.hasAttribute('download');
(a.getAttribute('rel') || '').split(/\s+/).includes('external');
return { url, external, target };
const download = url?.origin === location.origin && a.hasAttribute('download');
return { url, external, target, download };
}

@@ -180,9 +181,23 @@

/** @param {string | null} value */
function get_option_state(value) {
switch (value) {
case '':
case 'true':
return true;
case 'off':
case 'false':
return false;
default:
return null;
}
}
return {
preload_code: levels[preload_code ?? 'off'],
preload_data: levels[preload_data ?? 'off'],
keep_focus: keep_focus === 'off' ? false : keep_focus === '' ? true : null,
noscroll: noscroll === 'off' ? false : noscroll === '' ? true : null,
reload: reload === 'off' ? false : reload === '' ? true : null,
replace_state: replace_state === 'off' ? false : replace_state === '' ? true : null
keep_focus: get_option_state(keep_focus),
noscroll: get_option_state(noscroll),
reload: get_option_state(reload),
replace_state: get_option_state(replace_state)
};

@@ -224,2 +239,9 @@ }

if (DEV || !BROWSER) {
return {
subscribe,
check: async () => false
};
}
const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__;

@@ -232,4 +254,2 @@

async function check() {
if (DEV || !BROWSER) return false;
clearTimeout(timeout);

@@ -239,10 +259,14 @@

const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
headers: {
pragma: 'no-cache',
'cache-control': 'no-cache'
try {
const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
headers: {
pragma: 'no-cache',
'cache-control': 'no-cache'
}
});
if (!res.ok) {
return false;
}
});
if (res.ok) {
const data = await res.json();

@@ -257,4 +281,4 @@ const updated = data.version !== version;

return updated;
} else {
throw new Error(`Version check failed: ${res.status}`);
} catch {
return false;
}

@@ -261,0 +285,0 @@ }

@@ -1,2 +0,2 @@

export let HttpError = class HttpError {
export class HttpError {
/**

@@ -20,5 +20,5 @@ * @param {number} status

}
};
}
export let Redirect = class Redirect {
export class Redirect {
/**

@@ -32,3 +32,3 @@ * @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status

}
};
}

@@ -38,3 +38,3 @@ /**

*/
export let ActionFailure = class ActionFailure {
export class ActionFailure {
/**

@@ -48,3 +48,3 @@ * @param {number} status

}
};
}

@@ -64,5 +64,8 @@ /**

export function replace_implementations(implementations) {
ActionFailure = implementations.ActionFailure;
HttpError = implementations.HttpError;
Redirect = implementations.Redirect;
// @ts-expect-error
ActionFailure = implementations.ActionFailure; // eslint-disable-line no-class-assign
// @ts-expect-error
HttpError = implementations.HttpError; // eslint-disable-line no-class-assign
// @ts-expect-error
Redirect = implementations.Redirect; // eslint-disable-line no-class-assign
}
declare module '__SERVER__/internal.js' {
export const options: import('types').SSROptions;
export const get_hooks: () => Promise<{
handle?: import('types').Handle;
handleError?: import('types').HandleServerError;
handleFetch?: import('types').HandleFetch;
handle?: import('@sveltejs/kit').Handle;
handleError?: import('@sveltejs/kit').HandleServerError;
handleFetch?: import('@sveltejs/kit').HandleFetch;
}>;
}

@@ -40,7 +40,7 @@ import { parse, serialize } from 'cookie';

/** @type {import('types').Cookies} */
/** @type {import('@sveltejs/kit').Cookies} */
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('types').Cookies}` above is not
// typescript users. `@type {import('@sveltejs/kit').Cookies}` above is not
// sufficient to do so.

@@ -111,28 +111,3 @@

set(name, value, opts = {}) {
let path = opts.path ?? default_path;
new_cookies[name] = {
name,
value,
options: {
...defaults,
...opts,
path
}
};
if (__SVELTEKIT_DEV__) {
const serialized = serialize(name, value, new_cookies[name].options);
if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
}
cookie_paths[name] ??= new Set();
if (!value) {
cookie_paths[name].delete(path);
} else {
cookie_paths[name].add(path);
}
}
set_internal(name, value, { ...defaults, ...opts });
},

@@ -198,3 +173,36 @@

return { cookies, new_cookies, get_cookie_header };
/**
* @param {string} name
* @param {string} value
* @param {import('cookie').CookieSerializeOptions} opts
*/
function set_internal(name, value, opts) {
const path = opts.path ?? default_path;
new_cookies[name] = {
name,
value,
options: {
...opts,
path
}
};
if (__SVELTEKIT_DEV__) {
const serialized = serialize(name, value, new_cookies[name].options);
if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
}
cookie_paths[name] ??= new Set();
if (!value) {
cookie_paths[name].delete(path);
} else {
cookie_paths[name].add(path);
}
}
}
return { cookies, new_cookies, get_cookie_header, set_internal };
}

@@ -201,0 +209,0 @@

@@ -11,11 +11,9 @@ import { HttpError, Redirect } from '../../control.js';

export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
const encoder = new TextEncoder();
/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSRRoute} route
* @param {import('types').SSROptions} options
* @param {import('types').SSRManifest} manifest
* @param {import('@sveltejs/kit').SSRManifest} manifest
* @param {import('types').SSRState} state

@@ -82,3 +80,4 @@ * @param {boolean[] | undefined} invalidated_data_nodes

return data;
}
},
track_server_fetches: options.track_server_fetches
});

@@ -189,3 +188,3 @@ } catch (e) {

* async iterable containing their resolutions
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSROptions} options

@@ -192,0 +191,0 @@ * @param {Array<import('types').ServerDataSkippedNode | import('types').ServerDataNode | import('types').ServerErrorNode | null | undefined>} nodes

@@ -0,1 +1,2 @@

import { ENDPOINT_METHODS, PAGE_METHODS } from '../../constants.js';
import { negotiate } from '../../utils/http.js';

@@ -6,3 +7,3 @@ import { Redirect } from '../control.js';

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSREndpoint} mod

@@ -43,4 +44,4 @@ * @param {import('types').SSRState} state

try {
const response = await handler(
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
let response = await handler(
/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event)
);

@@ -55,2 +56,9 @@

if (state.prerendering) {
// the returned Response might have immutable Headers
// so we should clone them before trying to mutate them
response = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: new Headers(response.headers)
});
response.headers.set('x-sveltekit-prerender', String(prerender));

@@ -73,3 +81,3 @@ }

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
*/

@@ -79,4 +87,4 @@ export function is_endpoint_request(event) {

if (method === 'PUT' || method === 'PATCH' || method === 'DELETE' || method === 'OPTIONS') {
// These methods exist exclusively for endpoints
// These methods exist exclusively for endpoints
if (ENDPOINT_METHODS.has(method) && !PAGE_METHODS.has(method)) {
return true;

@@ -83,0 +91,0 @@ }

@@ -7,16 +7,15 @@ import * as set_cookie_parser from 'set-cookie-parser';

* @param {{
* event: import('types').RequestEvent;
* event: import('@sveltejs/kit').RequestEvent;
* options: import('types').SSROptions;
* manifest: import('types').SSRManifest;
* manifest: import('@sveltejs/kit').SSRManifest;
* state: import('types').SSRState;
* get_cookie_header: (url: URL, header: string | null) => string;
* set_internal: (name: string, value: string, opts: import('cookie').CookieSerializeOptions) => void;
* }} opts
* @returns {typeof fetch}
*/
export function create_fetch({ event, options, manifest, state, get_cookie_header }) {
export function create_fetch({ event, options, manifest, state, get_cookie_header, set_internal }) {
return async (info, init) => {
const original_request = normalize_fetch_input(info, init, event.url);
const request_body = init?.body;
// some runtimes (e.g. Cloudflare) error if you access `request.mode`,

@@ -72,5 +71,2 @@ // annoyingly, so we need to read the value from the `init` object instead

/** @type {Response} */
let response;
// handle fetch requests for static assets. e.g. prebaked data, etc.

@@ -116,11 +112,2 @@ // we need to support everything the browser's fetch supports

if (request_body && typeof request_body !== 'string' && !ArrayBuffer.isView(request_body)) {
// TODO is this still necessary? we just bail out below
// per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
// Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
// non-string bodies are irksome to deal with, but luckily aren't particularly useful
// in this context anyway, so we take the easy route and ban them
throw new Error('Request body must be a string or TypedArray');
}
if (!request.headers.has('accept')) {

@@ -137,3 +124,4 @@ request.headers.set('accept', '*/*');

response = await respond(request, options, manifest, {
/** @type {Response} */
const response = await respond(request, options, manifest, {
...state,

@@ -149,3 +137,3 @@ depth: state.depth + 1

// options.sameSite is string, something more specific is required - type cast is safe
event.cookies.set(
set_internal(
name,

@@ -152,0 +140,0 @@ value,

import { respond } from './respond.js';
import { set_private_env, set_public_env } from '../shared-server.js';
import { options, get_hooks } from '__SERVER__/internal.js';
import { DEV } from 'esm-env';
import { filter_private_env, filter_public_env } from '../../utils/env.js';

@@ -9,6 +11,6 @@ export class Server {

/** @type {import('types').SSRManifest} */
/** @type {import('@sveltejs/kit').SSRManifest} */
#manifest;
/** @param {import('types').SSRManifest} manifest */
/** @param {import('@sveltejs/kit').SSRManifest} manifest */
constructor(manifest) {

@@ -29,20 +31,38 @@ /** @type {import('types').SSROptions} */

// been done already.
const entries = Object.entries(env);
// set env, in case it's used in initialisation
set_private_env(
filter_private_env(env, {
public_prefix: this.#options.env_public_prefix,
private_prefix: this.#options.env_private_prefix
})
);
set_public_env(
filter_public_env(env, {
public_prefix: this.#options.env_public_prefix,
private_prefix: this.#options.env_private_prefix
})
);
const prefix = this.#options.env_public_prefix;
const prv = Object.fromEntries(entries.filter(([k]) => !k.startsWith(prefix)));
const pub = Object.fromEntries(entries.filter(([k]) => k.startsWith(prefix)));
set_private_env(prv);
set_public_env(pub);
if (!this.#options.hooks) {
const module = await get_hooks();
try {
const module = await get_hooks();
this.#options.hooks = {
handle: module.handle || (({ event, resolve }) => resolve(event)),
// @ts-expect-error
handleError: module.handleError || (({ error }) => console.error(error?.stack)),
handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request))
};
this.#options.hooks = {
handle: module.handle || (({ event, resolve }) => resolve(event)),
handleError: module.handleError || (({ error }) => console.error(error)),
handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request))
};
} catch (error) {
if (DEV) {
this.#options.hooks = {
handle: () => {
throw error;
},
handleError: ({ error }) => console.error(error),
handleFetch: ({ request, fetch }) => fetch(request)
};
} else {
throw error;
}
}
}

@@ -49,0 +69,0 @@ }

@@ -8,3 +8,3 @@ import * as devalue from 'devalue';

/** @param {import('types').RequestEvent} event */
/** @param {import('@sveltejs/kit').RequestEvent} event */
export function is_action_json_request(event) {

@@ -20,3 +20,3 @@ const accept = negotiate(event.request.headers.get('accept') ?? '*/*', [

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSROptions} options

@@ -77,7 +77,3 @@ * @param {import('types').SSRNode['server'] | undefined} server

if (err instanceof Redirect) {
return action_json({
type: 'redirect',
status: err.status,
location: err.location
});
return action_json_redirect(err);
}

@@ -102,3 +98,3 @@

return error instanceof ActionFailure
? new Error(`Cannot "throw fail()". Use "return fail()"`)
? new Error('Cannot "throw fail()". Use "return fail()"')
: error;

@@ -108,3 +104,14 @@ }

/**
* @param {import('types').ActionResult} data
* @param {import('@sveltejs/kit').Redirect} redirect
*/
export function action_json_redirect(redirect) {
return action_json({
type: 'redirect',
status: redirect.status,
location: redirect.location
});
}
/**
* @param {import('@sveltejs/kit').ActionResult} data
* @param {ResponseInit} [init]

@@ -117,3 +124,3 @@ */

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
*/

@@ -125,5 +132,5 @@ export function is_action_request(event) {

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSRNode['server'] | undefined} server
* @returns {Promise<import('types').ActionResult>}
* @returns {Promise<import('@sveltejs/kit').ActionResult>}
*/

@@ -188,3 +195,3 @@ export async function handle_action_request(event, server) {

/**
* @param {import('types').Actions} actions
* @param {import('@sveltejs/kit').Actions} actions
*/

@@ -194,3 +201,3 @@ function check_named_default_separate(actions) {

throw new Error(
`When using named actions, the default action cannot be used. See the docs for more info: https://kit.svelte.dev/docs/form-actions#named-actions`
'When using named actions, the default action cannot be used. See the docs for more info: https://kit.svelte.dev/docs/form-actions#named-actions'
);

@@ -201,3 +208,3 @@ }

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {NonNullable<import('types').SSRNode['server']['actions']>} actions

@@ -237,3 +244,3 @@ * @throws {Redirect | ActionFailure | HttpError | Error}

if (data instanceof Redirect) {
throw new Error(`Cannot \`return redirect(...)\` — use \`throw redirect(...)\` instead`);
throw new Error('Cannot `return redirect(...)` — use `throw redirect(...)` instead');
}

@@ -243,3 +250,3 @@

throw new Error(
`Cannot \`return error(...)\` — use \`throw error(...)\` or \`return fail(...)\` instead`
'Cannot `return error(...)` — use `throw error(...)` or `return fail(...)` instead'
);

@@ -246,0 +253,0 @@ }

@@ -181,4 +181,9 @@ import { escape_html_attr } from '../../../utils/escape.js';

get_meta() {
const content = escape_html_attr(this.get_header(true));
return `<meta http-equiv="content-security-policy" content=${content}>`;
const content = this.get_header(true);
if (!content) {
return;
}
return `<meta http-equiv="content-security-policy" content=${escape_html_attr(content)}>`;
}

@@ -185,0 +190,0 @@ }

@@ -25,6 +25,6 @@ import { text } from '../../../exports/index.js';

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').PageNodeIndexes} page
* @param {import('types').SSROptions} options
* @param {import('types').SSRManifest} manifest
* @param {import('@sveltejs/kit').SSRManifest} manifest
* @param {import('types').SSRState} state

@@ -58,3 +58,3 @@ * @param {import('types').RequiredResolveOptions} resolve_opts

/** @type {import('types').ActionResult | undefined} */
/** @type {import('@sveltejs/kit').ActionResult | undefined} */
let action_result = undefined;

@@ -123,3 +123,3 @@

/** @type {Array<import('./types.js').Loaded | null>} */
let branch = [];
const branch = [];

@@ -156,3 +156,4 @@ /** @type {Error | null} */

return data;
}
},
track_server_fetches: options.track_server_fetches
});

@@ -159,0 +160,0 @@ } catch (e) {

@@ -9,10 +9,18 @@ import { disable_search, make_trackable } from '../../../utils/url.js';

* @param {{
* event: import('types').RequestEvent;
* event: import('@sveltejs/kit').RequestEvent;
* state: import('types').SSRState;
* node: import('types').SSRNode | undefined;
* parent: () => Promise<Record<string, any>>;
* track_server_fetches: boolean;
* }} opts
* @returns {Promise<import('types').ServerDataNode | null>}
*/
export async function load_server_data({ event, state, node, parent }) {
export async function load_server_data({
event,
state,
node,
parent,
// TODO 2.0: Remove this
track_server_fetches
}) {
if (!node?.server) return null;

@@ -55,3 +63,6 @@

uses.dependencies.add(url.href);
// TODO 2.0: Remove this
if (track_server_fetches) {
uses.dependencies.add(url.href);
}

@@ -137,3 +148,3 @@ return event.fetch(info, init);

* @param {{
* event: import('types').RequestEvent;
* event: import('@sveltejs/kit').RequestEvent;
* fetched: import('./types').Fetched[];

@@ -185,7 +196,7 @@ * node: import('types').SSRNode | undefined;

/**
* @param {Pick<import('types').RequestEvent, 'fetch' | 'url' | 'request' | 'route'>} event
* @param {import("types").SSRState} state
* @param {import("./types").Fetched[]} fetched
* @param {Pick<import('@sveltejs/kit').RequestEvent, 'fetch' | 'url' | 'request' | 'route'>} event
* @param {import('types').SSRState} state
* @param {import('./types').Fetched[]} fetched
* @param {boolean} csr
* @param {Pick<Required<import("types").ResolveOptions>, 'filterSerializedResponseHeaders'>} resolve_opts
* @param {Pick<Required<import('@sveltejs/kit').ResolveOptions>, 'filterSerializedResponseHeaders'>} resolve_opts
*/

@@ -199,2 +210,8 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts) {

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)
: init?.headers;
let response = await event.fetch(input, init);

@@ -257,5 +274,5 @@

),
request_headers: init?.headers,
request_headers: cloned_headers,
response_body: body,
response: response
response
});

@@ -262,0 +279,0 @@ }

@@ -31,3 +31,3 @@ import * as devalue from 'devalue';

* options: import('types').SSROptions;
* manifest: import('types').SSRManifest;
* manifest: import('@sveltejs/kit').SSRManifest;
* state: import('types').SSRState;

@@ -37,5 +37,5 @@ * page_config: { ssr: boolean; csr: boolean };

* error: App.Error | null;
* event: import('types').RequestEvent;
* event: import('@sveltejs/kit').RequestEvent;
* resolve_opts: import('types').RequiredResolveOptions;
* action_result?: import('types').ActionResult;
* action_result?: import('@sveltejs/kit').ActionResult;
* }} opts

@@ -68,5 +68,5 @@ */

const modulepreloads = new Set([...client.start.imports, ...client.app.imports]);
const stylesheets = new Set(client.app.stylesheets);
const fonts = new Set(client.app.fonts);
const modulepreloads = new Set(client.imports);
const stylesheets = new Set(client.stylesheets);
const fonts = new Set(client.fonts);

@@ -101,20 +101,9 @@ /** @type {Set<string>} */

if (paths.relative !== false && !state.prerendering?.fallback) {
const segments = event.url.pathname.slice(paths.base.length).split('/');
const segments = event.url.pathname.slice(paths.base.length).split('/').slice(2);
if (segments.length === 1 && paths.base !== '') {
// if we're on `/my-base-path`, relative links need to start `./my-base-path` rather than `.`
base = `./${paths.base.split('/').at(-1)}`;
base = segments.map(() => '..').join('/') || '.';
base_expression = `new URL(${s(base)}, location).pathname`;
} else {
base =
segments
.slice(2)
.map(() => '..')
.join('/') || '.';
// resolve e.g. '../..' against current location, then remove trailing slash
base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;
// resolve e.g. '../..' against current location, then remove trailing slash
base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;
}
if (!paths.assets || (paths.assets[0] === '/' && paths.assets !== SVELTE_KIT_ASSETS)) {

@@ -175,3 +164,3 @@ assets = base;

console.warn(
`Avoid calling \`fetch\` eagerly during server side rendering — put your \`fetch\` calls inside \`onMount\` or a \`load\` function instead`
'Avoid calling `fetch` eagerly during server side rendering — put your `fetch` calls inside `onMount` or a `load` function instead'
);

@@ -276,3 +265,3 @@ warned = true;

const global = __SVELTEKIT_DEV__ ? `__sveltekit_dev` : `__sveltekit_${options.version_hash}`;
const global = __SVELTEKIT_DEV__ ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;

@@ -312,10 +301,9 @@ const { data, chunks } = get_data(

const properties = [
`env: ${s(public_env)}`,
paths.assets && `assets: ${s(paths.assets)}`,
`base: ${base_expression}`,
`element: document.currentScript.parentElement`
`env: ${s(public_env)}`
].filter(Boolean);
if (chunks) {
blocks.push(`const deferred = new Map();`);
blocks.push('const deferred = new Map();');

@@ -339,4 +327,6 @@ properties.push(`defer: (id) => new Promise((fulfil, reject) => {

const args = [`app`, `${global}.element`];
const args = ['app', 'element'];
blocks.push('const element = document.currentScript.parentElement;');
if (page_config.ssr) {

@@ -360,3 +350,3 @@ const serialized = { form: 'null', error: 'null' };

`node_ids: [${branch.map(({ node }) => node.index).join(', ')}]`,
`data`,
'data',
`form: ${serialized.form}`,

@@ -378,4 +368,4 @@ `error: ${serialized.error}`

blocks.push(`Promise.all([
import(${s(prefixed(client.start.file))}),
import(${s(prefixed(client.app.file))})
import(${s(prefixed(client.start))}),
import(${s(prefixed(client.app))})
]).then(([kit, app]) => {

@@ -386,3 +376,3 @@ kit.start(${args.join(', ')});

if (options.service_worker) {
const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
const opts = __SVELTEKIT_DEV__ ? ", { type: 'module' }" : '';

@@ -514,3 +504,3 @@ // we use an anonymous function instead of an arrow function to support

* async iterable containing their resolutions
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSROptions} options

@@ -517,0 +507,0 @@ * @param {Array<import('types').ServerDataNode | null>} nodes

@@ -13,5 +13,5 @@ import { render_response } from './render.js';

* @param {{
* event: import('types').RequestEvent;
* event: import('@sveltejs/kit').RequestEvent;
* options: import('types').SSROptions;
* manifest: import('types').SSRManifest;
* manifest: import('@sveltejs/kit').SSRManifest;
* state: import('types').SSRState;

@@ -48,3 +48,4 @@ * status: number;

node: default_layout,
parent: async () => ({})
parent: async () => ({}),
track_server_fetches: options.track_server_fetches
});

@@ -51,0 +52,0 @@

@@ -49,3 +49,3 @@ import { escape_html_attr } from '../../../utils/escape.js';

let age = null;
let vary = false;
let varyAny = false;

@@ -58,4 +58,4 @@ for (const [key, value] of fetched.response.headers) {

if (key === 'cache-control') cache_control = value;
if (key === 'age') age = value;
if (key === 'vary') vary = true;
else if (key === 'age') age = value;
else if (key === 'vary' && value.trim() === '*') varyAny = true;
}

@@ -94,6 +94,5 @@

// Compute the time the response should be cached, taking into account max-age and age.
// Do not cache at all if a vary header is present, as this indicates that the cache is
// likely to get busted. It would also mean we'd have to add more logic to computing the
// selector on the client which results in more code for 99% of people for the 1% who use vary.
if (!prerendering && fetched.method === 'GET' && cache_control && !vary) {
// Do not cache at all if a `Vary: *` header is present, as this indicates that the
// cache is likely to get busted.
if (!prerendering && fetched.method === 'GET' && cache_control && !varyAny) {
const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control);

@@ -100,0 +99,0 @@ if (match) {

@@ -8,3 +8,3 @@ import { DEV } from 'esm-env';

import { is_form_content_type } from '../../utils/http.js';
import { handle_fatal_error, redirect_response } from './utils.js';
import { handle_fatal_error, method_not_allowed, redirect_response } from './utils.js';
import {

@@ -19,3 +19,3 @@ decode_pathname,

import { exec } from '../../utils/routing.js';
import { INVALIDATED_PARAM, redirect_json_response, render_data } from './data/index.js';
import { redirect_json_response, render_data } from './data/index.js';
import { add_cookies_to_headers, get_cookies } from './cookie.js';

@@ -25,3 +25,5 @@ import { create_fetch } from './fetch.js';

import {
validate_common_exports,
validate_layout_exports,
validate_layout_server_exports,
validate_page_exports,
validate_page_server_exports,

@@ -32,2 +34,4 @@ validate_server_exports

import { error, json, text } from '../../exports/index.js';
import { action_json_redirect, is_action_json_request } from './page/actions.js';
import { INVALIDATED_PARAM } from '../shared.js';

@@ -45,6 +49,10 @@ /* global __SVELTEKIT_ADAPTER_NAME__ */

const page_methods = new Set(['GET', 'HEAD', 'POST']);
const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']);
/**
* @param {Request} request
* @param {import('types').SSROptions} options
* @param {import('types').SSRManifest} manifest
* @param {import('@sveltejs/kit').SSRManifest} manifest
* @param {import('types').SSRState} state

@@ -55,9 +63,12 @@ * @returns {Promise<Response>}

/** URL but stripped from the potential `/__data.json` suffix and its search param */
let url = new URL(request.url);
const url = new URL(request.url);
if (options.csrf_check_origin) {
const forbidden =
request.method === 'POST' &&
request.headers.get('origin') !== url.origin &&
is_form_content_type(request);
is_form_content_type(request) &&
(request.method === 'POST' ||
request.method === 'PUT' ||
request.method === 'PATCH' ||
request.method === 'DELETE') &&
request.headers.get('origin') !== url.origin;

@@ -99,3 +110,6 @@ if (forbidden) {

url.pathname = strip_data_suffix(url.pathname) || '/';
invalidated_data_nodes = url.searchParams.get(INVALIDATED_PARAM)?.split('_').map(Boolean);
invalidated_data_nodes = url.searchParams
.get(INVALIDATED_PARAM)
?.split('')
.map((node) => node === '1');
url.searchParams.delete(INVALIDATED_PARAM);

@@ -130,3 +144,3 @@ }

/** @type {import('types').RequestEvent} */
/** @type {import('@sveltejs/kit').RequestEvent} */
const event = {

@@ -156,3 +170,3 @@ // @ts-expect-error `cookies` and `fetch` need to be created after the `event` itself

throw new Error(
`Use \`event.cookies.set(name, value, options)\` instead of \`event.setHeaders\` to set cookies`
'Use `event.cookies.set(name, value, options)` instead of `event.setHeaders` to set cookies'
);

@@ -171,3 +185,4 @@ } else if (lower in headers) {

url,
isDataRequest: is_data_request
isDataRequest: is_data_request,
isSubRequest: state.depth > 0
};

@@ -184,4 +199,8 @@

// determine whether we need to redirect to add/remove a trailing slash
if (route && !is_data_request) {
if (route.page) {
if (route) {
// if `paths.base === '/a/b/c`, then the root route is `/a/b/c/`,
// regardless of the `trailingSlash` route option
if (url.pathname === base || url.pathname === base + '/') {
trailing_slash = 'always';
} else if (route.page) {
const nodes = await Promise.all([

@@ -199,4 +218,7 @@ // we use == here rather than === because [undefined] serializes as "[null]"

if (layout) {
validate_common_exports(layout.server, /** @type {string} */ (layout.server_id));
validate_common_exports(
validate_layout_server_exports(
layout.server,
/** @type {string} */ (layout.server_id)
);
validate_layout_exports(
layout.universal,

@@ -210,3 +232,3 @@ /** @type {string} */ (layout.universal_id)

validate_page_server_exports(page.server, /** @type {string} */ (page.server_id));
validate_common_exports(page.universal, /** @type {string} */ (page.universal_id));
validate_page_exports(page.universal, /** @type {string} */ (page.universal_id));
}

@@ -225,19 +247,21 @@ }

const normalized = normalize_path(url.pathname, trailing_slash ?? 'never');
if (!is_data_request) {
const normalized = normalize_path(url.pathname, trailing_slash ?? 'never');
if (normalized !== url.pathname && !state.prerendering?.fallback) {
return new Response(undefined, {
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 (normalized !== url.pathname && !state.prerendering?.fallback) {
return new Response(undefined, {
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)
}
});
}
}
}
const { cookies, new_cookies, get_cookie_header } = get_cookies(
const { cookies, new_cookies, get_cookie_header, set_internal } = get_cookies(
request,

@@ -250,3 +274,10 @@ url,

event.cookies = cookies;
event.fetch = create_fetch({ event, options, manifest, state, get_cookie_header });
event.fetch = create_fetch({
event,
options,
manifest,
state,
get_cookie_header,
set_internal
});

@@ -324,2 +355,4 @@ if (state.prerendering && !state.prerendering.fallback) disable_search(url);

? redirect_json_response(e)
: route?.page && is_action_json_request(event)
? action_json_redirect(e)
: redirect_response(e.status, e.location);

@@ -333,5 +366,4 @@ add_cookies_to_headers(response.headers, Object.values(cookies_to_add));

/**
*
* @param {import('types').RequestEvent} event
* @param {import('types').ResolveOptions} [opts]
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('@sveltejs/kit').ResolveOptions} [opts]
*/

@@ -370,2 +402,4 @@ async function resolve(event, opts) {

if (route) {
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
/** @type {Response} */

@@ -387,3 +421,28 @@ let response;

} else if (route.page) {
response = await render_page(event, route.page, options, manifest, state, resolve_opts);
if (page_methods.has(method)) {
response = await render_page(event, route.page, options, manifest, state, resolve_opts);
} else {
const allowed_methods = new Set(allowed_page_methods);
const node = await manifest._.nodes[route.page.leaf]();
if (node?.server?.actions) {
allowed_methods.add('POST');
}
if (method === 'OPTIONS') {
// This will deny CORS preflight requests implicitly because we don't
// add the required CORS headers to the response.
response = new Response(null, {
status: 204,
headers: {
allow: Array.from(allowed_methods.values()).join(', ')
}
});
} else {
const mod = [...allowed_methods].reduce((acc, curr) => {
acc[curr] = true;
return acc;
}, /** @type {Record<string, any>} */ ({}));
response = method_not_allowed(mod, method);
}
}
} else {

@@ -395,2 +454,14 @@ // a route will always have a page or an endpoint, but TypeScript

// If the route contains a page and an endpoint, we need to add a
// `Vary: Accept` header to the response because of browser caching
if (request.method === 'GET' && route.page && route.endpoint) {
const vary = response.headers
.get('vary')
?.split(',')
?.map((v) => v.trim().toLowerCase());
if (!(vary?.includes('accept') || vary?.includes('*'))) {
response.headers.append('Vary', 'Accept');
}
}
return response;

@@ -397,0 +468,0 @@ }

@@ -7,2 +7,3 @@ import { DEV } from 'esm-env';

import { fix_stack_trace } from '../shared-server.js';
import { ENDPOINT_METHODS } from '../../constants.js';

@@ -38,10 +39,6 @@ /** @param {any} body */

export function allowed_methods(mod) {
const allowed = [];
const allowed = Array.from(ENDPOINT_METHODS).filter((method) => method in mod);
for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) {
if (method in mod) allowed.push(method);
}
if ('GET' in mod || 'HEAD' in mod) allowed.push('HEAD');
if (mod.GET || mod.HEAD) allowed.push('HEAD');
return allowed;

@@ -72,3 +69,3 @@ }

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSROptions} options

@@ -82,3 +79,3 @@ * @param {unknown} error

// ideally we'd use sec-fetch-dest instead, but Safari — quelle surprise — doesn't support it
// ideally we'd use sec-fetch-dest instead, but Safari — quelle surprise — doesn't support it
const type = negotiate(event.request.headers.get('accept') || 'text/html', [

@@ -99,3 +96,3 @@ 'application/json',

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('types').SSROptions} options

@@ -142,3 +139,3 @@ * @param {any} error

/**
* @param {import('types').RequestEvent} event
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {Error & { path: string }} error

@@ -173,7 +170,7 @@ */

if (node.uses?.parent) uses.push(`"parent":1`);
if (node.uses?.route) uses.push(`"route":1`);
if (node.uses?.url) uses.push(`"url":1`);
if (node.uses?.parent) uses.push('"parent":1');
if (node.uses?.route) uses.push('"route":1');
if (node.uses?.url) uses.push('"url":1');
return `"uses":{${uses.join(',')}}`;
}

@@ -13,1 +13,3 @@ /**

}
export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';

@@ -1,3 +0,1 @@

import { HttpError, Redirect } from '../runtime/control.js';
/**

@@ -21,3 +19,5 @@ * @param {unknown} err

export function normalize_error(error) {
return /** @type {Redirect | HttpError | Error} */ (error);
return /** @type {import('../runtime/control.js').Redirect | import('../runtime/control.js').HttpError | Error} */ (
error
);
}
/**
* @param {string[]} expected
* @param {Set<string>} expected
*/
function validator(expected) {
const set = new Set(expected);
/**

@@ -15,7 +13,9 @@ * @param {any} module

for (const key in module) {
if (key[0] === '_' || set.has(key)) continue; // key is valid in this module
if (key[0] === '_' || expected.has(key)) continue; // key is valid in this module
const values = [...expected.values()];
const hint =
hint_for_supported_files(key, file?.slice(file.lastIndexOf('.'))) ??
`valid exports are ${expected.join(', ')}, or anything with a '_' prefix`;
`valid exports are ${values.join(', ')}, or anything with a '_' prefix`;

@@ -35,13 +35,21 @@ throw new Error(`Invalid export '${key}'${file ? ` in ${file}` : ''} (${hint})`);

function hint_for_supported_files(key, ext = '.js') {
let supported_files = [];
const supported_files = [];
if (valid_common_exports.includes(key)) {
if (valid_layout_exports.has(key)) {
supported_files.push(`+layout${ext}`);
}
if (valid_page_exports.has(key)) {
supported_files.push(`+page${ext}`);
}
if (valid_page_server_exports.includes(key)) {
if (valid_layout_server_exports.has(key)) {
supported_files.push(`+layout.server${ext}`);
}
if (valid_page_server_exports.has(key)) {
supported_files.push(`+page.server${ext}`);
}
if (valid_server_exports.includes(key)) {
if (valid_server_exports.has(key)) {
supported_files.push(`+server${ext}`);

@@ -51,8 +59,9 @@ }

if (supported_files.length > 0) {
return `'${key}' is a valid export in ${supported_files.join(` or `)}`;
return `'${key}' is a valid export in ${supported_files.slice(0, -1).join(', ')}${
supported_files.length > 1 ? ' or ' : ''
}${supported_files.at(-1)}`;
}
}
const valid_common_exports = ['load', 'prerender', 'csr', 'ssr', 'trailingSlash', 'config'];
const valid_page_server_exports = [
const valid_layout_exports = new Set([
'load',

@@ -62,7 +71,9 @@ 'prerender',

'ssr',
'actions',
'trailingSlash',
'config'
];
const valid_server_exports = [
]);
const valid_page_exports = new Set([...valid_layout_exports, 'entries']);
const valid_layout_server_exports = new Set([...valid_layout_exports]);
const valid_page_server_exports = new Set([...valid_layout_server_exports, 'actions', 'entries']);
const valid_server_exports = new Set([
'GET',

@@ -74,9 +85,13 @@ 'POST',

'OPTIONS',
'HEAD',
'prerender',
'trailingSlash',
'config'
];
'config',
'entries'
]);
export const validate_common_exports = validator(valid_common_exports);
export const validate_layout_exports = validator(valid_layout_exports);
export const validate_page_exports = validator(valid_page_exports);
export const validate_layout_server_exports = validator(valid_layout_server_exports);
export const validate_page_server_exports = validator(valid_page_server_exports);
export const validate_server_exports = validator(valid_server_exports);
import { fileURLToPath } from 'node:url';
import child_process from 'node:child_process';
import { Worker, parentPort } from 'node:worker_threads';

@@ -14,19 +14,17 @@ /**

export function forked(module, callback) {
if (process.env.SVELTEKIT_FORK && process.send) {
process.send({ type: 'ready', module });
process.on(
if (process.env.SVELTEKIT_FORK && parentPort) {
parentPort.on(
'message',
/** @param {any} data */ async (data) => {
if (data?.type === 'args' && data.module === module) {
if (process.send) {
process.send({
type: 'result',
module,
payload: await callback(data.payload)
});
}
parentPort?.postMessage({
type: 'result',
module,
payload: await callback(data.payload)
});
}
}
);
parentPort.postMessage({ type: 'ready', module });
}

@@ -40,16 +38,14 @@

return new Promise((fulfil, reject) => {
const child = child_process.fork(fileURLToPath(module), {
stdio: 'inherit',
const worker = new Worker(fileURLToPath(module), {
env: {
...process.env,
SVELTEKIT_FORK: 'true'
},
serialization: 'advanced'
}
});
child.on(
worker.on(
'message',
/** @param {any} data */ (data) => {
if (data?.type === 'ready' && data.module === module) {
child.send({
worker.postMessage({
type: 'args',

@@ -62,3 +58,3 @@ module,

if (data?.type === 'result' && data.module === module) {
child.kill();
worker.terminate();
fulfil(data.payload);

@@ -69,3 +65,3 @@ }

child.on('exit', (code) => {
worker.on('exit', (code) => {
if (code) {

@@ -72,0 +68,0 @@ reject(new Error(`Failed with code ${code}`));

@@ -62,5 +62,5 @@ /**

*/
export function is_content_type(request, ...types) {
function is_content_type(request, ...types) {
const type = request.headers.get('content-type')?.split(';', 1)[0].trim() ?? '';
return types.includes(type);
return types.includes(type.toLowerCase());
}

@@ -72,3 +72,10 @@

export function is_form_content_type(request) {
return is_content_type(request, 'application/x-www-form-urlencoded', 'multipart/form-data');
// These content types must be protected against CSRF
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype
return is_content_type(
request,
'application/x-www-form-urlencoded',
'multipart/form-data',
'text/plain'
);
}
/**
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
* @template {Option extends 'prerender' ? import('types').PrerenderOption : Option extends 'trailingSlash' ? import('types').TrailingSlash : boolean} Value
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash' | 'entries'} Option
* @template {(import('types').SSRNode['universal'] | import('types').SSRNode['server'])[Option]} Value
*

@@ -12,3 +12,3 @@ * @param {Array<import('types').SSRNode | undefined>} nodes

return nodes.reduce((value, node) => {
return /** @type {any} TypeScript's too dumb to understand this */ (
return /** @type {Value} TypeScript's too dumb to understand this */ (
node?.universal?.[option] ?? node?.server?.[option] ?? value

@@ -15,0 +15,0 @@ );

@@ -99,3 +99,14 @@ const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;

const optional_param_regex = /\/\[\[\w+?(?:=\w+)?\]\]/;
/**
* Removes optional params from a route ID.
* @param {string} id
* @returns The route id with optional params removed
*/
export function remove_optional_params(id) {
return id.replace(optional_param_regex, '');
}
/**
* Returns `false` for `(group)` segments

@@ -122,3 +133,3 @@ * @param {string} segment

* @param {import('types').RouteParam[]} params
* @param {Record<string, import('types').ParamMatcher>} matchers
* @param {Record<string, import('@sveltejs/kit').ParamMatcher>} matchers
*/

@@ -135,3 +146,3 @@ export function exec(match, params, matchers) {

const param = params[i];
const value = values[i - buffered];
let value = values[i - buffered];

@@ -141,3 +152,3 @@ // in the `[[a=b]]/.../[...rest]` case, if one or more optional parameters

if (param.chained && param.rest && buffered) {
result[param.name] = values
value = values
.slice(i - buffered, i + 1)

@@ -148,3 +159,2 @@ .filter((s) => s)

buffered = 0;
continue;
}

@@ -165,3 +175,3 @@

const next_value = values[i + 1];
if (next_param && !next_param.rest && next_param.optional && next_value) {
if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
buffered = 0;

@@ -168,0 +178,0 @@ }

/**
* @returns {import("types").Deferred & { promise: Promise<any> }}}
* @returns {import('types').Deferred & { promise: Promise<any> }}}
*/

@@ -26,3 +26,3 @@ function defer() {

export function create_async_iterator() {
let deferred = [defer()];
const deferred = [defer()];

@@ -29,0 +29,0 @@ return {

@@ -96,5 +96,11 @@ import { BROWSER } from 'esm-env';

* which excludes things like `origin`
* @type {Array<keyof URL>}
*/
const tracked_url_properties = ['href', 'pathname', 'search', 'searchParams', 'toString', 'toJSON'];
const tracked_url_properties = /** @type {const} */ ([
'href',
'pathname',
'search',
'searchParams',
'toString',
'toJSON'
]);

@@ -109,8 +115,6 @@ /**

for (const property of tracked_url_properties) {
let value = tracked[property];
Object.defineProperty(tracked, property, {
get() {
callback();
return value;
return url[property];
},

@@ -117,0 +121,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc