first
This commit is contained in:
commit
f4be03ceba
1826 changed files with 830924 additions and 0 deletions
95
frontend/node_modules/@sveltejs/kit/src/cli.js
generated
vendored
Normal file
95
frontend/node_modules/@sveltejs/kit/src/cli.js
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import fs from 'node:fs';
|
||||
import process from 'node:process';
|
||||
import { parseArgs } from 'node:util';
|
||||
import colors from 'kleur';
|
||||
import { load_config } from './core/config/index.js';
|
||||
import { coalesce_to_error } from './utils/error.js';
|
||||
|
||||
/** @param {unknown} e */
|
||||
function handle_error(e) {
|
||||
const error = coalesce_to_error(e);
|
||||
|
||||
if (error.name === 'SyntaxError') throw error;
|
||||
|
||||
console.error(colors.bold().red(`> ${error.message}`));
|
||||
if (error.stack) {
|
||||
console.error(colors.gray(error.stack.split('\n').slice(1).join('\n')));
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
||||
|
||||
const help = `
|
||||
Usage: svelte-kit <command> [options]
|
||||
|
||||
Commands:
|
||||
sync Synchronise generated type definitions
|
||||
|
||||
Options:
|
||||
--version, -v Show version number
|
||||
--help, -h Show this help message
|
||||
|
||||
Sync Options:
|
||||
--mode <mode> Specify a mode for loading environment variables (default: development)
|
||||
`;
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = parseArgs({
|
||||
options: {
|
||||
version: { type: 'boolean', short: 'v' },
|
||||
help: { type: 'boolean', short: 'h' },
|
||||
mode: { type: 'string', default: 'development' }
|
||||
},
|
||||
allowPositionals: true,
|
||||
strict: true
|
||||
});
|
||||
} catch (err) {
|
||||
const error = /** @type {Error} */ (err);
|
||||
console.error(colors.bold().red(`> ${error.message}`));
|
||||
console.log(help);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { values, positionals } = parsed;
|
||||
|
||||
if (values.version) {
|
||||
console.log(pkg.version);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.help) {
|
||||
console.log(help);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const command = positionals[0];
|
||||
|
||||
if (!command) {
|
||||
console.log(help);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (command === 'sync') {
|
||||
const config_files = ['js', 'ts']
|
||||
.map((ext) => `svelte.config.${ext}`)
|
||||
.filter((f) => fs.existsSync(f));
|
||||
if (config_files.length === 0) {
|
||||
console.warn(`Missing Svelte config file in ${process.cwd()} — skipping`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
const config = await load_config();
|
||||
const sync = await import('./core/sync/sync.js');
|
||||
sync.all_types(config, values.mode);
|
||||
} catch (error) {
|
||||
handle_error(error);
|
||||
}
|
||||
} else {
|
||||
console.error(colors.bold().red(`> Unknown command: ${command}`));
|
||||
console.log(help);
|
||||
process.exit(1);
|
||||
}
|
||||
13
frontend/node_modules/@sveltejs/kit/src/constants.js
generated
vendored
Normal file
13
frontend/node_modules/@sveltejs/kit/src/constants.js
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* A fake asset path used in `vite dev` and `vite preview`, so that we can
|
||||
* serve local assets while verifying that requests are correctly prefixed
|
||||
*/
|
||||
export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
|
||||
|
||||
export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
|
||||
|
||||
export const ENDPOINT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'];
|
||||
|
||||
export const MUTATIVE_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
|
||||
export const PAGE_METHODS = ['GET', 'POST', 'HEAD'];
|
||||
369
frontend/node_modules/@sveltejs/kit/src/core/adapt/builder.js
generated
vendored
Normal file
369
frontend/node_modules/@sveltejs/kit/src/core/adapt/builder.js
generated
vendored
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/** @import { Builder } from '@sveltejs/kit' */
|
||||
/** @import { ResolvedConfig } from 'vite' */
|
||||
/** @import { RouteDefinition } from '@sveltejs/kit' */
|
||||
/** @import { RouteData, ValidatedConfig, BuildData, ServerMetadata, ServerMetadataRoute, Prerendered, PrerenderMap, Logger, RemoteChunk } from 'types' */
|
||||
import colors from 'kleur';
|
||||
import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs';
|
||||
import { extname, resolve, join, dirname, relative } from 'node:path';
|
||||
import { pipeline } from 'node:stream';
|
||||
import { promisify } from 'node:util';
|
||||
import zlib from 'node:zlib';
|
||||
import { copy, rimraf, mkdirp, posixify } from '../../utils/filesystem.js';
|
||||
import { generate_manifest } from '../generate_manifest/index.js';
|
||||
import { get_route_segments } from '../../utils/routing.js';
|
||||
import { get_env } from '../../exports/vite/utils.js';
|
||||
import generate_fallback from '../postbuild/fallback.js';
|
||||
import { write } from '../sync/utils.js';
|
||||
import { list_files } from '../utils.js';
|
||||
import { find_server_assets } from '../generate_manifest/find_server_assets.js';
|
||||
import { reserved } from '../env.js';
|
||||
|
||||
const pipe = promisify(pipeline);
|
||||
const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm', '.txt'];
|
||||
|
||||
/**
|
||||
* Creates the Builder which is passed to adapters for building the application.
|
||||
* @param {{
|
||||
* config: ValidatedConfig;
|
||||
* build_data: BuildData;
|
||||
* server_metadata: ServerMetadata;
|
||||
* route_data: RouteData[];
|
||||
* prerendered: Prerendered;
|
||||
* prerender_map: PrerenderMap;
|
||||
* log: Logger;
|
||||
* vite_config: ResolvedConfig;
|
||||
* remotes: RemoteChunk[]
|
||||
* }} opts
|
||||
* @returns {Builder}
|
||||
*/
|
||||
export function create_builder({
|
||||
config,
|
||||
build_data,
|
||||
server_metadata,
|
||||
route_data,
|
||||
prerendered,
|
||||
prerender_map,
|
||||
log,
|
||||
vite_config,
|
||||
remotes
|
||||
}) {
|
||||
/** @type {Map<RouteDefinition, RouteData>} */
|
||||
const lookup = new Map();
|
||||
|
||||
/**
|
||||
* Rather than exposing the internal `RouteData` type, which is subject to change,
|
||||
* we expose a stable type that adapters can use to group/filter routes
|
||||
*/
|
||||
const routes = route_data.map((route) => {
|
||||
const { config, methods, page, api } = /** @type {ServerMetadataRoute} */ (
|
||||
server_metadata.routes.get(route.id)
|
||||
);
|
||||
|
||||
/** @type {RouteDefinition} */
|
||||
const facade = {
|
||||
id: route.id,
|
||||
api,
|
||||
page,
|
||||
segments: get_route_segments(route.id).map((segment) => ({
|
||||
dynamic: segment.includes('['),
|
||||
rest: segment.includes('[...'),
|
||||
content: segment
|
||||
})),
|
||||
pattern: route.pattern,
|
||||
prerender: prerender_map.get(route.id) ?? false,
|
||||
methods,
|
||||
config
|
||||
};
|
||||
|
||||
lookup.set(facade, route);
|
||||
|
||||
return facade;
|
||||
});
|
||||
|
||||
return {
|
||||
log,
|
||||
rimraf,
|
||||
mkdirp,
|
||||
copy,
|
||||
|
||||
config,
|
||||
prerendered,
|
||||
routes,
|
||||
|
||||
async compress(directory) {
|
||||
if (!existsSync(directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const files = list_files(directory, (file) => extensions.includes(extname(file))).map(
|
||||
(file) => resolve(directory, file)
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
files.flatMap((file) => [compress_file(file, 'gz'), compress_file(file, 'br')])
|
||||
);
|
||||
},
|
||||
|
||||
async createEntries(fn) {
|
||||
const seen = new Set();
|
||||
|
||||
for (let i = 0; i < route_data.length; i += 1) {
|
||||
const route = route_data[i];
|
||||
if (prerender_map.get(route.id) === true) continue;
|
||||
const { id, filter, complete } = fn(routes[i]);
|
||||
|
||||
if (seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
|
||||
const group = [route];
|
||||
|
||||
// figure out which lower priority routes should be considered fallbacks
|
||||
for (let j = i + 1; j < route_data.length; j += 1) {
|
||||
if (prerender_map.get(routes[j].id) === true) continue;
|
||||
if (filter(routes[j])) {
|
||||
group.push(route_data[j]);
|
||||
}
|
||||
}
|
||||
|
||||
const filtered = new Set(group);
|
||||
|
||||
// heuristic: if /foo/[bar] is included, /foo/[bar].json should
|
||||
// also be included, since the page likely needs the endpoint
|
||||
// TODO is this still necessary, given the new way of doing things?
|
||||
filtered.forEach((route) => {
|
||||
if (route.page) {
|
||||
const endpoint = route_data.find((candidate) => candidate.id === route.id + '.json');
|
||||
|
||||
if (endpoint) {
|
||||
filtered.add(endpoint);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (filtered.size > 0) {
|
||||
await complete({
|
||||
generateManifest: ({ relativePath }) =>
|
||||
generate_manifest({
|
||||
build_data,
|
||||
prerendered: [],
|
||||
relative_path: relativePath,
|
||||
routes: Array.from(filtered),
|
||||
remotes
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findServerAssets(route_data) {
|
||||
return find_server_assets(
|
||||
build_data,
|
||||
route_data.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
|
||||
);
|
||||
},
|
||||
|
||||
async generateFallback(dest) {
|
||||
const manifest_path = `${config.kit.outDir}/output/server/manifest-full.js`;
|
||||
const env = get_env(config.kit.env, vite_config.mode);
|
||||
|
||||
const fallback = await generate_fallback({
|
||||
manifest_path,
|
||||
env: { ...env.private, ...env.public }
|
||||
});
|
||||
|
||||
if (existsSync(dest)) {
|
||||
console.log(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(
|
||||
`Overwriting ${dest} with fallback page. Consider using a different name for the fallback.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
write(dest, fallback);
|
||||
},
|
||||
|
||||
generateEnvModule() {
|
||||
const dest = `${config.kit.outDir}/output/prerendered/dependencies/${config.kit.appDir}/env.js`;
|
||||
const env = get_env(config.kit.env, vite_config.mode);
|
||||
|
||||
write(dest, `export const env=${JSON.stringify(env.public)}`);
|
||||
},
|
||||
|
||||
generateManifest({ relativePath, routes: subset }) {
|
||||
return generate_manifest({
|
||||
build_data,
|
||||
prerendered: prerendered.paths,
|
||||
relative_path: relativePath,
|
||||
routes: subset
|
||||
? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
|
||||
: route_data.filter((route) => prerender_map.get(route.id) !== true),
|
||||
remotes
|
||||
});
|
||||
},
|
||||
|
||||
getBuildDirectory(name) {
|
||||
return `${config.kit.outDir}/${name}`;
|
||||
},
|
||||
|
||||
getClientDirectory() {
|
||||
return `${config.kit.outDir}/output/client`;
|
||||
},
|
||||
|
||||
getServerDirectory() {
|
||||
return `${config.kit.outDir}/output/server`;
|
||||
},
|
||||
|
||||
getAppPath() {
|
||||
return build_data.app_path;
|
||||
},
|
||||
|
||||
writeClient(dest) {
|
||||
return copy(`${config.kit.outDir}/output/client`, dest, {
|
||||
// avoid making vite build artefacts public
|
||||
filter: (basename) => basename !== '.vite'
|
||||
});
|
||||
},
|
||||
|
||||
writePrerendered(dest) {
|
||||
const source = `${config.kit.outDir}/output/prerendered`;
|
||||
|
||||
return [
|
||||
...copy(`${source}/pages`, dest),
|
||||
...copy(`${source}/dependencies`, dest),
|
||||
...copy(`${source}/data`, dest)
|
||||
];
|
||||
},
|
||||
|
||||
writeServer(dest) {
|
||||
return copy(`${config.kit.outDir}/output/server`, dest);
|
||||
},
|
||||
|
||||
hasServerInstrumentationFile() {
|
||||
return existsSync(`${config.kit.outDir}/output/server/instrumentation.server.js`);
|
||||
},
|
||||
|
||||
instrument({
|
||||
entrypoint,
|
||||
instrumentation,
|
||||
start = join(dirname(entrypoint), 'start.js'),
|
||||
module = {
|
||||
exports: ['default']
|
||||
}
|
||||
}) {
|
||||
if (!existsSync(instrumentation)) {
|
||||
throw new Error(
|
||||
`Instrumentation file ${instrumentation} not found. This is probably a bug in your adapter.`
|
||||
);
|
||||
}
|
||||
if (!existsSync(entrypoint)) {
|
||||
throw new Error(
|
||||
`Entrypoint file ${entrypoint} not found. This is probably a bug in your adapter.`
|
||||
);
|
||||
}
|
||||
|
||||
copy(entrypoint, start);
|
||||
if (existsSync(`${entrypoint}.map`)) {
|
||||
copy(`${entrypoint}.map`, `${start}.map`);
|
||||
}
|
||||
|
||||
const relative_instrumentation = posixify(relative(dirname(entrypoint), instrumentation));
|
||||
const relative_start = posixify(relative(dirname(entrypoint), start));
|
||||
|
||||
const facade =
|
||||
'generateText' in module
|
||||
? module.generateText({
|
||||
instrumentation: relative_instrumentation,
|
||||
start: relative_start
|
||||
})
|
||||
: create_instrumentation_facade({
|
||||
instrumentation: relative_instrumentation,
|
||||
start: relative_start,
|
||||
exports: module.exports
|
||||
});
|
||||
|
||||
rimraf(entrypoint);
|
||||
write(entrypoint, facade);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {'gz' | 'br'} format
|
||||
*/
|
||||
async function compress_file(file, format = 'gz') {
|
||||
const compress =
|
||||
format == 'br'
|
||||
? zlib.createBrotliCompress({
|
||||
params: {
|
||||
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
|
||||
[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
|
||||
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: statSync(file).size
|
||||
}
|
||||
})
|
||||
: zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
|
||||
const source = createReadStream(file);
|
||||
const destination = createWriteStream(`${file}.${format}`);
|
||||
|
||||
await pipe(source, compress, destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of exports, generate a facade that:
|
||||
* - Imports the instrumentation file
|
||||
* - Imports `exports` from the entrypoint (dynamically, if `tla` is true)
|
||||
* - Re-exports `exports` from the entrypoint
|
||||
*
|
||||
* `default` receives special treatment: It will be imported as `default` and exported with `export default`.
|
||||
*
|
||||
* @param {{ instrumentation: string; start: string; exports: string[] }} opts
|
||||
* @returns {string}
|
||||
*/
|
||||
function create_instrumentation_facade({ instrumentation, start, exports }) {
|
||||
const import_instrumentation = `import './${instrumentation}';`;
|
||||
|
||||
let alias_index = 0;
|
||||
const aliases = new Map();
|
||||
|
||||
for (const name of exports.filter((name) => reserved.has(name))) {
|
||||
/*
|
||||
* you can do evil things like `export { c as class }`.
|
||||
* in order to import these, you need to alias them, and then un-alias them when re-exporting
|
||||
* this map will allow us to generate the following:
|
||||
* import { class as _1 } from 'entrypoint';
|
||||
* export { _1 as class };
|
||||
*/
|
||||
let alias = `_${alias_index++}`;
|
||||
while (exports.includes(alias)) {
|
||||
alias = `_${alias_index++}`;
|
||||
}
|
||||
|
||||
aliases.set(name, alias);
|
||||
}
|
||||
|
||||
const import_statements = [];
|
||||
const export_statements = [];
|
||||
|
||||
for (const name of exports) {
|
||||
const alias = aliases.get(name);
|
||||
if (alias) {
|
||||
import_statements.push(`${name}: ${alias}`);
|
||||
export_statements.push(`${alias} as ${name}`);
|
||||
} else {
|
||||
import_statements.push(`${name}`);
|
||||
export_statements.push(`${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const entrypoint_facade = [
|
||||
`const { ${import_statements.join(', ')} } = await import('./${start}');`,
|
||||
export_statements.length > 0 ? `export { ${export_statements.join(', ')} };` : ''
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
|
||||
return `${import_instrumentation}\n${entrypoint_facade}`;
|
||||
}
|
||||
44
frontend/node_modules/@sveltejs/kit/src/core/adapt/index.js
generated
vendored
Normal file
44
frontend/node_modules/@sveltejs/kit/src/core/adapt/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import colors from 'kleur';
|
||||
import { create_builder } from './builder.js';
|
||||
|
||||
/**
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {import('types').BuildData} build_data
|
||||
* @param {import('types').ServerMetadata} server_metadata
|
||||
* @param {import('types').Prerendered} prerendered
|
||||
* @param {import('types').PrerenderMap} prerender_map
|
||||
* @param {import('types').Logger} log
|
||||
* @param {import('types').RemoteChunk[]} remotes
|
||||
* @param {import('vite').ResolvedConfig} vite_config
|
||||
*/
|
||||
export async function adapt(
|
||||
config,
|
||||
build_data,
|
||||
server_metadata,
|
||||
prerendered,
|
||||
prerender_map,
|
||||
log,
|
||||
remotes,
|
||||
vite_config
|
||||
) {
|
||||
// This is only called when adapter is truthy, so the cast is safe
|
||||
const { name, adapt } = /** @type {import('@sveltejs/kit').Adapter} */ (config.kit.adapter);
|
||||
|
||||
console.log(colors.bold().cyan(`\n> Using ${name}`));
|
||||
|
||||
const builder = create_builder({
|
||||
config,
|
||||
build_data,
|
||||
server_metadata,
|
||||
route_data: build_data.manifest_data.routes.filter((route) => route.page || route.endpoint),
|
||||
prerendered,
|
||||
prerender_map,
|
||||
log,
|
||||
remotes,
|
||||
vite_config
|
||||
});
|
||||
|
||||
await adapt(builder);
|
||||
|
||||
log.success('done');
|
||||
}
|
||||
80
frontend/node_modules/@sveltejs/kit/src/core/config/default-error.html
generated
vendored
Normal file
80
frontend/node_modules/@sveltejs/kit/src/core/config/default-error.html
generated
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>%sveltekit.error.message%</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
--bg: white;
|
||||
--fg: #222;
|
||||
--divider: #ccc;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 32rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-weight: 200;
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
top: -0.05rem;
|
||||
}
|
||||
|
||||
.message {
|
||||
border-left: 1px solid var(--divider);
|
||||
padding: 0 0 0 1rem;
|
||||
margin: 0 0 0 1rem;
|
||||
min-height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message h1 {
|
||||
font-weight: 400;
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
--bg: #222;
|
||||
--fg: #ddd;
|
||||
--divider: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error">
|
||||
<span class="status">%sveltekit.status%</span>
|
||||
<div class="message">
|
||||
<h1>%sveltekit.error.message%</h1>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
155
frontend/node_modules/@sveltejs/kit/src/core/config/index.js
generated
vendored
Normal file
155
frontend/node_modules/@sveltejs/kit/src/core/config/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import * as url from 'node:url';
|
||||
import options from './options.js';
|
||||
|
||||
/**
|
||||
* Loads the template (src/app.html by default) and validates that it has the
|
||||
* required content.
|
||||
* @param {string} cwd
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
export function load_template(cwd, { kit }) {
|
||||
const { env, files } = kit;
|
||||
|
||||
const relative = path.relative(cwd, files.appTemplate);
|
||||
|
||||
if (!fs.existsSync(files.appTemplate)) {
|
||||
throw new Error(`${relative} does not exist`);
|
||||
}
|
||||
|
||||
const contents = fs.readFileSync(files.appTemplate, 'utf8');
|
||||
|
||||
const expected_tags = ['%sveltekit.head%', '%sveltekit.body%'];
|
||||
expected_tags.forEach((tag) => {
|
||||
if (contents.indexOf(tag) === -1) {
|
||||
throw new Error(`${relative} is missing ${tag}`);
|
||||
}
|
||||
});
|
||||
|
||||
for (const match of contents.matchAll(/%sveltekit\.env\.([^%]+)%/g)) {
|
||||
if (!match[1].startsWith(env.publicPrefix)) {
|
||||
throw new Error(
|
||||
`Environment variables in ${relative} must start with ${env.publicPrefix} (saw %sveltekit.env.${match[1]}%)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the error page (src/error.html by default) if it exists.
|
||||
* Falls back to a generic error page content.
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
export function load_error_page(config) {
|
||||
let { errorTemplate } = config.kit.files;
|
||||
|
||||
// Don't do this inside resolving the config, because that would mean
|
||||
// adding/removing error.html isn't detected and would require a restart.
|
||||
if (!fs.existsSync(config.kit.files.errorTemplate)) {
|
||||
errorTemplate = url.fileURLToPath(new URL('./default-error.html', import.meta.url));
|
||||
}
|
||||
|
||||
return fs.readFileSync(errorTemplate, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and validates Svelte config file
|
||||
* @param {{ cwd?: string }} options
|
||||
* @returns {Promise<import('types').ValidatedConfig>}
|
||||
*/
|
||||
export async function load_config({ cwd = process.cwd() } = {}) {
|
||||
const config_files = ['js', 'ts']
|
||||
.map((ext) => path.join(cwd, `svelte.config.${ext}`))
|
||||
.filter((f) => fs.existsSync(f));
|
||||
|
||||
if (config_files.length === 0) {
|
||||
console.log(
|
||||
`No Svelte config file found in ${cwd} - using SvelteKit's default configuration without an adapter.`
|
||||
);
|
||||
return process_config({}, { cwd });
|
||||
}
|
||||
const config_file = config_files[0];
|
||||
if (config_files.length > 1) {
|
||||
console.log(
|
||||
`Found multiple Svelte config files in ${cwd}: ${config_files.map((f) => path.basename(f)).join(', ')}. Using ${path.basename(config_file)}`
|
||||
);
|
||||
}
|
||||
const config = await import(`${url.pathToFileURL(config_file).href}?ts=${Date.now()}`);
|
||||
|
||||
try {
|
||||
return process_config(config.default, { cwd });
|
||||
} catch (e) {
|
||||
const error = /** @type {Error} */ (e);
|
||||
|
||||
// redact the stack trace — it's not helpful to users
|
||||
error.stack = `Could not load ${config_file}: ${error.message}\n`;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@sveltejs/kit').Config} config
|
||||
* @returns {import('types').ValidatedConfig}
|
||||
*/
|
||||
function process_config(config, { cwd = process.cwd() } = {}) {
|
||||
const validated = validate_config(config);
|
||||
|
||||
validated.kit.outDir = path.resolve(cwd, validated.kit.outDir);
|
||||
|
||||
for (const key in validated.kit.files) {
|
||||
if (key === 'hooks') {
|
||||
validated.kit.files.hooks.client = path.resolve(cwd, validated.kit.files.hooks.client);
|
||||
validated.kit.files.hooks.server = path.resolve(cwd, validated.kit.files.hooks.server);
|
||||
validated.kit.files.hooks.universal = path.resolve(cwd, validated.kit.files.hooks.universal);
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
validated.kit.files[key] = path.resolve(cwd, validated.kit.files[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@sveltejs/kit').Config} config
|
||||
* @returns {import('types').ValidatedConfig}
|
||||
*/
|
||||
export function validate_config(config) {
|
||||
if (typeof config !== 'object') {
|
||||
throw new Error(
|
||||
'The Svelte config file must have a configuration object as its default export. See https://svelte.dev/docs/kit/configuration'
|
||||
);
|
||||
}
|
||||
|
||||
const validated = options(config, 'config');
|
||||
const files = validated.kit.files;
|
||||
|
||||
files.hooks.client ??= path.join(files.src, 'hooks.client');
|
||||
files.hooks.server ??= path.join(files.src, 'hooks.server');
|
||||
files.hooks.universal ??= path.join(files.src, 'hooks');
|
||||
files.lib ??= path.join(files.src, 'lib');
|
||||
files.params ??= path.join(files.src, 'params');
|
||||
files.routes ??= path.join(files.src, 'routes');
|
||||
files.serviceWorker ??= path.join(files.src, 'service-worker');
|
||||
files.appTemplate ??= path.join(files.src, 'app.html');
|
||||
files.errorTemplate ??= path.join(files.src, 'error.html');
|
||||
|
||||
if (validated.kit.router.resolution === 'server') {
|
||||
if (validated.kit.router.type === 'hash') {
|
||||
throw new Error(
|
||||
"The `router.resolution` option cannot be 'server' if `router.type` is 'hash'"
|
||||
);
|
||||
}
|
||||
if (validated.kit.output.bundleStrategy !== 'split') {
|
||||
throw new Error(
|
||||
"The `router.resolution` option cannot be 'server' if `output.bundleStrategy` is 'inline' or 'single'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
489
frontend/node_modules/@sveltejs/kit/src/core/config/options.js
generated
vendored
Normal file
489
frontend/node_modules/@sveltejs/kit/src/core/config/options.js
generated
vendored
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
import process from 'node:process';
|
||||
import colors from 'kleur';
|
||||
|
||||
/** @typedef {import('./types.js').Validator} Validator */
|
||||
|
||||
const directives = object({
|
||||
'child-src': string_array(),
|
||||
'default-src': string_array(),
|
||||
'frame-src': string_array(),
|
||||
'worker-src': string_array(),
|
||||
'connect-src': string_array(),
|
||||
'font-src': string_array(),
|
||||
'img-src': string_array(),
|
||||
'manifest-src': string_array(),
|
||||
'media-src': string_array(),
|
||||
'object-src': string_array(),
|
||||
'prefetch-src': string_array(),
|
||||
'script-src': string_array(),
|
||||
'script-src-elem': string_array(),
|
||||
'script-src-attr': string_array(),
|
||||
'style-src': string_array(),
|
||||
'style-src-elem': string_array(),
|
||||
'style-src-attr': string_array(),
|
||||
'base-uri': string_array(),
|
||||
sandbox: string_array(),
|
||||
'form-action': string_array(),
|
||||
'frame-ancestors': string_array(),
|
||||
'navigate-to': string_array(),
|
||||
'report-uri': string_array(),
|
||||
'report-to': string_array(),
|
||||
'require-trusted-types-for': string_array(),
|
||||
'trusted-types': string_array(),
|
||||
'upgrade-insecure-requests': boolean(false),
|
||||
'require-sri-for': string_array(),
|
||||
'block-all-mixed-content': boolean(false),
|
||||
'plugin-types': string_array(),
|
||||
referrer: string_array()
|
||||
});
|
||||
|
||||
/** @type {Validator} */
|
||||
const options = object(
|
||||
{
|
||||
extensions: validate(['.svelte'], (input, keypath) => {
|
||||
if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
|
||||
throw new Error(`${keypath} must be an array of strings`);
|
||||
}
|
||||
|
||||
input.forEach((extension) => {
|
||||
if (extension[0] !== '.') {
|
||||
throw new Error(`Each member of ${keypath} must start with '.' — saw '${extension}'`);
|
||||
}
|
||||
|
||||
if (!/^(\.[a-z0-9]+)+$/i.test(extension)) {
|
||||
throw new Error(`File extensions must be alphanumeric — saw '${extension}'`);
|
||||
}
|
||||
});
|
||||
|
||||
return input;
|
||||
}),
|
||||
|
||||
kit: object({
|
||||
adapter: validate(null, (input, keypath) => {
|
||||
if (typeof input !== 'object' || !input.adapt) {
|
||||
let message = `${keypath} should be an object with an "adapt" method`;
|
||||
|
||||
if (Array.isArray(input) || typeof input === 'string') {
|
||||
// for the early adapter adopters
|
||||
message += ', rather than the name of an adapter';
|
||||
}
|
||||
|
||||
throw new Error(`${message}. See https://svelte.dev/docs/kit/adapters`);
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
|
||||
alias: validate({}, (input, keypath) => {
|
||||
if (typeof input !== 'object') {
|
||||
throw new Error(`${keypath} should be an object`);
|
||||
}
|
||||
|
||||
for (const key in input) {
|
||||
assert_string(input[key], `${keypath}.${key}`);
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
|
||||
appDir: validate('_app', (input, keypath) => {
|
||||
assert_string(input, keypath);
|
||||
|
||||
if (input) {
|
||||
if (input.startsWith('/') || input.endsWith('/')) {
|
||||
throw new Error(
|
||||
"config.kit.appDir cannot start or end with '/'. See https://svelte.dev/docs/kit/configuration"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${keypath} cannot be empty`);
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
|
||||
csp: object({
|
||||
mode: list(['auto', 'hash', 'nonce']),
|
||||
directives,
|
||||
reportOnly: directives
|
||||
}),
|
||||
|
||||
csrf: object({
|
||||
checkOrigin: deprecate(
|
||||
boolean(true),
|
||||
(keypath) =>
|
||||
`\`${keypath}\` has been deprecated in favour of \`csrf.trustedOrigins\`. It will be removed in a future version`
|
||||
),
|
||||
trustedOrigins: string_array([])
|
||||
}),
|
||||
|
||||
embedded: boolean(false),
|
||||
|
||||
env: object({
|
||||
dir: string(process.cwd()),
|
||||
publicPrefix: string('PUBLIC_'),
|
||||
privatePrefix: string('')
|
||||
}),
|
||||
|
||||
experimental: object({
|
||||
tracing: object({
|
||||
server: boolean(false)
|
||||
}),
|
||||
instrumentation: object({
|
||||
server: boolean(false)
|
||||
}),
|
||||
remoteFunctions: boolean(false),
|
||||
forkPreloads: boolean(false),
|
||||
handleRenderingErrors: boolean(false)
|
||||
}),
|
||||
|
||||
files: object({
|
||||
src: string('src'),
|
||||
assets: string('static'),
|
||||
hooks: object({
|
||||
client: string(null),
|
||||
server: string(null),
|
||||
universal: string(null)
|
||||
}),
|
||||
lib: string(null),
|
||||
params: string(null),
|
||||
routes: string(null),
|
||||
serviceWorker: string(null),
|
||||
appTemplate: string(null),
|
||||
errorTemplate: string(null)
|
||||
}),
|
||||
|
||||
inlineStyleThreshold: number(0),
|
||||
|
||||
moduleExtensions: string_array(['.js', '.ts']),
|
||||
|
||||
outDir: string('.svelte-kit'),
|
||||
|
||||
output: object({
|
||||
preloadStrategy: list(['modulepreload', 'preload-js', 'preload-mjs']),
|
||||
bundleStrategy: list(['split', 'single', 'inline'])
|
||||
}),
|
||||
|
||||
paths: object({
|
||||
base: validate('', (input, keypath) => {
|
||||
assert_string(input, keypath);
|
||||
|
||||
if (input !== '' && (input.endsWith('/') || !input.startsWith('/'))) {
|
||||
throw new Error(
|
||||
`${keypath} option must either be the empty string or a root-relative path that starts but doesn't end with '/'. See https://svelte.dev/docs/kit/configuration#paths`
|
||||
);
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
assets: validate('', (input, keypath) => {
|
||||
assert_string(input, keypath);
|
||||
|
||||
if (input) {
|
||||
if (!/^[a-z]+:\/\//.test(input)) {
|
||||
throw new Error(
|
||||
`${keypath} option must be an absolute path, if specified. See https://svelte.dev/docs/kit/configuration#paths`
|
||||
);
|
||||
}
|
||||
|
||||
if (input.endsWith('/')) {
|
||||
throw new Error(
|
||||
`${keypath} option must not end with '/'. See https://svelte.dev/docs/kit/configuration#paths`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}),
|
||||
relative: boolean(true)
|
||||
}),
|
||||
|
||||
prerender: object({
|
||||
concurrency: number(1),
|
||||
crawl: boolean(true),
|
||||
entries: validate(['*'], (input, keypath) => {
|
||||
if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
|
||||
throw new Error(`${keypath} must be an array of strings`);
|
||||
}
|
||||
|
||||
input.forEach((page) => {
|
||||
if (page !== '*' && page[0] !== '/') {
|
||||
throw new Error(
|
||||
`Each member of ${keypath} must be either '*' or an absolute path beginning with '/' — saw '${page}'`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return input;
|
||||
}),
|
||||
|
||||
handleHttpError: validate(
|
||||
(/** @type {any} */ { message }) => {
|
||||
throw new Error(
|
||||
message +
|
||||
'\nTo suppress or handle this error, implement `handleHttpError` in https://svelte.dev/docs/kit/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(
|
||||
(/** @type {any} */ { message }) => {
|
||||
throw new Error(
|
||||
message +
|
||||
'\nTo suppress or handle this error, implement `handleMissingId` in https://svelte.dev/docs/kit/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://svelte.dev/docs/kit/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`);
|
||||
}
|
||||
),
|
||||
|
||||
handleUnseenRoutes: validate(
|
||||
(/** @type {any} */ { message }) => {
|
||||
throw new Error(
|
||||
message +
|
||||
'\nTo suppress or handle this error, implement `handleUnseenRoutes` in https://svelte.dev/docs/kit/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) => {
|
||||
assert_string(input, keypath);
|
||||
|
||||
let origin;
|
||||
|
||||
try {
|
||||
origin = new URL(input).origin;
|
||||
} catch {
|
||||
throw new Error(`${keypath} must be a valid origin`);
|
||||
}
|
||||
|
||||
if (input !== origin) {
|
||||
throw new Error(`${keypath} must be a valid origin (${origin} rather than ${input})`);
|
||||
}
|
||||
|
||||
return origin;
|
||||
})
|
||||
}),
|
||||
|
||||
router: object({
|
||||
type: list(['pathname', 'hash']),
|
||||
resolution: list(['client', 'server'])
|
||||
}),
|
||||
|
||||
serviceWorker: object({
|
||||
register: boolean(true),
|
||||
// options could be undefined but if it is defined we only validate that
|
||||
// it's an object since the type comes from the browser itself
|
||||
options: validate(undefined, object({}, true)),
|
||||
files: fun((filename) => !/\.DS_Store/.test(filename))
|
||||
}),
|
||||
|
||||
typescript: object({
|
||||
config: fun((config) => config)
|
||||
}),
|
||||
|
||||
version: object({
|
||||
name: string(Date.now().toString()),
|
||||
pollInterval: number(0)
|
||||
})
|
||||
})
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {Validator} fn
|
||||
* @param {(keypath: string) => string} get_message
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function deprecate(
|
||||
fn,
|
||||
get_message = (keypath) =>
|
||||
`The \`${keypath}\` option is deprecated, and will be removed in a future version`
|
||||
) {
|
||||
return (input, keypath) => {
|
||||
if (input !== undefined) {
|
||||
console.warn(colors.bold().yellow(get_message(keypath)));
|
||||
}
|
||||
|
||||
return fn(input, keypath);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, Validator>} children
|
||||
* @param {boolean} [allow_unknown]
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function object(children, allow_unknown = false) {
|
||||
return (input, keypath) => {
|
||||
/** @type {Record<string, any>} */
|
||||
const output = {};
|
||||
|
||||
if ((input && typeof input !== 'object') || Array.isArray(input)) {
|
||||
throw new Error(`${keypath} should be an object`);
|
||||
}
|
||||
|
||||
for (const key in input) {
|
||||
if (!(key in children)) {
|
||||
if (allow_unknown) {
|
||||
output[key] = input[key];
|
||||
} else {
|
||||
let message = `Unexpected option ${keypath}.${key}`;
|
||||
|
||||
// special case
|
||||
if (keypath === 'config.kit' && key in options) {
|
||||
message += ` (did you mean config.${key}?)`;
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in children) {
|
||||
const validator = children[key];
|
||||
output[key] = validator(input && input[key], `${keypath}.${key}`);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} fallback
|
||||
* @param {(value: any, keypath: string) => any} fn
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function validate(fallback, fn) {
|
||||
return (input, keypath) => {
|
||||
return input === undefined ? fallback : fn(input, keypath);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | null} fallback
|
||||
* @param {boolean} allow_empty
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function string(fallback, allow_empty = true) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
assert_string(input, keypath);
|
||||
|
||||
if (!allow_empty && input === '') {
|
||||
throw new Error(`${keypath} cannot be empty`);
|
||||
}
|
||||
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[] | undefined} [fallback]
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function string_array(fallback) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
if (!Array.isArray(input) || input.some((value) => typeof value !== 'string')) {
|
||||
throw new Error(`${keypath} must be an array of strings, if specified`);
|
||||
}
|
||||
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} fallback
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function number(fallback) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
if (typeof input !== 'number') {
|
||||
throw new Error(`${keypath} should be a number, if specified`);
|
||||
}
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} fallback
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function boolean(fallback) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
if (typeof input !== 'boolean') {
|
||||
throw new Error(`${keypath} should be true or false, if specified`);
|
||||
}
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} options
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function list(options, fallback = options[0]) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
if (!options.includes(input)) {
|
||||
// prettier-ignore
|
||||
const msg = options.length > 2
|
||||
? `${keypath} should be one of ${options.slice(0, -1).map(input => `"${input}"`).join(', ')} or "${options[options.length - 1]}"`
|
||||
: `${keypath} should be either "${options[0]}" or "${options[1]}"`;
|
||||
|
||||
throw new Error(msg);
|
||||
}
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(...args: any) => any} fallback
|
||||
* @returns {Validator}
|
||||
*/
|
||||
function fun(fallback) {
|
||||
return validate(fallback, (input, keypath) => {
|
||||
if (typeof input !== 'function') {
|
||||
throw new Error(`${keypath} should be a function, if specified`);
|
||||
}
|
||||
return input;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {string} keypath
|
||||
*/
|
||||
function assert_string(input, keypath) {
|
||||
if (typeof input !== 'string') {
|
||||
throw new Error(`${keypath} should be a string, if specified`);
|
||||
}
|
||||
}
|
||||
|
||||
export default options;
|
||||
1
frontend/node_modules/@sveltejs/kit/src/core/config/types.d.ts
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/core/config/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export type Validator<T = any> = (input: T, keypath: string) => T;
|
||||
152
frontend/node_modules/@sveltejs/kit/src/core/env.js
generated
vendored
Normal file
152
frontend/node_modules/@sveltejs/kit/src/core/env.js
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import { GENERATED_COMMENT } from '../constants.js';
|
||||
import { dedent } from './sync/utils.js';
|
||||
import { runtime_base } from './utils.js';
|
||||
|
||||
/**
|
||||
* @typedef {'public' | 'private'} EnvType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {Record<string, string>} env
|
||||
* @returns {string}
|
||||
*/
|
||||
export function create_static_module(id, env) {
|
||||
/** @type {string[]} */
|
||||
const declarations = [];
|
||||
|
||||
for (const key in env) {
|
||||
if (!valid_identifier.test(key) || reserved.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const comment = `/** @type {import('${id}').${key}} */`;
|
||||
const declaration = `export const ${key} = ${JSON.stringify(env[key])};`;
|
||||
|
||||
declarations.push(`${comment}\n${declaration}`);
|
||||
}
|
||||
|
||||
return GENERATED_COMMENT + declarations.join('\n\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {EnvType} type
|
||||
* @param {Record<string, string> | undefined} dev_values If in a development mode, values to pre-populate the module with.
|
||||
*/
|
||||
export function create_dynamic_module(type, dev_values) {
|
||||
if (dev_values) {
|
||||
const keys = Object.entries(dev_values).map(
|
||||
([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`
|
||||
);
|
||||
return `export const env = {\n${keys.join(',\n')}\n}`;
|
||||
}
|
||||
return `export { ${type}_env as env } from '${runtime_base}/shared-server.js';`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {EnvType} id
|
||||
* @param {import('types').Env} env
|
||||
* @returns {string}
|
||||
*/
|
||||
export function create_static_types(id, env) {
|
||||
const declarations = Object.keys(env[id])
|
||||
.filter((k) => valid_identifier.test(k))
|
||||
.map((k) => `export const ${k}: string;`);
|
||||
|
||||
return dedent`
|
||||
declare module '$env/static/${id}' {
|
||||
${declarations.join('\n')}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {EnvType} id
|
||||
* @param {import('types').Env} env
|
||||
* @param {{
|
||||
* public_prefix: string;
|
||||
* private_prefix: string;
|
||||
* }} prefixes
|
||||
* @returns {string}
|
||||
*/
|
||||
export function create_dynamic_types(id, env, { public_prefix, private_prefix }) {
|
||||
const properties = Object.keys(env[id])
|
||||
.filter((k) => valid_identifier.test(k))
|
||||
.map((k) => `${k}: string;`);
|
||||
|
||||
const public_prefixed = `[key: \`${public_prefix}\${string}\`]`;
|
||||
const private_prefixed = `[key: \`${private_prefix}\${string}\`]`;
|
||||
|
||||
if (id === 'private') {
|
||||
if (public_prefix) {
|
||||
properties.push(`${public_prefixed}: undefined;`);
|
||||
}
|
||||
properties.push(`${private_prefixed}: string | undefined;`);
|
||||
} else {
|
||||
if (private_prefix) {
|
||||
properties.push(`${private_prefixed}: undefined;`);
|
||||
}
|
||||
properties.push(`${public_prefixed}: string | undefined;`);
|
||||
}
|
||||
|
||||
return dedent`
|
||||
declare module '$env/dynamic/${id}' {
|
||||
export const env: {
|
||||
${properties.join('\n')}
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export const reserved = new Set([
|
||||
'do',
|
||||
'if',
|
||||
'in',
|
||||
'for',
|
||||
'let',
|
||||
'new',
|
||||
'try',
|
||||
'var',
|
||||
'case',
|
||||
'else',
|
||||
'enum',
|
||||
'eval',
|
||||
'null',
|
||||
'this',
|
||||
'true',
|
||||
'void',
|
||||
'with',
|
||||
'await',
|
||||
'break',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'false',
|
||||
'super',
|
||||
'throw',
|
||||
'while',
|
||||
'yield',
|
||||
'delete',
|
||||
'export',
|
||||
'import',
|
||||
'public',
|
||||
'return',
|
||||
'static',
|
||||
'switch',
|
||||
'typeof',
|
||||
'default',
|
||||
'extends',
|
||||
'finally',
|
||||
'package',
|
||||
'private',
|
||||
'continue',
|
||||
'debugger',
|
||||
'function',
|
||||
'arguments',
|
||||
'interface',
|
||||
'protected',
|
||||
'implements',
|
||||
'instanceof'
|
||||
]);
|
||||
|
||||
export const valid_identifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
||||
56
frontend/node_modules/@sveltejs/kit/src/core/generate_manifest/find_server_assets.js
generated
vendored
Normal file
56
frontend/node_modules/@sveltejs/kit/src/core/generate_manifest/find_server_assets.js
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { find_deps } from '../../exports/vite/build/utils.js';
|
||||
|
||||
/**
|
||||
* Finds all the assets that are imported by server files associated with `routes`
|
||||
* @param {import('types').BuildData} build_data
|
||||
* @param {import('types').RouteData[]} routes
|
||||
*/
|
||||
export function find_server_assets(build_data, routes) {
|
||||
/**
|
||||
* All nodes actually used in the routes definition (prerendered routes are omitted).
|
||||
* Root layout/error is always included as they are needed for 404 and root errors.
|
||||
* @type {Set<any>}
|
||||
*/
|
||||
const used_nodes = new Set([0, 1]);
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const server_assets = new Set();
|
||||
|
||||
/** @param {string} id */
|
||||
function add_assets(id) {
|
||||
if (id in build_data.server_manifest) {
|
||||
const deps = find_deps(build_data.server_manifest, id, false);
|
||||
for (const asset of deps.assets) {
|
||||
server_assets.add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.page) {
|
||||
for (const i of route.page.layouts) used_nodes.add(i);
|
||||
for (const i of route.page.errors) used_nodes.add(i);
|
||||
used_nodes.add(route.page.leaf);
|
||||
}
|
||||
|
||||
if (route.endpoint) {
|
||||
add_assets(route.endpoint.file);
|
||||
}
|
||||
}
|
||||
|
||||
for (const n of used_nodes) {
|
||||
const node = build_data.manifest_data.nodes[n];
|
||||
if (node?.universal) add_assets(node.universal);
|
||||
if (node?.server) add_assets(node.server);
|
||||
}
|
||||
|
||||
if (build_data.manifest_data.hooks.server) {
|
||||
add_assets(build_data.manifest_data.hooks.server);
|
||||
}
|
||||
|
||||
if (build_data.manifest_data.hooks.universal) {
|
||||
add_assets(build_data.manifest_data.hooks.universal);
|
||||
}
|
||||
|
||||
return Array.from(server_assets);
|
||||
}
|
||||
152
frontend/node_modules/@sveltejs/kit/src/core/generate_manifest/index.js
generated
vendored
Normal file
152
frontend/node_modules/@sveltejs/kit/src/core/generate_manifest/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/** @import { RemoteChunk } from 'types' */
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import * as mime from 'mrmime';
|
||||
import { s } from '../../utils/misc.js';
|
||||
import { get_mime_lookup } from '../utils.js';
|
||||
import { resolve_symlinks } from '../../exports/vite/build/utils.js';
|
||||
import { compact } from '../../utils/array.js';
|
||||
import { join_relative } from '../../utils/filesystem.js';
|
||||
import { dedent } from '../sync/utils.js';
|
||||
import { find_server_assets } from './find_server_assets.js';
|
||||
import { uneval } from 'devalue';
|
||||
|
||||
/**
|
||||
* Generates the data used to write the server-side manifest.js file. This data is used in the Vite
|
||||
* build process, to power routing, etc.
|
||||
* @param {{
|
||||
* build_data: import('types').BuildData;
|
||||
* prerendered: string[];
|
||||
* relative_path: string;
|
||||
* routes: import('types').RouteData[];
|
||||
* remotes: RemoteChunk[];
|
||||
* }} opts
|
||||
*/
|
||||
export function generate_manifest({ build_data, prerendered, relative_path, routes, remotes }) {
|
||||
/**
|
||||
* @type {Map<any, number>} The new index of each node in the filtered nodes array
|
||||
*/
|
||||
const reindexed = new Map();
|
||||
/**
|
||||
* All nodes actually used in the routes definition (prerendered routes are omitted).
|
||||
* Root layout/error is always included as they are needed for 404 and root errors.
|
||||
* @type {Set<any>}
|
||||
*/
|
||||
const used_nodes = new Set([0, 1]);
|
||||
|
||||
const server_assets = find_server_assets(build_data, routes);
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.page) {
|
||||
for (const i of route.page.layouts) used_nodes.add(i);
|
||||
for (const i of route.page.errors) used_nodes.add(i);
|
||||
used_nodes.add(route.page.leaf);
|
||||
}
|
||||
}
|
||||
|
||||
const node_paths = compact(
|
||||
build_data.manifest_data.nodes.map((_, i) => {
|
||||
if (used_nodes.has(i)) {
|
||||
reindexed.set(i, reindexed.size);
|
||||
return join_relative(relative_path, `/nodes/${i}.js`);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/** @type {(path: string) => string} */
|
||||
const loader = (path) => `__memo(() => import('${path}'))`;
|
||||
|
||||
const assets = build_data.manifest_data.assets.map((asset) => asset.file);
|
||||
if (build_data.service_worker) {
|
||||
assets.push(build_data.service_worker);
|
||||
}
|
||||
|
||||
// In case of server-side route resolution, we need to include all matchers. Prerendered routes are not part
|
||||
// of the server manifest, and they could reference matchers that then would not be included.
|
||||
const matchers = new Set(
|
||||
build_data.client?.nodes ? Object.keys(build_data.manifest_data.matchers) : undefined
|
||||
);
|
||||
|
||||
/** @param {Array<number | undefined>} indexes */
|
||||
function get_nodes(indexes) {
|
||||
const string = indexes.map((n) => reindexed.get(n) ?? '').join(',');
|
||||
|
||||
// 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},]`;
|
||||
}
|
||||
|
||||
const mime_types = get_mime_lookup(build_data.manifest_data);
|
||||
|
||||
/** @type {Record<string, number>} */
|
||||
const files = {};
|
||||
for (const file of server_assets) {
|
||||
files[file] = fs.statSync(path.resolve(build_data.out_dir, 'server', file)).size;
|
||||
|
||||
const ext = path.extname(file);
|
||||
mime_types[ext] ??= mime.lookup(ext) || '';
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
// String representation of
|
||||
/** @template {import('@sveltejs/kit').SSRManifest} T */
|
||||
const manifest_expr = dedent`
|
||||
{
|
||||
appDir: ${s(build_data.app_dir)},
|
||||
appPath: ${s(build_data.app_path)},
|
||||
assets: new Set(${s(assets)}),
|
||||
mimeTypes: ${s(mime_types)},
|
||||
_: {
|
||||
client: ${uneval(build_data.client)},
|
||||
nodes: [
|
||||
${(node_paths).map(loader).join(',\n')}
|
||||
],
|
||||
remotes: {
|
||||
${remotes.map((remote) => `'${remote.hash}': ${loader(join_relative(relative_path, `chunks/remote-${remote.hash}.js`))}`).join(',\n')}
|
||||
},
|
||||
routes: [
|
||||
${routes.map(route => {
|
||||
if (!route.page && !route.endpoint) return;
|
||||
|
||||
route.params.forEach(param => {
|
||||
if (param.matcher) matchers.add(param.matcher);
|
||||
});
|
||||
|
||||
return dedent`
|
||||
{
|
||||
id: ${s(route.id)},
|
||||
pattern: ${route.pattern},
|
||||
params: ${s(route.params)},
|
||||
page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
|
||||
endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'}
|
||||
}
|
||||
`;
|
||||
}).filter(Boolean).join(',\n')}
|
||||
],
|
||||
prerendered_routes: new Set(${s(prerendered)}),
|
||||
matchers: async () => {
|
||||
${Array.from(
|
||||
matchers,
|
||||
type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`
|
||||
).join('\n')}
|
||||
return { ${Array.from(matchers).join(', ')} };
|
||||
},
|
||||
server_assets: ${s(files)}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// 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}
|
||||
})()
|
||||
`;
|
||||
}
|
||||
285
frontend/node_modules/@sveltejs/kit/src/core/postbuild/analyse.js
generated
vendored
Normal file
285
frontend/node_modules/@sveltejs/kit/src/core/postbuild/analyse.js
generated
vendored
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/** @import { RemoteChunk } from 'types' */
|
||||
import { join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { validate_server_exports } from '../../utils/exports.js';
|
||||
import { load_config } from '../config/index.js';
|
||||
import { forked } from '../../utils/fork.js';
|
||||
import { installPolyfills } from '../../exports/node/polyfills.js';
|
||||
import { ENDPOINT_METHODS } from '../../constants.js';
|
||||
import { filter_env } from '../../utils/env.js';
|
||||
import { has_server_load, resolve_route } from '../../utils/routing.js';
|
||||
import { check_feature } from '../../utils/features.js';
|
||||
import { createReadableStream } from '@sveltejs/kit/node';
|
||||
import { PageNodes } from '../../utils/page_nodes.js';
|
||||
import { build_server_nodes } from '../../exports/vite/build/build_server.js';
|
||||
|
||||
export default forked(import.meta.url, analyse);
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* hash: boolean;
|
||||
* manifest_path: string;
|
||||
* manifest_data: import('types').ManifestData;
|
||||
* server_manifest: import('vite').Manifest;
|
||||
* tracked_features: Record<string, string[]>;
|
||||
* env: Record<string, string>;
|
||||
* out: string;
|
||||
* output_config: import('types').RecursiveRequired<import('types').ValidatedConfig['kit']['output']>;
|
||||
* remotes: RemoteChunk[];
|
||||
* }} opts
|
||||
*/
|
||||
async function analyse({
|
||||
hash,
|
||||
manifest_path,
|
||||
manifest_data,
|
||||
server_manifest,
|
||||
tracked_features,
|
||||
env,
|
||||
out,
|
||||
output_config,
|
||||
remotes
|
||||
}) {
|
||||
/** @type {import('@sveltejs/kit').SSRManifest} */
|
||||
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
||||
|
||||
/** @type {import('types').ValidatedKitConfig} */
|
||||
const config = (await load_config()).kit;
|
||||
|
||||
const server_root = join(config.outDir, 'output');
|
||||
|
||||
/** @type {import('types').ServerInternalModule} */
|
||||
const internal = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
|
||||
|
||||
installPolyfills();
|
||||
|
||||
// configure `import { building } from '$app/environment'` —
|
||||
// essential we do this before analysing the code
|
||||
internal.set_building();
|
||||
|
||||
// set env, `read`, and `manifest`, in case they're used in initialisation
|
||||
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
|
||||
const private_env = filter_env(env, private_prefix, public_prefix);
|
||||
const public_env = filter_env(env, public_prefix, private_prefix);
|
||||
internal.set_private_env(private_env);
|
||||
internal.set_public_env(public_env);
|
||||
internal.set_manifest(manifest);
|
||||
internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
|
||||
|
||||
// first, build server nodes without the client manifest so we can analyse it
|
||||
build_server_nodes(out, config, manifest_data, server_manifest, null, null, null, output_config);
|
||||
|
||||
/** @type {import('types').ServerMetadata} */
|
||||
const metadata = {
|
||||
nodes: [],
|
||||
routes: new Map(),
|
||||
remotes: new Map()
|
||||
};
|
||||
|
||||
const nodes = await Promise.all(manifest._.nodes.map((loader) => loader()));
|
||||
|
||||
// analyse nodes
|
||||
for (const node of nodes) {
|
||||
if (hash && node.universal) {
|
||||
const options = Object.keys(node.universal).filter((o) => o !== 'load');
|
||||
if (options.length > 0) {
|
||||
throw new Error(
|
||||
`Page options are ignored when \`router.type === 'hash'\` (${node.universal_id} has ${options
|
||||
.filter((o) => o !== 'load')
|
||||
.map((o) => `'${o}'`)
|
||||
.join(', ')})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
metadata.nodes[node.index] = {
|
||||
has_server_load: has_server_load(node),
|
||||
has_universal_load: node.universal?.load !== undefined
|
||||
};
|
||||
}
|
||||
|
||||
// analyse routes
|
||||
for (const route of manifest._.routes) {
|
||||
const page =
|
||||
route.page &&
|
||||
analyse_page(
|
||||
route.page.layouts.map((n) => (n === undefined ? n : nodes[n])),
|
||||
nodes[route.page.leaf]
|
||||
);
|
||||
|
||||
const endpoint = route.endpoint && analyse_endpoint(route, await route.endpoint());
|
||||
|
||||
if (page?.prerender && endpoint?.prerender) {
|
||||
throw new Error(`Cannot prerender a route with both +page and +server files (${route.id})`);
|
||||
}
|
||||
|
||||
if (page?.config && endpoint?.config) {
|
||||
for (const key in { ...page.config, ...endpoint.config }) {
|
||||
if (JSON.stringify(page.config[key]) !== JSON.stringify(endpoint.config[key])) {
|
||||
throw new Error(
|
||||
`Mismatched route config for ${route.id} — the +page and +server files must export the same config, if any`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const route_config = page?.config ?? endpoint?.config ?? {};
|
||||
const prerender = page?.prerender ?? endpoint?.prerender;
|
||||
|
||||
if (prerender !== true) {
|
||||
for (const feature of list_features(
|
||||
route,
|
||||
manifest_data,
|
||||
server_manifest,
|
||||
tracked_features
|
||||
)) {
|
||||
check_feature(route.id, route_config, feature, config.adapter);
|
||||
}
|
||||
}
|
||||
|
||||
const page_methods = page?.methods ?? [];
|
||||
const api_methods = endpoint?.methods ?? [];
|
||||
const entries = page?.entries ?? endpoint?.entries;
|
||||
|
||||
metadata.routes.set(route.id, {
|
||||
config: route_config,
|
||||
methods: Array.from(new Set([...page_methods, ...api_methods])),
|
||||
page: {
|
||||
methods: page_methods
|
||||
},
|
||||
api: {
|
||||
methods: api_methods
|
||||
},
|
||||
prerender,
|
||||
entries:
|
||||
entries && (await entries()).map((entry_object) => resolve_route(route.id, entry_object))
|
||||
});
|
||||
}
|
||||
|
||||
// analyse remotes
|
||||
for (const remote of remotes) {
|
||||
const loader = manifest._.remotes[remote.hash];
|
||||
const { default: functions } = await loader();
|
||||
|
||||
const exports = new Map();
|
||||
|
||||
for (const name in functions) {
|
||||
const info = /** @type {import('types').RemoteInfo} */ (functions[name].__);
|
||||
const type = info.type;
|
||||
|
||||
exports.set(name, {
|
||||
type,
|
||||
dynamic: type !== 'prerender' || info.dynamic
|
||||
});
|
||||
}
|
||||
|
||||
metadata.remotes.set(remote.hash, exports);
|
||||
}
|
||||
|
||||
return { metadata };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').SSRRoute} route
|
||||
* @param {import('types').SSREndpoint} mod
|
||||
*/
|
||||
function analyse_endpoint(route, mod) {
|
||||
validate_server_exports(mod, route.id);
|
||||
|
||||
if (mod.prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
|
||||
throw new Error(
|
||||
`Cannot prerender a +server file with POST, PATCH, PUT, or DELETE (${route.id})`
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {Array<import('types').HttpMethod | '*'>} */
|
||||
const methods = [];
|
||||
|
||||
for (const method of /** @type {import('types').HttpMethod[]} */ (ENDPOINT_METHODS)) {
|
||||
if (mod[method]) methods.push(method);
|
||||
}
|
||||
|
||||
if (mod.fallback) {
|
||||
methods.push('*');
|
||||
}
|
||||
|
||||
return {
|
||||
config: mod.config,
|
||||
entries: mod.entries,
|
||||
methods,
|
||||
prerender: mod.prerender ?? false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<import('types').SSRNode | undefined>} layouts
|
||||
* @param {import('types').SSRNode} leaf
|
||||
*/
|
||||
function analyse_page(layouts, leaf) {
|
||||
/** @type {Array<'GET' | 'POST'>} */
|
||||
const methods = ['GET'];
|
||||
if (leaf.server?.actions) methods.push('POST');
|
||||
|
||||
const nodes = new PageNodes([...layouts, leaf]);
|
||||
nodes.validate();
|
||||
|
||||
return {
|
||||
config: nodes.get_config(),
|
||||
entries: leaf.universal?.entries ?? leaf.server?.entries,
|
||||
methods,
|
||||
prerender: nodes.prerender()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').SSRRoute} route
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {import('vite').Manifest} server_manifest
|
||||
* @param {Record<string, string[]>} tracked_features
|
||||
*/
|
||||
function list_features(route, manifest_data, server_manifest, tracked_features) {
|
||||
const features = new Set();
|
||||
|
||||
const route_data = /** @type {import('types').RouteData} */ (
|
||||
manifest_data.routes.find((r) => r.id === route.id)
|
||||
);
|
||||
|
||||
const visited = new Set();
|
||||
/** @param {string} id */
|
||||
function visit(id) {
|
||||
if (visited.has(id)) return;
|
||||
visited.add(id);
|
||||
|
||||
const chunk = server_manifest[id];
|
||||
if (!chunk) return;
|
||||
|
||||
if (chunk.file in tracked_features) {
|
||||
for (const feature of tracked_features[chunk.file]) {
|
||||
features.add(feature);
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.imports) {
|
||||
for (const id of chunk.imports) {
|
||||
visit(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let page_node = route_data?.leaf;
|
||||
while (page_node) {
|
||||
if (page_node.server) visit(page_node.server);
|
||||
page_node = page_node.parent ?? null;
|
||||
}
|
||||
|
||||
if (route_data.endpoint) {
|
||||
visit(route_data.endpoint.file);
|
||||
}
|
||||
|
||||
if (manifest_data.hooks.server) {
|
||||
// TODO if hooks.server.js imports `read`, it will be in the entry chunk
|
||||
// we don't currently account for that case
|
||||
visit(manifest_data.hooks.server);
|
||||
}
|
||||
|
||||
return Array.from(features);
|
||||
}
|
||||
243
frontend/node_modules/@sveltejs/kit/src/core/postbuild/crawl.js
generated
vendored
Normal file
243
frontend/node_modules/@sveltejs/kit/src/core/postbuild/crawl.js
generated
vendored
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
import { resolve, decode_uri } from '../../utils/url.js';
|
||||
import { decode } from './entities.js';
|
||||
|
||||
const DOCTYPE = 'DOCTYPE';
|
||||
const CDATA_OPEN = '[CDATA[';
|
||||
const CDATA_CLOSE = ']]>';
|
||||
const COMMENT_OPEN = '--';
|
||||
const COMMENT_CLOSE = '-->';
|
||||
|
||||
const TAG_OPEN = /[a-zA-Z]/;
|
||||
const TAG_CHAR = /[a-zA-Z0-9]/;
|
||||
const ATTRIBUTE_NAME = /[^\t\n\f />"'=]/;
|
||||
|
||||
const WHITESPACE = /[\s\n\r]/;
|
||||
|
||||
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'
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @param {string} base
|
||||
*/
|
||||
export function crawl(html, base) {
|
||||
/** @type {string[]} */
|
||||
const ids = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const hrefs = [];
|
||||
|
||||
let i = 0;
|
||||
main: while (i < html.length) {
|
||||
const char = html[i];
|
||||
|
||||
if (char === '<') {
|
||||
if (html[i + 1] === '!') {
|
||||
i += 2;
|
||||
|
||||
if (html.slice(i, i + DOCTYPE.length).toUpperCase() === DOCTYPE) {
|
||||
i += DOCTYPE.length;
|
||||
while (i < html.length) {
|
||||
if (html[i++] === '>') {
|
||||
continue main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// skip cdata
|
||||
if (html.slice(i, i + CDATA_OPEN.length) === CDATA_OPEN) {
|
||||
i += CDATA_OPEN.length;
|
||||
while (i < html.length) {
|
||||
if (html.slice(i, i + CDATA_CLOSE.length) === CDATA_CLOSE) {
|
||||
i += CDATA_CLOSE.length;
|
||||
continue main;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// skip comments
|
||||
if (html.slice(i, i + COMMENT_OPEN.length) === COMMENT_OPEN) {
|
||||
i += COMMENT_OPEN.length;
|
||||
while (i < html.length) {
|
||||
if (html.slice(i, i + COMMENT_CLOSE.length) === COMMENT_CLOSE) {
|
||||
i += COMMENT_CLOSE.length;
|
||||
continue main;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse opening tags
|
||||
const start = ++i;
|
||||
if (TAG_OPEN.test(html[start])) {
|
||||
while (i < html.length) {
|
||||
if (!TAG_CHAR.test(html[i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const tag = html.slice(start, i).toUpperCase();
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const attributes = {};
|
||||
|
||||
if (tag === 'SCRIPT' || tag === 'STYLE') {
|
||||
while (i < html.length) {
|
||||
if (
|
||||
html[i] === '<' &&
|
||||
html[i + 1] === '/' &&
|
||||
html.slice(i + 2, i + 2 + tag.length).toUpperCase() === tag
|
||||
) {
|
||||
continue main;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < html.length) {
|
||||
const start = i;
|
||||
|
||||
const char = html[start];
|
||||
if (char === '>') break;
|
||||
|
||||
if (ATTRIBUTE_NAME.test(char)) {
|
||||
i += 1;
|
||||
|
||||
while (i < html.length) {
|
||||
if (!ATTRIBUTE_NAME.test(html[i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const name = html.slice(start, i).toLowerCase();
|
||||
|
||||
while (WHITESPACE.test(html[i])) i += 1;
|
||||
|
||||
if (html[i] === '=') {
|
||||
i += 1;
|
||||
while (WHITESPACE.test(html[i])) i += 1;
|
||||
|
||||
let value;
|
||||
|
||||
if (html[i] === "'" || html[i] === '"') {
|
||||
const quote = html[i++];
|
||||
|
||||
const start = i;
|
||||
let escaped = false;
|
||||
|
||||
while (i < html.length) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else {
|
||||
const char = html[i];
|
||||
|
||||
if (html[i] === quote) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
value = html.slice(start, i);
|
||||
} else {
|
||||
const start = i;
|
||||
while (html[i] !== '>' && !WHITESPACE.test(html[i])) i += 1;
|
||||
value = html.slice(start, i);
|
||||
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
value = decode(value);
|
||||
attributes[name] = value;
|
||||
} else {
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
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(decode_uri(id));
|
||||
}
|
||||
|
||||
if (name && tag === 'A') {
|
||||
ids.push(decode_uri(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return { ids, hrefs };
|
||||
}
|
||||
2252
frontend/node_modules/@sveltejs/kit/src/core/postbuild/entities.js
generated
vendored
Normal file
2252
frontend/node_modules/@sveltejs/kit/src/core/postbuild/entities.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
55
frontend/node_modules/@sveltejs/kit/src/core/postbuild/fallback.js
generated
vendored
Normal file
55
frontend/node_modules/@sveltejs/kit/src/core/postbuild/fallback.js
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { installPolyfills } from '../../exports/node/polyfills.js';
|
||||
import { load_config } from '../config/index.js';
|
||||
import { forked } from '../../utils/fork.js';
|
||||
|
||||
export default forked(import.meta.url, generate_fallback);
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* manifest_path: string;
|
||||
* env: Record<string, string>
|
||||
* }} opts
|
||||
*/
|
||||
async function generate_fallback({ manifest_path, env }) {
|
||||
/** @type {import('types').ValidatedKitConfig} */
|
||||
const config = (await load_config()).kit;
|
||||
|
||||
installPolyfills();
|
||||
|
||||
const server_root = join(config.outDir, 'output');
|
||||
|
||||
/** @type {import('types').ServerInternalModule} */
|
||||
const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
|
||||
|
||||
/** @type {import('types').ServerModule} */
|
||||
const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
|
||||
|
||||
/** @type {import('@sveltejs/kit').SSRManifest} */
|
||||
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
||||
|
||||
set_building();
|
||||
|
||||
const server = new Server(manifest);
|
||||
await server.init({ env });
|
||||
|
||||
const response = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
|
||||
getClientAddress: () => {
|
||||
throw new Error('Cannot read clientAddress during prerendering');
|
||||
},
|
||||
prerendering: {
|
||||
fallback: true,
|
||||
dependencies: new Map(),
|
||||
remote_responses: new Map()
|
||||
},
|
||||
read: (file) => readFileSync(join(config.files.assets, file))
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
throw new Error(`Could not create a fallback page — failed with status ${response.status}`);
|
||||
}
|
||||
593
frontend/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js
generated
vendored
Normal file
593
frontend/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js
generated
vendored
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { installPolyfills } from '../../exports/node/polyfills.js';
|
||||
import { mkdirp, posixify, walk } from '../../utils/filesystem.js';
|
||||
import { decode_uri, is_root_relative, resolve } from '../../utils/url.js';
|
||||
import { escape_html } from '../../utils/escape.js';
|
||||
import { logger } from '../utils.js';
|
||||
import { load_config } from '../config/index.js';
|
||||
import { get_route_segments } from '../../utils/routing.js';
|
||||
import { queue } from './queue.js';
|
||||
import { crawl } from './crawl.js';
|
||||
import { forked } from '../../utils/fork.js';
|
||||
import * as devalue from 'devalue';
|
||||
import { createReadableStream } from '@sveltejs/kit/node';
|
||||
import generate_fallback from './fallback.js';
|
||||
import { stringify_remote_arg } from '../../runtime/shared.js';
|
||||
import { filter_env } from '../../utils/env.js';
|
||||
|
||||
export default forked(import.meta.url, prerender);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
||||
// "If fragment is the empty string, then return the special value top of the document."
|
||||
// ...and
|
||||
// "If decodedFragment is an ASCII case-insensitive match for the string 'top', then return the top of the document."
|
||||
const SPECIAL_HASHLINKS = new Set(['', 'top']);
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* hash: boolean;
|
||||
* out: string;
|
||||
* manifest_path: string;
|
||||
* metadata: import('types').ServerMetadata;
|
||||
* verbose: boolean;
|
||||
* env: Record<string, string>
|
||||
* }} opts
|
||||
*/
|
||||
async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
|
||||
/** @type {import('@sveltejs/kit').SSRManifest} */
|
||||
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
||||
|
||||
/** @type {import('types').ServerInternalModule} */
|
||||
const internal = await import(pathToFileURL(`${out}/server/internal.js`).href);
|
||||
|
||||
/** @type {import('types').ServerModule} */
|
||||
const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
|
||||
|
||||
// configure `import { building } from '$app/environment'` —
|
||||
// essential we do this before analysing the code
|
||||
internal.set_building();
|
||||
internal.set_prerendering();
|
||||
|
||||
/**
|
||||
* @template {{message: string}} T
|
||||
* @template {Omit<T, 'message'>} K
|
||||
* @param {import('types').Logger} log
|
||||
* @param {'fail' | 'warn' | 'ignore' | ((details: T) => void)} input
|
||||
* @param {(details: K) => string} format
|
||||
* @returns {(details: K) => void}
|
||||
*/
|
||||
function normalise_error_handler(log, input, format) {
|
||||
switch (input) {
|
||||
case 'fail':
|
||||
return (details) => {
|
||||
throw new Error(format(details));
|
||||
};
|
||||
case 'warn':
|
||||
return (details) => {
|
||||
log.error(format(details));
|
||||
};
|
||||
case 'ignore':
|
||||
return () => {};
|
||||
default:
|
||||
// @ts-expect-error TS thinks T might be of a different kind, but it's not
|
||||
return (details) => input({ ...details, message: format(details) });
|
||||
}
|
||||
}
|
||||
|
||||
const OK = 2;
|
||||
const REDIRECT = 3;
|
||||
|
||||
/** @type {import('types').Prerendered} */
|
||||
const prerendered = {
|
||||
pages: new Map(),
|
||||
assets: new Map(),
|
||||
redirects: new Map(),
|
||||
paths: []
|
||||
};
|
||||
|
||||
/** @type {import('types').PrerenderMap} */
|
||||
const prerender_map = new Map();
|
||||
|
||||
for (const [id, { prerender }] of metadata.routes) {
|
||||
if (prerender !== undefined) {
|
||||
prerender_map.set(id, prerender);
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const prerendered_routes = new Set();
|
||||
|
||||
/** @type {import('types').ValidatedKitConfig} */
|
||||
const config = (await load_config()).kit;
|
||||
|
||||
if (hash) {
|
||||
const fallback = await generate_fallback({
|
||||
manifest_path,
|
||||
env
|
||||
});
|
||||
|
||||
const file = output_filename('/', true);
|
||||
const dest = `${config.outDir}/output/prerendered/pages/${file}`;
|
||||
|
||||
mkdirp(dirname(dest));
|
||||
writeFileSync(dest, fallback);
|
||||
|
||||
prerendered.pages.set('/', { file });
|
||||
|
||||
return { prerendered, prerender_map };
|
||||
}
|
||||
|
||||
const emulator = await config.adapter?.emulate?.();
|
||||
|
||||
/** @type {import('types').Logger} */
|
||||
const log = logger({ verbose });
|
||||
|
||||
installPolyfills();
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
const saved = new Map();
|
||||
|
||||
const handle_http_error = normalise_error_handler(
|
||||
log,
|
||||
config.prerender.handleHttpError,
|
||||
({ status, path, referrer, referenceType }) => {
|
||||
const message =
|
||||
status === 404 && !path.startsWith(config.paths.base)
|
||||
? `${path} does not begin with \`base\`, which is configured in \`paths.base\` and can be imported from \`$app/paths\` - see https://svelte.dev/docs/kit/configuration#paths for more info`
|
||||
: path;
|
||||
|
||||
return `${status} ${message}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
|
||||
}
|
||||
);
|
||||
|
||||
const handle_missing_id = normalise_error_handler(
|
||||
log,
|
||||
config.prerender.handleMissingId,
|
||||
({ path, id, referrers }) => {
|
||||
return (
|
||||
`The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path} - see the \`handleMissingId\` option in https://svelte.dev/docs/kit/configuration#prerender for more info:` +
|
||||
referrers.map((l) => `\n - ${l}`).join('')
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
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://svelte.dev/docs/kit/configuration#prerender for more info.`;
|
||||
}
|
||||
);
|
||||
|
||||
const handle_not_prerendered_route = normalise_error_handler(
|
||||
log,
|
||||
config.prerender.handleUnseenRoutes,
|
||||
({ routes }) => {
|
||||
const list = routes.map((id) => ` - ${id}`).join('\n');
|
||||
return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}\n\nSee the \`handleUnseenRoutes\` option in https://svelte.dev/docs/kit/configuration#prerender for more info.`;
|
||||
}
|
||||
);
|
||||
|
||||
const q = queue(config.prerender.concurrency);
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {boolean} is_html
|
||||
*/
|
||||
function output_filename(path, is_html) {
|
||||
const file = path.slice(config.paths.base.length + 1) || 'index.html';
|
||||
|
||||
if (is_html && !file.endsWith('.html')) {
|
||||
return file + (file.endsWith('/') ? 'index.html' : '.html');
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
const files = new Set(walk(`${out}/client`).map(posixify));
|
||||
files.add(`${config.appDir}/env.js`);
|
||||
|
||||
const immutable = `${config.appDir}/immutable`;
|
||||
if (existsSync(`${out}/server/${immutable}`)) {
|
||||
for (const file of walk(`${out}/server/${immutable}`)) {
|
||||
files.add(posixify(`${config.appDir}/immutable/${file}`));
|
||||
}
|
||||
}
|
||||
|
||||
const remote_prefix = `${config.paths.base}/${config.appDir}/remote/`;
|
||||
|
||||
const seen = new Set();
|
||||
const written = new Set();
|
||||
const remote_responses = new Map();
|
||||
|
||||
/** @type {Map<string, Set<string>>} */
|
||||
const expected_hashlinks = new Map();
|
||||
|
||||
/** @type {Map<string, string[]>} */
|
||||
const actual_hashlinks = new Map();
|
||||
|
||||
/**
|
||||
* @param {string | null} referrer
|
||||
* @param {string} decoded
|
||||
* @param {string} [encoded]
|
||||
* @param {string} [generated_from_id]
|
||||
*/
|
||||
function enqueue(referrer, decoded, encoded, generated_from_id) {
|
||||
if (seen.has(decoded)) return;
|
||||
seen.add(decoded);
|
||||
|
||||
const file = decoded.slice(config.paths.base.length + 1);
|
||||
if (files.has(file)) return;
|
||||
|
||||
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} decoded
|
||||
* @param {string} encoded
|
||||
* @param {string?} referrer
|
||||
* @param {string} [generated_from_id]
|
||||
*/
|
||||
async function visit(decoded, encoded, referrer, generated_from_id) {
|
||||
if (!decoded.startsWith(config.paths.base)) {
|
||||
handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {Map<string, import('types').PrerenderDependency>} */
|
||||
const dependencies = new Map();
|
||||
|
||||
const response = await server.respond(new Request(config.prerender.origin + encoded), {
|
||||
getClientAddress() {
|
||||
throw new Error('Cannot read clientAddress during prerendering');
|
||||
},
|
||||
prerendering: {
|
||||
dependencies,
|
||||
remote_responses
|
||||
},
|
||||
read: (file) => {
|
||||
// stuff we just wrote
|
||||
const filepath = saved.get(file);
|
||||
if (filepath) return readFileSync(filepath);
|
||||
|
||||
// Static assets emitted during build
|
||||
if (file.startsWith(config.appDir)) {
|
||||
return readFileSync(`${out}/server/${file}`);
|
||||
}
|
||||
|
||||
// stuff in `static`
|
||||
return readFileSync(join(config.files.assets, file));
|
||||
},
|
||||
emulator
|
||||
});
|
||||
|
||||
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());
|
||||
|
||||
const category = decoded.startsWith(remote_prefix) ? 'data' : 'pages';
|
||||
save(category, response, body, decoded, encoded, referrer, 'linked');
|
||||
|
||||
for (const [dependency_path, result] of dependencies) {
|
||||
// this seems circuitous, but using new URL allows us to not care
|
||||
// whether dependency_path is encoded or not
|
||||
const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname;
|
||||
const decoded_dependency_path = decode_uri(encoded_dependency_path);
|
||||
|
||||
const headers = Object.fromEntries(result.response.headers);
|
||||
|
||||
const prerender = headers['x-sveltekit-prerender'];
|
||||
if (prerender) {
|
||||
const encoded_route_id = headers['x-sveltekit-routeid'];
|
||||
if (encoded_route_id != null) {
|
||||
const route_id = decode_uri(encoded_route_id);
|
||||
const existing_value = prerender_map.get(route_id);
|
||||
if (existing_value !== 'auto') {
|
||||
prerender_map.set(route_id, prerender === 'true' ? true : 'auto');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const body = result.body ?? new Uint8Array(await result.response.arrayBuffer());
|
||||
|
||||
const category = decoded_dependency_path.startsWith(remote_prefix) ? 'data' : 'dependencies';
|
||||
|
||||
save(
|
||||
category,
|
||||
result.response,
|
||||
body,
|
||||
decoded_dependency_path,
|
||||
encoded_dependency_path,
|
||||
decoded,
|
||||
'fetched'
|
||||
);
|
||||
}
|
||||
|
||||
// avoid triggering `filterSerializeResponseHeaders` guard
|
||||
const headers = Object.fromEntries(response.headers);
|
||||
|
||||
// if it's a 200 HTML response, crawl it. Skip error responses, as we don't save those
|
||||
if (response.ok && config.prerender.crawl && headers['content-type'] === 'text/html') {
|
||||
const { ids, hrefs } = crawl(body.toString(), decoded);
|
||||
|
||||
actual_hashlinks.set(decoded, ids);
|
||||
|
||||
/** @param {string} href */
|
||||
const removePrerenderOrigin = (href) => {
|
||||
if (href.startsWith(config.prerender.origin)) {
|
||||
if (href === config.prerender.origin) return '/';
|
||||
if (href.at(config.prerender.origin.length) !== '/') return href;
|
||||
return href.slice(config.prerender.origin.length);
|
||||
}
|
||||
return href;
|
||||
};
|
||||
|
||||
for (const href of hrefs.map(removePrerenderOrigin)) {
|
||||
if (!is_root_relative(href)) continue;
|
||||
|
||||
const { pathname, search, hash } = new URL(href, 'http://localhost');
|
||||
|
||||
if (search) {
|
||||
// TODO warn that query strings have no effect on statically-exported pages
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
const key = decode_uri(pathname + hash);
|
||||
|
||||
if (!expected_hashlinks.has(key)) {
|
||||
expected_hashlinks.set(key, new Set());
|
||||
}
|
||||
|
||||
/** @type {Set<string>} */ (expected_hashlinks.get(key)).add(decoded);
|
||||
}
|
||||
|
||||
void enqueue(decoded, decode_uri(pathname), pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'pages' | 'dependencies' | 'data'} category
|
||||
* @param {Response} response
|
||||
* @param {string | Uint8Array} body
|
||||
* @param {string} decoded
|
||||
* @param {string} encoded
|
||||
* @param {string | null} referrer
|
||||
* @param {'linked' | 'fetched'} referenceType
|
||||
*/
|
||||
function save(category, response, body, decoded, encoded, referrer, referenceType) {
|
||||
const response_type = Math.floor(response.status / 100);
|
||||
const headers = Object.fromEntries(response.headers);
|
||||
|
||||
const type = headers['content-type'];
|
||||
const is_html = response_type === REDIRECT || type === 'text/html';
|
||||
|
||||
const file = output_filename(decoded, is_html);
|
||||
const dest = `${config.outDir}/output/prerendered/${category}/${file}`;
|
||||
|
||||
if (written.has(file)) return;
|
||||
|
||||
const encoded_route_id = response.headers.get('x-sveltekit-routeid');
|
||||
const route_id = encoded_route_id != null ? decode_uri(encoded_route_id) : null;
|
||||
if (route_id !== null) prerendered_routes.add(route_id);
|
||||
|
||||
if (response_type === REDIRECT) {
|
||||
const location = headers['location'];
|
||||
|
||||
if (location) {
|
||||
const resolved = resolve(encoded, location);
|
||||
if (is_root_relative(resolved)) {
|
||||
void enqueue(decoded, decode_uri(resolved), resolved);
|
||||
}
|
||||
|
||||
if (!headers['x-sveltekit-normalize']) {
|
||||
mkdirp(dirname(dest));
|
||||
|
||||
log.warn(`${response.status} ${decoded} -> ${location}`);
|
||||
|
||||
writeFileSync(
|
||||
dest,
|
||||
`<script>location.href=${devalue.uneval(
|
||||
location
|
||||
)};</script><meta http-equiv="refresh" content="${escape_html(
|
||||
`0;url=${location}`,
|
||||
true
|
||||
)}">`
|
||||
);
|
||||
|
||||
written.add(file);
|
||||
|
||||
if (!prerendered.redirects.has(decoded)) {
|
||||
prerendered.redirects.set(decoded, {
|
||||
status: response.status,
|
||||
location: resolved
|
||||
});
|
||||
|
||||
prerendered.paths.push(decoded);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn(`location header missing on redirect received from ${decoded}`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
if (existsSync(dest) && statSync(dest).isDirectory()) {
|
||||
throw new Error(
|
||||
`Cannot save ${decoded} as it is already a directory. See https://svelte.dev/docs/kit/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://svelte.dev/docs/kit/page-options#prerender-route-conflicts for more information`
|
||||
);
|
||||
}
|
||||
|
||||
mkdirp(dir);
|
||||
|
||||
log.info(`${response.status} ${decoded}`);
|
||||
writeFileSync(dest, body);
|
||||
written.add(file);
|
||||
|
||||
if (is_html) {
|
||||
prerendered.pages.set(decoded, {
|
||||
file
|
||||
});
|
||||
} else {
|
||||
prerendered.assets.set(decoded, {
|
||||
type
|
||||
});
|
||||
}
|
||||
|
||||
prerendered.paths.push(decoded);
|
||||
} else if (response_type !== OK) {
|
||||
handle_http_error({ status: response.status, path: decoded, referrer, referenceType });
|
||||
}
|
||||
|
||||
manifest.assets.add(file);
|
||||
saved.set(file, dest);
|
||||
}
|
||||
|
||||
/** @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 });
|
||||
}
|
||||
}
|
||||
|
||||
let should_prerender = false;
|
||||
|
||||
for (const value of prerender_map.values()) {
|
||||
if (value) {
|
||||
should_prerender = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// the user's remote function modules may reference environment variables,
|
||||
// `read` or the `manifest` at the top-level so we need to set them before
|
||||
// evaluating those modules to avoid potential runtime errors
|
||||
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
|
||||
const private_env = filter_env(env, private_prefix, public_prefix);
|
||||
const public_env = filter_env(env, public_prefix, private_prefix);
|
||||
internal.set_private_env(private_env);
|
||||
internal.set_public_env(public_env);
|
||||
internal.set_manifest(manifest);
|
||||
internal.set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
|
||||
|
||||
/** @type {Array<import('types').RemoteInfo & { type: 'prerender'}>} */
|
||||
const prerender_functions = [];
|
||||
|
||||
for (const loader of Object.values(manifest._.remotes)) {
|
||||
const module = await loader();
|
||||
|
||||
for (const fn of Object.values(module.default)) {
|
||||
if (fn?.__?.type === 'prerender') {
|
||||
prerender_functions.push(fn.__);
|
||||
should_prerender = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_prerender) {
|
||||
return { prerendered, prerender_map };
|
||||
}
|
||||
|
||||
// only run the server after the `should_prerender` check so that we
|
||||
// don't run the user's init hook unnecessarily
|
||||
const server = new Server(manifest);
|
||||
await server.init({
|
||||
env,
|
||||
read: (file) => createReadableStream(`${config.outDir}/output/server/${file}`)
|
||||
});
|
||||
|
||||
log.info('Prerendering');
|
||||
|
||||
for (const entry of config.prerender.entries) {
|
||||
if (entry === '*') {
|
||||
for (const [id, prerender] of prerender_map) {
|
||||
if (prerender) {
|
||||
// remove optional parameters from the route
|
||||
const segments = get_route_segments(id).filter((segment) => !segment.startsWith('[['));
|
||||
const processed_id = '/' + segments.join('/');
|
||||
|
||||
if (processed_id.includes('[')) continue;
|
||||
const path = `/${get_route_segments(processed_id).join('/')}`;
|
||||
void enqueue(null, config.paths.base + path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
void enqueue(null, config.paths.base + entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { id, entries } of route_level_entries) {
|
||||
for (const entry of entries) {
|
||||
void enqueue(null, config.paths.base + entry, undefined, id);
|
||||
}
|
||||
}
|
||||
|
||||
const transport = (await internal.get_hooks()).transport ?? {};
|
||||
for (const info of prerender_functions) {
|
||||
if (info.has_arg) {
|
||||
for (const arg of (await info.inputs?.()) ?? []) {
|
||||
void enqueue(null, remote_prefix + info.id + '/' + stringify_remote_arg(arg, transport));
|
||||
}
|
||||
} else {
|
||||
void enqueue(null, remote_prefix + info.id);
|
||||
}
|
||||
}
|
||||
|
||||
await q.done();
|
||||
|
||||
// handle invalid fragment links
|
||||
for (const [key, referrers] of expected_hashlinks) {
|
||||
const index = key.indexOf('#');
|
||||
const path = key.slice(0, index);
|
||||
const id = key.slice(index + 1);
|
||||
|
||||
const hashlinks = actual_hashlinks.get(path);
|
||||
// ignore fragment links to pages that were not prerendered
|
||||
if (!hashlinks) continue;
|
||||
|
||||
if (!hashlinks.includes(id) && !SPECIAL_HASHLINKS.has(id)) {
|
||||
handle_missing_id({ id, path, referrers: Array.from(referrers) });
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
const not_prerendered = [];
|
||||
|
||||
for (const [route_id, prerender] of prerender_map) {
|
||||
if (prerender === true && !prerendered_routes.has(route_id)) {
|
||||
not_prerendered.push(route_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (not_prerendered.length > 0) {
|
||||
handle_not_prerendered_route({ routes: not_prerendered });
|
||||
}
|
||||
|
||||
return { prerendered, prerender_map };
|
||||
}
|
||||
72
frontend/node_modules/@sveltejs/kit/src/core/postbuild/queue.js
generated
vendored
Normal file
72
frontend/node_modules/@sveltejs/kit/src/core/postbuild/queue.js
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/** @import { PromiseWithResolvers } from '../../utils/promise.js' */
|
||||
import { with_resolvers } from '../../utils/promise.js';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* fn: () => Promise<any>,
|
||||
* fulfil: (value: any) => void,
|
||||
* reject: (error: Error) => void
|
||||
* }} Task
|
||||
*/
|
||||
|
||||
/** @param {number} concurrency */
|
||||
export function queue(concurrency) {
|
||||
/** @type {Task[]} */
|
||||
const tasks = [];
|
||||
const { promise, resolve, reject } = /** @type {PromiseWithResolvers<void>} */ (with_resolvers());
|
||||
|
||||
let current = 0;
|
||||
let closed = false;
|
||||
|
||||
promise.catch(() => {
|
||||
// this is necessary in case a catch handler is never added
|
||||
// to the done promise by the user
|
||||
});
|
||||
|
||||
function dequeue() {
|
||||
if (current < concurrency) {
|
||||
const task = tasks.shift();
|
||||
|
||||
if (task) {
|
||||
current += 1;
|
||||
const promise = Promise.resolve(task.fn());
|
||||
|
||||
void promise
|
||||
.then(task.fulfil, (err) => {
|
||||
task.reject(err);
|
||||
reject(err);
|
||||
})
|
||||
.then(() => {
|
||||
current -= 1;
|
||||
dequeue();
|
||||
});
|
||||
} else if (current === 0) {
|
||||
closed = true;
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
/** @param {() => any} fn */
|
||||
add: (fn) => {
|
||||
if (closed) throw new Error('Cannot add tasks to a queue that has ended');
|
||||
|
||||
const promise = new Promise((fulfil, reject) => {
|
||||
tasks.push({ fn, fulfil, reject });
|
||||
});
|
||||
|
||||
dequeue();
|
||||
return promise;
|
||||
},
|
||||
|
||||
done: () => {
|
||||
if (current === 0) {
|
||||
closed = true;
|
||||
resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
}
|
||||
0
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/conflict.js
generated
vendored
Normal file
0
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/conflict.js
generated
vendored
Normal file
613
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/index.js
generated
vendored
Normal file
613
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import colors from 'kleur';
|
||||
import { lookup } from 'mrmime';
|
||||
import { list_files, runtime_directory } from '../../utils.js';
|
||||
import { posixify, resolve_entry } from '../../../utils/filesystem.js';
|
||||
import { parse_route_id } from '../../../utils/routing.js';
|
||||
import { sort_routes } from './sort.js';
|
||||
import { isSvelte5Plus } from '../utils.js';
|
||||
import {
|
||||
create_node_analyser,
|
||||
get_page_options
|
||||
} from '../../../exports/vite/static_analysis/index.js';
|
||||
|
||||
/**
|
||||
* Generates the manifest data used for the client-side manifest and types generation.
|
||||
* @param {{
|
||||
* config: import('types').ValidatedConfig;
|
||||
* fallback?: string;
|
||||
* cwd?: string;
|
||||
* }} opts
|
||||
* @returns {import('types').ManifestData}
|
||||
*/
|
||||
export default function create_manifest_data({
|
||||
config,
|
||||
fallback = `${runtime_directory}/components/${isSvelte5Plus() ? 'svelte-5' : 'svelte-4'}`,
|
||||
cwd = process.cwd()
|
||||
}) {
|
||||
const assets = create_assets(config);
|
||||
const hooks = create_hooks(config, cwd);
|
||||
const matchers = create_matchers(config, cwd);
|
||||
const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
|
||||
|
||||
for (const route of routes) {
|
||||
for (const param of route.params) {
|
||||
if (param.matcher && !matchers[param.matcher]) {
|
||||
throw new Error(`No matcher found for parameter '${param.matcher}' in route ${route.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
assets,
|
||||
hooks,
|
||||
matchers,
|
||||
nodes,
|
||||
routes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
export function create_assets(config) {
|
||||
return list_files(config.kit.files.assets).map((file) => ({
|
||||
file,
|
||||
size: fs.statSync(path.resolve(config.kit.files.assets, file)).size,
|
||||
type: lookup(file) || null
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} cwd
|
||||
*/
|
||||
function create_hooks(config, cwd) {
|
||||
const client = resolve_entry(config.kit.files.hooks.client);
|
||||
const server = resolve_entry(config.kit.files.hooks.server);
|
||||
const universal = resolve_entry(config.kit.files.hooks.universal);
|
||||
|
||||
return {
|
||||
client: client && posixify(path.relative(cwd, client)),
|
||||
server: server && posixify(path.relative(cwd, server)),
|
||||
universal: universal && posixify(path.relative(cwd, universal))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} cwd
|
||||
*/
|
||||
function create_matchers(config, cwd) {
|
||||
const params_base = path.relative(cwd, config.kit.files.params);
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const matchers = {};
|
||||
if (fs.existsSync(config.kit.files.params)) {
|
||||
for (const file of fs.readdirSync(config.kit.files.params)) {
|
||||
const ext = path.extname(file);
|
||||
if (!config.kit.moduleExtensions.includes(ext)) continue;
|
||||
const type = file.slice(0, -ext.length);
|
||||
|
||||
if (/^\w+$/.test(type)) {
|
||||
const matcher_file = path.join(params_base, file);
|
||||
|
||||
// Disallow same matcher with different extensions
|
||||
if (matchers[type]) {
|
||||
throw new Error(`Duplicate matchers: ${matcher_file} and ${matchers[type]}`);
|
||||
} else {
|
||||
matchers[type] = matcher_file;
|
||||
}
|
||||
} else {
|
||||
// Allow for matcher test collocation
|
||||
if (type.endsWith('.test') || type.endsWith('.spec')) continue;
|
||||
|
||||
throw new Error(
|
||||
`Matcher names can only have underscores and alphanumeric characters — "${file}" is invalid`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} cwd
|
||||
* @param {string} fallback
|
||||
*/
|
||||
function create_routes_and_nodes(cwd, config, fallback) {
|
||||
/** @type {import('types').RouteData[]} */
|
||||
const routes = [];
|
||||
|
||||
const routes_base = posixify(path.relative(cwd, config.kit.files.routes));
|
||||
|
||||
const valid_extensions = [...config.extensions, ...config.kit.moduleExtensions];
|
||||
|
||||
/** @type {import('types').PageNode[]} */
|
||||
const nodes = [];
|
||||
|
||||
if (fs.existsSync(config.kit.files.routes)) {
|
||||
/**
|
||||
* @param {number} depth
|
||||
* @param {string} id
|
||||
* @param {string} segment
|
||||
* @param {import('types').RouteData | null} parent
|
||||
*/
|
||||
const walk = (depth, id, segment, parent) => {
|
||||
const unescaped = id.replace(/\[([ux])\+([^\]]+)\]/gi, (match, type, code) => {
|
||||
if (match !== match.toLowerCase()) {
|
||||
throw new Error(`Character escape sequence in ${id} must be lowercase`);
|
||||
}
|
||||
|
||||
if (!/[0-9a-f]+/.test(code)) {
|
||||
throw new Error(`Invalid character escape sequence in ${id}`);
|
||||
}
|
||||
|
||||
if (type === 'x') {
|
||||
if (code.length !== 2) {
|
||||
throw new Error(`Hexadecimal escape sequence in ${id} must be two characters`);
|
||||
}
|
||||
|
||||
return String.fromCharCode(parseInt(code, 16));
|
||||
} else {
|
||||
if (code.length < 4 || code.length > 6) {
|
||||
throw new Error(
|
||||
`Unicode escape sequence in ${id} must be between four and six characters`
|
||||
);
|
||||
}
|
||||
|
||||
return String.fromCharCode(parseInt(code, 16));
|
||||
}
|
||||
});
|
||||
|
||||
if (/\]\[/.test(unescaped)) {
|
||||
throw new Error(`Invalid route ${id} — parameters must be separated`);
|
||||
}
|
||||
|
||||
if (count_occurrences('[', id) !== count_occurrences(']', id)) {
|
||||
throw new Error(`Invalid route ${id} — brackets are unbalanced`);
|
||||
}
|
||||
|
||||
if (/#/.test(segment)) {
|
||||
// Vite will barf on files with # in them
|
||||
throw new Error(`Route ${id} should be renamed to ${id.replace(/#/g, '[x+23]')}`);
|
||||
}
|
||||
|
||||
if (/\[\.\.\.\w+\]\/\[\[/.test(id)) {
|
||||
throw new Error(
|
||||
`Invalid route ${id} — an [[optional]] route segment cannot follow a [...rest] route segment`
|
||||
);
|
||||
}
|
||||
|
||||
if (/\[\[\.\.\./.test(id)) {
|
||||
throw new Error(
|
||||
`Invalid route ${id} — a rest route segment is always optional, remove the outer square brackets`
|
||||
);
|
||||
}
|
||||
|
||||
const { pattern, params } = parse_route_id(id);
|
||||
|
||||
/** @type {import('types').RouteData} */
|
||||
const route = {
|
||||
id,
|
||||
parent,
|
||||
|
||||
segment,
|
||||
pattern,
|
||||
params,
|
||||
|
||||
layout: null,
|
||||
error: null,
|
||||
leaf: null,
|
||||
page: null,
|
||||
endpoint: null
|
||||
};
|
||||
|
||||
// important to do this before walking children, so that child
|
||||
// routes appear later
|
||||
routes.push(route);
|
||||
|
||||
// if we don't do this, the route map becomes unwieldy to console.log
|
||||
Object.defineProperty(route, 'parent', { enumerable: false });
|
||||
|
||||
const dir = path.join(cwd, routes_base, id);
|
||||
|
||||
// We can't use withFileTypes because of a NodeJs bug which returns wrong results
|
||||
// with isDirectory() in case of symlinks: https://github.com/nodejs/node/issues/30646
|
||||
const files = fs.readdirSync(dir).map((name) => ({
|
||||
is_dir: fs.statSync(path.join(dir, name)).isDirectory(),
|
||||
name
|
||||
}));
|
||||
|
||||
// process files first
|
||||
for (const file of files) {
|
||||
if (file.is_dir) continue;
|
||||
|
||||
const ext = valid_extensions.find((ext) => file.name.endsWith(ext));
|
||||
if (!ext) continue;
|
||||
|
||||
if (!file.name.startsWith('+')) {
|
||||
const name = file.name.slice(0, -ext.length);
|
||||
// check if it is a valid route filename but missing the + prefix
|
||||
const typo =
|
||||
/^(?:(page(?:@(.*))?)|(layout(?:@(.*))?)|(error))$/.test(name) ||
|
||||
/^(?:(server)|(page(?:(@[a-zA-Z0-9_-]*))?(\.server)?)|(layout(?:(@[a-zA-Z0-9_-]*))?(\.server)?))$/.test(
|
||||
name
|
||||
);
|
||||
if (typo) {
|
||||
console.log(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(
|
||||
`Missing route file prefix. Did you mean +${file.name}?` +
|
||||
` at ${path.join(dir, file.name)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.name.endsWith('.d.ts')) {
|
||||
let name = file.name.slice(0, -5);
|
||||
const ext = valid_extensions.find((ext) => name.endsWith(ext));
|
||||
if (ext) name = name.slice(0, -ext.length);
|
||||
|
||||
const valid =
|
||||
/^\+(?:(page(?:@(.*))?)|(layout(?:@(.*))?)|(error))$/.test(name) ||
|
||||
/^\+(?:(server)|(page(?:(@[a-zA-Z0-9_-]*))?(\.server)?)|(layout(?:(@[a-zA-Z0-9_-]*))?(\.server)?))$/.test(
|
||||
name
|
||||
);
|
||||
|
||||
if (valid) continue;
|
||||
}
|
||||
|
||||
const project_relative = posixify(path.relative(cwd, path.join(dir, file.name)));
|
||||
|
||||
const item = analyze(
|
||||
project_relative,
|
||||
file.name,
|
||||
config.extensions,
|
||||
config.kit.moduleExtensions
|
||||
);
|
||||
|
||||
if (config.kit.router.type === 'hash' && item.kind === 'server') {
|
||||
throw new Error(
|
||||
`Cannot use server-only files in an app with \`router.type === 'hash': ${project_relative}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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') {
|
||||
if (item.is_error) {
|
||||
route.error = {
|
||||
depth,
|
||||
component: project_relative
|
||||
};
|
||||
} else if (item.is_layout) {
|
||||
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 };
|
||||
} else if (route.leaf.component) {
|
||||
throw duplicate_files_error('page component', route.leaf.component);
|
||||
}
|
||||
|
||||
route.leaf.component = project_relative;
|
||||
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: [] };
|
||||
} 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 };
|
||||
} 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 = {
|
||||
file: project_relative,
|
||||
page_options: null // will be filled later
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// then handle children
|
||||
for (const file of files) {
|
||||
if (file.is_dir) {
|
||||
walk(depth + 1, path.posix.join(id, file.name), file.name, route);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
walk(0, '/', '', null);
|
||||
|
||||
if (routes.length === 1) {
|
||||
const root = routes[0];
|
||||
if (!root.leaf && !root.error && !root.layout && !root.endpoint) {
|
||||
throw new Error(
|
||||
'No routes found. If you are using a custom src/routes directory, make sure it is specified in your Svelte config file'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there's no routes directory, we'll just create a single empty route. This ensures the root layout and
|
||||
// error components are included in the manifest, which is needed for subsequent build/dev commands to work
|
||||
routes.push({
|
||||
id: '/',
|
||||
segment: '',
|
||||
pattern: /^$/,
|
||||
params: [],
|
||||
parent: null,
|
||||
layout: null,
|
||||
error: null,
|
||||
leaf: null,
|
||||
page: null,
|
||||
endpoint: null
|
||||
});
|
||||
}
|
||||
|
||||
prevent_conflicts(routes);
|
||||
|
||||
const root = routes[0];
|
||||
|
||||
if (!root.layout?.component) {
|
||||
if (!root.layout) root.layout = { depth: 0, child_pages: [] };
|
||||
root.layout.component = posixify(path.relative(cwd, `${fallback}/layout.svelte`));
|
||||
}
|
||||
|
||||
if (!root.error?.component) {
|
||||
if (!root.error) root.error = { depth: 0 };
|
||||
root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
|
||||
}
|
||||
|
||||
// we do layouts/errors first as they are more likely to be reused,
|
||||
// and smaller indexes take fewer bytes. also, this guarantees that
|
||||
// the default error/layout are 0/1
|
||||
for (const route of routes) {
|
||||
if (route.layout) {
|
||||
if (!route.layout?.component) {
|
||||
route.layout.component = posixify(path.relative(cwd, `${fallback}/layout.svelte`));
|
||||
}
|
||||
nodes.push(route.layout);
|
||||
}
|
||||
if (route.error) nodes.push(route.error);
|
||||
}
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.leaf) nodes.push(route.leaf);
|
||||
}
|
||||
|
||||
const indexes = new Map(nodes.map((node, i) => [node, i]));
|
||||
|
||||
const node_analyser = create_node_analyser();
|
||||
|
||||
for (const route of routes) {
|
||||
if (!route.leaf) continue;
|
||||
|
||||
route.page = {
|
||||
layouts: [],
|
||||
errors: [],
|
||||
leaf: /** @type {number} */ (indexes.get(route.leaf))
|
||||
};
|
||||
|
||||
/** @type {import('types').RouteData | null} */
|
||||
let current_route = route;
|
||||
let current_node = route.leaf;
|
||||
let parent_id = route.leaf.parent_id;
|
||||
|
||||
while (current_route) {
|
||||
if (parent_id === undefined || current_route.segment === parent_id) {
|
||||
if (current_route.layout || current_route.error) {
|
||||
route.page.layouts.unshift(
|
||||
current_route.layout ? indexes.get(current_route.layout) : undefined
|
||||
);
|
||||
route.page.errors.unshift(
|
||||
current_route.error ? indexes.get(current_route.error) : undefined
|
||||
);
|
||||
}
|
||||
|
||||
if (current_route.layout) {
|
||||
/** @type {import('types').PageNode[]} */ (current_route.layout.child_pages).push(
|
||||
route.leaf
|
||||
);
|
||||
current_node.parent = current_node = current_route.layout;
|
||||
parent_id = current_node.parent_id;
|
||||
} else {
|
||||
parent_id = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
current_route = current_route.parent;
|
||||
}
|
||||
|
||||
if (parent_id !== undefined) {
|
||||
throw new Error(`${current_node.component} references missing segment "${parent_id}"`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
node.page_options = node_analyser.get_page_options(node);
|
||||
}
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.endpoint) {
|
||||
route.endpoint.page_options = get_page_options(route.endpoint.file);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nodes,
|
||||
routes: sort_routes(routes)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} project_relative
|
||||
* @param {string} file
|
||||
* @param {string[]} component_extensions
|
||||
* @param {string[]} module_extensions
|
||||
* @returns {import('./types.js').RouteFile}
|
||||
*/
|
||||
function analyze(project_relative, file, component_extensions, module_extensions) {
|
||||
const component_extension = component_extensions.find((ext) => file.endsWith(ext));
|
||||
if (component_extension) {
|
||||
const name = file.slice(0, -component_extension.length);
|
||||
const pattern = /^\+(?:(page(?:@(.*))?)|(layout(?:@(.*))?)|(error))$/;
|
||||
const match = pattern.exec(name);
|
||||
if (!match) {
|
||||
throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`);
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'component',
|
||||
is_page: !!match[1],
|
||||
is_layout: !!match[3],
|
||||
is_error: !!match[5],
|
||||
uses_layout: match[2] ?? match[4]
|
||||
};
|
||||
}
|
||||
|
||||
const module_extension = module_extensions.find((ext) => file.endsWith(ext));
|
||||
if (module_extension) {
|
||||
const name = file.slice(0, -module_extension.length);
|
||||
const pattern =
|
||||
/^\+(?:(server)|(page(?:(@[a-zA-Z0-9_-]*))?(\.server)?)|(layout(?:(@[a-zA-Z0-9_-]*))?(\.server)?))$/;
|
||||
const match = pattern.exec(name);
|
||||
if (!match) {
|
||||
throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`);
|
||||
} else if (match[3] || match[6]) {
|
||||
throw new Error(
|
||||
// prettier-ignore
|
||||
`Only Svelte files can reference named layouts. Remove '${match[3] || match[6]}' from ${file} (at ${project_relative})`
|
||||
);
|
||||
}
|
||||
|
||||
const kind = match[1] || match[4] || match[7] ? 'server' : 'universal';
|
||||
|
||||
return {
|
||||
kind,
|
||||
is_page: !!match[2],
|
||||
is_layout: !!match[5]
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Files and directories prefixed with + are reserved (saw ${project_relative})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} needle
|
||||
* @param {string} haystack
|
||||
*/
|
||||
function count_occurrences(needle, haystack) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < haystack.length; i += 1) {
|
||||
if (haystack[i] === needle) count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** @param {import('types').RouteData[]} routes */
|
||||
function prevent_conflicts(routes) {
|
||||
/** @type {Map<string, string>} */
|
||||
const lookup = new Map();
|
||||
|
||||
for (const route of routes) {
|
||||
if (!route.leaf && !route.endpoint) continue;
|
||||
|
||||
const normalized = normalize_route_id(route.id);
|
||||
|
||||
// find all permutations created by optional parameters
|
||||
const split = normalized.split(/<\?(.+?)>/g);
|
||||
|
||||
let permutations = [/** @type {string} */ (split[0])];
|
||||
|
||||
// turn `x/[[optional]]/y` into `x/y` and `x/[required]/y`
|
||||
for (let i = 1; i < split.length; i += 2) {
|
||||
const matcher = split[i];
|
||||
const next = split[i + 1];
|
||||
|
||||
permutations = permutations.reduce((a, b) => {
|
||||
a.push(b + next);
|
||||
if (!(matcher === '*' && b.endsWith('//'))) a.push(b + `<${matcher}>${next}`);
|
||||
return a;
|
||||
}, /** @type {string[]} */ ([]));
|
||||
}
|
||||
|
||||
for (const permutation of permutations) {
|
||||
// remove leading/trailing/duplicated slashes caused by prior
|
||||
// manipulation of optional parameters and (groups)
|
||||
const key = permutation
|
||||
.replace(/\/{2,}/, '/')
|
||||
.replace(/^\//, '')
|
||||
.replace(/\/$/, '');
|
||||
|
||||
if (lookup.has(key)) {
|
||||
throw new Error(
|
||||
`The "${lookup.get(key)}" and "${route.id}" routes conflict with each other`
|
||||
);
|
||||
}
|
||||
|
||||
lookup.set(key, route.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} id */
|
||||
function normalize_route_id(id) {
|
||||
return (
|
||||
id
|
||||
// remove groups
|
||||
.replace(/(?<=^|\/)\(.+?\)(?=$|\/)/g, '')
|
||||
|
||||
.replace(/\[[ux]\+([0-9a-f]+)\]/g, (_, x) =>
|
||||
String.fromCharCode(parseInt(x, 16)).replace(/\//g, '%2f')
|
||||
)
|
||||
|
||||
// replace `[param]` with `<*>`, `[param=x]` with `<x>`, and `[[param]]` with `<?*>`
|
||||
.replace(
|
||||
/\[(?:(\[)|(\.\.\.))?.+?(=.+?)?\]\]?/g,
|
||||
(_, optional, rest, matcher) => `<${optional ? '?' : ''}${rest ?? ''}${matcher ?? '*'}>`
|
||||
)
|
||||
);
|
||||
}
|
||||
162
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/sort.js
generated
vendored
Normal file
162
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/sort.js
generated
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import { get_route_segments } from '../../../utils/routing.js';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* type: 'static' | 'required' | 'optional' | 'rest';
|
||||
* content: string;
|
||||
* matched: boolean;
|
||||
* }} Part
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Part[]} Segment
|
||||
*/
|
||||
|
||||
const EMPTY = { type: 'static', content: '', matched: false };
|
||||
|
||||
/** @param {import('types').RouteData[]} routes */
|
||||
export function sort_routes(routes) {
|
||||
/** @type {Map<string, Part[]>} */
|
||||
const segment_cache = new Map();
|
||||
|
||||
/** @param {string} segment */
|
||||
function get_parts(segment) {
|
||||
if (!segment_cache.has(segment)) {
|
||||
segment_cache.set(segment, split(segment));
|
||||
}
|
||||
|
||||
return segment_cache.get(segment);
|
||||
}
|
||||
|
||||
/** @param {string} id */
|
||||
function split(id) {
|
||||
/** @type {Part[]} */
|
||||
const parts = [];
|
||||
|
||||
let i = 0;
|
||||
while (i <= id.length) {
|
||||
const start = id.indexOf('[', i);
|
||||
if (start === -1) {
|
||||
parts.push({ type: 'static', content: id.slice(i), matched: false });
|
||||
break;
|
||||
}
|
||||
|
||||
parts.push({ type: 'static', content: id.slice(i, start), matched: false });
|
||||
|
||||
const type = id[start + 1] === '[' ? 'optional' : id[start + 1] === '.' ? 'rest' : 'required';
|
||||
const delimiter = type === 'optional' ? ']]' : ']';
|
||||
const end = id.indexOf(delimiter, start);
|
||||
|
||||
if (end === -1) {
|
||||
throw new Error(`Invalid route ID ${id}`);
|
||||
}
|
||||
|
||||
const content = id.slice(start, (i = end + delimiter.length));
|
||||
|
||||
parts.push({
|
||||
type,
|
||||
content,
|
||||
matched: content.includes('=')
|
||||
});
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
return routes.sort((route_a, route_b) => {
|
||||
const segments_a = split_route_id(route_a.id).map(get_parts);
|
||||
const segments_b = split_route_id(route_b.id).map(get_parts);
|
||||
|
||||
for (let i = 0; i < Math.max(segments_a.length, segments_b.length); i += 1) {
|
||||
const segment_a = segments_a[i] ?? [EMPTY];
|
||||
const segment_b = segments_b[i] ?? [EMPTY];
|
||||
|
||||
for (let j = 0; j < Math.max(segment_a.length, segment_b.length); j += 1) {
|
||||
const a = segment_a[j];
|
||||
const b = segment_b[j];
|
||||
|
||||
// first part of each segment is always static
|
||||
// (though it may be the empty string), then
|
||||
// it alternates between dynamic and static
|
||||
// (i.e. [foo][bar] is disallowed)
|
||||
const dynamic = j % 2 === 1;
|
||||
|
||||
if (dynamic) {
|
||||
if (!a) return -1;
|
||||
if (!b) return +1;
|
||||
|
||||
// get the next static chunk, so we can handle [...rest] edge cases
|
||||
const next_a = segment_a[j + 1].content || segments_a[i + 1]?.[0].content;
|
||||
const next_b = segment_b[j + 1].content || segments_b[i + 1]?.[0].content;
|
||||
|
||||
// `[...rest]/x` outranks `[...rest]`
|
||||
if (a.type === 'rest' && b.type === 'rest') {
|
||||
if (next_a && next_b) continue;
|
||||
if (next_a) return -1;
|
||||
if (next_b) return +1;
|
||||
}
|
||||
|
||||
// `[...rest]/x` outranks `[required]` or `[required]/[required]`
|
||||
// but not `[required]/x`
|
||||
if (a.type === 'rest') {
|
||||
return next_a && !next_b ? -1 : +1;
|
||||
}
|
||||
|
||||
if (b.type === 'rest') {
|
||||
return next_b && !next_a ? +1 : -1;
|
||||
}
|
||||
|
||||
// part with matcher outranks one without
|
||||
if (a.matched !== b.matched) {
|
||||
return a.matched ? -1 : +1;
|
||||
}
|
||||
|
||||
if (a.type !== b.type) {
|
||||
// `[...rest]` has already been accounted for, so here
|
||||
// we're comparing between `[required]` and `[[optional]]`
|
||||
if (a.type === 'required') return -1;
|
||||
if (b.type === 'required') return +1;
|
||||
}
|
||||
} else if (a.content !== b.content) {
|
||||
// shallower path outranks deeper path
|
||||
if (a === EMPTY) return -1;
|
||||
if (b === EMPTY) return +1;
|
||||
|
||||
return sort_static(a.content, b.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return route_a.id < route_b.id ? +1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {string} id */
|
||||
function split_route_id(id) {
|
||||
return get_route_segments(
|
||||
id
|
||||
// remove all [[optional]] parts unless they're at the very end
|
||||
// or it ends with a route group
|
||||
.replace(/\[\[[^\]]+\]\](?!(?:\/\([^/]+\))*$)/g, '')
|
||||
).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort two strings lexicographically, except `foobar` outranks `foo`
|
||||
* @param {string} a
|
||||
* @param {string} b
|
||||
*/
|
||||
function sort_static(a, b) {
|
||||
if (a === b) return 0;
|
||||
|
||||
for (let i = 0; true; i += 1) {
|
||||
const char_a = a[i];
|
||||
const char_b = b[i];
|
||||
|
||||
if (char_a !== char_b) {
|
||||
if (char_a === undefined) return +1;
|
||||
if (char_b === undefined) return -1;
|
||||
return char_a < char_b ? -1 : +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/types.d.ts
generated
vendored
Normal file
37
frontend/node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { PageNode } from 'types';
|
||||
|
||||
interface Part {
|
||||
dynamic: boolean;
|
||||
optional: boolean;
|
||||
rest: boolean;
|
||||
type: string | null;
|
||||
}
|
||||
|
||||
interface RouteTreeNode {
|
||||
error: PageNode | undefined;
|
||||
layout: PageNode | undefined;
|
||||
}
|
||||
|
||||
export type RouteTree = Map<string, RouteTreeNode>;
|
||||
|
||||
interface RouteComponent {
|
||||
kind: 'component';
|
||||
is_page: boolean;
|
||||
is_layout: boolean;
|
||||
is_error: boolean;
|
||||
uses_layout: string | undefined;
|
||||
}
|
||||
|
||||
interface RouteSharedModule {
|
||||
kind: 'universal';
|
||||
is_page: boolean;
|
||||
is_layout: boolean;
|
||||
}
|
||||
|
||||
interface RouteServerModule {
|
||||
kind: 'server';
|
||||
is_page: boolean;
|
||||
is_layout: boolean;
|
||||
}
|
||||
|
||||
export type RouteFile = RouteComponent | RouteSharedModule | RouteServerModule;
|
||||
96
frontend/node_modules/@sveltejs/kit/src/core/sync/sync.js
generated
vendored
Normal file
96
frontend/node_modules/@sveltejs/kit/src/core/sync/sync.js
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import path from 'node:path';
|
||||
import create_manifest_data from './create_manifest_data/index.js';
|
||||
import { write_client_manifest } from './write_client_manifest.js';
|
||||
import { write_root } from './write_root.js';
|
||||
import { write_tsconfig } from './write_tsconfig.js';
|
||||
import { write_types, write_all_types } from './write_types/index.js';
|
||||
import { write_ambient } from './write_ambient.js';
|
||||
import { write_non_ambient } from './write_non_ambient.js';
|
||||
import { write_server } from './write_server.js';
|
||||
import {
|
||||
create_node_analyser,
|
||||
get_page_options
|
||||
} from '../../exports/vite/static_analysis/index.js';
|
||||
|
||||
/**
|
||||
* Initialize SvelteKit's generated files that only depend on the config and mode.
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} mode
|
||||
*/
|
||||
export function init(config, mode) {
|
||||
write_tsconfig(config.kit);
|
||||
write_ambient(config.kit, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update SvelteKit's generated files
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
export function create(config) {
|
||||
const manifest_data = create_manifest_data({ config });
|
||||
|
||||
const output = path.join(config.kit.outDir, 'generated');
|
||||
|
||||
write_client_manifest(config.kit, manifest_data, `${output}/client`);
|
||||
write_server(config, output);
|
||||
write_root(manifest_data, config, output);
|
||||
write_all_types(config, manifest_data);
|
||||
write_non_ambient(config.kit, manifest_data);
|
||||
|
||||
return { manifest_data };
|
||||
}
|
||||
|
||||
/**
|
||||
* Update SvelteKit's generated files in response to a single file content update.
|
||||
* Do not call this when the file in question was created/deleted.
|
||||
*
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {string} file
|
||||
*/
|
||||
export function update(config, manifest_data, file) {
|
||||
const node_analyser = create_node_analyser();
|
||||
|
||||
for (const node of manifest_data.nodes) {
|
||||
node.page_options = node_analyser.get_page_options(node);
|
||||
}
|
||||
|
||||
for (const route of manifest_data.routes) {
|
||||
if (route.endpoint) {
|
||||
route.endpoint.page_options = get_page_options(route.endpoint.file);
|
||||
}
|
||||
}
|
||||
|
||||
write_types(config, manifest_data, file);
|
||||
write_non_ambient(config.kit, manifest_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run sync.init and sync.create in series, returning the result from sync.create.
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} mode The Vite mode
|
||||
*/
|
||||
export function all(config, mode) {
|
||||
init(config, mode);
|
||||
return create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run sync.init and then generate all type files.
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} mode The Vite mode
|
||||
*/
|
||||
export function all_types(config, mode) {
|
||||
init(config, mode);
|
||||
const manifest_data = create_manifest_data({ config });
|
||||
write_all_types(config, manifest_data);
|
||||
write_non_ambient(config.kit, manifest_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate __SERVER__/internal.js in response to src/{app.html,error.html,service-worker.js} changing
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
export function server(config) {
|
||||
write_server(config, path.join(config.kit.outDir, 'generated'));
|
||||
}
|
||||
6
frontend/node_modules/@sveltejs/kit/src/core/sync/ts.js
generated
vendored
Normal file
6
frontend/node_modules/@sveltejs/kit/src/core/sync/ts.js
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('typescript')} */
|
||||
// @ts-ignore
|
||||
export let ts = undefined;
|
||||
try {
|
||||
ts = (await import('typescript')).default;
|
||||
} catch {}
|
||||
78
frontend/node_modules/@sveltejs/kit/src/core/sync/utils.js
generated
vendored
Normal file
78
frontend/node_modules/@sveltejs/kit/src/core/sync/utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { mkdirp } from '../../utils/filesystem.js';
|
||||
import { import_peer } from '../../utils/import.js';
|
||||
|
||||
/** @type {{ VERSION: string }} */
|
||||
const { VERSION } = await import_peer('svelte/compiler');
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
const previous_contents = new Map();
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {string} code
|
||||
*/
|
||||
export function write_if_changed(file, code) {
|
||||
if (code !== previous_contents.get(file)) {
|
||||
write(file, code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {string} code
|
||||
*/
|
||||
export function write(file, code) {
|
||||
previous_contents.set(file, code);
|
||||
mkdirp(path.dirname(file));
|
||||
fs.writeFileSync(file, code);
|
||||
}
|
||||
|
||||
/** @type {WeakMap<TemplateStringsArray, { strings: string[], indents: string[] }>} */
|
||||
const dedent_map = new WeakMap();
|
||||
|
||||
/**
|
||||
* Allows indenting template strings without the extra indentation ending up in the result.
|
||||
* Still allows indentation of lines relative to one another in the template string.
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {any[]} values
|
||||
*/
|
||||
export function dedent(strings, ...values) {
|
||||
let dedented = dedent_map.get(strings);
|
||||
|
||||
if (!dedented) {
|
||||
const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(strings[0]))[1];
|
||||
const pattern = new RegExp(`^${indentation}`, 'gm');
|
||||
|
||||
dedented = {
|
||||
strings: strings.map((str) => str.replace(pattern, '')),
|
||||
indents: []
|
||||
};
|
||||
|
||||
let current = '\n';
|
||||
|
||||
for (let i = 0; i < values.length; i += 1) {
|
||||
const string = dedented.strings[i];
|
||||
const match = /\n([ \t]*)$/.exec(string);
|
||||
|
||||
if (match) current = match[0];
|
||||
dedented.indents[i] = current;
|
||||
}
|
||||
|
||||
dedent_map.set(strings, dedented);
|
||||
}
|
||||
|
||||
let str = dedented.strings[0];
|
||||
for (let i = 0; i < values.length; i += 1) {
|
||||
str += String(values[i]).replace(/\n/g, dedented.indents[i]) + dedented.strings[i + 1];
|
||||
}
|
||||
|
||||
str = str.trim();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
export function isSvelte5Plus() {
|
||||
return Number(VERSION[0]) >= 5;
|
||||
}
|
||||
63
frontend/node_modules/@sveltejs/kit/src/core/sync/write_ambient.js
generated
vendored
Normal file
63
frontend/node_modules/@sveltejs/kit/src/core/sync/write_ambient.js
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { get_env } from '../../exports/vite/utils.js';
|
||||
import { GENERATED_COMMENT } from '../../constants.js';
|
||||
import { create_dynamic_types, create_static_types } from '../env.js';
|
||||
import { write_if_changed } from './utils.js';
|
||||
|
||||
// TODO these types should be described in a neutral place, rather than
|
||||
// inside either `packages/kit` or `svelte.dev/docs/kit`
|
||||
const descriptions_dir = fileURLToPath(new URL('../../../src/types/synthetic', import.meta.url));
|
||||
|
||||
/** @param {string} filename */
|
||||
function read_description(filename) {
|
||||
const content = fs.readFileSync(`${descriptions_dir}/${filename}`, 'utf8');
|
||||
return `/**\n${content
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((line) => ` * ${line}`)
|
||||
.join('\n')}\n */`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').Env} env
|
||||
* @param {{
|
||||
* public_prefix: string;
|
||||
* private_prefix: string;
|
||||
* }} prefixes
|
||||
*/
|
||||
const template = (env, prefixes) => `
|
||||
${GENERATED_COMMENT}
|
||||
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
${read_description('$env+static+private.md')}
|
||||
${create_static_types('private', env)}
|
||||
|
||||
${read_description('$env+static+public.md')}
|
||||
${create_static_types('public', env)}
|
||||
|
||||
${read_description('$env+dynamic+private.md')}
|
||||
${create_dynamic_types('private', env, prefixes)}
|
||||
|
||||
${read_description('$env+dynamic+public.md')}
|
||||
${create_dynamic_types('public', env, prefixes)}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Writes ambient declarations including types reference to @sveltejs/kit,
|
||||
* and the existing environment variables in process.env to
|
||||
* $env/static/private and $env/static/public
|
||||
* @param {import('types').ValidatedKitConfig} config
|
||||
* @param {string} mode The Vite mode
|
||||
*/
|
||||
export function write_ambient(config, mode) {
|
||||
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, { public_prefix, private_prefix })
|
||||
);
|
||||
}
|
||||
203
frontend/node_modules/@sveltejs/kit/src/core/sync/write_client_manifest.js
generated
vendored
Normal file
203
frontend/node_modules/@sveltejs/kit/src/core/sync/write_client_manifest.js
generated
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
import path from 'node:path';
|
||||
import { relative_path, resolve_entry } from '../../utils/filesystem.js';
|
||||
import { s } from '../../utils/misc.js';
|
||||
import { dedent, isSvelte5Plus, write_if_changed } from './utils.js';
|
||||
import colors from 'kleur';
|
||||
|
||||
/**
|
||||
* Writes the client manifest to disk. The manifest is used to power the router. It contains the
|
||||
* list of routes and corresponding Svelte components (i.e. pages and layouts).
|
||||
* @param {import('types').ValidatedKitConfig} kit
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {string} output
|
||||
* @param {import('types').ServerMetadata['nodes']} [metadata] If this is omitted, we have to assume that all routes with a `+layout/page.server.js` file have a server load function
|
||||
*/
|
||||
export function write_client_manifest(kit, manifest_data, output, metadata) {
|
||||
const client_routing = kit.router.resolution === 'client';
|
||||
|
||||
/**
|
||||
* Creates a module that exports a `CSRPageNode`
|
||||
* @param {import('types').PageNode} node
|
||||
*/
|
||||
function generate_node(node) {
|
||||
const declarations = [];
|
||||
|
||||
if (node.universal) {
|
||||
declarations.push(
|
||||
`import * as universal from ${s(relative_path(`${output}/nodes`, node.universal))};`,
|
||||
'export { universal };'
|
||||
);
|
||||
}
|
||||
|
||||
if (node.component) {
|
||||
declarations.push(
|
||||
`export { default as component } from ${s(
|
||||
relative_path(`${output}/nodes`, node.component)
|
||||
)};`
|
||||
);
|
||||
}
|
||||
|
||||
return declarations.join('\n');
|
||||
}
|
||||
|
||||
/** @type {Map<import('types').PageNode, number>} */
|
||||
const indices = new Map();
|
||||
const nodes = manifest_data.nodes
|
||||
.map((node, i) => {
|
||||
indices.set(node, i);
|
||||
|
||||
write_if_changed(`${output}/nodes/${i}.js`, generate_node(node));
|
||||
return `() => import('./nodes/${i}')`;
|
||||
})
|
||||
// If route resolution happens on the server, we only need the root layout and root error page
|
||||
// upfront, the rest is loaded on demand as the user navigates the app
|
||||
.slice(0, client_routing ? manifest_data.nodes.length : 2)
|
||||
.join(',\n');
|
||||
|
||||
const layouts_with_server_load = new Set();
|
||||
|
||||
let dictionary = dedent`
|
||||
{
|
||||
${manifest_data.routes
|
||||
.map((route) => {
|
||||
if (route.page) {
|
||||
const errors = route.page.errors.slice(1).map((n) => n ?? '');
|
||||
const layouts = route.page.layouts.slice(1).map((n) => n ?? '');
|
||||
|
||||
while (layouts.at(-1) === '') layouts.pop();
|
||||
while (errors.at(-1) === '') errors.pop();
|
||||
|
||||
let leaf_has_server_load = false;
|
||||
if (route.leaf) {
|
||||
if (metadata) {
|
||||
const i = /** @type {number} */ (indices.get(route.leaf));
|
||||
|
||||
leaf_has_server_load = metadata[i].has_server_load;
|
||||
} else if (route.leaf.server) {
|
||||
leaf_has_server_load = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode whether or not the route uses server data
|
||||
// using the ones' complement, to save space
|
||||
const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`];
|
||||
|
||||
// Encode whether or not the layout uses server data.
|
||||
// It's a different method compared to pages because layouts
|
||||
// are reused across pages, so we save space by doing it this way.
|
||||
route.page.layouts.forEach((layout) => {
|
||||
if (layout == undefined) return;
|
||||
|
||||
let layout_has_server_load = false;
|
||||
|
||||
if (metadata) {
|
||||
layout_has_server_load = metadata[layout].has_server_load;
|
||||
} else if (manifest_data.nodes[layout].server) {
|
||||
layout_has_server_load = true;
|
||||
}
|
||||
|
||||
if (layout_has_server_load) {
|
||||
layouts_with_server_load.add(layout);
|
||||
}
|
||||
});
|
||||
|
||||
// only include non-root layout/error nodes if they exist
|
||||
if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`);
|
||||
if (errors.length > 0) array.push(`[${errors.join(',')}]`);
|
||||
|
||||
return `${s(route.id)}: [${array.join(',')}]`;
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(',\n')}
|
||||
}
|
||||
`;
|
||||
|
||||
if (!client_routing) {
|
||||
dictionary = '{}';
|
||||
const root_layout = layouts_with_server_load.has(0);
|
||||
layouts_with_server_load.clear();
|
||||
if (root_layout) layouts_with_server_load.add(0);
|
||||
}
|
||||
|
||||
const client_hooks_file = resolve_entry(kit.files.hooks.client);
|
||||
const universal_hooks_file = resolve_entry(kit.files.hooks.universal);
|
||||
|
||||
const typo = resolve_entry('src/+hooks.client');
|
||||
if (typo) {
|
||||
console.log(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(
|
||||
`Unexpected + prefix. Did you mean ${typo.split('/').at(-1)?.slice(1)}?` +
|
||||
` at ${path.resolve(typo)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Stringified version of
|
||||
/** @type {import('../../runtime/client/types.js').SvelteKitApp} */
|
||||
write_if_changed(
|
||||
`${output}/app.js`,
|
||||
dedent`
|
||||
${
|
||||
client_hooks_file
|
||||
? `import * as client_hooks from '${relative_path(output, client_hooks_file)}';`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
universal_hooks_file
|
||||
? `import * as universal_hooks from '${relative_path(output, universal_hooks_file)}';`
|
||||
: ''
|
||||
}
|
||||
|
||||
${client_routing ? "export { matchers } from './matchers.js';" : 'export const matchers = {};'}
|
||||
|
||||
export const nodes = [
|
||||
${nodes}
|
||||
];
|
||||
|
||||
export const server_loads = [${[...layouts_with_server_load].join(',')}];
|
||||
|
||||
export const dictionary = ${dictionary};
|
||||
|
||||
export const hooks = {
|
||||
handleError: ${
|
||||
client_hooks_file ? 'client_hooks.handleError || ' : ''
|
||||
}(({ error }) => { console.error(error) }),
|
||||
${client_hooks_file ? 'init: client_hooks.init,' : ''}
|
||||
reroute: ${universal_hooks_file ? 'universal_hooks.reroute || ' : ''}(() => {}),
|
||||
transport: ${universal_hooks_file ? 'universal_hooks.transport || ' : ''}{}
|
||||
};
|
||||
|
||||
export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode]));
|
||||
export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode]));
|
||||
|
||||
export const hash = ${s(kit.router.type === 'hash')};
|
||||
|
||||
export const decode = (type, value) => decoders[type](value);
|
||||
|
||||
export { default as root } from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';
|
||||
`
|
||||
);
|
||||
|
||||
if (client_routing) {
|
||||
// write matchers to a separate module so that we don't
|
||||
// need to worry about name conflicts
|
||||
const imports = [];
|
||||
const matchers = [];
|
||||
|
||||
for (const key in manifest_data.matchers) {
|
||||
const src = manifest_data.matchers[key];
|
||||
|
||||
imports.push(`import { match as ${key} } from ${s(relative_path(output, src))};`);
|
||||
matchers.push(key);
|
||||
}
|
||||
|
||||
const module = imports.length
|
||||
? `${imports.join('\n')}\n\nexport const matchers = { ${matchers.join(', ')} };`
|
||||
: 'export const matchers = {};';
|
||||
|
||||
write_if_changed(`${output}/matchers.js`, module);
|
||||
}
|
||||
}
|
||||
266
frontend/node_modules/@sveltejs/kit/src/core/sync/write_non_ambient.js
generated
vendored
Normal file
266
frontend/node_modules/@sveltejs/kit/src/core/sync/write_non_ambient.js
generated
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
import path from 'node:path';
|
||||
import { GENERATED_COMMENT } from '../../constants.js';
|
||||
import { posixify } from '../../utils/filesystem.js';
|
||||
import { write_if_changed } from './utils.js';
|
||||
import { s } from '../../utils/misc.js';
|
||||
import { get_route_segments } from '../../utils/routing.js';
|
||||
|
||||
const replace_optional_params = (/** @type {string} */ id) =>
|
||||
id.replace(/\/\[\[[^\]]+\]\]/g, '${string}');
|
||||
const replace_required_params = (/** @type {string} */ id) =>
|
||||
id.replace(/\/\[[^\]]+\]/g, '/${string}');
|
||||
/** Convert route ID to pathname by removing layout groups */
|
||||
const remove_group_segments = (/** @type {string} */ id) => {
|
||||
return '/' + get_route_segments(id).join('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get pathnames to add based on trailingSlash settings
|
||||
* @param {string} pathname
|
||||
* @param {import('types').RouteData} route
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function get_pathnames_for_trailing_slash(pathname, route) {
|
||||
if (pathname === '/') {
|
||||
return [pathname];
|
||||
}
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const pathnames = new Set();
|
||||
|
||||
/**
|
||||
* @param {{ trailingSlash?: import('types').TrailingSlash } | null | undefined} page_options
|
||||
*/
|
||||
const add_pathnames = (page_options) => {
|
||||
if (page_options === null || page_options?.trailingSlash === 'ignore') {
|
||||
pathnames.add(pathname);
|
||||
pathnames.add(pathname + '/');
|
||||
} else if (page_options?.trailingSlash === 'always') {
|
||||
pathnames.add(pathname + '/');
|
||||
} else {
|
||||
pathnames.add(pathname);
|
||||
}
|
||||
};
|
||||
|
||||
if (route.leaf) add_pathnames(route.leaf.page_options ?? null);
|
||||
if (route.endpoint) add_pathnames(route.endpoint.page_options);
|
||||
|
||||
return Array.from(pathnames);
|
||||
}
|
||||
|
||||
// `declare module "svelte/elements"` needs to happen in a non-ambient module, and dts-buddy generates one big ambient module,
|
||||
// so we can't add it there - therefore generate the typings ourselves here.
|
||||
// We're not using the `declare namespace svelteHTML` variant because that one doesn't augment the HTMLAttributes interface
|
||||
// people could use to type their own components.
|
||||
// The T generic is needed or else there's a "all declarations must have identical type parameters" error.
|
||||
const template = `
|
||||
${GENERATED_COMMENT}
|
||||
|
||||
declare module "svelte/elements" {
|
||||
export interface HTMLAttributes<T> {
|
||||
'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-preload-code'?:
|
||||
| true
|
||||
| ''
|
||||
| 'eager'
|
||||
| 'viewport'
|
||||
| 'hover'
|
||||
| 'tap'
|
||||
| 'off'
|
||||
| undefined
|
||||
| null;
|
||||
'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null;
|
||||
'data-sveltekit-reload'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-replacestate'?: true | '' | 'off' | undefined | null;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
`;
|
||||
|
||||
/**
|
||||
* Generate app types interface extension
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {import('types').ValidatedKitConfig} config
|
||||
*/
|
||||
function generate_app_types(manifest_data, config) {
|
||||
/** @param {string} matcher */
|
||||
const path_to_matcher = (matcher) =>
|
||||
posixify(path.relative(config.outDir, path.join(config.files.params, matcher + '.js')));
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
const matcher_types = new Map();
|
||||
|
||||
/** @param {string | undefined} matcher */
|
||||
const get_matcher_type = (matcher) => {
|
||||
if (!matcher) return 'string';
|
||||
|
||||
let type = matcher_types.get(matcher);
|
||||
if (!type) {
|
||||
type = `MatcherParam<typeof import('${path_to_matcher(matcher)}').match>`;
|
||||
matcher_types.set(matcher, type);
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
/** @param {Set<string> | null} matchers */
|
||||
const get_matchers_type = (matchers) => {
|
||||
if (matchers === null) return 'string';
|
||||
|
||||
return Array.from(matchers)
|
||||
.map((matcher) => get_matcher_type(matcher))
|
||||
.join(' | ');
|
||||
};
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const route_ids = new Set(manifest_data.routes.map((route) => route.id));
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const get_ancestor_route_ids = (id) => {
|
||||
/** @type {string[]} */
|
||||
const ancestors = [];
|
||||
|
||||
if (route_ids.has('/')) {
|
||||
ancestors.push('/');
|
||||
}
|
||||
|
||||
let current = '';
|
||||
for (const segment of id.slice(1).split('/')) {
|
||||
if (!segment) continue;
|
||||
|
||||
current += '/' + segment;
|
||||
if (route_ids.has(current)) {
|
||||
ancestors.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
return ancestors;
|
||||
};
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const pathnames = new Set();
|
||||
|
||||
/** @type {string[]} */
|
||||
const dynamic_routes = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const layouts = [];
|
||||
|
||||
/** @type {Map<string, Map<string, { optional: boolean, matchers: Set<string> | null }>>} */
|
||||
const layout_params_by_route = new Map(
|
||||
manifest_data.routes.map((route) => [
|
||||
route.id,
|
||||
new Map(
|
||||
route.params.map((p) => [
|
||||
p.name,
|
||||
{ optional: p.optional, matchers: p.matcher ? new Set([p.matcher]) : null }
|
||||
])
|
||||
)
|
||||
])
|
||||
);
|
||||
|
||||
for (const route of manifest_data.routes) {
|
||||
const ancestors = get_ancestor_route_ids(route.id);
|
||||
|
||||
for (const ancestor_id of ancestors) {
|
||||
const ancestor_params = layout_params_by_route.get(ancestor_id);
|
||||
if (!ancestor_params) continue;
|
||||
|
||||
for (const p of route.params) {
|
||||
const matcher = p.matcher ?? null;
|
||||
const entry = ancestor_params.get(p.name);
|
||||
if (!entry) {
|
||||
ancestor_params.set(p.name, {
|
||||
optional: true,
|
||||
matchers: matcher === null ? null : new Set([matcher])
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.matchers === null) continue;
|
||||
|
||||
if (matcher === null) {
|
||||
entry.matchers = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.matchers.add(matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const route of manifest_data.routes) {
|
||||
const pathname = remove_group_segments(route.id);
|
||||
let normalized_pathname = pathname;
|
||||
|
||||
/** @type {(path: string) => string} */
|
||||
let serialise = s;
|
||||
|
||||
if (route.params.length > 0) {
|
||||
const params = route.params.map((p) => {
|
||||
const type = get_matcher_type(p.matcher);
|
||||
return `${p.name}${p.optional ? '?:' : ':'} ${type}`;
|
||||
});
|
||||
const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
|
||||
|
||||
dynamic_routes.push(route_type);
|
||||
|
||||
normalized_pathname = replace_required_params(replace_optional_params(pathname));
|
||||
serialise = (p) => `\`${p}\` & {}`;
|
||||
}
|
||||
|
||||
for (const p of get_pathnames_for_trailing_slash(normalized_pathname, route)) {
|
||||
pathnames.add(serialise(p));
|
||||
}
|
||||
|
||||
let layout_type = 'Record<string, never>';
|
||||
|
||||
const layout_params = layout_params_by_route.get(route.id);
|
||||
if (layout_params) {
|
||||
const params = Array.from(layout_params)
|
||||
.map(([name, { optional, matchers }]) => {
|
||||
const type = get_matchers_type(matchers);
|
||||
return `${name}${optional ? '?:' : ':'} ${type}`;
|
||||
})
|
||||
.join('; ');
|
||||
|
||||
if (params.length > 0) layout_type = `{ ${params} }`;
|
||||
}
|
||||
|
||||
layouts.push(`${s(route.id)}: ${layout_type}`);
|
||||
}
|
||||
|
||||
const assets = manifest_data.assets.map((asset) => s('/' + asset.file));
|
||||
|
||||
return [
|
||||
'declare module "$app/types" {',
|
||||
'\ttype MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;',
|
||||
'',
|
||||
'\texport interface AppTypes {',
|
||||
`\t\tRouteId(): ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
|
||||
`\t\tRouteParams(): {\n\t\t\t${dynamic_routes.join(';\n\t\t\t')}\n\t\t};`,
|
||||
`\t\tLayoutParams(): {\n\t\t\t${layouts.join(';\n\t\t\t')}\n\t\t};`,
|
||||
`\t\tPathname(): ${Array.from(pathnames).join(' | ')};`,
|
||||
'\t\tResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes[\'Pathname\']>}`;',
|
||||
`\t\tAsset(): ${assets.concat('string & {}').join(' | ')};`,
|
||||
'\t}',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes non-ambient declarations to the output directory
|
||||
* @param {import('types').ValidatedKitConfig} config
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
*/
|
||||
export function write_non_ambient(config, manifest_data) {
|
||||
const app_types = generate_app_types(manifest_data, config);
|
||||
const content = [template, app_types].join('\n\n');
|
||||
|
||||
write_if_changed(path.join(config.outDir, 'non-ambient.d.ts'), content);
|
||||
}
|
||||
196
frontend/node_modules/@sveltejs/kit/src/core/sync/write_root.js
generated
vendored
Normal file
196
frontend/node_modules/@sveltejs/kit/src/core/sync/write_root.js
generated
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import { dedent, isSvelte5Plus, write_if_changed } from './utils.js';
|
||||
|
||||
/**
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} output
|
||||
*/
|
||||
export function write_root(manifest_data, config, output) {
|
||||
// TODO remove default layout altogether
|
||||
|
||||
const use_boundaries = config.kit.experimental.handleRenderingErrors && isSvelte5Plus();
|
||||
|
||||
const max_depth = Math.max(
|
||||
...manifest_data.routes.map((route) =>
|
||||
route.page ? route.page.layouts.filter(Boolean).length + 1 : 0
|
||||
),
|
||||
1
|
||||
);
|
||||
|
||||
const levels = [];
|
||||
for (let i = 0; i <= max_depth; i += 1) {
|
||||
levels.push(i);
|
||||
}
|
||||
|
||||
let l = max_depth;
|
||||
/** @type {string} */
|
||||
let pyramid;
|
||||
|
||||
if (isSvelte5Plus() && use_boundaries) {
|
||||
// with the @const we force the data[depth] access to be derived, which is important to not fire updates needlessly
|
||||
// TODO in Svelte 5 we should rethink the client.js side, we can likely make data a $state and only update indexes that changed there, simplifying this a lot
|
||||
pyramid = dedent`
|
||||
{#snippet pyramid(depth)}
|
||||
{@const Pyramid = constructors[depth]}
|
||||
{#snippet failed(error)}
|
||||
{@const ErrorPage = errors[depth]}
|
||||
<ErrorPage {error} />
|
||||
{/snippet}
|
||||
<svelte:boundary failed={errors[depth] ? failed : undefined}>
|
||||
{#if constructors[depth + 1]}
|
||||
{@const d = data[depth]}
|
||||
<!-- svelte-ignore binding_property_non_reactive -->
|
||||
<Pyramid bind:this={components[depth]} data={d} {form} params={page.params}>
|
||||
{@render pyramid(depth + 1)}
|
||||
</Pyramid>
|
||||
{:else}
|
||||
{@const d = data[depth]}
|
||||
<!-- svelte-ignore binding_property_non_reactive -->
|
||||
<Pyramid bind:this={components[depth]} data={d} {form} params={page.params} {error} />
|
||||
{/if}
|
||||
</svelte:boundary>
|
||||
{/snippet}
|
||||
|
||||
{@render pyramid(0)}
|
||||
`;
|
||||
} else {
|
||||
pyramid = dedent`
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? `<!-- svelte-ignore binding_property_non_reactive -->
|
||||
<Pyramid_${l} bind:this={components[${l}]} data={data_${l}} {form} params={page.params} />`
|
||||
: `<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} params={page.params} />`
|
||||
}`;
|
||||
|
||||
while (l--) {
|
||||
pyramid = dedent`
|
||||
{#if constructors[${l + 1}]}
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? dedent`{@const Pyramid_${l} = constructors[${l}]}
|
||||
<!-- svelte-ignore binding_property_non_reactive -->
|
||||
<Pyramid_${l} bind:this={components[${l}]} data={data_${l}} {form} params={page.params}>
|
||||
${pyramid}
|
||||
</Pyramid_${l}>`
|
||||
: dedent`<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} params={page.params}>
|
||||
${pyramid}
|
||||
</svelte:component>`
|
||||
}
|
||||
|
||||
{:else}
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? dedent`
|
||||
{@const Pyramid_${l} = constructors[${l}]}
|
||||
<!-- svelte-ignore binding_property_non_reactive -->
|
||||
<Pyramid_${l} bind:this={components[${l}]} data={data_${l}} {form} params={page.params} />
|
||||
`
|
||||
: dedent`<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} params={page.params} />`
|
||||
}
|
||||
|
||||
{/if}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
write_if_changed(
|
||||
`${output}/root.svelte`,
|
||||
dedent`
|
||||
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
|
||||
${isSvelte5Plus() ? '<svelte:options runes={true} />' : ''}
|
||||
<script>
|
||||
import { setContext, ${isSvelte5Plus() ? '' : 'afterUpdate, '}onMount, tick } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// stores
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? dedent`
|
||||
let { stores, page, constructors, components = [], form, ${use_boundaries ? 'errors = [], error, ' : ''}${levels
|
||||
.map((l) => `data_${l} = null`)
|
||||
.join(', ')} } = $props();
|
||||
${use_boundaries ? `let data = $derived({${levels.map((l) => `'${l}': data_${l}`).join(', ')}})` : ''}
|
||||
`
|
||||
: dedent`
|
||||
export let stores;
|
||||
export let page;
|
||||
|
||||
export let constructors;
|
||||
export let components = [];
|
||||
export let form;
|
||||
${levels.map((l) => `export let data_${l} = null;`).join('\n')}
|
||||
`
|
||||
}
|
||||
|
||||
if (!browser) {
|
||||
// svelte-ignore state_referenced_locally
|
||||
setContext('__svelte__', stores);
|
||||
}
|
||||
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? dedent`
|
||||
if (browser) {
|
||||
$effect.pre(() => stores.page.set(page));
|
||||
} else {
|
||||
// svelte-ignore state_referenced_locally
|
||||
stores.page.set(page);
|
||||
}
|
||||
`
|
||||
: '$: stores.page.set(page);'
|
||||
}
|
||||
${
|
||||
isSvelte5Plus()
|
||||
? dedent`
|
||||
$effect(() => {
|
||||
stores;page;constructors;components;form;${use_boundaries ? 'errors;error;' : ''}${levels.map((l) => `data_${l}`).join(';')};
|
||||
stores.page.notify();
|
||||
});
|
||||
`
|
||||
: 'afterUpdate(stores.page.notify);'
|
||||
}
|
||||
|
||||
let mounted = ${isSvelte5Plus() ? '$state(false)' : 'false'};
|
||||
let navigated = ${isSvelte5Plus() ? '$state(false)' : 'false'};
|
||||
let title = ${isSvelte5Plus() ? '$state(null)' : 'null'};
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribe = stores.page.subscribe(() => {
|
||||
if (mounted) {
|
||||
navigated = true;
|
||||
tick().then(() => {
|
||||
title = document.title || 'untitled page';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mounted = true;
|
||||
return unsubscribe;
|
||||
});
|
||||
|
||||
${isSvelte5Plus() ? `const Pyramid_${max_depth}=$derived(constructors[${max_depth}])` : ''}
|
||||
</script>
|
||||
|
||||
${pyramid}
|
||||
|
||||
{#if mounted}
|
||||
<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px">
|
||||
{#if navigated}
|
||||
{title}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
`
|
||||
);
|
||||
|
||||
if (isSvelte5Plus()) {
|
||||
write_if_changed(
|
||||
`${output}/root.js`,
|
||||
dedent`
|
||||
import { asClassComponent } from 'svelte/legacy';
|
||||
import Root from './root.svelte';
|
||||
export default asClassComponent(Root);
|
||||
`
|
||||
);
|
||||
}
|
||||
}
|
||||
143
frontend/node_modules/@sveltejs/kit/src/core/sync/write_server.js
generated
vendored
Normal file
143
frontend/node_modules/@sveltejs/kit/src/core/sync/write_server.js
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { hash } from '../../utils/hash.js';
|
||||
import { posixify, resolve_entry } from '../../utils/filesystem.js';
|
||||
import { s } from '../../utils/misc.js';
|
||||
import { load_error_page, load_template } from '../config/index.js';
|
||||
import { runtime_directory } from '../utils.js';
|
||||
import { isSvelte5Plus, write_if_changed } from './utils.js';
|
||||
import colors from 'kleur';
|
||||
import { escape_html } from '../../utils/escape.js';
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* server_hooks: string | null;
|
||||
* universal_hooks: string | null;
|
||||
* config: import('types').ValidatedConfig;
|
||||
* has_service_worker: boolean;
|
||||
* runtime_directory: string;
|
||||
* template: string;
|
||||
* error_page: string;
|
||||
* }} opts
|
||||
*/
|
||||
const server_template = ({
|
||||
config,
|
||||
server_hooks,
|
||||
universal_hooks,
|
||||
has_service_worker,
|
||||
runtime_directory,
|
||||
template,
|
||||
error_page
|
||||
}) => `
|
||||
import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';
|
||||
import { set_building, set_prerendering } from '__sveltekit/environment';
|
||||
import { set_assets } from '$app/paths/internal/server';
|
||||
import { set_manifest, set_read_implementation } from '__sveltekit/server';
|
||||
import { set_private_env, set_public_env } from '${runtime_directory}/shared-server.js';
|
||||
|
||||
export const options = {
|
||||
app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
|
||||
async: ${s(!!config.compilerOptions?.experimental?.async)},
|
||||
csp: ${s(config.kit.csp)},
|
||||
csrf_check_origin: ${s(config.kit.csrf.checkOrigin && !config.kit.csrf.trustedOrigins.includes('*'))},
|
||||
csrf_trusted_origins: ${s(config.kit.csrf.trustedOrigins)},
|
||||
embedded: ${config.kit.embedded},
|
||||
env_public_prefix: '${config.kit.env.publicPrefix}',
|
||||
env_private_prefix: '${config.kit.env.privatePrefix}',
|
||||
hash_routing: ${s(config.kit.router.type === 'hash')},
|
||||
hooks: null, // added lazily, via \`get_hooks\`
|
||||
preload_strategy: ${s(config.kit.output.preloadStrategy)},
|
||||
root,
|
||||
service_worker: ${has_service_worker},
|
||||
service_worker_options: ${config.kit.serviceWorker.register ? s(config.kit.serviceWorker.options) : 'null'},
|
||||
server_error_boundaries: ${s(!!config.kit.experimental.handleRenderingErrors)},
|
||||
templates: {
|
||||
app: ({ head, body, assets, nonce, env }) => ${s(template)
|
||||
.replace('%sveltekit.head%', '" + head + "')
|
||||
.replace('%sveltekit.body%', '" + body + "')
|
||||
.replace(/%sveltekit\.assets%/g, '" + assets + "')
|
||||
.replace(/%sveltekit\.nonce%/g, '" + nonce + "')
|
||||
.replace(/%sveltekit\.version%/g, escape_html(config.kit.version.name))
|
||||
.replace(
|
||||
/%sveltekit\.env\.([^%]+)%/g,
|
||||
(_match, capture) => `" + (env[${s(capture)}] ?? "") + "`
|
||||
)},
|
||||
error: ({ status, message }) => ${s(error_page)
|
||||
.replace(/%sveltekit\.status%/g, '" + status + "')
|
||||
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
|
||||
},
|
||||
version_hash: ${s(hash(config.kit.version.name))}
|
||||
};
|
||||
|
||||
export async function get_hooks() {
|
||||
let handle;
|
||||
let handleFetch;
|
||||
let handleError;
|
||||
let handleValidationError;
|
||||
let init;
|
||||
${server_hooks ? `({ handle, handleFetch, handleError, handleValidationError, init } = await import(${s(server_hooks)}));` : ''}
|
||||
|
||||
let reroute;
|
||||
let transport;
|
||||
${universal_hooks ? `({ reroute, transport } = await import(${s(universal_hooks)}));` : ''}
|
||||
|
||||
return {
|
||||
handle,
|
||||
handleFetch,
|
||||
handleError,
|
||||
handleValidationError,
|
||||
init,
|
||||
reroute,
|
||||
transport
|
||||
};
|
||||
}
|
||||
|
||||
export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };
|
||||
`;
|
||||
|
||||
// TODO need to re-run this whenever src/app.html or src/error.html are
|
||||
// created or changed, or src/service-worker.js is created or deleted.
|
||||
// Also, need to check that updating hooks.server.js works
|
||||
|
||||
/**
|
||||
* Write server configuration to disk
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {string} output
|
||||
*/
|
||||
export function write_server(config, output) {
|
||||
const server_hooks_file = resolve_entry(config.kit.files.hooks.server);
|
||||
const universal_hooks_file = resolve_entry(config.kit.files.hooks.universal);
|
||||
|
||||
const typo = resolve_entry('src/+hooks.server');
|
||||
if (typo) {
|
||||
console.log(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(
|
||||
`Unexpected + prefix. Did you mean ${typo.split('/').at(-1)?.slice(1)}?` +
|
||||
` at ${path.resolve(typo)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {string} file */
|
||||
function relative(file) {
|
||||
return posixify(path.relative(`${output}/server`, file));
|
||||
}
|
||||
|
||||
// Contains the stringified version of
|
||||
/** @type {import('types').SSROptions} */
|
||||
write_if_changed(
|
||||
`${output}/server/internal.js`,
|
||||
server_template({
|
||||
config,
|
||||
server_hooks: server_hooks_file ? relative(server_hooks_file) : null,
|
||||
universal_hooks: universal_hooks_file ? relative(universal_hooks_file) : null,
|
||||
has_service_worker:
|
||||
config.kit.serviceWorker.register && !!resolve_entry(config.kit.files.serviceWorker),
|
||||
runtime_directory: relative(runtime_directory),
|
||||
template: load_template(process.cwd(), config),
|
||||
error_page: load_error_page(config)
|
||||
})
|
||||
);
|
||||
}
|
||||
243
frontend/node_modules/@sveltejs/kit/src/core/sync/write_tsconfig.js
generated
vendored
Normal file
243
frontend/node_modules/@sveltejs/kit/src/core/sync/write_tsconfig.js
generated
vendored
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import colors from 'kleur';
|
||||
import { posixify } from '../../utils/filesystem.js';
|
||||
import { write_if_changed } from './utils.js';
|
||||
|
||||
/**
|
||||
* @param {string} cwd
|
||||
* @param {string} file
|
||||
*/
|
||||
function maybe_file(cwd, file) {
|
||||
const resolved = path.resolve(cwd, file);
|
||||
if (fs.existsSync(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
*/
|
||||
function project_relative(file) {
|
||||
return posixify(path.relative('.', file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
*/
|
||||
function remove_trailing_slashstar(file) {
|
||||
if (file.endsWith('/*')) {
|
||||
return file.slice(0, -2);
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the tsconfig that the user's tsconfig inherits from.
|
||||
* @param {import('types').ValidatedKitConfig} kit
|
||||
*/
|
||||
export function write_tsconfig(kit, cwd = process.cwd()) {
|
||||
const out = path.join(kit.outDir, 'tsconfig.json');
|
||||
|
||||
const user_config = load_user_tsconfig(cwd);
|
||||
if (user_config) validate_user_config(cwd, out, user_config);
|
||||
|
||||
write_if_changed(out, JSON.stringify(get_tsconfig(kit), null, '\t'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the tsconfig that the user's tsconfig inherits from.
|
||||
* @param {import('types').ValidatedKitConfig} kit
|
||||
*/
|
||||
export function get_tsconfig(kit) {
|
||||
/** @param {string} file */
|
||||
const config_relative = (file) => posixify(path.relative(kit.outDir, file));
|
||||
|
||||
const include = new Set([
|
||||
'ambient.d.ts', // careful: changing this name would be a breaking change, because it's referenced in the service-workers documentation
|
||||
'non-ambient.d.ts',
|
||||
'./types/**/$types.d.ts',
|
||||
config_relative('vite.config.js'),
|
||||
config_relative('vite.config.ts')
|
||||
]);
|
||||
const src_includes = [kit.files.routes, kit.files.lib, kit.files.src].filter((dir) => {
|
||||
const relative = path.relative(kit.files.src, dir);
|
||||
return !relative || relative.startsWith('..');
|
||||
});
|
||||
for (const dir of src_includes) {
|
||||
include.add(config_relative(`${dir}/**/*.js`));
|
||||
include.add(config_relative(`${dir}/**/*.ts`));
|
||||
include.add(config_relative(`${dir}/**/*.svelte`));
|
||||
}
|
||||
|
||||
// Test folder is a special case - we advocate putting tests in a top-level test folder
|
||||
// and it's not configurable (should we make it?)
|
||||
const test_folder = project_relative('test');
|
||||
include.add(config_relative(`${test_folder}/**/*.js`));
|
||||
include.add(config_relative(`${test_folder}/**/*.ts`));
|
||||
include.add(config_relative(`${test_folder}/**/*.svelte`));
|
||||
const tests_folder = project_relative('tests');
|
||||
include.add(config_relative(`${tests_folder}/**/*.js`));
|
||||
include.add(config_relative(`${tests_folder}/**/*.ts`));
|
||||
include.add(config_relative(`${tests_folder}/**/*.svelte`));
|
||||
|
||||
const exclude = [config_relative('node_modules/**')];
|
||||
// Add service worker to exclude list so that worker types references in it don't spill over into the rest of the app
|
||||
// (i.e. suddenly ServiceWorkerGlobalScope would be available throughout the app, and some types might even clash)
|
||||
if (path.extname(kit.files.serviceWorker)) {
|
||||
exclude.push(config_relative(kit.files.serviceWorker));
|
||||
} else {
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}.js`));
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.js`));
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}.ts`));
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.ts`));
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}.d.ts`));
|
||||
exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.d.ts`));
|
||||
}
|
||||
|
||||
const config = {
|
||||
compilerOptions: {
|
||||
// generated options
|
||||
paths: {
|
||||
...get_tsconfig_paths(kit),
|
||||
'$app/types': ['./types/index.d.ts']
|
||||
},
|
||||
rootDirs: [config_relative('.'), './types'],
|
||||
|
||||
// essential options
|
||||
// svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
// to enforce using \`import type\` instead of \`import\` for Types.
|
||||
// Also, TypeScript doesn't know about import usages in the template because it only sees the
|
||||
// script of a Svelte file. Therefore preserve all value imports.
|
||||
verbatimModuleSyntax: true,
|
||||
// Vite compiles modules one at a time
|
||||
isolatedModules: true,
|
||||
|
||||
// This is required for svelte-package to work as expected
|
||||
// Can be overwritten
|
||||
lib: ['esnext', 'DOM', 'DOM.Iterable'],
|
||||
moduleResolution: 'bundler',
|
||||
module: 'esnext',
|
||||
noEmit: true, // prevent tsconfig error "overwriting input files" - Vite handles the build and ignores this
|
||||
target: 'esnext'
|
||||
},
|
||||
include: [...include],
|
||||
exclude
|
||||
};
|
||||
|
||||
return kit.typescript.config(config) ?? config;
|
||||
}
|
||||
|
||||
/** @param {string} cwd */
|
||||
function load_user_tsconfig(cwd) {
|
||||
const file = maybe_file(cwd, 'tsconfig.json') || maybe_file(cwd, 'jsconfig.json');
|
||||
|
||||
if (!file) return;
|
||||
|
||||
// we have to eval the file, since it's not parseable as JSON (contains comments)
|
||||
const json = fs.readFileSync(file, 'utf-8');
|
||||
|
||||
return {
|
||||
kind: path.basename(file),
|
||||
options: (0, eval)(`(${json})`)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} cwd
|
||||
* @param {string} out
|
||||
* @param {{ kind: string, options: any }} config
|
||||
*/
|
||||
function validate_user_config(cwd, out, config) {
|
||||
// we need to check that the user's tsconfig extends the framework config
|
||||
const extend = config.options.extends;
|
||||
const extends_framework_config =
|
||||
typeof extend === 'string'
|
||||
? path.resolve(cwd, extend) === out
|
||||
: Array.isArray(extend)
|
||||
? extend.some((e) => path.resolve(cwd, e) === out)
|
||||
: false;
|
||||
|
||||
const options = config.options.compilerOptions || {};
|
||||
|
||||
if (extends_framework_config) {
|
||||
const { paths, baseUrl } = options;
|
||||
|
||||
if (baseUrl || paths) {
|
||||
console.warn(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(
|
||||
`You have specified a baseUrl and/or paths in your ${config.kind} which interferes with SvelteKit's auto-generated tsconfig.json. ` +
|
||||
'Remove it to avoid problems with intellisense. For path aliases, use `kit.alias` instead: https://svelte.dev/docs/kit/configuration#alias'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let relative = posixify(path.relative('.', out));
|
||||
if (!relative.startsWith('./')) relative = './' + relative;
|
||||
|
||||
console.warn(
|
||||
colors
|
||||
.bold()
|
||||
.yellow(`Your ${config.kind} should extend the configuration generated by SvelteKit:`)
|
||||
);
|
||||
console.warn(`{\n "extends": "${relative}"\n}`);
|
||||
}
|
||||
}
|
||||
|
||||
// <something><optional /*>
|
||||
const alias_regex = /^(.+?)(\/\*)?$/;
|
||||
// <path><optional /* or .fileending>
|
||||
const value_regex = /^(.*?)((\/\*)|(\.\w+))?$/;
|
||||
|
||||
/**
|
||||
* Generates tsconfig path aliases from kit's aliases.
|
||||
* Related to vite alias creation.
|
||||
*
|
||||
* @param {import('types').ValidatedKitConfig} config
|
||||
*/
|
||||
function get_tsconfig_paths(config) {
|
||||
/** @param {string} file */
|
||||
const config_relative = (file) => {
|
||||
let relative_path = path.relative(config.outDir, file);
|
||||
if (!relative_path.startsWith('..')) {
|
||||
relative_path = './' + relative_path;
|
||||
}
|
||||
return posixify(relative_path);
|
||||
};
|
||||
|
||||
const alias = { ...config.alias };
|
||||
if (fs.existsSync(project_relative(config.files.lib))) {
|
||||
alias['$lib'] = project_relative(config.files.lib);
|
||||
}
|
||||
|
||||
/** @type {Record<string, string[]>} */
|
||||
const paths = {};
|
||||
|
||||
for (const [key, value] of Object.entries(alias)) {
|
||||
const key_match = alias_regex.exec(key);
|
||||
if (!key_match) throw new Error(`Invalid alias key: ${key}`);
|
||||
|
||||
const value_match = value_regex.exec(value);
|
||||
if (!value_match) throw new Error(`Invalid alias value: ${value}`);
|
||||
|
||||
const rel_path = config_relative(remove_trailing_slashstar(value));
|
||||
const slashstar = key_match[2];
|
||||
|
||||
if (slashstar) {
|
||||
paths[key] = [rel_path + '/*'];
|
||||
} else {
|
||||
paths[key] = [rel_path];
|
||||
const fileending = value_match[4];
|
||||
|
||||
if (!fileending && !(key + '/*' in alias)) {
|
||||
paths[key + '/*'] = [rel_path + '/*'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
869
frontend/node_modules/@sveltejs/kit/src/core/sync/write_types/index.js
generated
vendored
Normal file
869
frontend/node_modules/@sveltejs/kit/src/core/sync/write_types/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,869 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import MagicString from 'magic-string';
|
||||
import { posixify, rimraf, walk } from '../../../utils/filesystem.js';
|
||||
import { compact } from '../../../utils/array.js';
|
||||
import { ts } from '../ts.js';
|
||||
const remove_relative_parent_traversals = (/** @type {string} */ path) =>
|
||||
path.replace(/\.\.\//g, '');
|
||||
const is_whitespace = (/** @type {string} */ char) => /\s/.test(char);
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* file_name: string;
|
||||
* modified: boolean;
|
||||
* code: string;
|
||||
* exports: any[];
|
||||
* } | null} Proxy
|
||||
*
|
||||
* @typedef {{
|
||||
* server: Proxy,
|
||||
* universal: Proxy
|
||||
* }} Proxies
|
||||
*
|
||||
* @typedef {Map<import('types').PageNode, {route: import('types').RouteData, proxies: Proxies}>} RoutesMap
|
||||
*/
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
/**
|
||||
* Creates types for the whole manifest
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
*/
|
||||
export function write_all_types(config, manifest_data) {
|
||||
if (!ts) return;
|
||||
|
||||
const types_dir = `${config.kit.outDir}/types`;
|
||||
|
||||
// empty out files that no longer need to exist
|
||||
const routes_dir = remove_relative_parent_traversals(
|
||||
posixify(path.relative('.', config.kit.files.routes))
|
||||
);
|
||||
const expected_directories = new Set(
|
||||
manifest_data.routes.map((route) => path.join(routes_dir, route.id))
|
||||
);
|
||||
|
||||
if (fs.existsSync(types_dir)) {
|
||||
for (const file of walk(types_dir)) {
|
||||
const dir = path.dirname(file);
|
||||
if (!expected_directories.has(dir)) {
|
||||
rimraf(path.join(types_dir, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read/write meta data on each invocation, not once per node process,
|
||||
// it could be invoked by another process in the meantime.
|
||||
const meta_data_file = `${types_dir}/route_meta_data.json`;
|
||||
const has_meta_data = fs.existsSync(meta_data_file);
|
||||
const meta_data = has_meta_data
|
||||
? /** @type {Record<string, string[]>} */ (JSON.parse(fs.readFileSync(meta_data_file, 'utf-8')))
|
||||
: {};
|
||||
const routes_map = create_routes_map(manifest_data);
|
||||
// For each directory, write $types.d.ts
|
||||
for (const route of manifest_data.routes) {
|
||||
if (!route.leaf && !route.layout && !route.endpoint) continue; // nothing to do
|
||||
|
||||
const outdir = path.join(config.kit.outDir, 'types', routes_dir, route.id);
|
||||
|
||||
// check if the types are out of date
|
||||
/** @type {string[]} */
|
||||
const input_files = [];
|
||||
|
||||
/** @type {import('types').PageNode | null} */
|
||||
let node = route.leaf;
|
||||
while (node) {
|
||||
if (node.universal) input_files.push(node.universal);
|
||||
if (node.server) input_files.push(node.server);
|
||||
node = node.parent ?? null;
|
||||
}
|
||||
|
||||
/** @type {import('types').PageNode | null} */
|
||||
node = route.layout;
|
||||
while (node) {
|
||||
if (node.universal) input_files.push(node.universal);
|
||||
if (node.server) input_files.push(node.server);
|
||||
node = node.parent ?? null;
|
||||
}
|
||||
|
||||
if (route.endpoint) {
|
||||
input_files.push(route.endpoint.file);
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(outdir, { recursive: true });
|
||||
} catch {}
|
||||
|
||||
const output_files = compact(
|
||||
fs.readdirSync(outdir).map((name) => {
|
||||
const stats = fs.statSync(path.join(outdir, name));
|
||||
if (stats.isDirectory()) return;
|
||||
return {
|
||||
name,
|
||||
updated: stats.mtimeMs
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const source_last_updated = Math.max(
|
||||
// ctimeMs includes move operations whereas mtimeMs does not
|
||||
...input_files.map((file) => fs.statSync(file).ctimeMs)
|
||||
);
|
||||
const types_last_updated = Math.max(...output_files.map((file) => file.updated));
|
||||
|
||||
const should_generate =
|
||||
// source files were generated more recently than the types
|
||||
source_last_updated > types_last_updated ||
|
||||
// no meta data file exists yet
|
||||
!has_meta_data ||
|
||||
// some file was deleted
|
||||
!meta_data[route.id]?.every((file) => input_files.includes(file));
|
||||
|
||||
if (should_generate) {
|
||||
// track which old files end up being surplus to requirements
|
||||
const to_delete = new Set(output_files.map((file) => file.name));
|
||||
update_types(config, routes_map, route, to_delete);
|
||||
meta_data[route.id] = input_files;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(meta_data_file, JSON.stringify(meta_data, null, '\t'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates types related to the given file. This should only be called
|
||||
* if the file in question was edited, not if it was created/deleted/moved.
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
* @param {string} file
|
||||
*/
|
||||
export function write_types(config, manifest_data, file) {
|
||||
if (!ts) return;
|
||||
|
||||
if (!path.basename(file).startsWith('+')) {
|
||||
// Not a route file
|
||||
return;
|
||||
}
|
||||
|
||||
const id = '/' + posixify(path.relative(config.kit.files.routes, path.dirname(file)));
|
||||
|
||||
const route = manifest_data.routes.find((route) => route.id === id);
|
||||
if (!route) return;
|
||||
if (!route.leaf && !route.layout && !route.endpoint) return; // nothing to do
|
||||
|
||||
update_types(config, create_routes_map(manifest_data), route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all leafs into a leaf -> route map
|
||||
* @param {import('types').ManifestData} manifest_data
|
||||
*/
|
||||
function create_routes_map(manifest_data) {
|
||||
/** @type {RoutesMap} */
|
||||
const map = new Map();
|
||||
for (const route of manifest_data.routes) {
|
||||
if (route.leaf) {
|
||||
map.set(route.leaf, { route, proxies: { server: null, universal: null } });
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update types for a specific route
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
* @param {RoutesMap} routes
|
||||
* @param {import('types').RouteData} route
|
||||
* @param {Set<string>} [to_delete]
|
||||
*/
|
||||
function update_types(config, routes, route, to_delete = new Set()) {
|
||||
const routes_dir = remove_relative_parent_traversals(
|
||||
posixify(path.relative('.', config.kit.files.routes))
|
||||
);
|
||||
const outdir = path.join(config.kit.outDir, 'types', routes_dir, route.id);
|
||||
|
||||
// now generate new types
|
||||
const imports = ["import type * as Kit from '@sveltejs/kit';"];
|
||||
|
||||
/** @type {string[]} */
|
||||
const declarations = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const exports = [];
|
||||
|
||||
// add 'Expand' helper
|
||||
// Makes sure a type is "repackaged" and therefore more readable
|
||||
declarations.push('type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;');
|
||||
|
||||
// returns the predicate of a matcher's type guard - or string if there is no type guard
|
||||
declarations.push(
|
||||
'type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;'
|
||||
);
|
||||
|
||||
declarations.push(
|
||||
'type RouteParams = ' + generate_params_type(route.params, outdir, config) + ';'
|
||||
);
|
||||
|
||||
if (route.params.length > 0) {
|
||||
exports.push(
|
||||
'export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;'
|
||||
);
|
||||
}
|
||||
|
||||
declarations.push(`type RouteId = '${route.id}';`);
|
||||
|
||||
// These could also be placed in our public types, but it would bloat them unnecessarily and we may want to change these in the future
|
||||
if (route.layout || route.leaf) {
|
||||
declarations.push(
|
||||
// If T extends the empty object, void is also allowed as a return type
|
||||
'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];',
|
||||
|
||||
// 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>>',
|
||||
|
||||
// null & {} == null, we need to prevent that in some situations
|
||||
'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;',
|
||||
|
||||
// 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>;'
|
||||
);
|
||||
}
|
||||
|
||||
if (route.leaf) {
|
||||
let route_info = routes.get(route.leaf);
|
||||
if (!route_info) {
|
||||
// This should be defined, but belts and braces
|
||||
route_info = { route, proxies: { server: null, universal: null } };
|
||||
routes.set(route.leaf, route_info);
|
||||
}
|
||||
|
||||
const {
|
||||
declarations: d,
|
||||
exports: e,
|
||||
proxies
|
||||
} = process_node(route.leaf, outdir, true, route_info.proxies);
|
||||
|
||||
exports.push(...e);
|
||||
declarations.push(...d);
|
||||
|
||||
if (proxies.server) {
|
||||
route_info.proxies.server = proxies.server;
|
||||
if (proxies.server?.modified) to_delete.delete(proxies.server.file_name);
|
||||
}
|
||||
if (proxies.universal) {
|
||||
route_info.proxies.universal = proxies.universal;
|
||||
if (proxies.universal?.modified) to_delete.delete(proxies.universal.file_name);
|
||||
}
|
||||
|
||||
if (route.leaf.server) {
|
||||
exports.push(
|
||||
'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>'
|
||||
);
|
||||
}
|
||||
|
||||
if (route.leaf.server) {
|
||||
exports.push(
|
||||
'export type PageProps = { params: RouteParams; data: PageData; form: ActionData }'
|
||||
);
|
||||
} else {
|
||||
exports.push('export type PageProps = { params: RouteParams; data: PageData }');
|
||||
}
|
||||
}
|
||||
|
||||
if (route.layout) {
|
||||
let all_pages_have_load = true;
|
||||
/** @type {import('types').RouteParam[]} */
|
||||
const layout_params = [];
|
||||
const ids = ['RouteId'];
|
||||
|
||||
route.layout.child_pages?.forEach((page) => {
|
||||
const leaf = routes.get(page);
|
||||
if (leaf) {
|
||||
if (leaf.route.page) ids.push(`"${leaf.route.id}"`);
|
||||
|
||||
for (const param of leaf.route.params) {
|
||||
// skip if already added
|
||||
if (layout_params.some((p) => p.name === param.name)) continue;
|
||||
layout_params.push({ ...param, optional: true });
|
||||
}
|
||||
|
||||
ensureProxies(page, leaf.proxies);
|
||||
|
||||
if (
|
||||
// Be defensive - if a proxy doesn't exist (because it couldn't be created), assume a load function exists.
|
||||
// If we didn't and it's a false negative, the user could wrongfully get a type error on layouts.
|
||||
(leaf.proxies.server && !leaf.proxies.server.exports.includes('load')) ||
|
||||
(leaf.proxies.universal && !leaf.proxies.universal.exports.includes('load'))
|
||||
) {
|
||||
all_pages_have_load = false;
|
||||
}
|
||||
}
|
||||
if (!page.server && !page.universal) {
|
||||
all_pages_have_load = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (route.id === '/') {
|
||||
// root layout is used for fallback error page, where ID can be null
|
||||
ids.push('null');
|
||||
}
|
||||
|
||||
declarations.push(`type LayoutRouteId = ${ids.join(' | ')}`);
|
||||
|
||||
declarations.push(
|
||||
'type LayoutParams = RouteParams & ' + generate_params_type(layout_params, outdir, config)
|
||||
);
|
||||
|
||||
const {
|
||||
exports: e,
|
||||
declarations: d,
|
||||
proxies
|
||||
} = process_node(
|
||||
route.layout,
|
||||
outdir,
|
||||
false,
|
||||
{ server: null, universal: null },
|
||||
all_pages_have_load
|
||||
);
|
||||
|
||||
exports.push(...e);
|
||||
declarations.push(...d);
|
||||
|
||||
if (proxies.server?.modified) to_delete.delete(proxies.server.file_name);
|
||||
if (proxies.universal?.modified) to_delete.delete(proxies.universal.file_name);
|
||||
|
||||
exports.push(
|
||||
'export type LayoutProps = { params: LayoutParams; data: LayoutData; children: import("svelte").Snippet }'
|
||||
);
|
||||
}
|
||||
|
||||
if (route.endpoint) {
|
||||
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>;');
|
||||
}
|
||||
|
||||
const output = [imports.join('\n'), declarations.join('\n'), exports.join('\n')]
|
||||
.filter(Boolean)
|
||||
.join('\n\n');
|
||||
|
||||
fs.writeFileSync(`${outdir}/$types.d.ts`, output);
|
||||
to_delete.delete('$types.d.ts');
|
||||
|
||||
for (const file of to_delete) {
|
||||
fs.unlinkSync(path.join(outdir, file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').PageNode} node
|
||||
* @param {string} outdir
|
||||
* @param {boolean} is_page
|
||||
* @param {Proxies} proxies
|
||||
* @param {boolean} [all_pages_have_load]
|
||||
*/
|
||||
function process_node(node, outdir, is_page, proxies, all_pages_have_load = true) {
|
||||
const params = `${is_page ? 'Route' : 'Layout'}Params`;
|
||||
const prefix = is_page ? 'Page' : 'Layout';
|
||||
|
||||
const route_id = is_page ? 'RouteId' : 'LayoutRouteId';
|
||||
|
||||
/** @type {string[]} */
|
||||
const declarations = [];
|
||||
/** @type {string[]} */
|
||||
const exports = [];
|
||||
|
||||
/** @type {string} */
|
||||
let server_data;
|
||||
/** @type {string} */
|
||||
let data;
|
||||
|
||||
ensureProxies(node, proxies);
|
||||
|
||||
if (node.server) {
|
||||
const basename = path.basename(node.server);
|
||||
const proxy = proxies.server;
|
||||
if (proxy?.modified) {
|
||||
fs.writeFileSync(`${outdir}/proxy${basename}`, proxy.code);
|
||||
}
|
||||
|
||||
server_data = get_data_type(node.server, 'null', proxy, true);
|
||||
|
||||
const parent_type = `${prefix}ServerParentData`;
|
||||
|
||||
declarations.push(`type ${parent_type} = ${get_parent_type(node, 'LayoutServerData')};`);
|
||||
|
||||
// +page.js load present -> server can return all-optional data
|
||||
const output_data_shape =
|
||||
node.universal || (!is_page && all_pages_have_load)
|
||||
? 'Partial<App.PageData> & Record<string, any> | void'
|
||||
: `OutputDataShape<${parent_type}>`;
|
||||
exports.push(
|
||||
`export type ${prefix}ServerLoad<OutputData extends ${output_data_shape} = ${output_data_shape}> = Kit.ServerLoad<${params}, ${parent_type}, OutputData, ${route_id}>;`
|
||||
);
|
||||
|
||||
exports.push(`export type ${prefix}ServerLoadEvent = Parameters<${prefix}ServerLoad>[0];`);
|
||||
|
||||
if (is_page) {
|
||||
let type = 'unknown';
|
||||
if (proxy && proxy.exports.includes('actions')) {
|
||||
// If the file wasn't tweaked, we can use the return type of the original file.
|
||||
// The advantage is that type updates are reflected without saving.
|
||||
const from = proxy.modified
|
||||
? `./proxy${replace_ext_with_js(basename)}`
|
||||
: path_to_original(outdir, node.server);
|
||||
|
||||
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 ActionsExport = typeof import('${from}').actions`,
|
||||
'export type SubmitFunction = Kit.SubmitFunction<Expand<ActionsSuccess<ActionsExport>>, Expand<ActionsFailure<ActionsExport>>>'
|
||||
);
|
||||
|
||||
type = 'Expand<Kit.AwaitedActions<ActionsExport>> | null';
|
||||
}
|
||||
exports.push(`export type ActionData = ${type};`);
|
||||
}
|
||||
} else {
|
||||
server_data = 'null';
|
||||
}
|
||||
exports.push(`export type ${prefix}ServerData = ${server_data};`);
|
||||
|
||||
const parent_type = `${prefix}ParentData`;
|
||||
declarations.push(`type ${parent_type} = ${get_parent_type(node, 'LayoutData')};`);
|
||||
|
||||
if (node.universal) {
|
||||
const proxy = proxies.universal;
|
||||
if (proxy?.modified) {
|
||||
fs.writeFileSync(`${outdir}/proxy${path.basename(node.universal)}`, proxy.code);
|
||||
}
|
||||
|
||||
const type = get_data_type(
|
||||
node.universal,
|
||||
`${parent_type} & EnsureDefined<${prefix}ServerData>`,
|
||||
proxy
|
||||
);
|
||||
|
||||
data = `Expand<Omit<${parent_type}, keyof ${type}> & OptionalUnion<EnsureDefined<${type}>>>`;
|
||||
|
||||
const output_data_shape =
|
||||
!is_page && all_pages_have_load
|
||||
? 'Partial<App.PageData> & Record<string, any> | void'
|
||||
: `OutputDataShape<${parent_type}>`;
|
||||
exports.push(
|
||||
`export type ${prefix}Load<OutputData extends ${output_data_shape} = ${output_data_shape}> = Kit.Load<${params}, ${prefix}ServerData, ${parent_type}, OutputData, ${route_id}>;`
|
||||
);
|
||||
|
||||
exports.push(`export type ${prefix}LoadEvent = Parameters<${prefix}Load>[0];`);
|
||||
} else if (server_data === 'null') {
|
||||
data = `Expand<${parent_type}>`;
|
||||
} else {
|
||||
data = `Expand<Omit<${parent_type}, keyof ${prefix}ServerData> & EnsureDefined<${prefix}ServerData>>`;
|
||||
}
|
||||
|
||||
exports.push(`export type ${prefix}Data = ${data};`);
|
||||
|
||||
return { declarations, exports, proxies };
|
||||
|
||||
/**
|
||||
* @param {string} file_path
|
||||
* @param {string} fallback
|
||||
* @param {Proxy} proxy
|
||||
* @param {boolean} expand
|
||||
*/
|
||||
function get_data_type(file_path, fallback, proxy, expand = false) {
|
||||
if (proxy) {
|
||||
if (proxy.exports.includes('load')) {
|
||||
// If the file wasn't tweaked, we can use the return type of the original file.
|
||||
// The advantage is that type updates are reflected without saving.
|
||||
const from = proxy.modified
|
||||
? `./proxy${replace_ext_with_js(path.basename(file_path))}`
|
||||
: path_to_original(outdir, file_path);
|
||||
const type = `Kit.LoadProperties<Awaited<ReturnType<typeof import('${from}').load>>>`;
|
||||
return expand ? `Expand<OptionalUnion<EnsureDefined<${type}>>>` : type;
|
||||
} else {
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function populates the proxies object, if necessary and not already done.
|
||||
* Proxies are used to tweak the code of a file before it's typechecked.
|
||||
* They are needed in two places - when generating the types for a page or layout.
|
||||
* To not do the same work twice, we generate the proxies once and pass them around.
|
||||
*
|
||||
* @param {import('types').PageNode} node
|
||||
* @param {Proxies} proxies
|
||||
*/
|
||||
function ensureProxies(node, proxies) {
|
||||
if (node.server && !proxies.server) {
|
||||
proxies.server = createProxy(node.server, true);
|
||||
}
|
||||
|
||||
if (node.universal && !proxies.universal) {
|
||||
proxies.universal = createProxy(node.universal, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file_path
|
||||
* @param {boolean} is_server
|
||||
* @returns {Proxy}
|
||||
*/
|
||||
function createProxy(file_path, is_server) {
|
||||
const proxy = tweak_types(fs.readFileSync(file_path, 'utf8'), is_server);
|
||||
if (proxy) {
|
||||
return {
|
||||
...proxy,
|
||||
file_name: `proxy${path.basename(file_path)}`
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent type string by recursively looking up the parent layout and accumulate them to one type.
|
||||
* @param {import('types').PageNode} node
|
||||
* @param {string} type
|
||||
*/
|
||||
function get_parent_type(node, type) {
|
||||
const parent_imports = [];
|
||||
|
||||
let parent = node.parent;
|
||||
|
||||
while (parent) {
|
||||
const d = node.depth - parent.depth;
|
||||
// unshift because we need it the other way round for the import string
|
||||
parent_imports.unshift(
|
||||
`${d === 0 ? '' : `import('${'../'.repeat(d)}${'$types.js'}').`}${type}`
|
||||
);
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
let parent_str = `EnsureDefined<${parent_imports[0] || '{}'}>`;
|
||||
for (let i = 1; i < parent_imports.length; i++) {
|
||||
// Omit is necessary because a parent could have a property with the same key which would
|
||||
// cause a type conflict. At runtime the child overwrites the parent property in this case,
|
||||
// so reflect that in the type definition.
|
||||
// EnsureDefined is necessary because {something: string} & null becomes null.
|
||||
// Output types of server loads can be null but when passed in through the `parent` parameter they are the empty object instead.
|
||||
parent_str = `Omit<${parent_str}, keyof ${parent_imports[i]}> & EnsureDefined<${parent_imports[i]}>`;
|
||||
}
|
||||
return parent_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} outdir
|
||||
* @param {string} file_path
|
||||
*/
|
||||
function path_to_original(outdir, file_path) {
|
||||
return posixify(path.relative(outdir, path.join(cwd, replace_ext_with_js(file_path))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file_path
|
||||
*/
|
||||
function replace_ext_with_js(file_path) {
|
||||
// Another extension than `.js` (or nothing, but that fails with node16 moduleResolution)
|
||||
// will result in TS failing to lookup the file
|
||||
const ext = path.extname(file_path);
|
||||
return file_path.slice(0, -ext.length) + '.js';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').RouteParam[]} params
|
||||
* @param {string} outdir
|
||||
* @param {import('types').ValidatedConfig} config
|
||||
*/
|
||||
function generate_params_type(params, outdir, config) {
|
||||
/** @param {string} matcher */
|
||||
const path_to_matcher = (matcher) =>
|
||||
posixify(path.relative(outdir, path.join(config.kit.files.params, matcher + '.js')));
|
||||
|
||||
return `{ ${params
|
||||
.map(
|
||||
(param) =>
|
||||
`${param.name}${param.optional ? '?' : ''}: ${
|
||||
param.matcher
|
||||
? `MatcherParam<typeof import('${path_to_matcher(param.matcher)}').match>`
|
||||
: 'string'
|
||||
}`
|
||||
)
|
||||
.join('; ')} }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} content
|
||||
* @param {boolean} is_server
|
||||
* @returns {Omit<NonNullable<Proxy>, 'file_name'> | null}
|
||||
*/
|
||||
export function tweak_types(content, is_server) {
|
||||
const names = new Set(is_server ? ['load', 'actions'] : ['load']);
|
||||
|
||||
try {
|
||||
let modified = false;
|
||||
|
||||
const ast = ts.createSourceFile(
|
||||
'filename.ts',
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
false,
|
||||
ts.ScriptKind.TS
|
||||
);
|
||||
|
||||
const code = new MagicString(content);
|
||||
|
||||
const exports = new Map();
|
||||
|
||||
ast.forEachChild((node) => {
|
||||
if (
|
||||
ts.isExportDeclaration(node) &&
|
||||
node.exportClause &&
|
||||
ts.isNamedExports(node.exportClause)
|
||||
) {
|
||||
node.exportClause.elements.forEach((element) => {
|
||||
const exported = element.name;
|
||||
if (names.has(element.name.text)) {
|
||||
const local = element.propertyName || element.name;
|
||||
exports.set(exported.text, local.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
ts.canHaveModifiers(node) &&
|
||||
ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
|
||||
) {
|
||||
if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) {
|
||||
exports.set(node.name.text, node.name.text);
|
||||
}
|
||||
|
||||
if (ts.isVariableStatement(node)) {
|
||||
node.declarationList.declarations.forEach((declaration) => {
|
||||
if (ts.isIdentifier(declaration.name) && names.has(declaration.name.text)) {
|
||||
exports.set(declaration.name.text, declaration.name.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('typescript').Node} node
|
||||
* @param {import('typescript').Node} value
|
||||
*/
|
||||
function replace_jsdoc_type_tags(node, value) {
|
||||
let _modified = false;
|
||||
// @ts-ignore
|
||||
if (node.jsDoc) {
|
||||
// @ts-ignore
|
||||
for (const comment of node.jsDoc) {
|
||||
for (const tag of comment.tags ?? []) {
|
||||
if (ts.isJSDocTypeTag(tag)) {
|
||||
const is_fn =
|
||||
ts.isFunctionDeclaration(value) ||
|
||||
ts.isFunctionExpression(value) ||
|
||||
ts.isArrowFunction(value);
|
||||
|
||||
if (is_fn && value.parameters?.length > 0) {
|
||||
const name = ts.isIdentifier(value.parameters[0].name)
|
||||
? value.parameters[0].name.text
|
||||
: 'event';
|
||||
code.overwrite(tag.tagName.pos, tag.tagName.end, 'param');
|
||||
code.prependRight(tag.typeExpression.pos + 1, 'Parameters<');
|
||||
code.appendLeft(tag.typeExpression.end - 1, '>[0]');
|
||||
code.appendLeft(tag.typeExpression.end, ` ${name}`);
|
||||
} else {
|
||||
code.overwrite(tag.pos, tag.end, '');
|
||||
}
|
||||
_modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
modified = modified || _modified;
|
||||
return _modified;
|
||||
}
|
||||
|
||||
ast.forEachChild((node) => {
|
||||
if (ts.isFunctionDeclaration(node) && node.name?.text && node.name?.text === 'load') {
|
||||
// remove JSDoc comment above `export function load ...`
|
||||
replace_jsdoc_type_tags(node, node);
|
||||
}
|
||||
|
||||
if (ts.isVariableStatement(node)) {
|
||||
// remove JSDoc comment above `export const load = ...`
|
||||
if (
|
||||
ts.isIdentifier(node.declarationList.declarations[0].name) &&
|
||||
names.has(node.declarationList.declarations[0].name.text) &&
|
||||
node.declarationList.declarations[0].initializer
|
||||
) {
|
||||
replace_jsdoc_type_tags(node, node.declarationList.declarations[0].initializer);
|
||||
}
|
||||
|
||||
for (const declaration of node.declarationList.declarations) {
|
||||
if (
|
||||
ts.isIdentifier(declaration.name) &&
|
||||
declaration.name.text === 'load' &&
|
||||
declaration.initializer
|
||||
) {
|
||||
// edge case — remove JSDoc comment above individual export
|
||||
replace_jsdoc_type_tags(declaration, declaration.initializer);
|
||||
|
||||
// remove type from `export const load: Load ...`
|
||||
if (declaration.type) {
|
||||
let a = declaration.type.pos;
|
||||
const b = declaration.type.end;
|
||||
while (is_whitespace(content[a])) a += 1;
|
||||
|
||||
const type = content.slice(a, b);
|
||||
code.remove(declaration.name.end, declaration.type.end);
|
||||
|
||||
const rhs = declaration.initializer;
|
||||
|
||||
if (
|
||||
rhs &&
|
||||
(ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
|
||||
rhs.parameters.length
|
||||
) {
|
||||
const arg = rhs.parameters[0];
|
||||
const add_parens = content[arg.pos - 1] !== '(';
|
||||
|
||||
if (add_parens) code.prependRight(arg.pos, '(');
|
||||
|
||||
if (arg && !arg.type) {
|
||||
code.appendLeft(
|
||||
arg.name.end,
|
||||
`: Parameters<${type}>[0]` + (add_parens ? ')' : '')
|
||||
);
|
||||
} else {
|
||||
// prevent "type X is imported but not used" (isn't silenced by @ts-nocheck) when svelte-check runs
|
||||
code.append(`;null as any as ${type};`);
|
||||
}
|
||||
} else {
|
||||
// prevent "type X is imported but not used" (isn't silenced by @ts-nocheck) when svelte-check runs
|
||||
code.append(`;null as any as ${type};`);
|
||||
}
|
||||
|
||||
modified = true;
|
||||
}
|
||||
} else if (
|
||||
is_server &&
|
||||
ts.isIdentifier(declaration.name) &&
|
||||
declaration.name?.text === 'actions' &&
|
||||
declaration.initializer
|
||||
) {
|
||||
// remove JSDoc comment from `export const actions = ..`
|
||||
const removed = replace_jsdoc_type_tags(node, declaration.initializer);
|
||||
// ... and move type to each individual action
|
||||
if (removed) {
|
||||
const rhs = declaration.initializer;
|
||||
if (ts.isObjectLiteralExpression(rhs)) {
|
||||
for (const prop of rhs.properties) {
|
||||
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
||||
const rhs = prop.initializer;
|
||||
const replaced = replace_jsdoc_type_tags(prop, rhs);
|
||||
if (
|
||||
!replaced &&
|
||||
rhs &&
|
||||
(ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
|
||||
rhs.parameters?.[0]
|
||||
) {
|
||||
const name = ts.isIdentifier(rhs.parameters[0].name)
|
||||
? rhs.parameters[0].name.text
|
||||
: 'event';
|
||||
code.prependRight(
|
||||
rhs.pos,
|
||||
`/** @param {import('./$types').RequestEvent} ${name} */ `
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove type from `export const actions: Actions ...`
|
||||
if (declaration.type) {
|
||||
let a = declaration.type.pos;
|
||||
const b = declaration.type.end;
|
||||
while (is_whitespace(content[a])) a += 1;
|
||||
|
||||
const type = content.slice(a, b);
|
||||
code.remove(declaration.name.end, declaration.type.end);
|
||||
code.append(`;null as any as ${type};`);
|
||||
modified = true;
|
||||
|
||||
// ... and move type to each individual action
|
||||
const rhs = declaration.initializer;
|
||||
if (ts.isObjectLiteralExpression(rhs)) {
|
||||
for (const prop of rhs.properties) {
|
||||
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
||||
const rhs = prop.initializer;
|
||||
|
||||
if (
|
||||
rhs &&
|
||||
(ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs)) &&
|
||||
rhs.parameters.length
|
||||
) {
|
||||
const arg = rhs.parameters[0];
|
||||
const add_parens = content[arg.pos - 1] !== '(';
|
||||
|
||||
if (add_parens) code.prependRight(arg.pos, '(');
|
||||
|
||||
if (arg && !arg.type) {
|
||||
code.appendLeft(
|
||||
arg.name.end,
|
||||
": import('./$types').RequestEvent" + (add_parens ? ')' : '')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (modified) {
|
||||
// Ignore all type errors so they don't show up twice when svelte-check runs
|
||||
// Account for possible @ts-check which would overwrite @ts-nocheck
|
||||
if (code.original.startsWith('// @ts-check')) {
|
||||
code.prependLeft('// @ts-check'.length, '\n// @ts-nocheck\n');
|
||||
} else {
|
||||
code.prepend('// @ts-nocheck\n');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
modified,
|
||||
code: code.toString(),
|
||||
exports: Array.from(exports.keys())
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
87
frontend/node_modules/@sveltejs/kit/src/core/utils.js
generated
vendored
Normal file
87
frontend/node_modules/@sveltejs/kit/src/core/utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import colors from 'kleur';
|
||||
import { posixify, to_fs } from '../utils/filesystem.js';
|
||||
|
||||
/**
|
||||
* Resolved path of the `runtime` directory
|
||||
*
|
||||
* TODO Windows issue:
|
||||
* Vite or sth else somehow sets the driver letter inconsistently to lower or upper case depending on the run environment.
|
||||
* In playwright debug mode run through VS Code this a root-to-lowercase conversion is needed in order for the tests to run.
|
||||
* If we do this conversion in other cases it has the opposite effect though and fails.
|
||||
*/
|
||||
export const runtime_directory = posixify(fileURLToPath(new URL('../runtime', import.meta.url)));
|
||||
|
||||
/**
|
||||
* This allows us to import SvelteKit internals that aren't exposed via `pkg.exports` in a
|
||||
* way that works whether `@sveltejs/kit` is installed inside the project's `node_modules`
|
||||
* or in a workspace root
|
||||
*/
|
||||
export const runtime_base = runtime_directory.startsWith(process.cwd())
|
||||
? `/${path.relative('.', runtime_directory)}`
|
||||
: to_fs(runtime_directory);
|
||||
|
||||
function noop() {}
|
||||
|
||||
/** @param {{ verbose: boolean }} opts */
|
||||
export function logger({ verbose }) {
|
||||
/** @type {import('types').Logger} */
|
||||
const log = (msg) => console.log(msg.replace(/^/gm, ' '));
|
||||
|
||||
/** @param {string} msg */
|
||||
const err = (msg) => console.error(msg.replace(/^/gm, ' '));
|
||||
|
||||
log.success = (msg) => log(colors.green(`✔ ${msg}`));
|
||||
log.error = (msg) => err(colors.bold().red(msg));
|
||||
log.warn = (msg) => log(colors.bold().yellow(msg));
|
||||
|
||||
log.minor = verbose ? (msg) => log(colors.grey(msg)) : noop;
|
||||
log.info = verbose ? log : noop;
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
/** @param {import('types').ManifestData} manifest_data */
|
||||
export function get_mime_lookup(manifest_data) {
|
||||
/** @type {Record<string, string>} */
|
||||
const mime = {};
|
||||
|
||||
manifest_data.assets.forEach((asset) => {
|
||||
if (asset.type) {
|
||||
const ext = path.extname(asset.file);
|
||||
mime[ext] = asset.type;
|
||||
}
|
||||
});
|
||||
|
||||
return mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
1
frontend/node_modules/@sveltejs/kit/src/exports/hooks/index.js
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/exports/hooks/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { sequence } from './sequence.js';
|
||||
145
frontend/node_modules/@sveltejs/kit/src/exports/hooks/sequence.js
generated
vendored
Normal file
145
frontend/node_modules/@sveltejs/kit/src/exports/hooks/sequence.js
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/** @import { Handle, RequestEvent, ResolveOptions } from '@sveltejs/kit' */
|
||||
/** @import { MaybePromise } from 'types' */
|
||||
import {
|
||||
merge_tracing,
|
||||
get_request_store,
|
||||
with_request_store
|
||||
} from '@sveltejs/kit/internal/server';
|
||||
|
||||
/**
|
||||
* 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');
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* 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');
|
||||
* return true;
|
||||
* },
|
||||
* filterSerializedResponseHeaders: () => {
|
||||
* // this one wins as it's the first defined in the chain
|
||||
* console.log('second filterSerializedResponseHeaders');
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* 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 {...Handle} handlers The chain of `handle` functions
|
||||
* @returns {Handle}
|
||||
*/
|
||||
export function sequence(...handlers) {
|
||||
const length = handlers.length;
|
||||
if (!length) return ({ event, resolve }) => resolve(event);
|
||||
|
||||
return ({ event, resolve }) => {
|
||||
const { state } = get_request_store();
|
||||
return apply_handle(0, event, {});
|
||||
|
||||
/**
|
||||
* @param {number} i
|
||||
* @param {RequestEvent} event
|
||||
* @param {ResolveOptions | undefined} parent_options
|
||||
* @returns {MaybePromise<Response>}
|
||||
*/
|
||||
function apply_handle(i, event, parent_options) {
|
||||
const handle = handlers[i];
|
||||
|
||||
return state.tracing.record_span({
|
||||
name: `sveltekit.handle.sequenced.${handle.name ? handle.name : i}`,
|
||||
attributes: {},
|
||||
fn: async (current) => {
|
||||
const traced_event = merge_tracing(event, current);
|
||||
return await with_request_store({ event: traced_event, state }, () =>
|
||||
handle({
|
||||
event: traced_event,
|
||||
resolve: (event, options) => {
|
||||
/** @type {ResolveOptions['transformPageChunk']} */
|
||||
const transformPageChunk = async ({ html, done }) => {
|
||||
if (options?.transformPageChunk) {
|
||||
html = (await options.transformPageChunk({ html, done })) ?? '';
|
||||
}
|
||||
|
||||
if (parent_options?.transformPageChunk) {
|
||||
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
/** @type {ResolveOptions['filterSerializedResponseHeaders']} */
|
||||
const filterSerializedResponseHeaders =
|
||||
parent_options?.filterSerializedResponseHeaders ??
|
||||
options?.filterSerializedResponseHeaders;
|
||||
|
||||
/** @type {ResolveOptions['preload']} */
|
||||
const preload = parent_options?.preload ?? options?.preload;
|
||||
|
||||
return i < length - 1
|
||||
? apply_handle(i + 1, event, {
|
||||
transformPageChunk,
|
||||
filterSerializedResponseHeaders,
|
||||
preload
|
||||
})
|
||||
: resolve(event, {
|
||||
transformPageChunk,
|
||||
filterSerializedResponseHeaders,
|
||||
preload
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
308
frontend/node_modules/@sveltejs/kit/src/exports/index.js
generated
vendored
Normal file
308
frontend/node_modules/@sveltejs/kit/src/exports/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
|
||||
import { HttpError, Redirect, ActionFailure, ValidationError } from './internal/index.js';
|
||||
import { BROWSER, DEV } from 'esm-env';
|
||||
import {
|
||||
add_data_suffix,
|
||||
add_resolution_suffix,
|
||||
has_data_suffix,
|
||||
has_resolution_suffix,
|
||||
strip_data_suffix,
|
||||
strip_resolution_suffix
|
||||
} from '../runtime/pathname.js';
|
||||
import { text_encoder } from '../runtime/utils.js';
|
||||
|
||||
export { VERSION } from '../version.js';
|
||||
|
||||
// TODO 3.0: remove these types as they are not used anymore (we can't remove them yet because that would be a breaking change)
|
||||
/**
|
||||
* @template {number} TNumber
|
||||
* @template {any[]} [TArray=[]]
|
||||
* @typedef {TNumber extends TArray['length'] ? TArray[number] : LessThan<TNumber, [...TArray, TArray['length']]>} LessThan
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {number} TStart
|
||||
* @template {number} TEnd
|
||||
* @typedef {Exclude<TEnd | LessThan<TEnd>, LessThan<TStart>>} NumericRange
|
||||
*/
|
||||
|
||||
// Keep the status codes as `number` because restricting to certain numbers makes it unnecessarily hard to use compared to the benefits
|
||||
// (we have runtime errors already to check for invalid codes). Also see https://github.com/sveltejs/kit/issues/11780
|
||||
|
||||
// we have to repeat the JSDoc because the display for function overloads is broken
|
||||
// see https://github.com/microsoft/TypeScript/issues/55056
|
||||
|
||||
/**
|
||||
* Throws an error with a HTTP status code and an optional message.
|
||||
* When called during request handling, this 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 {App.Error} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
|
||||
* @overload
|
||||
* @param {number} status
|
||||
* @param {App.Error} body
|
||||
* @return {never}
|
||||
* @throws {HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
||||
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
||||
*/
|
||||
/**
|
||||
* Throws an error with a HTTP status code and an optional message.
|
||||
* When called during request handling, this 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.
|
||||
* @overload
|
||||
* @param {number} status
|
||||
* @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} [body]
|
||||
* @return {never}
|
||||
* @throws {HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
||||
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
||||
*/
|
||||
/**
|
||||
* Throws an error with a HTTP status code and an optional message.
|
||||
* When called during request handling, this 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.
|
||||
* @return {never}
|
||||
* @throws {HttpError} This error instructs SvelteKit to initiate HTTP error handling.
|
||||
* @throws {Error} If the provided status is invalid (not between 400 and 599).
|
||||
*/
|
||||
export function error(status, body) {
|
||||
if ((!BROWSER || DEV) && (isNaN(status) || status < 400 || status > 599)) {
|
||||
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
|
||||
}
|
||||
|
||||
throw new HttpError(status, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this is an error thrown by {@link error}.
|
||||
* @template {number} T
|
||||
* @param {unknown} e
|
||||
* @param {T} [status] The status to filter for.
|
||||
* @return {e is (HttpError & { status: T extends undefined ? never : T })}
|
||||
*/
|
||||
export function isHttpError(e, status) {
|
||||
if (!(e instanceof HttpError)) return false;
|
||||
return !status || e.status === status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect a request. When called 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.
|
||||
*
|
||||
* Most common status codes:
|
||||
* * `303 See Other`: redirect as a GET request (often used after a form POST request)
|
||||
* * `307 Temporary Redirect`: redirect will keep the request method
|
||||
* * `308 Permanent Redirect`: redirect will keep the request method, SEO will be transferred to the new page
|
||||
*
|
||||
* [See all redirect status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages)
|
||||
*
|
||||
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number)} 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 | URL} location The location to redirect to.
|
||||
* @throws {Redirect} This error instructs SvelteKit to redirect to the specified location.
|
||||
* @throws {Error} If the provided status is invalid.
|
||||
* @return {never}
|
||||
*/
|
||||
export function redirect(status, location) {
|
||||
if ((!BROWSER || DEV) && (isNaN(status) || status < 300 || status > 308)) {
|
||||
throw new Error('Invalid status code');
|
||||
}
|
||||
|
||||
throw new Redirect(
|
||||
// @ts-ignore
|
||||
status,
|
||||
location.toString()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this is a redirect thrown by {@link redirect}.
|
||||
* @param {unknown} e The object to check.
|
||||
* @return {e is Redirect}
|
||||
*/
|
||||
export function isRedirect(e) {
|
||||
return e instanceof Redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// TODO deprecate this in favour of `Response.json` when it's
|
||||
// more widely supported
|
||||
const body = JSON.stringify(data);
|
||||
|
||||
// we can't just do `text(JSON.stringify(data), init)` because
|
||||
// it will set a default `content-type` header. duplicated code
|
||||
// means less duplicated work
|
||||
const headers = new Headers(init?.headers);
|
||||
if (!headers.has('content-length')) {
|
||||
headers.set('content-length', text_encoder.encode(body).byteLength.toString());
|
||||
}
|
||||
|
||||
if (!headers.has('content-type')) {
|
||||
headers.set('content-type', 'application/json');
|
||||
}
|
||||
|
||||
return new Response(body, {
|
||||
...init,
|
||||
headers
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')) {
|
||||
const encoded = text_encoder.encode(body);
|
||||
headers.set('content-length', encoded.byteLength.toString());
|
||||
return new Response(encoded, {
|
||||
...init,
|
||||
headers
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(body, {
|
||||
...init,
|
||||
headers
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an `ActionFailure` object. Call when form submission fails.
|
||||
* @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.
|
||||
* @overload
|
||||
* @param {number} status
|
||||
* @returns {import('./public.js').ActionFailure<undefined>}
|
||||
*/
|
||||
/**
|
||||
* Create an `ActionFailure` object. Call when form submission fails.
|
||||
* @template [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)
|
||||
* @overload
|
||||
* @param {number} status
|
||||
* @param {T} data
|
||||
* @returns {import('./public.js').ActionFailure<T>}
|
||||
*/
|
||||
/**
|
||||
* Create an `ActionFailure` object. Call when form submission fails.
|
||||
* @param {number} status
|
||||
* @param {any} [data]
|
||||
* @returns {import('./public.js').ActionFailure<any>}
|
||||
*/
|
||||
export function fail(status, data) {
|
||||
// @ts-expect-error unique symbol missing
|
||||
return new ActionFailure(status, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this is an action failure thrown by {@link fail}.
|
||||
* @param {unknown} e The object to check.
|
||||
* @return {e is import('./public.js').ActionFailure}
|
||||
*/
|
||||
export function isActionFailure(e) {
|
||||
return e instanceof ActionFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to throw a validation error to imperatively fail form validation.
|
||||
* Can be used in combination with `issue` passed to form actions to create field-specific issues.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { invalid } from '@sveltejs/kit';
|
||||
* import { form } from '$app/server';
|
||||
* import { tryLogin } from '$lib/server/auth';
|
||||
* import * as v from 'valibot';
|
||||
*
|
||||
* export const login = form(
|
||||
* v.object({ name: v.string(), _password: v.string() }),
|
||||
* async ({ name, _password }) => {
|
||||
* const success = tryLogin(name, _password);
|
||||
* if (!success) {
|
||||
* invalid('Incorrect username or password');
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
* @param {...(StandardSchemaV1.Issue | string)} issues
|
||||
* @returns {never}
|
||||
* @since 2.47.3
|
||||
*/
|
||||
export function invalid(...issues) {
|
||||
throw new ValidationError(
|
||||
issues.map((issue) => (typeof issue === 'string' ? { message: issue } : issue))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this is an validation error thrown by {@link invalid}.
|
||||
* @param {unknown} e The object to check.
|
||||
* @return {e is import('./public.js').ActionFailure}
|
||||
* @since 2.47.3
|
||||
*/
|
||||
export function isValidationError(e) {
|
||||
return e instanceof ValidationError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips possible SvelteKit-internal suffixes and trailing slashes from the URL pathname.
|
||||
* Returns the normalized URL as well as a method for adding the potential suffix back
|
||||
* based on a new pathname (possibly including search) or URL.
|
||||
* ```js
|
||||
* import { normalizeUrl } from '@sveltejs/kit';
|
||||
*
|
||||
* const { url, denormalize } = normalizeUrl('/blog/post/__data.json');
|
||||
* console.log(url.pathname); // /blog/post
|
||||
* console.log(denormalize('/blog/post/a')); // /blog/post/a/__data.json
|
||||
* ```
|
||||
* @param {URL | string} url
|
||||
* @returns {{ url: URL, wasNormalized: boolean, denormalize: (url?: string | URL) => URL }}
|
||||
* @since 2.18.0
|
||||
*/
|
||||
export function normalizeUrl(url) {
|
||||
url = new URL(url, 'a://a');
|
||||
|
||||
const is_route_resolution = has_resolution_suffix(url.pathname);
|
||||
const is_data_request = has_data_suffix(url.pathname);
|
||||
const has_trailing_slash = url.pathname !== '/' && url.pathname.endsWith('/');
|
||||
|
||||
if (is_route_resolution) {
|
||||
url.pathname = strip_resolution_suffix(url.pathname);
|
||||
} else if (is_data_request) {
|
||||
url.pathname = strip_data_suffix(url.pathname);
|
||||
} else if (has_trailing_slash) {
|
||||
url.pathname = url.pathname.slice(0, -1);
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
wasNormalized: is_data_request || is_route_resolution || has_trailing_slash,
|
||||
denormalize: (new_url = url) => {
|
||||
new_url = new URL(new_url, url);
|
||||
if (is_route_resolution) {
|
||||
new_url.pathname = add_resolution_suffix(new_url.pathname);
|
||||
} else if (is_data_request) {
|
||||
new_url.pathname = add_data_suffix(new_url.pathname);
|
||||
} else if (has_trailing_slash && !new_url.pathname.endsWith('/')) {
|
||||
new_url.pathname += '/';
|
||||
}
|
||||
return new_url;
|
||||
}
|
||||
};
|
||||
}
|
||||
85
frontend/node_modules/@sveltejs/kit/src/exports/internal/event.js
generated
vendored
Normal file
85
frontend/node_modules/@sveltejs/kit/src/exports/internal/event.js
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/** @import { RequestEvent } from '@sveltejs/kit' */
|
||||
/** @import { RequestStore } from 'types' */
|
||||
/** @import { AsyncLocalStorage } from 'node:async_hooks' */
|
||||
|
||||
import { IN_WEBCONTAINER } from '../../runtime/server/constants.js';
|
||||
|
||||
/** @type {RequestStore | null} */
|
||||
let sync_store = null;
|
||||
|
||||
/** @type {AsyncLocalStorage<RequestStore | null> | null} */
|
||||
let als;
|
||||
|
||||
import('node:async_hooks')
|
||||
.then((hooks) => (als = new hooks.AsyncLocalStorage()))
|
||||
.catch(() => {
|
||||
// can't use AsyncLocalStorage, but can still call getRequestEvent synchronously.
|
||||
// this isn't behind `supports` because it's basically just StackBlitz (i.e.
|
||||
// in-browser usage) that doesn't support it AFAICT
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the current `RequestEvent`. Can be used inside server hooks, server `load` functions, actions, and endpoints (and functions called by them).
|
||||
*
|
||||
* In environments without [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage), this must be called synchronously (i.e. not after an `await`).
|
||||
* @since 2.20.0
|
||||
*
|
||||
* @returns {RequestEvent}
|
||||
*/
|
||||
export function getRequestEvent() {
|
||||
const event = try_get_request_store()?.event;
|
||||
|
||||
if (!event) {
|
||||
let message =
|
||||
'Can only read the current request event inside functions invoked during `handle`, such as server `load` functions, actions, endpoints, and other server hooks.';
|
||||
|
||||
if (!als) {
|
||||
message +=
|
||||
' In environments without `AsyncLocalStorage`, the event must be read synchronously, not after an `await`.';
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
export function get_request_store() {
|
||||
const result = try_get_request_store();
|
||||
if (!result) {
|
||||
let message = 'Could not get the request store.';
|
||||
|
||||
if (als) {
|
||||
message += ' This is an internal error.';
|
||||
} else {
|
||||
message +=
|
||||
' In environments without `AsyncLocalStorage`, the request store (used by e.g. remote functions) must be accessed synchronously, not after an `await`.' +
|
||||
' If it was accessed synchronously then this is an internal error.';
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function try_get_request_store() {
|
||||
return sync_store ?? als?.getStore() ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {RequestStore | null} store
|
||||
* @param {() => T} fn
|
||||
*/
|
||||
export function with_request_store(store, fn) {
|
||||
try {
|
||||
sync_store = store;
|
||||
return als ? als.run(store, fn) : fn();
|
||||
} finally {
|
||||
// Since AsyncLocalStorage is not working in webcontainers, we don't reset `sync_store`
|
||||
// and handle only one request at a time in `src/runtime/server/index.js`.
|
||||
if (!IN_WEBCONTAINER) {
|
||||
sync_store = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
frontend/node_modules/@sveltejs/kit/src/exports/internal/index.js
generated
vendored
Normal file
81
frontend/node_modules/@sveltejs/kit/src/exports/internal/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
|
||||
export class HttpError {
|
||||
/**
|
||||
* @param {number} status
|
||||
* @param {{message: string} extends App.Error ? (App.Error | string | undefined) : App.Error} body
|
||||
*/
|
||||
constructor(status, body) {
|
||||
this.status = status;
|
||||
if (typeof body === 'string') {
|
||||
this.body = { message: body };
|
||||
} else if (body) {
|
||||
this.body = body;
|
||||
} else {
|
||||
this.body = { message: `Error: ${status}` };
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return JSON.stringify(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
export class Redirect {
|
||||
/**
|
||||
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
|
||||
* @param {string} location
|
||||
*/
|
||||
constructor(status, location) {
|
||||
this.status = status;
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error that was thrown from within the SvelteKit runtime that is not fatal and doesn't result in a 500, such as a 404.
|
||||
* `SvelteKitError` goes through `handleError`.
|
||||
* @extends Error
|
||||
*/
|
||||
export class SvelteKitError extends Error {
|
||||
/**
|
||||
* @param {number} status
|
||||
* @param {string} text
|
||||
* @param {string} message
|
||||
*/
|
||||
constructor(status, text, message) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template [T=undefined]
|
||||
*/
|
||||
export class ActionFailure {
|
||||
/**
|
||||
* @param {number} status
|
||||
* @param {T} data
|
||||
*/
|
||||
constructor(status, data) {
|
||||
this.status = status;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when form validation fails imperatively
|
||||
*/
|
||||
export class ValidationError extends Error {
|
||||
/**
|
||||
* @param {StandardSchemaV1.Issue[]} issues
|
||||
*/
|
||||
constructor(issues) {
|
||||
super('Validation failed');
|
||||
this.name = 'ValidationError';
|
||||
this.issues = issues;
|
||||
}
|
||||
}
|
||||
|
||||
export { init_remote_functions } from './remote-functions.js';
|
||||
28
frontend/node_modules/@sveltejs/kit/src/exports/internal/remote-functions.js
generated
vendored
Normal file
28
frontend/node_modules/@sveltejs/kit/src/exports/internal/remote-functions.js
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @import { RemoteInfo } from 'types' */
|
||||
|
||||
/** @type {RemoteInfo['type'][]} */
|
||||
const types = ['command', 'form', 'prerender', 'query', 'query_batch'];
|
||||
|
||||
/**
|
||||
* @param {Record<string, any>} module
|
||||
* @param {string} file
|
||||
* @param {string} hash
|
||||
*/
|
||||
export function init_remote_functions(module, file, hash) {
|
||||
if (module.default) {
|
||||
throw new Error(
|
||||
`Cannot export \`default\` from a remote module (${file}) — please use named exports instead`
|
||||
);
|
||||
}
|
||||
|
||||
for (const [name, fn] of Object.entries(module)) {
|
||||
if (!types.includes(fn?.__?.type)) {
|
||||
throw new Error(
|
||||
`\`${name}\` exported from ${file} is invalid — all exports from this file must be remote functions`
|
||||
);
|
||||
}
|
||||
|
||||
fn.__.id = `${hash}/${name}`;
|
||||
fn.__.name = name;
|
||||
}
|
||||
}
|
||||
22
frontend/node_modules/@sveltejs/kit/src/exports/internal/server.js
generated
vendored
Normal file
22
frontend/node_modules/@sveltejs/kit/src/exports/internal/server.js
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @template {{ tracing: { enabled: boolean, root: import('@opentelemetry/api').Span, current: import('@opentelemetry/api').Span } }} T
|
||||
* @param {T} event_like
|
||||
* @param {import('@opentelemetry/api').Span} current
|
||||
* @returns {T}
|
||||
*/
|
||||
export function merge_tracing(event_like, current) {
|
||||
return {
|
||||
...event_like,
|
||||
tracing: {
|
||||
...event_like.tracing,
|
||||
current
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
with_request_store,
|
||||
getRequestEvent,
|
||||
get_request_store,
|
||||
try_get_request_store
|
||||
} from './event.js';
|
||||
238
frontend/node_modules/@sveltejs/kit/src/exports/node/index.js
generated
vendored
Normal file
238
frontend/node_modules/@sveltejs/kit/src/exports/node/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import { createReadStream } from 'node:fs';
|
||||
import { Readable } from 'node:stream';
|
||||
import * as set_cookie_parser from 'set-cookie-parser';
|
||||
import { SvelteKitError } from '../internal/index.js';
|
||||
|
||||
/**
|
||||
* @param {import('http').IncomingMessage} req
|
||||
* @param {number} [body_size_limit]
|
||||
*/
|
||||
function get_raw_body(req, body_size_limit) {
|
||||
const h = req.headers;
|
||||
|
||||
if (!h['content-type']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content_length = Number(h['content-length']);
|
||||
|
||||
// check if no request body
|
||||
if (
|
||||
(req.httpVersionMajor === 1 && isNaN(content_length) && h['transfer-encoding'] == null) ||
|
||||
content_length === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (req.destroyed) {
|
||||
const readable = new ReadableStream();
|
||||
void readable.cancel();
|
||||
return readable;
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
let cancelled = false;
|
||||
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
if (body_size_limit !== undefined && content_length > body_size_limit) {
|
||||
let message = `Content-length of ${content_length} exceeds limit of ${body_size_limit} bytes.`;
|
||||
|
||||
if (body_size_limit === 0) {
|
||||
// https://github.com/sveltejs/kit/pull/11589
|
||||
// TODO this exists to aid migration — remove in a future version
|
||||
message += ' To disable body size limits, specify Infinity rather than 0.';
|
||||
}
|
||||
|
||||
const error = new SvelteKitError(413, 'Payload Too Large', message);
|
||||
|
||||
controller.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
req.on('error', (error) => {
|
||||
cancelled = true;
|
||||
controller.error(error);
|
||||
});
|
||||
|
||||
req.on('end', () => {
|
||||
if (cancelled) return;
|
||||
controller.close();
|
||||
});
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
if (cancelled) return;
|
||||
|
||||
size += chunk.length;
|
||||
if (size > content_length) {
|
||||
cancelled = true;
|
||||
|
||||
const constraint = content_length ? 'content-length' : 'BODY_SIZE_LIMIT';
|
||||
const message = `request body size exceeded ${constraint} of ${content_length}`;
|
||||
|
||||
const error = new SvelteKitError(413, 'Payload Too Large', message);
|
||||
controller.error(error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
controller.enqueue(chunk);
|
||||
|
||||
if (controller.desiredSize === null || controller.desiredSize <= 0) {
|
||||
req.pause();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
pull() {
|
||||
req.resume();
|
||||
},
|
||||
|
||||
cancel(reason) {
|
||||
cancelled = true;
|
||||
req.destroy(reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* request: import('http').IncomingMessage;
|
||||
* base: string;
|
||||
* bodySizeLimit?: number;
|
||||
* }} options
|
||||
* @returns {Promise<Request>}
|
||||
*/
|
||||
// TODO 3.0 make the signature synchronous?
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export async function getRequest({ request, base, bodySizeLimit }) {
|
||||
let headers = /** @type {Record<string, string>} */ (request.headers);
|
||||
if (request.httpVersionMajor >= 2) {
|
||||
// the Request constructor rejects headers with ':' in the name
|
||||
headers = Object.assign({}, headers);
|
||||
// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.3.5
|
||||
if (headers[':authority']) {
|
||||
headers.host = headers[':authority'];
|
||||
}
|
||||
delete headers[':authority'];
|
||||
delete headers[':method'];
|
||||
delete headers[':path'];
|
||||
delete headers[':scheme'];
|
||||
}
|
||||
|
||||
// TODO: Whenever Node >=22 is minimum supported version, we can use `request.readableAborted`
|
||||
// @see https://github.com/nodejs/node/blob/5cf3c3e24c7257a0c6192ed8ef71efec8ddac22b/lib/internal/streams/readable.js#L1443-L1453
|
||||
const controller = new AbortController();
|
||||
let errored = false;
|
||||
let end_emitted = false;
|
||||
request.once('error', () => (errored = true));
|
||||
request.once('end', () => (end_emitted = true));
|
||||
request.once('close', () => {
|
||||
if ((errored || request.destroyed) && !end_emitted) {
|
||||
controller.abort();
|
||||
}
|
||||
});
|
||||
|
||||
return new Request(base + request.url, {
|
||||
// @ts-expect-error
|
||||
duplex: 'half',
|
||||
method: request.method,
|
||||
headers: Object.entries(headers),
|
||||
signal: controller.signal,
|
||||
body:
|
||||
request.method === 'GET' || request.method === 'HEAD'
|
||||
? undefined
|
||||
: get_raw_body(request, bodySizeLimit)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('http').ServerResponse} res
|
||||
* @param {Response} response
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
// TODO 3.0 make the signature synchronous?
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export async function setResponse(res, response) {
|
||||
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);
|
||||
|
||||
if (!response.body) {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.body.locked) {
|
||||
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()')."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
|
||||
if (res.destroyed) {
|
||||
void reader.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
const cancel = (/** @type {Error|undefined} */ error) => {
|
||||
res.off('close', cancel);
|
||||
res.off('error', cancel);
|
||||
|
||||
// If the reader has already been interrupted with an error earlier,
|
||||
// then it will appear here, it is useless, but it needs to be catch.
|
||||
reader.cancel(error).catch(() => {});
|
||||
if (error) res.destroy(error);
|
||||
};
|
||||
|
||||
res.on('close', cancel);
|
||||
res.on('error', cancel);
|
||||
|
||||
void next();
|
||||
async function next() {
|
||||
try {
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) break;
|
||||
|
||||
if (!res.write(value)) {
|
||||
res.once('drain', next);
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
} catch (error) {
|
||||
cancel(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a file on disk to a readable stream
|
||||
* @param {string} file
|
||||
* @returns {ReadableStream}
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export function createReadableStream(file) {
|
||||
return /** @type {ReadableStream} */ (Readable.toWeb(createReadStream(file)));
|
||||
}
|
||||
30
frontend/node_modules/@sveltejs/kit/src/exports/node/polyfills.js
generated
vendored
Normal file
30
frontend/node_modules/@sveltejs/kit/src/exports/node/polyfills.js
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import buffer from 'node:buffer';
|
||||
import { webcrypto as crypto } from 'node:crypto';
|
||||
|
||||
// `buffer.File` was added in Node 18.13.0 while the `File` global was added in Node 20.0.0
|
||||
const File = /** @type {import('node:buffer') & { File?: File}} */ (buffer).File;
|
||||
|
||||
/** @type {Record<string, any>} */
|
||||
const globals = {
|
||||
crypto,
|
||||
File
|
||||
};
|
||||
|
||||
// exported for dev/preview and node environments
|
||||
/**
|
||||
* Make various web APIs available as globals:
|
||||
* - `crypto`
|
||||
* - `File`
|
||||
*/
|
||||
export function installPolyfills() {
|
||||
for (const name in globals) {
|
||||
if (name in globalThis) continue;
|
||||
|
||||
Object.defineProperty(globalThis, name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: globals[name]
|
||||
});
|
||||
}
|
||||
}
|
||||
2186
frontend/node_modules/@sveltejs/kit/src/exports/public.d.ts
generated
vendored
Normal file
2186
frontend/node_modules/@sveltejs/kit/src/exports/public.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
669
frontend/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js
generated
vendored
Normal file
669
frontend/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { URL } from 'node:url';
|
||||
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||
import colors from 'kleur';
|
||||
import sirv from 'sirv';
|
||||
import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite';
|
||||
import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
|
||||
import { installPolyfills } from '../../../exports/node/polyfills.js';
|
||||
import { coalesce_to_error } from '../../../utils/error.js';
|
||||
import { from_fs, posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js';
|
||||
import { load_error_page } from '../../../core/config/index.js';
|
||||
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
||||
import * as sync from '../../../core/sync/sync.js';
|
||||
import { get_mime_lookup, runtime_base } from '../../../core/utils.js';
|
||||
import { compact } from '../../../utils/array.js';
|
||||
import { not_found } from '../utils.js';
|
||||
import { SCHEME } from '../../../utils/url.js';
|
||||
import { check_feature } from '../../../utils/features.js';
|
||||
import { escape_html } from '../../../utils/escape.js';
|
||||
|
||||
const cwd = process.cwd();
|
||||
// vite-specifc queries that we should skip handling for css urls
|
||||
const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
|
||||
|
||||
/**
|
||||
* @param {import('vite').ViteDevServer} vite
|
||||
* @param {import('vite').ResolvedConfig} vite_config
|
||||
* @param {import('types').ValidatedConfig} svelte_config
|
||||
* @param {() => Array<{ hash: string, file: string }>} get_remotes
|
||||
* @return {Promise<Promise<() => void>>}
|
||||
*/
|
||||
export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
||||
installPolyfills();
|
||||
|
||||
const async_local_storage = new AsyncLocalStorage();
|
||||
|
||||
globalThis.__SVELTEKIT_TRACK__ = (label) => {
|
||||
const context = async_local_storage.getStore();
|
||||
if (!context || context.prerender === true) return;
|
||||
|
||||
check_feature(context.event.route.id, context.config, label, svelte_config.kit.adapter);
|
||||
};
|
||||
|
||||
const fetch = globalThis.fetch;
|
||||
globalThis.fetch = (info, init) => {
|
||||
if (typeof info === 'string' && !SCHEME.test(info)) {
|
||||
throw new Error(
|
||||
`Cannot use relative URL (${info}) with global fetch — use \`event.fetch\` instead: https://svelte.dev/docs/kit/web-standards#fetch-apis`
|
||||
);
|
||||
}
|
||||
|
||||
return fetch(info, init);
|
||||
};
|
||||
|
||||
sync.init(svelte_config, vite_config.mode);
|
||||
|
||||
/** @type {import('types').ManifestData} */
|
||||
let manifest_data;
|
||||
/** @type {import('@sveltejs/kit').SSRManifest} */
|
||||
let manifest;
|
||||
|
||||
/** @type {Error | null} */
|
||||
let manifest_error = null;
|
||||
|
||||
/** @param {string} url */
|
||||
async function loud_ssr_load_module(url) {
|
||||
try {
|
||||
return await vite.ssrLoadModule(url, { fixStacktrace: true });
|
||||
} catch (/** @type {any} */ err) {
|
||||
const msg = buildErrorMessage(err, [colors.red(`Internal server error: ${err.message}`)]);
|
||||
|
||||
if (!vite.config.logger.hasErrorLogged(err)) {
|
||||
vite.config.logger.error(msg, { error: err });
|
||||
}
|
||||
|
||||
vite.ws.send({
|
||||
type: 'error',
|
||||
err: {
|
||||
...err,
|
||||
// these properties are non-enumerable and will
|
||||
// not be serialized unless we explicitly include them
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
});
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} id */
|
||||
async function resolve(id) {
|
||||
const url = id.startsWith('..') ? to_fs(path.posix.resolve(id)) : `/${id}`;
|
||||
|
||||
const module = await loud_ssr_load_module(url);
|
||||
|
||||
const module_node = await vite.moduleGraph.getModuleByUrl(url);
|
||||
if (!module_node) throw new Error(`Could not find node for ${url}`);
|
||||
|
||||
return { module, module_node, url };
|
||||
}
|
||||
|
||||
function update_manifest() {
|
||||
try {
|
||||
({ manifest_data } = sync.create(svelte_config));
|
||||
|
||||
if (manifest_error) {
|
||||
manifest_error = null;
|
||||
vite.ws.send({ type: 'full-reload' });
|
||||
}
|
||||
} catch (error) {
|
||||
manifest_error = /** @type {Error} */ (error);
|
||||
|
||||
console.error(colors.bold().red(manifest_error.message));
|
||||
vite.ws.send({
|
||||
type: 'error',
|
||||
err: {
|
||||
message: manifest_error.message ?? 'Invalid routes',
|
||||
stack: ''
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
manifest = {
|
||||
appDir: svelte_config.kit.appDir,
|
||||
appPath: svelte_config.kit.appDir,
|
||||
assets: new Set(manifest_data.assets.map((asset) => asset.file)),
|
||||
mimeTypes: get_mime_lookup(manifest_data),
|
||||
_: {
|
||||
client: {
|
||||
start: `${runtime_base}/client/entry.js`,
|
||||
app: `${to_fs(svelte_config.kit.outDir)}/generated/client/app.js`,
|
||||
imports: [],
|
||||
stylesheets: [],
|
||||
fonts: [],
|
||||
uses_env_dynamic_public: true,
|
||||
nodes:
|
||||
svelte_config.kit.router.resolution === 'client'
|
||||
? undefined
|
||||
: manifest_data.nodes.map((node, i) => {
|
||||
if (node.component || node.universal) {
|
||||
return `${svelte_config.kit.paths.base}${to_fs(svelte_config.kit.outDir)}/generated/client/nodes/${i}.js`;
|
||||
}
|
||||
}),
|
||||
// `css` is not necessary in dev, as the JS file from `nodes` will reference the CSS file
|
||||
routes:
|
||||
svelte_config.kit.router.resolution === 'client'
|
||||
? undefined
|
||||
: compact(
|
||||
manifest_data.routes.map((route) => {
|
||||
if (!route.page) return;
|
||||
|
||||
return {
|
||||
id: route.id,
|
||||
pattern: route.pattern,
|
||||
params: route.params,
|
||||
layouts: route.page.layouts.map((l) =>
|
||||
l !== undefined ? [!!manifest_data.nodes[l].server, l] : undefined
|
||||
),
|
||||
errors: route.page.errors,
|
||||
leaf: [!!manifest_data.nodes[route.page.leaf].server, route.page.leaf]
|
||||
};
|
||||
})
|
||||
)
|
||||
},
|
||||
server_assets: new Proxy(
|
||||
{},
|
||||
{
|
||||
has: (_, /** @type {string} */ file) => fs.existsSync(from_fs(file)),
|
||||
get: (_, /** @type {string} */ file) => fs.statSync(from_fs(file)).size
|
||||
}
|
||||
),
|
||||
nodes: manifest_data.nodes.map((node, index) => {
|
||||
return async () => {
|
||||
/** @type {import('types').SSRNode} */
|
||||
const result = {};
|
||||
result.index = index;
|
||||
result.universal_id = node.universal;
|
||||
result.server_id = node.server;
|
||||
|
||||
// these are unused in dev, but it's easier to include them
|
||||
result.imports = [];
|
||||
result.stylesheets = [];
|
||||
result.fonts = [];
|
||||
|
||||
/** @type {import('vite').ModuleNode[]} */
|
||||
const module_nodes = [];
|
||||
|
||||
if (node.component) {
|
||||
result.component = async () => {
|
||||
const { module_node, module } = await resolve(
|
||||
/** @type {string} */ (node.component)
|
||||
);
|
||||
|
||||
module_nodes.push(module_node);
|
||||
|
||||
return module.default;
|
||||
};
|
||||
}
|
||||
|
||||
if (node.universal) {
|
||||
if (node.page_options?.ssr === false) {
|
||||
result.universal = node.page_options;
|
||||
} else {
|
||||
// TODO: explain why the file was loaded on the server if we fail to load it
|
||||
const { module, module_node } = await resolve(node.universal);
|
||||
module_nodes.push(module_node);
|
||||
result.universal = module;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.server) {
|
||||
const { module } = await resolve(node.server);
|
||||
result.server = module;
|
||||
}
|
||||
|
||||
// in dev we inline all styles to avoid FOUC. this gets populated lazily so that
|
||||
// components/stylesheets loaded via import() during `load` are included
|
||||
result.inline_styles = async () => {
|
||||
/** @type {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} */
|
||||
const deps = new Set();
|
||||
|
||||
for (const module_node of module_nodes) {
|
||||
await find_deps(vite, module_node, deps);
|
||||
}
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const styles = {};
|
||||
|
||||
for (const dep of deps) {
|
||||
if (isCSSRequest(dep.url) && !vite_css_query_regex.test(dep.url)) {
|
||||
const inlineCssUrl = dep.url.includes('?')
|
||||
? dep.url.replace('?', '?inline&')
|
||||
: dep.url + '?inline';
|
||||
try {
|
||||
const mod = await vite.ssrLoadModule(inlineCssUrl);
|
||||
styles[dep.url] = mod.default;
|
||||
} catch {
|
||||
// this can happen with dynamically imported modules, I think
|
||||
// because the Vite module graph doesn't distinguish between
|
||||
// static and dynamic imports? TODO investigate, submit fix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
}),
|
||||
prerendered_routes: new Set(),
|
||||
get remotes() {
|
||||
return Object.fromEntries(
|
||||
get_remotes().map((remote) => [
|
||||
remote.hash,
|
||||
() => vite.ssrLoadModule(remote.file).then((module) => ({ default: module }))
|
||||
])
|
||||
);
|
||||
},
|
||||
routes: compact(
|
||||
manifest_data.routes.map((route) => {
|
||||
if (!route.page && !route.endpoint) return null;
|
||||
|
||||
const endpoint = route.endpoint;
|
||||
|
||||
return {
|
||||
id: route.id,
|
||||
pattern: route.pattern,
|
||||
params: route.params,
|
||||
page: route.page,
|
||||
endpoint: endpoint
|
||||
? async () => {
|
||||
const url = path.resolve(cwd, endpoint.file);
|
||||
return await loud_ssr_load_module(url);
|
||||
}
|
||||
: null,
|
||||
endpoint_id: endpoint?.file
|
||||
};
|
||||
})
|
||||
),
|
||||
matchers: async () => {
|
||||
/** @type {Record<string, import('@sveltejs/kit').ParamMatcher>} */
|
||||
const matchers = {};
|
||||
|
||||
for (const key in manifest_data.matchers) {
|
||||
const file = manifest_data.matchers[key];
|
||||
const url = path.resolve(cwd, file);
|
||||
const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
|
||||
|
||||
if (module.match) {
|
||||
matchers[key] = module.match;
|
||||
} else {
|
||||
throw new Error(`${file} does not export a \`match\` function`);
|
||||
}
|
||||
}
|
||||
|
||||
return matchers;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {Error} error */
|
||||
function fix_stack_trace(error) {
|
||||
try {
|
||||
vite.ssrFixStacktrace(error);
|
||||
} catch {
|
||||
// ssrFixStacktrace can fail on StackBlitz web containers and we don't know why
|
||||
// by ignoring it the line numbers are wrong, but at least we can show the error
|
||||
}
|
||||
return error.stack;
|
||||
}
|
||||
|
||||
update_manifest();
|
||||
|
||||
/**
|
||||
* @param {string} event
|
||||
* @param {(file: string) => void} cb
|
||||
*/
|
||||
const watch = (event, cb) => {
|
||||
vite.watcher.on(event, (file) => {
|
||||
if (
|
||||
file.startsWith(svelte_config.kit.files.routes + path.sep) ||
|
||||
file.startsWith(svelte_config.kit.files.params + path.sep) ||
|
||||
svelte_config.kit.moduleExtensions.some((ext) => file.endsWith(`.remote${ext}`)) ||
|
||||
// in contrast to server hooks, client hooks are written to the client manifest
|
||||
// and therefore need rebuilding when they are added/removed
|
||||
file.startsWith(svelte_config.kit.files.hooks.client)
|
||||
) {
|
||||
cb(file);
|
||||
}
|
||||
});
|
||||
};
|
||||
/** @type {NodeJS.Timeout | null } */
|
||||
let timeout = null;
|
||||
/** @param {() => void} to_run */
|
||||
const debounce = (to_run) => {
|
||||
timeout && clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
to_run();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// flag to skip watchers if server is already restarting
|
||||
let restarting = false;
|
||||
|
||||
// Debounce add/unlink events because in case of folder deletion or moves
|
||||
// they fire in rapid succession, causing needless invocations.
|
||||
// These watchers only run for routes, param matchers, and client hooks.
|
||||
watch('add', () => debounce(update_manifest));
|
||||
watch('unlink', () => debounce(update_manifest));
|
||||
watch('change', (file) => {
|
||||
// Don't run for a single file if the whole manifest is about to get updated
|
||||
// Unless it's a file where the trailing slash page option might have changed
|
||||
if (timeout || restarting || !/\+(page|layout|server).*$/.test(file)) return;
|
||||
sync.update(svelte_config, manifest_data, file);
|
||||
});
|
||||
|
||||
const { appTemplate, errorTemplate, serviceWorker, hooks } = svelte_config.kit.files;
|
||||
|
||||
// vite client only executes a full reload if the triggering html file path is index.html
|
||||
// kit defaults to src/app.html, so unless user changed that to index.html
|
||||
// send the vite client a full-reload event without path being set
|
||||
if (appTemplate !== 'index.html') {
|
||||
vite.watcher.on('change', (file) => {
|
||||
if (file === appTemplate && !restarting) {
|
||||
vite.ws.send({ type: 'full-reload' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vite.watcher.on('all', (_, file) => {
|
||||
if (
|
||||
file === appTemplate ||
|
||||
file === errorTemplate ||
|
||||
file.startsWith(serviceWorker) ||
|
||||
file.startsWith(hooks.server)
|
||||
) {
|
||||
sync.server(svelte_config);
|
||||
}
|
||||
});
|
||||
|
||||
vite.watcher.on('change', async (file) => {
|
||||
// changing the svelte config requires restarting the dev server
|
||||
// the config is only read on start and passed on to vite-plugin-svelte
|
||||
// which needs up-to-date values to operate correctly
|
||||
if (file.match(/[/\\]svelte\.config\.[jt]s$/)) {
|
||||
console.log(`svelte config changed, restarting vite dev-server. changed file: ${file}`);
|
||||
restarting = true;
|
||||
await vite.restart();
|
||||
}
|
||||
});
|
||||
|
||||
const assets = svelte_config.kit.paths.assets ? SVELTE_KIT_ASSETS : svelte_config.kit.paths.base;
|
||||
const asset_server = sirv(svelte_config.kit.files.assets, {
|
||||
dev: true,
|
||||
etag: true,
|
||||
maxAge: 0,
|
||||
extensions: [],
|
||||
setHeaders: (res) => {
|
||||
res.setHeader('access-control-allow-origin', '*');
|
||||
}
|
||||
});
|
||||
|
||||
vite.middlewares.use((req, res, next) => {
|
||||
const base = `${vite.config.server.https ? 'https' : 'http'}://${
|
||||
req.headers[':authority'] || req.headers.host
|
||||
}`;
|
||||
|
||||
const decoded = decodeURI(new URL(base + req.url).pathname);
|
||||
|
||||
if (decoded.startsWith(assets)) {
|
||||
const pathname = decoded.slice(assets.length);
|
||||
const file = svelte_config.kit.files.assets + pathname;
|
||||
|
||||
if (fs.existsSync(file) && !fs.statSync(file).isDirectory()) {
|
||||
if (has_correct_case(file, svelte_config.kit.files.assets)) {
|
||||
req.url = encodeURI(pathname); // don't need query/hash
|
||||
asset_server(req, res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
const env = loadEnv(vite_config.mode, svelte_config.kit.env.dir, '');
|
||||
const emulator = await svelte_config.kit.adapter?.emulate?.();
|
||||
|
||||
return () => {
|
||||
const serve_static_middleware = vite.middlewares.stack.find(
|
||||
(middleware) =>
|
||||
/** @type {function} */ (middleware.handle).name === 'viteServeStaticMiddleware'
|
||||
);
|
||||
|
||||
// Vite will give a 403 on URLs like /test, /static, and /package.json preventing us from
|
||||
// serving routes with those names. See https://github.com/vitejs/vite/issues/7363
|
||||
remove_static_middlewares(vite.middlewares);
|
||||
|
||||
vite.middlewares.use(async (req, res) => {
|
||||
// Vite's base middleware strips out the base path. Restore it
|
||||
const original_url = req.url;
|
||||
req.url = req.originalUrl;
|
||||
try {
|
||||
const base = `${vite.config.server.https ? 'https' : 'http'}://${
|
||||
req.headers[':authority'] || req.headers.host
|
||||
}`;
|
||||
|
||||
const decoded = decodeURI(new URL(base + req.url).pathname);
|
||||
const file = posixify(path.resolve(decoded.slice(svelte_config.kit.paths.base.length + 1)));
|
||||
const is_file = fs.existsSync(file) && !fs.statSync(file).isDirectory();
|
||||
const allowed =
|
||||
!vite_config.server.fs.strict ||
|
||||
vite_config.server.fs.allow.some((dir) => file.startsWith(dir));
|
||||
|
||||
if (is_file && allowed) {
|
||||
req.url = original_url;
|
||||
// @ts-expect-error
|
||||
serve_static_middleware.handle(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decoded.startsWith(svelte_config.kit.paths.base)) {
|
||||
return not_found(req, res, svelte_config.kit.paths.base);
|
||||
}
|
||||
|
||||
if (decoded === svelte_config.kit.paths.base + '/service-worker.js') {
|
||||
const resolved = resolve_entry(svelte_config.kit.files.serviceWorker);
|
||||
|
||||
if (resolved) {
|
||||
res.writeHead(200, {
|
||||
'content-type': 'application/javascript'
|
||||
});
|
||||
res.end(`import '${svelte_config.kit.paths.base}${to_fs(resolved)}';`);
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end('not found');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const resolved_instrumentation = resolve_entry(
|
||||
path.join(svelte_config.kit.files.src, 'instrumentation.server')
|
||||
);
|
||||
|
||||
if (resolved_instrumentation) {
|
||||
await vite.ssrLoadModule(resolved_instrumentation);
|
||||
}
|
||||
|
||||
// we have to import `Server` before calling `set_assets`
|
||||
const { Server } = /** @type {import('types').ServerModule} */ (
|
||||
await vite.ssrLoadModule(`${runtime_base}/server/index.js`, { fixStacktrace: true })
|
||||
);
|
||||
|
||||
const { set_fix_stack_trace } = await vite.ssrLoadModule(
|
||||
`${runtime_base}/shared-server.js`
|
||||
);
|
||||
set_fix_stack_trace(fix_stack_trace);
|
||||
|
||||
const { set_assets } = await vite.ssrLoadModule('$app/paths/internal/server');
|
||||
set_assets(assets);
|
||||
|
||||
const server = new Server(manifest);
|
||||
|
||||
await server.init({
|
||||
env,
|
||||
read: (file) => createReadableStream(from_fs(file))
|
||||
});
|
||||
|
||||
const request = await getRequest({
|
||||
base,
|
||||
request: req
|
||||
});
|
||||
|
||||
if (manifest_error) {
|
||||
console.error(colors.bold().red(manifest_error.message));
|
||||
|
||||
const error_page = load_error_page(svelte_config);
|
||||
|
||||
/** @param {{ status: number; message: string }} opts */
|
||||
const error_template = ({ status, message }) => {
|
||||
return error_page
|
||||
.replace(/%sveltekit\.status%/g, String(status))
|
||||
.replace(/%sveltekit\.error\.message%/g, escape_html(message));
|
||||
};
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/html; charset=utf-8'
|
||||
});
|
||||
res.end(
|
||||
error_template({ status: 500, message: manifest_error.message ?? 'Invalid routes' })
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const rendered = await server.respond(request, {
|
||||
getClientAddress: () => {
|
||||
const { remoteAddress } = req.socket;
|
||||
if (remoteAddress) return remoteAddress;
|
||||
throw new Error('Could not determine clientAddress');
|
||||
},
|
||||
read: (file) => {
|
||||
if (file in manifest._.server_assets) {
|
||||
return fs.readFileSync(from_fs(file));
|
||||
}
|
||||
|
||||
return fs.readFileSync(path.join(svelte_config.kit.files.assets, file));
|
||||
},
|
||||
before_handle: (event, config, prerender) => {
|
||||
async_local_storage.enterWith({ event, config, prerender });
|
||||
},
|
||||
emulator
|
||||
});
|
||||
|
||||
if (rendered.status === 404) {
|
||||
// @ts-expect-error
|
||||
serve_static_middleware.handle(req, res, () => {
|
||||
void setResponse(res, rendered);
|
||||
});
|
||||
} else {
|
||||
void setResponse(res, rendered);
|
||||
}
|
||||
} catch (e) {
|
||||
const error = coalesce_to_error(e);
|
||||
res.statusCode = 500;
|
||||
res.end(fix_stack_trace(error));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('connect').Server} server
|
||||
*/
|
||||
function remove_static_middlewares(server) {
|
||||
const static_middlewares = ['viteServeStaticMiddleware', 'viteServePublicMiddleware'];
|
||||
for (let i = server.stack.length - 1; i > 0; i--) {
|
||||
// @ts-expect-error using internals
|
||||
if (static_middlewares.includes(server.stack[i].handle.name)) {
|
||||
server.stack.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('vite').ViteDevServer} vite
|
||||
* @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node
|
||||
* @param {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} deps
|
||||
*/
|
||||
async function find_deps(vite, node, deps) {
|
||||
// since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
|
||||
// instead of using `await`, we resolve all branches in parallel.
|
||||
/** @type {Promise<void>[]} */
|
||||
const branches = [];
|
||||
|
||||
/** @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node */
|
||||
async function add(node) {
|
||||
if (!deps.has(node)) {
|
||||
deps.add(node);
|
||||
await find_deps(vite, node, deps);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} url */
|
||||
async function add_by_url(url) {
|
||||
const node = await get_server_module_by_url(vite, url);
|
||||
|
||||
if (node) {
|
||||
await add(node);
|
||||
}
|
||||
}
|
||||
|
||||
const transform_result =
|
||||
/** @type {import('vite').ModuleNode} */ (node).ssrTransformResult || node.transformResult;
|
||||
|
||||
if (transform_result) {
|
||||
if (transform_result.deps) {
|
||||
transform_result.deps.forEach((url) => branches.push(add_by_url(url)));
|
||||
}
|
||||
|
||||
if (transform_result.dynamicDeps) {
|
||||
transform_result.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
|
||||
}
|
||||
} else {
|
||||
node.importedModules.forEach((node) => branches.push(add(node)));
|
||||
}
|
||||
|
||||
await Promise.all(branches);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('vite').ViteDevServer} vite
|
||||
* @param {string} url
|
||||
*/
|
||||
function get_server_module_by_url(vite, url) {
|
||||
return vite.environments
|
||||
? vite.environments.ssr.moduleGraph.getModuleByUrl(url)
|
||||
: vite.moduleGraph.getModuleByUrl(url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a file is being requested with the correct case,
|
||||
* to ensure consistent behaviour between dev and prod and across
|
||||
* operating systems. Note that we can't use realpath here,
|
||||
* because we don't want to follow symlinks
|
||||
* @param {string} file
|
||||
* @param {string} assets
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function has_correct_case(file, assets) {
|
||||
if (file === assets) return true;
|
||||
|
||||
const parent = path.dirname(file);
|
||||
|
||||
if (fs.readdirSync(parent).includes(path.basename(file))) {
|
||||
return has_correct_case(parent, assets);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
1464
frontend/node_modules/@sveltejs/kit/src/exports/vite/index.js
generated
vendored
Normal file
1464
frontend/node_modules/@sveltejs/kit/src/exports/vite/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
16
frontend/node_modules/@sveltejs/kit/src/exports/vite/module_ids.js
generated
vendored
Normal file
16
frontend/node_modules/@sveltejs/kit/src/exports/vite/module_ids.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { posixify } from '../../utils/filesystem.js';
|
||||
|
||||
export const env_static_private = '\0virtual:env/static/private';
|
||||
export const env_static_public = '\0virtual:env/static/public';
|
||||
export const env_dynamic_private = '\0virtual:env/dynamic/private';
|
||||
export const env_dynamic_public = '\0virtual:env/dynamic/public';
|
||||
|
||||
export const service_worker = '\0virtual:service-worker';
|
||||
|
||||
export const sveltekit_environment = '\0virtual:__sveltekit/environment';
|
||||
export const sveltekit_server = '\0virtual:__sveltekit/server';
|
||||
|
||||
export const app_server = posixify(
|
||||
fileURLToPath(new URL('../../runtime/app/server/index.js', import.meta.url))
|
||||
);
|
||||
261
frontend/node_modules/@sveltejs/kit/src/exports/vite/preview/index.js
generated
vendored
Normal file
261
frontend/node_modules/@sveltejs/kit/src/exports/vite/preview/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
import fs from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { lookup } from 'mrmime';
|
||||
import sirv from 'sirv';
|
||||
import { loadEnv, normalizePath } from 'vite';
|
||||
import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
|
||||
import { installPolyfills } from '../../../exports/node/polyfills.js';
|
||||
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
||||
import { not_found } from '../utils.js';
|
||||
|
||||
/** @typedef {import('http').IncomingMessage} Req */
|
||||
/** @typedef {import('http').ServerResponse} Res */
|
||||
/** @typedef {(req: Req, res: Res, next: () => void) => void} Handler */
|
||||
|
||||
/**
|
||||
* @param {import('vite').PreviewServer} vite
|
||||
* @param {import('vite').ResolvedConfig} vite_config
|
||||
* @param {import('types').ValidatedConfig} svelte_config
|
||||
*/
|
||||
export async function preview(vite, vite_config, svelte_config) {
|
||||
installPolyfills();
|
||||
|
||||
const { paths } = svelte_config.kit;
|
||||
const base = paths.base;
|
||||
const assets = paths.assets ? SVELTE_KIT_ASSETS : paths.base;
|
||||
|
||||
const protocol = vite_config.preview.https ? 'https' : 'http';
|
||||
|
||||
const etag = `"${Date.now()}"`;
|
||||
|
||||
const dir = join(svelte_config.kit.outDir, 'output/server');
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
throw new Error(`Server files not found at ${dir}, did you run \`build\` first?`);
|
||||
}
|
||||
|
||||
const instrumentation = join(dir, 'instrumentation.server.js');
|
||||
if (fs.existsSync(instrumentation)) {
|
||||
await import(pathToFileURL(instrumentation).href);
|
||||
}
|
||||
|
||||
/** @type {import('types').ServerInternalModule} */
|
||||
const { set_assets } = await import(pathToFileURL(join(dir, 'internal.js')).href);
|
||||
|
||||
/** @type {import('types').ServerModule} */
|
||||
const { Server } = await import(pathToFileURL(join(dir, 'index.js')).href);
|
||||
|
||||
const { manifest } = await import(pathToFileURL(join(dir, 'manifest.js')).href);
|
||||
|
||||
set_assets(assets);
|
||||
|
||||
const server = new Server(manifest);
|
||||
await server.init({
|
||||
env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''),
|
||||
read: (file) => createReadableStream(`${dir}/${file}`)
|
||||
});
|
||||
|
||||
const emulator = await svelte_config.kit.adapter?.emulate?.();
|
||||
|
||||
return () => {
|
||||
// Remove the base middleware. It screws with the URL.
|
||||
// It also only lets through requests beginning with the base path, so that requests beginning
|
||||
// with the assets URL never reach us. We could serve assets separately before the base
|
||||
// middleware, but we'd need that to occur after the compression and cors middlewares, so would
|
||||
// need to insert it manually into the stack, which would be at least as bad as doing this.
|
||||
for (let i = vite.middlewares.stack.length - 1; i > 0; i--) {
|
||||
// @ts-expect-error using internals
|
||||
if (vite.middlewares.stack[i].handle.name === 'viteBaseMiddleware') {
|
||||
vite.middlewares.stack.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// generated client assets and the contents of `static`
|
||||
vite.middlewares.use(
|
||||
scoped(
|
||||
assets,
|
||||
sirv(join(svelte_config.kit.outDir, 'output/client'), {
|
||||
setHeaders: (res, pathname) => {
|
||||
// only apply to immutable directory, not e.g. version.json
|
||||
if (pathname.startsWith(`/${svelte_config.kit.appDir}/immutable`)) {
|
||||
res.setHeader('cache-control', 'public,max-age=31536000,immutable');
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
vite.middlewares.use((req, res, next) => {
|
||||
const original_url = /** @type {string} */ (req.url);
|
||||
const { pathname, search } = new URL(original_url, 'http://dummy');
|
||||
|
||||
// if `paths.base === '/a/b/c`, then the root route is `/a/b/c/`,
|
||||
// regardless of the `trailingSlash` route option
|
||||
if (base.length > 1 && pathname === base) {
|
||||
let location = base + '/';
|
||||
if (search) location += search;
|
||||
res.writeHead(307, {
|
||||
location
|
||||
});
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.startsWith(base)) {
|
||||
next();
|
||||
} else {
|
||||
res.statusCode = 404;
|
||||
not_found(req, res, base);
|
||||
}
|
||||
});
|
||||
|
||||
// prerendered dependencies
|
||||
vite.middlewares.use(
|
||||
scoped(base, mutable(join(svelte_config.kit.outDir, 'output/prerendered/dependencies')))
|
||||
);
|
||||
|
||||
// prerendered pages (we can't just use sirv because we need to
|
||||
// preserve the correct trailingSlash behaviour)
|
||||
vite.middlewares.use(
|
||||
scoped(base, (req, res, next) => {
|
||||
let if_none_match_value = req.headers['if-none-match'];
|
||||
|
||||
if (if_none_match_value?.startsWith('W/"')) {
|
||||
if_none_match_value = if_none_match_value.substring(2);
|
||||
}
|
||||
|
||||
if (if_none_match_value === etag) {
|
||||
res.statusCode = 304;
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const { pathname, search } = new URL(/** @type {string} */ (req.url), 'http://dummy');
|
||||
|
||||
const dir = pathname.startsWith(`/${svelte_config.kit.appDir}/remote/`) ? 'data' : 'pages';
|
||||
|
||||
let filename = normalizePath(
|
||||
join(svelte_config.kit.outDir, `output/prerendered/${dir}` + pathname)
|
||||
);
|
||||
|
||||
try {
|
||||
filename = decodeURI(filename);
|
||||
} catch {
|
||||
// malformed URI
|
||||
}
|
||||
|
||||
let prerendered = is_file(filename);
|
||||
|
||||
if (!prerendered) {
|
||||
const has_trailing_slash = pathname.endsWith('/');
|
||||
const html_filename = `${filename}${has_trailing_slash ? 'index.html' : '.html'}`;
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let redirect;
|
||||
|
||||
if (is_file(html_filename)) {
|
||||
filename = html_filename;
|
||||
prerendered = true;
|
||||
} else if (has_trailing_slash) {
|
||||
if (is_file(filename.slice(0, -1) + '.html')) {
|
||||
redirect = pathname.slice(0, -1);
|
||||
}
|
||||
} else if (is_file(filename + '/index.html')) {
|
||||
redirect = pathname + '/';
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
if (search) redirect += search;
|
||||
res.writeHead(307, {
|
||||
location: redirect
|
||||
});
|
||||
|
||||
res.end();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (prerendered) {
|
||||
res.writeHead(200, {
|
||||
'content-type': lookup(pathname) || 'text/html',
|
||||
etag
|
||||
});
|
||||
|
||||
fs.createReadStream(filename).pipe(res);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// SSR
|
||||
vite.middlewares.use(async (req, res) => {
|
||||
const host = req.headers[':authority'] || req.headers.host;
|
||||
|
||||
const request = await getRequest({
|
||||
base: `${protocol}://${host}`,
|
||||
request: req
|
||||
});
|
||||
|
||||
await setResponse(
|
||||
res,
|
||||
await server.respond(request, {
|
||||
getClientAddress: () => {
|
||||
const { remoteAddress } = req.socket;
|
||||
if (remoteAddress) return remoteAddress;
|
||||
throw new Error('Could not determine clientAddress');
|
||||
},
|
||||
read: (file) => {
|
||||
if (file in manifest._.server_assets) {
|
||||
return fs.readFileSync(join(dir, file));
|
||||
}
|
||||
|
||||
return fs.readFileSync(join(svelte_config.kit.files.assets, file));
|
||||
},
|
||||
emulator
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Handler}
|
||||
*/
|
||||
const mutable = (dir) =>
|
||||
fs.existsSync(dir)
|
||||
? sirv(dir, {
|
||||
etag: true,
|
||||
maxAge: 0
|
||||
})
|
||||
: (_req, _res, next) => next();
|
||||
|
||||
/**
|
||||
* @param {string} scope
|
||||
* @param {Handler} handler
|
||||
* @returns {Handler}
|
||||
*/
|
||||
function scoped(scope, handler) {
|
||||
if (scope === '') return handler;
|
||||
|
||||
return (req, res, next) => {
|
||||
if (req.url?.startsWith(scope)) {
|
||||
const original_url = req.url;
|
||||
req.url = req.url.slice(scope.length);
|
||||
handler(req, res, () => {
|
||||
req.url = original_url;
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {string} path */
|
||||
function is_file(path) {
|
||||
return fs.existsSync(path) && !fs.statSync(path).isDirectory();
|
||||
}
|
||||
301
frontend/node_modules/@sveltejs/kit/src/exports/vite/static_analysis/index.js
generated
vendored
Normal file
301
frontend/node_modules/@sveltejs/kit/src/exports/vite/static_analysis/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
||||
import { Parser } from 'acorn';
|
||||
import { read } from '../../../utils/filesystem.js';
|
||||
|
||||
const valid_page_options_array = /** @type {const} */ ([
|
||||
'ssr',
|
||||
'prerender',
|
||||
'csr',
|
||||
'trailingSlash',
|
||||
'config',
|
||||
'entries',
|
||||
'load'
|
||||
]);
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const valid_page_options = new Set(valid_page_options_array);
|
||||
|
||||
/** @typedef {typeof valid_page_options_array[number]} ValidPageOption */
|
||||
/** @typedef {Partial<Record<ValidPageOption, any>>} PageOptions */
|
||||
|
||||
const skip_parsing_regex = new RegExp(
|
||||
`${Array.from(valid_page_options).join('|')}|(?:export[\\s\\n]+\\*[\\s\\n]+from)`
|
||||
);
|
||||
|
||||
const parser = Parser.extend(tsPlugin());
|
||||
|
||||
/**
|
||||
* Collects page options from a +page.js/+layout.js file, ignoring reassignments
|
||||
* and using the declared value (except for load functions, for which the value is `true`).
|
||||
* Returns `null` if any export is too difficult to analyse.
|
||||
* @param {string} filename The name of the file to report when an error occurs
|
||||
* @param {string} input
|
||||
* @returns {PageOptions | null}
|
||||
*/
|
||||
export function statically_analyse_page_options(filename, input) {
|
||||
// if there's a chance there are no page exports or an unparseable
|
||||
// export all declaration, then we can skip the AST parsing which is expensive
|
||||
if (!skip_parsing_regex.test(input)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const source = parser.parse(input, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest'
|
||||
});
|
||||
|
||||
/** @type {Map<string, import('acorn').Literal['value']>} */
|
||||
const page_options = new Map();
|
||||
|
||||
for (const statement of source.body) {
|
||||
// ignore export all declarations with aliases that are not page options
|
||||
if (
|
||||
statement.type === 'ExportAllDeclaration' &&
|
||||
statement.exported &&
|
||||
!valid_page_options.has(get_name(statement.exported))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
statement.type === 'ExportDefaultDeclaration' ||
|
||||
statement.type === 'ExportAllDeclaration'
|
||||
) {
|
||||
return null;
|
||||
} else if (statement.type !== 'ExportNamedDeclaration') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (statement.specifiers.length) {
|
||||
/** @type {Map<string, string>} */
|
||||
const export_specifiers = new Map();
|
||||
for (const specifier of statement.specifiers) {
|
||||
const exported_name = get_name(specifier.exported);
|
||||
if (!valid_page_options.has(exported_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (statement.source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export_specifiers.set(get_name(specifier.local), exported_name);
|
||||
}
|
||||
|
||||
for (const statement of source.body) {
|
||||
switch (statement.type) {
|
||||
case 'ImportDeclaration': {
|
||||
for (const import_specifier of statement.specifiers) {
|
||||
if (export_specifiers.has(import_specifier.local.name)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ExportNamedDeclaration':
|
||||
case 'VariableDeclaration':
|
||||
case 'FunctionDeclaration':
|
||||
case 'ClassDeclaration': {
|
||||
const declaration =
|
||||
statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
|
||||
|
||||
if (!declaration) {
|
||||
break;
|
||||
}
|
||||
|
||||
// class and function declarations
|
||||
if (declaration.type !== 'VariableDeclaration') {
|
||||
if (export_specifiers.has(declaration.id.name)) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (const variable_declarator of declaration.declarations) {
|
||||
if (
|
||||
variable_declarator.id.type !== 'Identifier' ||
|
||||
!export_specifiers.has(variable_declarator.id.name)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (variable_declarator.init?.type === 'Literal') {
|
||||
page_options.set(
|
||||
/** @type {string} */ (export_specifiers.get(variable_declarator.id.name)),
|
||||
variable_declarator.init.value
|
||||
);
|
||||
export_specifiers.delete(variable_declarator.id.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special case: We only want to know that 'load' is exported (in a way that doesn't cause truthy checks in other places to trigger)
|
||||
if (variable_declarator.id.name === 'load') {
|
||||
page_options.set('load', null);
|
||||
export_specifiers.delete('load');
|
||||
continue;
|
||||
}
|
||||
|
||||
// references a declaration we can't easily evaluate statically
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// there were some export specifiers that we couldn't resolve
|
||||
if (export_specifiers.size) {
|
||||
return null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!statement.declaration) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// class and function declarations
|
||||
if (statement.declaration.type !== 'VariableDeclaration') {
|
||||
if (valid_page_options.has(statement.declaration.id.name)) {
|
||||
// Special case: We only want to know that 'load' is exported (in a way that doesn't cause truthy checks in other places to trigger)
|
||||
if (statement.declaration.id.name === 'load') {
|
||||
page_options.set('load', null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const declaration of statement.declaration.declarations) {
|
||||
if (declaration.id.type !== 'Identifier') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!valid_page_options.has(declaration.id.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (declaration.init?.type === 'Literal') {
|
||||
page_options.set(declaration.id.name, declaration.init.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special case: We only want to know that 'load' is exported (in a way that doesn't cause truthy checks in other places to trigger)
|
||||
if (declaration.id.name === 'load') {
|
||||
page_options.set('load', null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// references a declaration we can't easily evaluate statically
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.fromEntries(page_options);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
error.message = `Failed to statically analyse page options for ${filename}. ${error.message}`;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('acorn').Identifier | import('acorn').Literal} node
|
||||
* @returns {string}
|
||||
*/
|
||||
function get_name(node) {
|
||||
return node.type === 'Identifier' ? node.name : /** @type {string} */ (node.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and statically analyses a file for page options
|
||||
* @param {string} filepath
|
||||
* @returns {PageOptions | null} Returns the page options for the file or `null` if unanalysable
|
||||
*/
|
||||
export function get_page_options(filepath) {
|
||||
try {
|
||||
const input = read(filepath);
|
||||
const page_options = statically_analyse_page_options(filepath, input);
|
||||
if (page_options === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return page_options;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function create_node_analyser() {
|
||||
const static_exports = new Map();
|
||||
|
||||
/**
|
||||
* @param {string | undefined} key
|
||||
* @param {PageOptions | null} page_options
|
||||
*/
|
||||
const cache = (key, page_options) => {
|
||||
if (key) static_exports.set(key, { page_options, children: [] });
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the final page options (may include load function as `load: null`; special case) for a node (if possible). Otherwise, returns `null`.
|
||||
* @param {import('types').PageNode} node
|
||||
* @returns {PageOptions | null}
|
||||
*/
|
||||
const crawl = (node) => {
|
||||
const key = node.universal || node.server;
|
||||
if (key && static_exports.has(key)) {
|
||||
return { ...static_exports.get(key)?.page_options };
|
||||
}
|
||||
|
||||
/** @type {PageOptions} */
|
||||
let page_options = {};
|
||||
|
||||
if (node.parent) {
|
||||
const parent_options = crawl(node.parent);
|
||||
|
||||
const parent_key = node.parent.universal || node.parent.server;
|
||||
if (key && parent_key) {
|
||||
static_exports.get(parent_key)?.children.push(key);
|
||||
}
|
||||
|
||||
if (parent_options === null) {
|
||||
// if the parent cannot be analysed, we can't know what page options
|
||||
// the child node inherits, so we also mark it as unanalysable
|
||||
cache(key, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
page_options = { ...parent_options };
|
||||
}
|
||||
|
||||
if (node.server) {
|
||||
const server_page_options = get_page_options(node.server);
|
||||
if (server_page_options === null) {
|
||||
cache(key, null);
|
||||
return null;
|
||||
}
|
||||
page_options = { ...page_options, ...server_page_options };
|
||||
}
|
||||
|
||||
if (node.universal) {
|
||||
const universal_page_options = get_page_options(node.universal);
|
||||
if (universal_page_options === null) {
|
||||
cache(key, null);
|
||||
return null;
|
||||
}
|
||||
page_options = { ...page_options, ...universal_page_options };
|
||||
}
|
||||
|
||||
cache(key, page_options);
|
||||
|
||||
return page_options;
|
||||
};
|
||||
|
||||
return {
|
||||
get_page_options: crawl
|
||||
};
|
||||
}
|
||||
118
frontend/node_modules/@sveltejs/kit/src/exports/vite/static_analysis/utils.js
generated
vendored
Normal file
118
frontend/node_modules/@sveltejs/kit/src/exports/vite/static_analysis/utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* Check if content has children rendering (slot, @render, or children prop forwarding)
|
||||
* @param {string} content - The markup content
|
||||
* @param {boolean} is_svelte_5_plus - Whether the project uses Svelte 5+
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function has_children(content, is_svelte_5_plus) {
|
||||
return (
|
||||
content.includes('<slot') ||
|
||||
(is_svelte_5_plus &&
|
||||
(content.includes('{@render') ||
|
||||
// children may be forwarded to a child component as a prop
|
||||
content.includes('{children}') ||
|
||||
content.includes('children={')))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a match position is within a comment or a string
|
||||
* @param {string} content - The full content
|
||||
* @param {number} match_index - The index where the match starts
|
||||
* @returns {boolean} - True if the match is within a comment
|
||||
*/
|
||||
export function should_ignore(content, match_index) {
|
||||
// Track if we're inside different types of quotes and comments
|
||||
let in_single_quote = false;
|
||||
let in_double_quote = false;
|
||||
let in_template_literal = false;
|
||||
let in_single_line_comment = false;
|
||||
let in_multi_line_comment = false;
|
||||
let in_html_comment = false;
|
||||
|
||||
for (let i = 0; i < match_index; i++) {
|
||||
const char = content[i];
|
||||
const next_two = content.slice(i, i + 2);
|
||||
const next_four = content.slice(i, i + 4);
|
||||
|
||||
// Handle end of single line comment
|
||||
if (in_single_line_comment && char === '\n') {
|
||||
in_single_line_comment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle end of multi-line comment
|
||||
if (in_multi_line_comment && next_two === '*/') {
|
||||
in_multi_line_comment = false;
|
||||
i++; // Skip the '/' part
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle end of HTML comment
|
||||
if (in_html_comment && content.slice(i, i + 3) === '-->') {
|
||||
in_html_comment = false;
|
||||
i += 2; // Skip the '-->' part
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're in any comment, skip processing
|
||||
if (in_single_line_comment || in_multi_line_comment || in_html_comment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escape sequences in strings
|
||||
if ((in_single_quote || in_double_quote || in_template_literal) && char === '\\') {
|
||||
i++; // Skip the escaped character
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle string boundaries
|
||||
if (!in_double_quote && !in_template_literal && char === "'") {
|
||||
in_single_quote = !in_single_quote;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_single_quote && !in_template_literal && char === '"') {
|
||||
in_double_quote = !in_double_quote;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_single_quote && !in_double_quote && char === '`') {
|
||||
in_template_literal = !in_template_literal;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're inside any string, don't process comment delimiters
|
||||
if (in_single_quote || in_double_quote || in_template_literal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for comment starts
|
||||
if (next_two === '//') {
|
||||
in_single_line_comment = true;
|
||||
i++; // Skip the second '/'
|
||||
continue;
|
||||
}
|
||||
|
||||
if (next_two === '/*') {
|
||||
in_multi_line_comment = true;
|
||||
i++; // Skip the '*'
|
||||
continue;
|
||||
}
|
||||
|
||||
if (next_four === '<!--') {
|
||||
in_html_comment = true;
|
||||
i += 3; // Skip the '<!--'
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
in_single_line_comment ||
|
||||
in_multi_line_comment ||
|
||||
in_html_comment ||
|
||||
in_single_quote ||
|
||||
in_double_quote ||
|
||||
in_template_literal
|
||||
);
|
||||
}
|
||||
3
frontend/node_modules/@sveltejs/kit/src/exports/vite/types.d.ts
generated
vendored
Normal file
3
frontend/node_modules/@sveltejs/kit/src/exports/vite/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export interface EnforcedConfig {
|
||||
[key: string]: EnforcedConfig | true;
|
||||
}
|
||||
216
frontend/node_modules/@sveltejs/kit/src/exports/vite/utils.js
generated
vendored
Normal file
216
frontend/node_modules/@sveltejs/kit/src/exports/vite/utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import path from 'node:path';
|
||||
import { loadEnv } from 'vite';
|
||||
import { posixify } from '../../utils/filesystem.js';
|
||||
import { negotiate } from '../../utils/http.js';
|
||||
import { filter_env } from '../../utils/env.js';
|
||||
import { escape_html } from '../../utils/escape.js';
|
||||
import { dedent } from '../../core/sync/utils.js';
|
||||
import {
|
||||
app_server,
|
||||
env_dynamic_private,
|
||||
env_dynamic_public,
|
||||
env_static_private,
|
||||
env_static_public,
|
||||
service_worker
|
||||
} from './module_ids.js';
|
||||
|
||||
/**
|
||||
* Transforms kit.alias to a valid vite.resolve.alias array.
|
||||
*
|
||||
* Related to tsconfig path alias creation.
|
||||
*
|
||||
* @param {import('types').ValidatedKitConfig} config
|
||||
* */
|
||||
export function get_config_aliases(config) {
|
||||
/** @type {import('vite').Alias[]} */
|
||||
const alias = [
|
||||
// For now, we handle `$lib` specially here rather than make it a default value for
|
||||
// `config.kit.alias` since it has special meaning for packaging, etc.
|
||||
{ find: '$lib', replacement: config.files.lib }
|
||||
];
|
||||
|
||||
for (let [key, value] of Object.entries(config.alias)) {
|
||||
value = posixify(value);
|
||||
if (value.endsWith('/*')) {
|
||||
value = value.slice(0, -2);
|
||||
}
|
||||
if (key.endsWith('/*')) {
|
||||
// Doing just `{ find: key.slice(0, -2) ,..}` would mean `import .. from "key"` would also be matched, which we don't want
|
||||
alias.push({
|
||||
find: new RegExp(`^${escape_for_regexp(key.slice(0, -2))}\\/(.+)$`),
|
||||
replacement: `${path.resolve(value)}/$1`
|
||||
});
|
||||
} else if (key + '/*' in config.alias) {
|
||||
// key and key/* both exist -> the replacement for key needs to happen _only_ on import .. from "key"
|
||||
alias.push({
|
||||
find: new RegExp(`^${escape_for_regexp(key)}$`),
|
||||
replacement: path.resolve(value)
|
||||
});
|
||||
} else {
|
||||
alias.push({ find: key, replacement: path.resolve(value) });
|
||||
}
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
*/
|
||||
function escape_for_regexp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load environment variables from process.env and .env files
|
||||
* @param {import('types').ValidatedKitConfig['env']} env_config
|
||||
* @param {string} mode
|
||||
*/
|
||||
export function get_env(env_config, mode) {
|
||||
const { publicPrefix: public_prefix, privatePrefix: private_prefix } = env_config;
|
||||
const env = loadEnv(mode, env_config.dir, '');
|
||||
|
||||
return {
|
||||
public: filter_env(env, public_prefix, private_prefix),
|
||||
private: filter_env(env, private_prefix, public_prefix)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('http').IncomingMessage} req
|
||||
* @param {import('http').ServerResponse} res
|
||||
* @param {string} base
|
||||
*/
|
||||
export function not_found(req, res, base) {
|
||||
const type = negotiate(req.headers.accept ?? '*', ['text/plain', 'text/html']);
|
||||
|
||||
// special case — handle `/` request automatically
|
||||
if (req.url === '/' && type === 'text/html') {
|
||||
res.statusCode = 307;
|
||||
res.setHeader('location', base);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
res.statusCode = 404;
|
||||
|
||||
const prefixed = base + req.url;
|
||||
|
||||
if (type === 'text/html') {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(
|
||||
`The server is configured with a public base URL of ${escape_html(
|
||||
base
|
||||
)} - did you mean to visit <a href="${escape_html(prefixed, true)}">${escape_html(
|
||||
prefixed
|
||||
)}</a> instead?`
|
||||
);
|
||||
} else {
|
||||
res.end(
|
||||
`The server is configured with a public base URL of ${escape_html(
|
||||
base
|
||||
)} - did you mean to visit ${escape_html(prefixed)} instead?`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const query_pattern = /\?.*$/s;
|
||||
|
||||
/**
|
||||
* Removes cwd/lib path from the start of the id
|
||||
* @param {string} id
|
||||
* @param {string} lib
|
||||
* @param {string} cwd
|
||||
*/
|
||||
export function normalize_id(id, lib, cwd) {
|
||||
id = id.replace(query_pattern, '');
|
||||
|
||||
if (id.startsWith(lib)) {
|
||||
id = id.replace(lib, '$lib');
|
||||
}
|
||||
|
||||
if (id.startsWith(cwd)) {
|
||||
id = path.relative(cwd, id);
|
||||
}
|
||||
|
||||
if (id === app_server) {
|
||||
return '$app/server';
|
||||
}
|
||||
|
||||
if (id === env_static_private) {
|
||||
return '$env/static/private';
|
||||
}
|
||||
|
||||
if (id === env_static_public) {
|
||||
return '$env/static/public';
|
||||
}
|
||||
|
||||
if (id === env_dynamic_private) {
|
||||
return '$env/dynamic/private';
|
||||
}
|
||||
|
||||
if (id === env_dynamic_public) {
|
||||
return '$env/dynamic/public';
|
||||
}
|
||||
|
||||
if (id === service_worker) {
|
||||
return '$service-worker';
|
||||
}
|
||||
|
||||
return posixify(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* For times when you need to throw an error, but without
|
||||
* displaying a useless stack trace (since the developer
|
||||
* can't do anything useful with it)
|
||||
* @param {string} message
|
||||
*/
|
||||
export function stackless(message) {
|
||||
const error = new Error(message);
|
||||
error.stack = '';
|
||||
return error;
|
||||
}
|
||||
|
||||
export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
|
||||
|
||||
/**
|
||||
* For `error_for_missing_config('instrumentation.server.js', 'kit.experimental.instrumentation.server', true)`,
|
||||
* returns:
|
||||
*
|
||||
* ```
|
||||
* To enable `instrumentation.server.js`, add the following to your `svelte.config.js`:
|
||||
*
|
||||
*\`\`\`js
|
||||
* kit:
|
||||
* experimental:
|
||||
* instrumentation:
|
||||
* server: true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*\`\`\`
|
||||
*```
|
||||
* @param {string} feature_name
|
||||
* @param {string} path
|
||||
* @param {string} value
|
||||
* @returns {never}
|
||||
*/
|
||||
export function error_for_missing_config(feature_name, path, value) {
|
||||
const hole = '__HOLE__';
|
||||
|
||||
const result = path.split('.').reduce((acc, part, i, parts) => {
|
||||
const indent = ' '.repeat(i);
|
||||
const rhs = i === parts.length - 1 ? value : `{\n${hole}\n${indent}}`;
|
||||
|
||||
return acc.replace(hole, `${indent}${part}: ${rhs}`);
|
||||
}, hole);
|
||||
|
||||
throw stackless(
|
||||
dedent`\
|
||||
To enable ${feature_name}, add the following to your \`svelte.config.js\`:
|
||||
|
||||
${result}
|
||||
`
|
||||
);
|
||||
}
|
||||
2
frontend/node_modules/@sveltejs/kit/src/runtime/app/environment/index.js
generated
vendored
Normal file
2
frontend/node_modules/@sveltejs/kit/src/runtime/app/environment/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export { BROWSER as browser, DEV as dev } from 'esm-env';
|
||||
export { building, version } from '__sveltekit/environment';
|
||||
19
frontend/node_modules/@sveltejs/kit/src/runtime/app/environment/types.d.ts
generated
vendored
Normal file
19
frontend/node_modules/@sveltejs/kit/src/runtime/app/environment/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* `true` if the app is running in the browser.
|
||||
*/
|
||||
export const browser: boolean;
|
||||
|
||||
/**
|
||||
* Whether the dev server is running. This is not guaranteed to correspond to `NODE_ENV` or `MODE`.
|
||||
*/
|
||||
export const dev: boolean;
|
||||
|
||||
/**
|
||||
* SvelteKit analyses your app during the `build` step by running it. During this process, `building` is `true`. This also applies during prerendering.
|
||||
*/
|
||||
export const building: boolean;
|
||||
|
||||
/**
|
||||
* The value of `config.kit.version.name`.
|
||||
*/
|
||||
export const version: string;
|
||||
231
frontend/node_modules/@sveltejs/kit/src/runtime/app/forms.js
generated
vendored
Normal file
231
frontend/node_modules/@sveltejs/kit/src/runtime/app/forms.js
generated
vendored
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
import * as devalue from 'devalue';
|
||||
import { BROWSER, DEV } from 'esm-env';
|
||||
import { invalidateAll } from './navigation.js';
|
||||
import { app as client_app, applyAction } from '../client/client.js';
|
||||
import { app as server_app } from '../server/app.js';
|
||||
|
||||
export { applyAction };
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const parsed = JSON.parse(result);
|
||||
|
||||
if (parsed.data) {
|
||||
// the decoders should never be initialised at the top-level because `app`
|
||||
// will not be initialised yet if `kit.output.bundleStrategy` is 'single' or 'inline'
|
||||
parsed.data = devalue.parse(parsed.data, BROWSER ? client_app.decoders : server_app.decoders);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 on the 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.
|
||||
* It accepts an options object
|
||||
* - `reset: false` if you don't want the `<form>` values to be reset after a successful submission
|
||||
* - `invalidateAll: false` if you don't want the action to call `invalidateAll` after submission
|
||||
* @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"');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* action: URL;
|
||||
* invalidateAll?: boolean;
|
||||
* result: import('@sveltejs/kit').ActionResult;
|
||||
* reset?: boolean
|
||||
* }} opts
|
||||
*/
|
||||
const fallback_callback = async ({
|
||||
action,
|
||||
result,
|
||||
reset = true,
|
||||
invalidateAll: shouldInvalidateAll = true
|
||||
}) => {
|
||||
if (result.type === 'success') {
|
||||
if (reset) {
|
||||
// We call reset from the prototype to avoid DOM clobbering
|
||||
HTMLFormElement.prototype.reset.call(form_element);
|
||||
}
|
||||
if (shouldInvalidateAll) {
|
||||
await invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
// For success/failure results, only apply action if it belongs to the
|
||||
// current page, otherwise `form` will be updated erroneously
|
||||
if (
|
||||
location.origin + location.pathname === action.origin + action.pathname ||
|
||||
result.type === 'redirect' ||
|
||||
result.type === 'error'
|
||||
) {
|
||||
await applyAction(result);
|
||||
}
|
||||
};
|
||||
|
||||
/** @param {SubmitEvent} event */
|
||||
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();
|
||||
|
||||
const action = new URL(
|
||||
// We can't do submitter.formAction directly because that property is always set
|
||||
event.submitter?.hasAttribute('formaction')
|
||||
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
|
||||
: clone(form_element).action
|
||||
);
|
||||
|
||||
const enctype = event.submitter?.hasAttribute('formenctype')
|
||||
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formEnctype
|
||||
: clone(form_element).enctype;
|
||||
|
||||
const form_data = new FormData(form_element, event.submitter);
|
||||
|
||||
if (DEV && enctype !== 'multipart/form-data') {
|
||||
for (const value of form_data.values()) {
|
||||
if (value instanceof File) {
|
||||
throw new Error(
|
||||
'Your form contains <input type="file"> fields, but is missing the necessary `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.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
let cancelled = false;
|
||||
const cancel = () => (cancelled = true);
|
||||
|
||||
const callback =
|
||||
(await submit({
|
||||
action,
|
||||
cancel,
|
||||
controller,
|
||||
formData: form_data,
|
||||
formElement: form_element,
|
||||
submitter: event.submitter
|
||||
})) ?? fallback_callback;
|
||||
if (cancelled) return;
|
||||
|
||||
/** @type {import('@sveltejs/kit').ActionResult} */
|
||||
let result;
|
||||
|
||||
try {
|
||||
const headers = new Headers({
|
||||
accept: 'application/json',
|
||||
'x-sveltekit-action': 'true'
|
||||
});
|
||||
|
||||
// do not explicitly set the `Content-Type` header when sending `FormData`
|
||||
// or else it will interfere with the browser's header setting
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects#sect4
|
||||
if (enctype !== 'multipart/form-data') {
|
||||
headers.set(
|
||||
'Content-Type',
|
||||
/^(:?application\/x-www-form-urlencoded|text\/plain)$/.test(enctype)
|
||||
? enctype
|
||||
: 'application/x-www-form-urlencoded'
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error `URLSearchParams(form_data)` is kosher, but typescript doesn't know that
|
||||
const body = enctype === 'multipart/form-data' ? form_data : new URLSearchParams(form_data);
|
||||
|
||||
const response = await fetch(action, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
cache: 'no-store',
|
||||
body,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
result = deserialize(await response.text());
|
||||
if (result.type === 'error') result.status = response.status;
|
||||
} catch (error) {
|
||||
if (/** @type {any} */ (error)?.name === 'AbortError') return;
|
||||
result = { type: 'error', error };
|
||||
}
|
||||
|
||||
await callback({
|
||||
action,
|
||||
formData: form_data,
|
||||
formElement: form_element,
|
||||
update: (opts) =>
|
||||
fallback_callback({
|
||||
action,
|
||||
result,
|
||||
reset: opts?.reset,
|
||||
invalidateAll: opts?.invalidateAll
|
||||
}),
|
||||
// @ts-expect-error generic constraints stuff we don't care about
|
||||
result
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
HTMLFormElement.prototype.addEventListener.call(form_element, 'submit', handle_submit);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
// @ts-expect-error
|
||||
HTMLFormElement.prototype.removeEventListener.call(form_element, 'submit', handle_submit);
|
||||
}
|
||||
};
|
||||
}
|
||||
14
frontend/node_modules/@sveltejs/kit/src/runtime/app/navigation.js
generated
vendored
Normal file
14
frontend/node_modules/@sveltejs/kit/src/runtime/app/navigation.js
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export {
|
||||
afterNavigate,
|
||||
beforeNavigate,
|
||||
disableScrollHandling,
|
||||
goto,
|
||||
invalidate,
|
||||
invalidateAll,
|
||||
refreshAll,
|
||||
onNavigate,
|
||||
preloadCode,
|
||||
preloadData,
|
||||
pushState,
|
||||
replaceState
|
||||
} from '../client/client.js';
|
||||
99
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/client.js
generated
vendored
Normal file
99
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/client.js
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/** @import { Asset, RouteId, RouteIdWithSearchOrHash, Pathname, PathnameWithSearchOrHash, ResolvedPathname } from '$app/types' */
|
||||
/** @import { ResolveArgs } from './types.js' */
|
||||
import { base, assets, hash_routing } from './internal/client.js';
|
||||
import { resolve_route } from '../../../utils/routing.js';
|
||||
import { get_navigation_intent } from '../../client/client.js';
|
||||
|
||||
/**
|
||||
* Resolve the URL of an asset in your `static` directory, by prefixing it with [`config.kit.paths.assets`](https://svelte.dev/docs/kit/configuration#paths) if configured, or otherwise by prefixing it with the base path.
|
||||
*
|
||||
* During server rendering, the base path is relative and depends on the page currently being rendered.
|
||||
*
|
||||
* @example
|
||||
* ```svelte
|
||||
* <script>
|
||||
* import { asset } from '$app/paths';
|
||||
* </script>
|
||||
*
|
||||
* <img alt="a potato" src={asset('/potato.jpg')} />
|
||||
* ```
|
||||
* @since 2.26
|
||||
*
|
||||
* @param {Asset} file
|
||||
* @returns {string}
|
||||
*/
|
||||
export function asset(file) {
|
||||
return (assets || base) + file;
|
||||
}
|
||||
|
||||
const pathname_prefix = hash_routing ? '#' : '';
|
||||
|
||||
/**
|
||||
* Resolve a pathname by prefixing it with the base path, if any, or resolve a route ID by populating dynamic segments with parameters.
|
||||
*
|
||||
* During server rendering, the base path is relative and depends on the page currently being rendered.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* import { resolve } from '$app/paths';
|
||||
*
|
||||
* // using a pathname
|
||||
* const resolved = resolve(`/blog/hello-world`);
|
||||
*
|
||||
* // using a route ID plus parameters
|
||||
* const resolved = resolve('/blog/[slug]', {
|
||||
* slug: 'hello-world'
|
||||
* });
|
||||
* ```
|
||||
* @since 2.26
|
||||
*
|
||||
* @template {RouteIdWithSearchOrHash | PathnameWithSearchOrHash} T
|
||||
* @param {ResolveArgs<T>} args
|
||||
* @returns {ResolvedPathname}
|
||||
*/
|
||||
export function resolve(...args) {
|
||||
// The type error is correct here, and if someone doesn't pass params when they should there's a runtime error,
|
||||
// but we don't want to adjust the internal resolve_route function to accept `undefined`, hence the type cast.
|
||||
return (
|
||||
base + pathname_prefix + resolve_route(args[0], /** @type {Record<string, string>} */ (args[1]))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a path or URL to a route ID and extracts any parameters.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* import { match } from '$app/paths';
|
||||
*
|
||||
* const route = await match('/blog/hello-world');
|
||||
*
|
||||
* if (route?.id === '/blog/[slug]') {
|
||||
* const slug = route.params.slug;
|
||||
* const response = await fetch(`/api/posts/${slug}`);
|
||||
* const post = await response.json();
|
||||
* }
|
||||
* ```
|
||||
* @since 2.52.0
|
||||
*
|
||||
* @param {Pathname | URL | (string & {})} url
|
||||
* @returns {Promise<{ id: RouteId, params: Record<string, string> } | null>}
|
||||
*/
|
||||
export async function match(url) {
|
||||
if (typeof url === 'string') {
|
||||
url = new URL(url, location.href);
|
||||
}
|
||||
|
||||
const intent = await get_navigation_intent(url, false);
|
||||
|
||||
if (intent) {
|
||||
return {
|
||||
id: /** @type {RouteId} */ (intent.route.id),
|
||||
params: intent.params
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export { base, assets, resolve as resolveRoute };
|
||||
1
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/index.js
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from '#app/paths';
|
||||
4
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/internal/client.js
generated
vendored
Normal file
4
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/internal/client.js
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const base = __SVELTEKIT_PAYLOAD__?.base ?? __SVELTEKIT_PATHS_BASE__;
|
||||
export const assets = __SVELTEKIT_PAYLOAD__?.assets ?? base ?? __SVELTEKIT_PATHS_ASSETS__;
|
||||
export const app_dir = __SVELTEKIT_APP_DIR__;
|
||||
export const hash_routing = __SVELTEKIT_HASH_ROUTING__;
|
||||
30
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/internal/server.js
generated
vendored
Normal file
30
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/internal/server.js
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export let base = __SVELTEKIT_PATHS_BASE__;
|
||||
export let assets = __SVELTEKIT_PATHS_ASSETS__ || base;
|
||||
export const app_dir = __SVELTEKIT_APP_DIR__;
|
||||
export const relative = __SVELTEKIT_PATHS_RELATIVE__;
|
||||
|
||||
const initial = { base, assets };
|
||||
|
||||
/**
|
||||
* `base` could be overridden during rendering to be relative;
|
||||
* this one's the original non-relative base path
|
||||
*/
|
||||
export const initial_base = initial.base;
|
||||
|
||||
/**
|
||||
* @param {{ base: string, assets: string }} paths
|
||||
*/
|
||||
export function override(paths) {
|
||||
base = paths.base;
|
||||
assets = paths.assets;
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
base = initial.base;
|
||||
assets = initial.assets;
|
||||
}
|
||||
|
||||
/** @param {string} path */
|
||||
export function set_assets(path) {
|
||||
assets = initial.assets = path;
|
||||
}
|
||||
29
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/public.d.ts
generated
vendored
Normal file
29
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/public.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { RouteIdWithSearchOrHash, PathnameWithSearchOrHash, ResolvedPathname } from '$app/types';
|
||||
import { ResolveArgs } from './types.js';
|
||||
|
||||
export { resolve, asset, match } from './client.js';
|
||||
|
||||
/**
|
||||
* A string that matches [`config.kit.paths.base`](https://svelte.dev/docs/kit/configuration#paths).
|
||||
*
|
||||
* Example usage: `<a href="{base}/your-page">Link</a>`
|
||||
*
|
||||
* @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
|
||||
*/
|
||||
export let base: '' | `/${string}`;
|
||||
|
||||
/**
|
||||
* An absolute path that matches [`config.kit.paths.assets`](https://svelte.dev/docs/kit/configuration#paths).
|
||||
*
|
||||
* > [!NOTE] If a value for `config.kit.paths.assets` is specified, it will be replaced with `'/_svelte_kit_assets'` during `vite dev` or `vite preview`, since the assets don't yet live at their eventual URL.
|
||||
*
|
||||
* @deprecated Use [`asset(...)`](https://svelte.dev/docs/kit/$app-paths#asset) instead
|
||||
*/
|
||||
export let assets: '' | `https://${string}` | `http://${string}` | '/_svelte_kit_assets';
|
||||
|
||||
/**
|
||||
* @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
|
||||
*/
|
||||
export function resolveRoute<T extends RouteIdWithSearchOrHash | PathnameWithSearchOrHash>(
|
||||
...args: ResolveArgs<T>
|
||||
): ResolvedPathname;
|
||||
71
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/server.js
generated
vendored
Normal file
71
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/server.js
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { base, assets, relative, initial_base } from './internal/server.js';
|
||||
import { resolve_route, find_route } from '../../../utils/routing.js';
|
||||
import { decode_pathname } from '../../../utils/url.js';
|
||||
import { try_get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { manifest } from '__sveltekit/server';
|
||||
import { get_hooks } from '__SERVER__/internal.js';
|
||||
|
||||
/** @type {import('./client.js').asset} */
|
||||
export function asset(file) {
|
||||
// @ts-expect-error we use the `resolve` mechanism, but with the 'wrong' input
|
||||
return assets ? assets + file : resolve(file);
|
||||
}
|
||||
|
||||
/** @type {import('./client.js').resolve} */
|
||||
export function resolve(id, params) {
|
||||
const resolved = resolve_route(id, /** @type {Record<string, string>} */ (params));
|
||||
|
||||
if (relative) {
|
||||
const store = try_get_request_store();
|
||||
|
||||
if (store && !store.state.prerendering?.fallback) {
|
||||
const after_base = store.event.url.pathname.slice(initial_base.length);
|
||||
const segments = after_base.split('/').slice(2);
|
||||
const prefix = segments.map(() => '..').join('/') || '.';
|
||||
|
||||
return prefix + resolved;
|
||||
}
|
||||
}
|
||||
|
||||
return base + resolved;
|
||||
}
|
||||
|
||||
/** @type {import('./client.js').match} */
|
||||
export async function match(url) {
|
||||
const store = try_get_request_store();
|
||||
|
||||
if (typeof url === 'string') {
|
||||
const origin = store?.event.url.origin ?? 'a://a';
|
||||
url = new URL(url, origin);
|
||||
}
|
||||
|
||||
const { reroute } = await get_hooks();
|
||||
|
||||
let resolved_path;
|
||||
|
||||
try {
|
||||
resolved_path = decode_pathname(
|
||||
(await reroute?.({ url: new URL(url), fetch: store?.event.fetch ?? fetch })) ?? url.pathname
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (base && resolved_path.startsWith(base)) {
|
||||
resolved_path = resolved_path.slice(base.length) || '/';
|
||||
}
|
||||
|
||||
const matchers = await manifest._.matchers();
|
||||
const result = find_route(resolved_path, manifest._.routes, matchers);
|
||||
|
||||
if (result) {
|
||||
return {
|
||||
id: /** @type {import('$app/types').RouteId} */ (result.route.id),
|
||||
params: result.params
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export { base, assets, resolve as resolveRoute };
|
||||
23
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/types.d.ts
generated
vendored
Normal file
23
frontend/node_modules/@sveltejs/kit/src/runtime/app/paths/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
PathnameWithSearchOrHash,
|
||||
RouteId,
|
||||
RouteIdWithSearchOrHash,
|
||||
RouteParams
|
||||
} from '$app/types';
|
||||
|
||||
type StripSearchOrHash<T extends string> = T extends `${infer Pathname}?${string}`
|
||||
? Pathname
|
||||
: T extends `${infer Pathname}#${string}`
|
||||
? Pathname
|
||||
: T;
|
||||
|
||||
export type ResolveArgs<T extends RouteIdWithSearchOrHash | PathnameWithSearchOrHash> =
|
||||
T extends RouteId
|
||||
? RouteParams<T> extends Record<string, never>
|
||||
? [route: T]
|
||||
: [route: T, params: RouteParams<T>]
|
||||
: StripSearchOrHash<T> extends infer U extends RouteId
|
||||
? RouteParams<U> extends Record<string, never>
|
||||
? [route: T]
|
||||
: [route: T, params: RouteParams<U>]
|
||||
: [route: T];
|
||||
78
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/index.js
generated
vendored
Normal file
78
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { read_implementation, manifest } from '__sveltekit/server';
|
||||
import { base } from '$app/paths';
|
||||
import { DEV } from 'esm-env';
|
||||
import { base64_decode } from '../../utils.js';
|
||||
|
||||
/**
|
||||
* Read the contents of an imported asset from the filesystem
|
||||
* @example
|
||||
* ```js
|
||||
* import { read } from '$app/server';
|
||||
* import somefile from './somefile.txt';
|
||||
*
|
||||
* const asset = read(somefile);
|
||||
* const text = await asset.text();
|
||||
* ```
|
||||
* @param {string} asset
|
||||
* @returns {Response}
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export function read(asset) {
|
||||
__SVELTEKIT_TRACK__('$app/server:read');
|
||||
|
||||
if (!read_implementation) {
|
||||
throw new Error(
|
||||
'No `read` implementation was provided. Please ensure that your adapter is up to date and supports this feature'
|
||||
);
|
||||
}
|
||||
|
||||
// handle inline assets internally
|
||||
const match = /^data:([^;,]+)?(;base64)?,/.exec(asset);
|
||||
if (match) {
|
||||
const type = match[1] ?? 'application/octet-stream';
|
||||
const data = asset.slice(match[0].length);
|
||||
|
||||
if (match[2] !== undefined) {
|
||||
const decoded = base64_decode(data);
|
||||
|
||||
// @ts-ignore passing a Uint8Array to `new Response(...)` is fine
|
||||
return new Response(decoded, {
|
||||
headers: {
|
||||
'Content-Length': decoded.byteLength.toString(),
|
||||
'Content-Type': type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const decoded = decodeURIComponent(data);
|
||||
|
||||
return new Response(decoded, {
|
||||
headers: {
|
||||
'Content-Length': decoded.length.toString(),
|
||||
'Content-Type': type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const file = decodeURIComponent(
|
||||
DEV && asset.startsWith('/@fs') ? asset : asset.slice(base.length + 1)
|
||||
);
|
||||
|
||||
if (file in manifest._.server_assets) {
|
||||
const length = manifest._.server_assets[file];
|
||||
const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))];
|
||||
|
||||
return new Response(read_implementation(file), {
|
||||
headers: {
|
||||
'Content-Length': '' + length,
|
||||
'Content-Type': type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error(`Asset does not exist: ${file}`);
|
||||
}
|
||||
|
||||
export { getRequestEvent } from '@sveltejs/kit/internal/server';
|
||||
|
||||
export { query, prerender, command, form } from './remote/index.js';
|
||||
97
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/command.js
generated
vendored
Normal file
97
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/command.js
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/** @import { RemoteCommand } from '@sveltejs/kit' */
|
||||
/** @import { RemoteInfo, MaybePromise } from 'types' */
|
||||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
import { get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { create_validator, run_remote_function } from './shared.js';
|
||||
import { MUTATIVE_METHODS } from '../../../../constants.js';
|
||||
|
||||
/**
|
||||
* Creates a remote command. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
|
||||
*
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {() => Output} fn
|
||||
* @returns {RemoteCommand<void, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote command. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
|
||||
*
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {'unchecked'} validate
|
||||
* @param {(arg: Input) => Output} fn
|
||||
* @returns {RemoteCommand<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote command. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
|
||||
*
|
||||
* @template {StandardSchemaV1} Schema
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {Schema} validate
|
||||
* @param {(arg: StandardSchemaV1.InferOutput<Schema>) => Output} fn
|
||||
* @returns {RemoteCommand<StandardSchemaV1.InferInput<Schema>, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @param {any} validate_or_fn
|
||||
* @param {(arg?: Input) => Output} [maybe_fn]
|
||||
* @returns {RemoteCommand<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
export function command(validate_or_fn, maybe_fn) {
|
||||
/** @type {(arg?: Input) => Output} */
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
|
||||
/** @type {(arg?: any) => MaybePromise<Input>} */
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
|
||||
/** @type {RemoteInfo} */
|
||||
const __ = { type: 'command', id: '', name: '' };
|
||||
|
||||
/** @type {RemoteCommand<Input, Output> & { __: RemoteInfo }} */
|
||||
const wrapper = (arg) => {
|
||||
const { event, state } = get_request_store();
|
||||
|
||||
if (!state.allows_commands) {
|
||||
const disallowed_method = !MUTATIVE_METHODS.includes(event.request.method);
|
||||
throw new Error(
|
||||
`Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) ${disallowed_method ? `from a ${event.request.method} handler or ` : ''}during server-side rendering`
|
||||
);
|
||||
}
|
||||
|
||||
state.refreshes ??= {};
|
||||
|
||||
const promise = Promise.resolve(
|
||||
run_remote_function(event, state, true, () => validate(arg), fn)
|
||||
);
|
||||
|
||||
// @ts-expect-error
|
||||
promise.updates = () => {
|
||||
throw new Error(`Cannot call '${__.name}(...).updates(...)' on the server`);
|
||||
};
|
||||
|
||||
return /** @type {ReturnType<RemoteCommand<Input, Output>>} */ (promise);
|
||||
};
|
||||
|
||||
Object.defineProperty(wrapper, '__', { value: __ });
|
||||
|
||||
// On the server, pending is always 0
|
||||
Object.defineProperty(wrapper, 'pending', {
|
||||
get: () => 0
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
373
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/form.js
generated
vendored
Normal file
373
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/form.js
generated
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/** @import { RemoteFormInput, RemoteForm, InvalidField } from '@sveltejs/kit' */
|
||||
/** @import { InternalRemoteFormIssue, MaybePromise, RemoteInfo } from 'types' */
|
||||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
import { get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { DEV } from 'esm-env';
|
||||
import {
|
||||
create_field_proxy,
|
||||
set_nested_value,
|
||||
throw_on_old_property_access,
|
||||
deep_set,
|
||||
normalize_issue,
|
||||
flatten_issues
|
||||
} from '../../../form-utils.js';
|
||||
import { get_cache, run_remote_function } from './shared.js';
|
||||
import { ValidationError } from '@sveltejs/kit/internal';
|
||||
|
||||
/**
|
||||
* Creates a form object that can be spread onto a `<form>` element.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
|
||||
*
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {() => MaybePromise<Output>} fn
|
||||
* @returns {RemoteForm<void, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a form object that can be spread onto a `<form>` element.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
|
||||
*
|
||||
* @template {RemoteFormInput} Input
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {'unchecked'} validate
|
||||
* @param {(data: Input, issue: InvalidField<Input>) => MaybePromise<Output>} fn
|
||||
* @returns {RemoteForm<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a form object that can be spread onto a `<form>` element.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
|
||||
*
|
||||
* @template {StandardSchemaV1<RemoteFormInput, Record<string, any>>} Schema
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {Schema} validate
|
||||
* @param {(data: StandardSchemaV1.InferOutput<Schema>, issue: InvalidField<StandardSchemaV1.InferInput<Schema>>) => MaybePromise<Output>} fn
|
||||
* @returns {RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* @template {RemoteFormInput} Input
|
||||
* @template Output
|
||||
* @param {any} validate_or_fn
|
||||
* @param {(data_or_issue: any, issue?: any) => MaybePromise<Output>} [maybe_fn]
|
||||
* @returns {RemoteForm<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
// @ts-ignore we don't want to prefix `fn` with an underscore, as that will be user-visible
|
||||
export function form(validate_or_fn, maybe_fn) {
|
||||
/** @type {any} */
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
|
||||
/** @type {StandardSchemaV1 | null} */
|
||||
const schema =
|
||||
!maybe_fn || validate_or_fn === 'unchecked' ? null : /** @type {any} */ (validate_or_fn);
|
||||
|
||||
/**
|
||||
* @param {string | number | boolean} [key]
|
||||
*/
|
||||
function create_instance(key) {
|
||||
/** @type {RemoteForm<Input, Output>} */
|
||||
const instance = {};
|
||||
|
||||
instance.method = 'POST';
|
||||
|
||||
Object.defineProperty(instance, 'enhance', {
|
||||
value: () => {
|
||||
return { action: instance.action, method: instance.method };
|
||||
}
|
||||
});
|
||||
|
||||
/** @type {RemoteInfo} */
|
||||
const __ = {
|
||||
type: 'form',
|
||||
name: '',
|
||||
id: '',
|
||||
fn: async (data, meta, form_data) => {
|
||||
// TODO 3.0 remove this warning
|
||||
if (DEV && !data) {
|
||||
const error = () => {
|
||||
throw new Error(
|
||||
'Remote form functions no longer get passed a FormData object. ' +
|
||||
"`form` now has the same signature as `query` or `command`, i.e. it expects to be invoked like `form(schema, callback)` or `form('unchecked', callback)`. " +
|
||||
'The payload of the callback function is now a POJO instead of a FormData object. See https://kit.svelte.dev/docs/remote-functions#form for details.'
|
||||
);
|
||||
};
|
||||
data = {};
|
||||
for (const key of [
|
||||
'append',
|
||||
'delete',
|
||||
'entries',
|
||||
'forEach',
|
||||
'get',
|
||||
'getAll',
|
||||
'has',
|
||||
'keys',
|
||||
'set',
|
||||
'values'
|
||||
]) {
|
||||
Object.defineProperty(data, key, { get: error });
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {{ submission: true, input?: Record<string, any>, issues?: InternalRemoteFormIssue[], result: Output }} */
|
||||
const output = {};
|
||||
|
||||
// make it possible to differentiate between user submission and programmatic `field.set(...)` updates
|
||||
output.submission = true;
|
||||
|
||||
const { event, state } = get_request_store();
|
||||
const validated = await schema?.['~standard'].validate(data);
|
||||
|
||||
if (meta.validate_only) {
|
||||
return validated?.issues?.map((issue) => normalize_issue(issue, true)) ?? [];
|
||||
}
|
||||
|
||||
if (validated?.issues !== undefined) {
|
||||
handle_issues(output, validated.issues, form_data);
|
||||
} else {
|
||||
if (validated !== undefined) {
|
||||
data = validated.value;
|
||||
}
|
||||
|
||||
state.refreshes ??= {};
|
||||
|
||||
const issue = create_issues();
|
||||
|
||||
try {
|
||||
output.result = await run_remote_function(
|
||||
event,
|
||||
state,
|
||||
true,
|
||||
() => data,
|
||||
(data) => (!maybe_fn ? fn() : fn(data, issue))
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof ValidationError) {
|
||||
handle_issues(output, e.issues, form_data);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to care about args or deduplicating calls, because uneval results are only relevant in full page reloads
|
||||
// where only one form submission is active at the same time
|
||||
if (!event.isRemoteRequest) {
|
||||
get_cache(__, state)[''] ??= output;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(instance, '__', { value: __ });
|
||||
|
||||
Object.defineProperty(instance, 'action', {
|
||||
get: () => `?/remote=${__.id}`,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(instance, 'fields', {
|
||||
get() {
|
||||
return create_field_proxy(
|
||||
{},
|
||||
() => get_cache(__)?.['']?.input ?? {},
|
||||
(path, value) => {
|
||||
const cache = get_cache(__);
|
||||
const data = cache[''];
|
||||
|
||||
if (data?.submission) {
|
||||
// don't override a submission
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.length === 0) {
|
||||
(cache[''] ??= {}).input = value;
|
||||
return;
|
||||
}
|
||||
|
||||
const input = data?.input ?? {};
|
||||
deep_set(input, path.map(String), value);
|
||||
(cache[''] ??= {}).input = input;
|
||||
},
|
||||
() => flatten_issues(get_cache(__)?.['']?.issues ?? [])
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO 3.0 remove
|
||||
if (DEV) {
|
||||
throw_on_old_property_access(instance);
|
||||
|
||||
Object.defineProperty(instance, 'buttonProps', {
|
||||
get() {
|
||||
throw new Error(
|
||||
'`form.buttonProps` has been removed: Instead of `<button {...form.buttonProps}>, use `<button {...form.fields.action.as("submit", "value")}>`.' +
|
||||
' See the PR for more info: https://github.com/sveltejs/kit/pull/14622'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Object.defineProperty(instance, 'result', {
|
||||
get() {
|
||||
try {
|
||||
return get_cache(__)?.['']?.result;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// On the server, pending is always 0
|
||||
Object.defineProperty(instance, 'pending', {
|
||||
get: () => 0
|
||||
});
|
||||
|
||||
Object.defineProperty(instance, 'preflight', {
|
||||
// preflight is a noop on the server
|
||||
value: () => instance
|
||||
});
|
||||
|
||||
Object.defineProperty(instance, 'validate', {
|
||||
value: () => {
|
||||
throw new Error('Cannot call validate() on the server');
|
||||
}
|
||||
});
|
||||
|
||||
if (key == undefined) {
|
||||
Object.defineProperty(instance, 'for', {
|
||||
/** @type {RemoteForm<any, any>['for']} */
|
||||
value: (key) => {
|
||||
const { state } = get_request_store();
|
||||
const cache_key = __.id + '|' + JSON.stringify(key);
|
||||
let instance = (state.form_instances ??= new Map()).get(cache_key);
|
||||
|
||||
if (!instance) {
|
||||
instance = create_instance(key);
|
||||
instance.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key))}`;
|
||||
instance.__.name = __.name;
|
||||
|
||||
state.form_instances.set(cache_key, instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
return create_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ issues?: InternalRemoteFormIssue[], input?: Record<string, any>, result: any }} output
|
||||
* @param {readonly StandardSchemaV1.Issue[]} issues
|
||||
* @param {FormData | null} form_data - null if the form is progressively enhanced
|
||||
*/
|
||||
function handle_issues(output, issues, form_data) {
|
||||
output.issues = issues.map((issue) => normalize_issue(issue, true));
|
||||
|
||||
// if it was a progressively-enhanced submission, we don't need
|
||||
// to return the input — it's already there
|
||||
if (form_data) {
|
||||
output.input = {};
|
||||
|
||||
for (let key of form_data.keys()) {
|
||||
// redact sensitive fields
|
||||
if (/^[.\]]?_/.test(key)) continue;
|
||||
|
||||
const is_array = key.endsWith('[]');
|
||||
const values = form_data.getAll(key).filter((value) => typeof value === 'string');
|
||||
|
||||
if (is_array) key = key.slice(0, -2);
|
||||
|
||||
set_nested_value(
|
||||
/** @type {Record<string, any>} */ (output.input),
|
||||
key,
|
||||
is_array ? values : values[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an invalid function that can be used to imperatively mark form fields as invalid
|
||||
* @returns {InvalidField<any>}
|
||||
*/
|
||||
function create_issues() {
|
||||
return /** @type {InvalidField<any>} */ (
|
||||
new Proxy(
|
||||
/** @param {string} message */
|
||||
(message) => {
|
||||
// TODO 3.0 remove
|
||||
if (typeof message !== 'string') {
|
||||
throw new Error(
|
||||
'`invalid` should now be imported from `@sveltejs/kit` to throw validation issues. ' +
|
||||
"The second parameter provided to the form function (renamed to `issue`) is still used to construct issues, e.g. `invalid(issue.field('message'))`. " +
|
||||
'For more info see https://github.com/sveltejs/kit/pulls/14768'
|
||||
);
|
||||
}
|
||||
|
||||
return create_issue(message);
|
||||
},
|
||||
{
|
||||
get(target, prop) {
|
||||
if (typeof prop === 'symbol') return /** @type {any} */ (target)[prop];
|
||||
|
||||
return create_issue_proxy(prop, []);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {(string | number)[]} path
|
||||
* @returns {StandardSchemaV1.Issue}
|
||||
*/
|
||||
function create_issue(message, path = []) {
|
||||
return {
|
||||
message,
|
||||
path
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proxy that builds up a path and returns a function to create an issue
|
||||
* @param {string | number} key
|
||||
* @param {(string | number)[]} path
|
||||
*/
|
||||
function create_issue_proxy(key, path) {
|
||||
const new_path = [...path, key];
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @returns {StandardSchemaV1.Issue}
|
||||
*/
|
||||
const issue_func = (message) => create_issue(message, new_path);
|
||||
|
||||
return new Proxy(issue_func, {
|
||||
get(target, prop) {
|
||||
if (typeof prop === 'symbol') return /** @type {any} */ (target)[prop];
|
||||
|
||||
// Handle array access like invalid.items[0]
|
||||
if (/^\d+$/.test(prop)) {
|
||||
return create_issue_proxy(parseInt(prop, 10), new_path);
|
||||
}
|
||||
|
||||
// Handle property access like invalid.field.nested
|
||||
return create_issue_proxy(prop, new_path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
4
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js
generated
vendored
Normal file
4
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { command } from './command.js';
|
||||
export { form } from './form.js';
|
||||
export { prerender } from './prerender.js';
|
||||
export { query } from './query.js';
|
||||
163
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/prerender.js
generated
vendored
Normal file
163
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/prerender.js
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit' */
|
||||
/** @import { RemotePrerenderInputsGenerator, RemoteInfo, MaybePromise } from 'types' */
|
||||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { DEV } from 'esm-env';
|
||||
import { get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { stringify, stringify_remote_arg } from '../../../shared.js';
|
||||
import { app_dir, base } from '$app/paths/internal/server';
|
||||
import {
|
||||
create_validator,
|
||||
get_cache,
|
||||
get_response,
|
||||
parse_remote_response,
|
||||
run_remote_function
|
||||
} from './shared.js';
|
||||
|
||||
/**
|
||||
* Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#prerender) for full documentation.
|
||||
*
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {() => MaybePromise<Output>} fn
|
||||
* @param {{ inputs?: RemotePrerenderInputsGenerator<void>, dynamic?: boolean }} [options]
|
||||
* @returns {RemotePrerenderFunction<void, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#prerender) for full documentation.
|
||||
*
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {'unchecked'} validate
|
||||
* @param {(arg: Input) => MaybePromise<Output>} fn
|
||||
* @param {{ inputs?: RemotePrerenderInputsGenerator<Input>, dynamic?: boolean }} [options]
|
||||
* @returns {RemotePrerenderFunction<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#prerender) for full documentation.
|
||||
*
|
||||
* @template {StandardSchemaV1} Schema
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {Schema} schema
|
||||
* @param {(arg: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>} fn
|
||||
* @param {{ inputs?: RemotePrerenderInputsGenerator<StandardSchemaV1.InferInput<Schema>>, dynamic?: boolean }} [options]
|
||||
* @returns {RemotePrerenderFunction<StandardSchemaV1.InferInput<Schema>, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @param {any} validate_or_fn
|
||||
* @param {any} [fn_or_options]
|
||||
* @param {{ inputs?: RemotePrerenderInputsGenerator<Input>, dynamic?: boolean }} [maybe_options]
|
||||
* @returns {RemotePrerenderFunction<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
||||
const maybe_fn = typeof fn_or_options === 'function' ? fn_or_options : undefined;
|
||||
|
||||
/** @type {typeof maybe_options} */
|
||||
const options = maybe_options ?? (maybe_fn ? undefined : fn_or_options);
|
||||
|
||||
/** @type {(arg?: Input) => MaybePromise<Output>} */
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
|
||||
/** @type {(arg?: any) => MaybePromise<Input>} */
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
|
||||
/** @type {RemoteInfo} */
|
||||
const __ = {
|
||||
type: 'prerender',
|
||||
id: '',
|
||||
name: '',
|
||||
has_arg: !!maybe_fn,
|
||||
inputs: options?.inputs,
|
||||
dynamic: options?.dynamic
|
||||
};
|
||||
|
||||
/** @type {RemotePrerenderFunction<Input, Output> & { __: RemoteInfo }} */
|
||||
const wrapper = (arg) => {
|
||||
/** @type {Promise<Output> & Partial<RemoteResource<Output>>} */
|
||||
const promise = (async () => {
|
||||
const { event, state } = get_request_store();
|
||||
const payload = stringify_remote_arg(arg, state.transport);
|
||||
const id = __.id;
|
||||
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
|
||||
|
||||
if (!state.prerendering && !DEV && !event.isRemoteRequest) {
|
||||
try {
|
||||
return await get_response(__, arg, state, async () => {
|
||||
const key = stringify_remote_arg(arg, state.transport);
|
||||
const cache = get_cache(__, state);
|
||||
|
||||
// TODO adapters can provide prerendered data more efficiently than
|
||||
// fetching from the public internet
|
||||
const promise = (cache[key] ??= fetch(new URL(url, event.url.origin).href).then(
|
||||
async (response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Prerendered response not found');
|
||||
}
|
||||
|
||||
const prerendered = await response.json();
|
||||
|
||||
if (prerendered.type === 'error') {
|
||||
error(prerendered.status, prerendered.error);
|
||||
}
|
||||
|
||||
return prerendered.result;
|
||||
}
|
||||
));
|
||||
|
||||
return parse_remote_response(await promise, state.transport);
|
||||
});
|
||||
} catch {
|
||||
// not available prerendered, fallback to normal function
|
||||
}
|
||||
}
|
||||
|
||||
if (state.prerendering?.remote_responses.has(url)) {
|
||||
return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
|
||||
}
|
||||
|
||||
const promise = get_response(__, arg, state, () =>
|
||||
run_remote_function(event, state, false, () => validate(arg), fn)
|
||||
);
|
||||
|
||||
if (state.prerendering) {
|
||||
state.prerendering.remote_responses.set(url, promise);
|
||||
}
|
||||
|
||||
const result = await promise;
|
||||
|
||||
if (state.prerendering) {
|
||||
const body = { type: 'result', result: stringify(result, state.transport) };
|
||||
state.prerendering.dependencies.set(url, {
|
||||
body: JSON.stringify(body),
|
||||
response: json(body)
|
||||
});
|
||||
}
|
||||
|
||||
// TODO this is missing error/loading/current/status
|
||||
return result;
|
||||
})();
|
||||
|
||||
promise.catch(() => {});
|
||||
|
||||
return /** @type {RemoteResource<Output>} */ (promise);
|
||||
};
|
||||
|
||||
Object.defineProperty(wrapper, '__', { value: __ });
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
314
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/query.js
generated
vendored
Normal file
314
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/query.js
generated
vendored
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
/** @import { RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
|
||||
/** @import { RemoteInfo, MaybePromise } from 'types' */
|
||||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
import { get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
|
||||
import { prerendering } from '__sveltekit/environment';
|
||||
import { create_validator, get_cache, get_response, run_remote_function } from './shared.js';
|
||||
import { handle_error_and_jsonify } from '../../../server/utils.js';
|
||||
import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
|
||||
|
||||
/**
|
||||
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query) for full documentation.
|
||||
*
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {() => MaybePromise<Output>} fn
|
||||
* @returns {RemoteQueryFunction<void, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query) for full documentation.
|
||||
*
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {'unchecked'} validate
|
||||
* @param {(arg: Input) => MaybePromise<Output>} fn
|
||||
* @returns {RemoteQueryFunction<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query) for full documentation.
|
||||
*
|
||||
* @template {StandardSchemaV1} Schema
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {Schema} schema
|
||||
* @param {(arg: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>} fn
|
||||
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/**
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @param {any} validate_or_fn
|
||||
* @param {(args?: Input) => MaybePromise<Output>} [maybe_fn]
|
||||
* @returns {RemoteQueryFunction<Input, Output>}
|
||||
* @since 2.27
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
export function query(validate_or_fn, maybe_fn) {
|
||||
/** @type {(arg?: Input) => Output} */
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
|
||||
/** @type {(arg?: any) => MaybePromise<Input>} */
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
|
||||
/** @type {RemoteInfo} */
|
||||
const __ = { type: 'query', id: '', name: '' };
|
||||
|
||||
/** @type {RemoteQueryFunction<Input, Output> & { __: RemoteInfo }} */
|
||||
const wrapper = (arg) => {
|
||||
if (prerendering) {
|
||||
throw new Error(
|
||||
`Cannot call query '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead`
|
||||
);
|
||||
}
|
||||
|
||||
const { event, state } = get_request_store();
|
||||
|
||||
const get_remote_function_result = () =>
|
||||
run_remote_function(event, state, false, () => validate(arg), fn);
|
||||
|
||||
/** @type {Promise<any> & Partial<RemoteQuery<any>>} */
|
||||
const promise = get_response(__, arg, state, get_remote_function_result);
|
||||
|
||||
promise.catch(() => {});
|
||||
|
||||
promise.set = (value) => update_refresh_value(get_refresh_context(__, 'set', arg), value);
|
||||
|
||||
promise.refresh = () => {
|
||||
const refresh_context = get_refresh_context(__, 'refresh', arg);
|
||||
const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key];
|
||||
const value = is_immediate_refresh ? promise : get_remote_function_result();
|
||||
return update_refresh_value(refresh_context, value, is_immediate_refresh);
|
||||
};
|
||||
|
||||
promise.withOverride = () => {
|
||||
throw new Error(`Cannot call '${__.name}.withOverride()' on the server`);
|
||||
};
|
||||
|
||||
return /** @type {RemoteQuery<Output>} */ (promise);
|
||||
};
|
||||
|
||||
Object.defineProperty(wrapper, '__', { value: __ });
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a batch query function that collects multiple calls and executes them in a single request
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.batch) for full documentation.
|
||||
*
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {'unchecked'} validate
|
||||
* @param {(args: Input[]) => MaybePromise<(arg: Input, idx: number) => Output>} fn
|
||||
* @returns {RemoteQueryFunction<Input, Output>}
|
||||
* @since 2.35
|
||||
*/
|
||||
/**
|
||||
* Creates a batch query function that collects multiple calls and executes them in a single request
|
||||
*
|
||||
* See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.batch) for full documentation.
|
||||
*
|
||||
* @template {StandardSchemaV1} Schema
|
||||
* @template Output
|
||||
* @overload
|
||||
* @param {Schema} schema
|
||||
* @param {(args: StandardSchemaV1.InferOutput<Schema>[]) => MaybePromise<(arg: StandardSchemaV1.InferOutput<Schema>, idx: number) => Output>} fn
|
||||
* @returns {RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>}
|
||||
* @since 2.35
|
||||
*/
|
||||
/**
|
||||
* @template Input
|
||||
* @template Output
|
||||
* @param {any} validate_or_fn
|
||||
* @param {(args?: Input[]) => MaybePromise<(arg: Input, idx: number) => Output>} [maybe_fn]
|
||||
* @returns {RemoteQueryFunction<Input, Output>}
|
||||
* @since 2.35
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
function batch(validate_or_fn, maybe_fn) {
|
||||
/** @type {(args?: Input[]) => MaybePromise<(arg: Input, idx: number) => Output>} */
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
|
||||
/** @type {(arg?: any) => MaybePromise<Input>} */
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
|
||||
/** @type {RemoteInfo & { type: 'query_batch' }} */
|
||||
const __ = {
|
||||
type: 'query_batch',
|
||||
id: '',
|
||||
name: '',
|
||||
run: async (args, options) => {
|
||||
const { event, state } = get_request_store();
|
||||
|
||||
return run_remote_function(
|
||||
event,
|
||||
state,
|
||||
false,
|
||||
async () => Promise.all(args.map(validate)),
|
||||
async (/** @type {any[]} */ input) => {
|
||||
const get_result = await fn(input);
|
||||
|
||||
return Promise.all(
|
||||
input.map(async (arg, i) => {
|
||||
try {
|
||||
return { type: 'result', data: get_result(arg, i) };
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'error',
|
||||
error: await handle_error_and_jsonify(event, state, options, error),
|
||||
status:
|
||||
error instanceof HttpError || error instanceof SvelteKitError
|
||||
? error.status
|
||||
: 500
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {{ args: any[], resolvers: Array<{resolve: (value: any) => void, reject: (error: any) => void}> }} */
|
||||
let batching = { args: [], resolvers: [] };
|
||||
|
||||
/** @type {RemoteQueryFunction<Input, Output> & { __: RemoteInfo }} */
|
||||
const wrapper = (arg) => {
|
||||
if (prerendering) {
|
||||
throw new Error(
|
||||
`Cannot call query.batch '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead`
|
||||
);
|
||||
}
|
||||
|
||||
const { event, state } = get_request_store();
|
||||
|
||||
const get_remote_function_result = () => {
|
||||
// Collect all the calls to the same query in the same macrotask,
|
||||
// then execute them as one backend request.
|
||||
return new Promise((resolve, reject) => {
|
||||
// We don't need to deduplicate args here, because get_response already caches/reuses identical calls
|
||||
batching.args.push(arg);
|
||||
batching.resolvers.push({ resolve, reject });
|
||||
|
||||
if (batching.args.length > 1) return;
|
||||
|
||||
setTimeout(async () => {
|
||||
const batched = batching;
|
||||
batching = { args: [], resolvers: [] };
|
||||
|
||||
try {
|
||||
return await run_remote_function(
|
||||
event,
|
||||
state,
|
||||
false,
|
||||
async () => Promise.all(batched.args.map(validate)),
|
||||
async (input) => {
|
||||
const get_result = await fn(input);
|
||||
|
||||
for (let i = 0; i < batched.resolvers.length; i++) {
|
||||
try {
|
||||
batched.resolvers[i].resolve(get_result(input[i], i));
|
||||
} catch (error) {
|
||||
batched.resolvers[i].reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
for (const resolver of batched.resolvers) {
|
||||
resolver.reject(error);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
|
||||
/** @type {Promise<any> & Partial<RemoteQuery<any>>} */
|
||||
const promise = get_response(__, arg, state, get_remote_function_result);
|
||||
|
||||
promise.catch(() => {});
|
||||
|
||||
promise.set = (value) => update_refresh_value(get_refresh_context(__, 'set', arg), value);
|
||||
|
||||
promise.refresh = () => {
|
||||
const refresh_context = get_refresh_context(__, 'refresh', arg);
|
||||
const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key];
|
||||
const value = is_immediate_refresh ? promise : get_remote_function_result();
|
||||
return update_refresh_value(refresh_context, value, is_immediate_refresh);
|
||||
};
|
||||
|
||||
promise.withOverride = () => {
|
||||
throw new Error(`Cannot call '${__.name}.withOverride()' on the server`);
|
||||
};
|
||||
|
||||
return /** @type {RemoteQuery<Output>} */ (promise);
|
||||
};
|
||||
|
||||
Object.defineProperty(wrapper, '__', { value: __ });
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Add batch as a property to the query function
|
||||
Object.defineProperty(query, 'batch', { value: batch, enumerable: true });
|
||||
|
||||
/**
|
||||
* @param {RemoteInfo} __
|
||||
* @param {'set' | 'refresh'} action
|
||||
* @param {any} [arg]
|
||||
* @returns {{ __: RemoteInfo; state: any; refreshes: Record<string, Promise<any>>; cache: Record<string, Promise<any>>; refreshes_key: string; cache_key: string }}
|
||||
*/
|
||||
function get_refresh_context(__, action, arg) {
|
||||
const { state } = get_request_store();
|
||||
const { refreshes } = state;
|
||||
|
||||
if (!refreshes) {
|
||||
const name = __.type === 'query_batch' ? `query.batch '${__.name}'` : `query '${__.name}'`;
|
||||
throw new Error(
|
||||
`Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function`
|
||||
);
|
||||
}
|
||||
|
||||
const cache = get_cache(__, state);
|
||||
const cache_key = stringify_remote_arg(arg, state.transport);
|
||||
const refreshes_key = create_remote_key(__.id, cache_key);
|
||||
|
||||
return { __, state, refreshes, refreshes_key, cache, cache_key };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ __: RemoteInfo; refreshes: Record<string, Promise<any>>; cache: Record<string, Promise<any>>; refreshes_key: string; cache_key: string }} context
|
||||
* @param {any} value
|
||||
* @param {boolean} [is_immediate_refresh=false]
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function update_refresh_value(
|
||||
{ __, refreshes, refreshes_key, cache, cache_key },
|
||||
value,
|
||||
is_immediate_refresh = false
|
||||
) {
|
||||
const promise = Promise.resolve(value);
|
||||
|
||||
if (!is_immediate_refresh) {
|
||||
cache[cache_key] = promise;
|
||||
}
|
||||
|
||||
if (__.id) {
|
||||
refreshes[refreshes_key] = promise;
|
||||
}
|
||||
|
||||
return promise.then(() => {});
|
||||
}
|
||||
161
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/shared.js
generated
vendored
Normal file
161
frontend/node_modules/@sveltejs/kit/src/runtime/app/server/remote/shared.js
generated
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/** @import { RequestEvent } from '@sveltejs/kit' */
|
||||
/** @import { ServerHooks, MaybePromise, RequestState, RemoteInfo, RequestStore } from 'types' */
|
||||
import { parse } from 'devalue';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
|
||||
import { stringify_remote_arg } from '../../../shared.js';
|
||||
|
||||
/**
|
||||
* @param {any} validate_or_fn
|
||||
* @param {(arg?: any) => any} [maybe_fn]
|
||||
* @returns {(arg?: any) => MaybePromise<any>}
|
||||
*/
|
||||
export function create_validator(validate_or_fn, maybe_fn) {
|
||||
// prevent functions without validators being called with arguments
|
||||
if (!maybe_fn) {
|
||||
return (arg) => {
|
||||
if (arg !== undefined) {
|
||||
error(400, 'Bad Request');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// if 'unchecked', pass input through without validating
|
||||
if (validate_or_fn === 'unchecked') {
|
||||
return (arg) => arg;
|
||||
}
|
||||
|
||||
// use https://standardschema.dev validator if provided
|
||||
if ('~standard' in validate_or_fn) {
|
||||
return async (arg) => {
|
||||
// Get event before async validation to ensure it's available in server environments without AsyncLocalStorage, too
|
||||
const { event, state } = get_request_store();
|
||||
// access property and call method in one go to preserve potential this context
|
||||
const result = await validate_or_fn['~standard'].validate(arg);
|
||||
|
||||
// if the `issues` field exists, the validation failed
|
||||
if (result.issues) {
|
||||
error(
|
||||
400,
|
||||
await state.handleValidationError({
|
||||
issues: result.issues,
|
||||
event
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return result.value;
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'Invalid validator passed to remote function. Expected "unchecked" or a Standard Schema (https://standardschema.dev)'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of a single remote function call, just returns the result.
|
||||
*
|
||||
* In case of a full page reload, returns the response for a remote function call,
|
||||
* either from the cache or by invoking the function.
|
||||
* Also saves an uneval'ed version of the result for later HTML inlining for hydration.
|
||||
*
|
||||
* @template {MaybePromise<any>} T
|
||||
* @param {RemoteInfo} info
|
||||
* @param {any} arg
|
||||
* @param {RequestState} state
|
||||
* @param {() => Promise<T>} get_result
|
||||
* @returns {Promise<T>}
|
||||
*/
|
||||
export async function get_response(info, arg, state, get_result) {
|
||||
// wait a beat, in case `myQuery().set(...)` or `myQuery().refresh()` is immediately called
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
await 0;
|
||||
|
||||
const cache = get_cache(info, state);
|
||||
|
||||
return (cache[stringify_remote_arg(arg, state.transport)] ??= get_result());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} data
|
||||
* @param {ServerHooks['transport']} transport
|
||||
*/
|
||||
export function parse_remote_response(data, transport) {
|
||||
/** @type {Record<string, any>} */
|
||||
const revivers = {};
|
||||
for (const key in transport) {
|
||||
revivers[key] = transport[key].decode;
|
||||
}
|
||||
|
||||
return parse(data, revivers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `with_event` but removes things from `event` you cannot see/call in remote functions, such as `setHeaders`.
|
||||
* @template T
|
||||
* @param {RequestEvent} event
|
||||
* @param {RequestState} state
|
||||
* @param {boolean} allow_cookies
|
||||
* @param {() => any} get_input
|
||||
* @param {(arg?: any) => T} fn
|
||||
*/
|
||||
export async function run_remote_function(event, state, allow_cookies, get_input, fn) {
|
||||
/** @type {RequestStore} */
|
||||
const store = {
|
||||
event: {
|
||||
...event,
|
||||
setHeaders: () => {
|
||||
throw new Error('setHeaders is not allowed in remote functions');
|
||||
},
|
||||
cookies: {
|
||||
...event.cookies,
|
||||
set: (name, value, opts) => {
|
||||
if (!allow_cookies) {
|
||||
throw new Error('Cannot set cookies in `query` or `prerender` functions');
|
||||
}
|
||||
|
||||
if (opts.path && !opts.path.startsWith('/')) {
|
||||
throw new Error('Cookies set in remote functions must have an absolute path');
|
||||
}
|
||||
|
||||
return event.cookies.set(name, value, opts);
|
||||
},
|
||||
delete: (name, opts) => {
|
||||
if (!allow_cookies) {
|
||||
throw new Error('Cannot delete cookies in `query` or `prerender` functions');
|
||||
}
|
||||
|
||||
if (opts.path && !opts.path.startsWith('/')) {
|
||||
throw new Error('Cookies deleted in remote functions must have an absolute path');
|
||||
}
|
||||
|
||||
return event.cookies.delete(name, opts);
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
...state,
|
||||
is_in_remote_function: true
|
||||
}
|
||||
};
|
||||
|
||||
// In two parts, each with_event, so that runtimes without async local storage can still get the event at the start of the function
|
||||
const input = await with_request_store(store, get_input);
|
||||
return with_request_store(store, () => fn(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {RemoteInfo} info
|
||||
* @param {RequestState} state
|
||||
*/
|
||||
export function get_cache(info, state = get_request_store().state) {
|
||||
let cache = state.remote_data?.get(info);
|
||||
|
||||
if (cache === undefined) {
|
||||
cache = {};
|
||||
(state.remote_data ??= new Map()).set(info, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
68
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/client.js
generated
vendored
Normal file
68
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/client.js
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import {
|
||||
page as _page,
|
||||
navigating as _navigating,
|
||||
updated as _updated
|
||||
} from '../../client/state.svelte.js';
|
||||
import { stores } from '../../client/client.js';
|
||||
|
||||
export const page = {
|
||||
get data() {
|
||||
return _page.data;
|
||||
},
|
||||
get error() {
|
||||
return _page.error;
|
||||
},
|
||||
get form() {
|
||||
return _page.form;
|
||||
},
|
||||
get params() {
|
||||
return _page.params;
|
||||
},
|
||||
get route() {
|
||||
return _page.route;
|
||||
},
|
||||
get state() {
|
||||
return _page.state;
|
||||
},
|
||||
get status() {
|
||||
return _page.status;
|
||||
},
|
||||
get url() {
|
||||
return _page.url;
|
||||
}
|
||||
};
|
||||
|
||||
export const navigating = {
|
||||
get from() {
|
||||
return _navigating.current ? _navigating.current.from : null;
|
||||
},
|
||||
get to() {
|
||||
return _navigating.current ? _navigating.current.to : null;
|
||||
},
|
||||
get type() {
|
||||
return _navigating.current ? _navigating.current.type : null;
|
||||
},
|
||||
get willUnload() {
|
||||
return _navigating.current ? _navigating.current.willUnload : null;
|
||||
},
|
||||
get delta() {
|
||||
return _navigating.current ? _navigating.current.delta : null;
|
||||
},
|
||||
get complete() {
|
||||
return _navigating.current ? _navigating.current.complete : null;
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(navigating, 'current', {
|
||||
get() {
|
||||
// between 2.12.0 and 2.12.1 `navigating.current` existed
|
||||
throw new Error('Replace navigating.current.<prop> with navigating.<prop>');
|
||||
}
|
||||
});
|
||||
|
||||
export const updated = {
|
||||
get current() {
|
||||
return _updated.current;
|
||||
},
|
||||
check: stores.updated.check
|
||||
};
|
||||
64
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/index.js
generated
vendored
Normal file
64
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import {
|
||||
page as client_page,
|
||||
navigating as client_navigating,
|
||||
updated as client_updated
|
||||
} from './client.js';
|
||||
import {
|
||||
page as server_page,
|
||||
navigating as server_navigating,
|
||||
updated as server_updated
|
||||
} from './server.js';
|
||||
import { BROWSER } from 'esm-env';
|
||||
|
||||
/**
|
||||
* A read-only reactive object with information about the current page, serving several use cases:
|
||||
* - retrieving the combined `data` of all pages/layouts anywhere in your component tree (also see [loading data](https://svelte.dev/docs/kit/load))
|
||||
* - retrieving the current value of the `form` prop anywhere in your component tree (also see [form actions](https://svelte.dev/docs/kit/form-actions))
|
||||
* - retrieving the page state that was set through `goto`, `pushState` or `replaceState` (also see [goto](https://svelte.dev/docs/kit/$app-navigation#goto) and [shallow routing](https://svelte.dev/docs/kit/shallow-routing))
|
||||
* - retrieving metadata such as the URL you're on, the current route and its parameters, and whether or not there was an error
|
||||
*
|
||||
* ```svelte
|
||||
* <!--- file: +layout.svelte --->
|
||||
* <script>
|
||||
* import { page } from '$app/state';
|
||||
* </script>
|
||||
*
|
||||
* <p>Currently at {page.url.pathname}</p>
|
||||
*
|
||||
* {#if page.error}
|
||||
* <span class="red">Problem detected</span>
|
||||
* {:else}
|
||||
* <span class="small">All systems operational</span>
|
||||
* {/if}
|
||||
* ```
|
||||
*
|
||||
* Changes to `page` are available exclusively with runes. (The legacy reactivity syntax will not reflect any changes)
|
||||
*
|
||||
* ```svelte
|
||||
* <!--- file: +page.svelte --->
|
||||
* <script>
|
||||
* import { page } from '$app/state';
|
||||
* const id = $derived(page.params.id); // This will correctly update id for usage on this page
|
||||
* $: badId = page.params.id; // Do not use; will never update after initial load
|
||||
* </script>
|
||||
* ```
|
||||
*
|
||||
* On the server, values can only be read during rendering (in other words _not_ in e.g. `load` functions). In the browser, the values can be read at any time.
|
||||
*
|
||||
* @type {import('@sveltejs/kit').Page}
|
||||
*/
|
||||
export const page = BROWSER ? client_page : server_page;
|
||||
|
||||
/**
|
||||
* A read-only object representing an in-progress navigation, with `from`, `to`, `type` and (if `type === 'popstate'`) `delta` properties.
|
||||
* Values are `null` when no navigation is occurring, or during server rendering.
|
||||
* @type {import('@sveltejs/kit').Navigation | { from: null, to: null, type: null, willUnload: null, delta: null, complete: null }}
|
||||
*/
|
||||
// @ts-expect-error
|
||||
export const navigating = BROWSER ? client_navigating : server_navigating;
|
||||
|
||||
/**
|
||||
* A read-only reactive value that's initially `false`. If [`version.pollInterval`](https://svelte.dev/docs/kit/configuration#version) is a non-zero value, SvelteKit will poll for new versions of the app and update `current` to `true` when it detects one. `updated.check()` will force an immediate check, regardless of polling.
|
||||
* @type {{ get current(): boolean; check(): Promise<boolean>; }}
|
||||
*/
|
||||
export const updated = BROWSER ? client_updated : server_updated;
|
||||
63
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/server.js
generated
vendored
Normal file
63
frontend/node_modules/@sveltejs/kit/src/runtime/app/state/server.js
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { DEV } from 'esm-env';
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
function context() {
|
||||
return getContext('__request__');
|
||||
}
|
||||
|
||||
/** @param {string} name */
|
||||
function context_dev(name) {
|
||||
try {
|
||||
return context();
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Can only read '${name}' on the server during rendering (not in e.g. \`load\` functions), as it is bound to the current request via component context. This prevents state from leaking between users. ` +
|
||||
'For more information, see https://svelte.dev/docs/kit/state-management#avoid-shared-state-on-the-server'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const page = {
|
||||
get data() {
|
||||
return (DEV ? context_dev('page.data') : context()).page.data;
|
||||
},
|
||||
get error() {
|
||||
return (DEV ? context_dev('page.error') : context()).page.error;
|
||||
},
|
||||
get form() {
|
||||
return (DEV ? context_dev('page.form') : context()).page.form;
|
||||
},
|
||||
get params() {
|
||||
return (DEV ? context_dev('page.params') : context()).page.params;
|
||||
},
|
||||
get route() {
|
||||
return (DEV ? context_dev('page.route') : context()).page.route;
|
||||
},
|
||||
get state() {
|
||||
return (DEV ? context_dev('page.state') : context()).page.state;
|
||||
},
|
||||
get status() {
|
||||
return (DEV ? context_dev('page.status') : context()).page.status;
|
||||
},
|
||||
get url() {
|
||||
return (DEV ? context_dev('page.url') : context()).page.url;
|
||||
}
|
||||
};
|
||||
|
||||
export const navigating = {
|
||||
from: null,
|
||||
to: null,
|
||||
type: null,
|
||||
willUnload: null,
|
||||
delta: null,
|
||||
complete: null
|
||||
};
|
||||
|
||||
export const updated = {
|
||||
get current() {
|
||||
return false;
|
||||
},
|
||||
check: () => {
|
||||
throw new Error('Can only call updated.check() in the browser');
|
||||
}
|
||||
};
|
||||
101
frontend/node_modules/@sveltejs/kit/src/runtime/app/stores.js
generated
vendored
Normal file
101
frontend/node_modules/@sveltejs/kit/src/runtime/app/stores.js
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import { getContext } from 'svelte';
|
||||
import { BROWSER, DEV } from 'esm-env';
|
||||
import { stores as browser_stores } from '../client/client.js';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated Use `$app/state` instead (requires Svelte 5, [see docs for more info](https://svelte.dev/docs/kit/migrating-to-sveltekit-2#SvelteKit-2.12:-$app-stores-deprecated))
|
||||
*/
|
||||
export const getStores = () => {
|
||||
const stores = BROWSER ? browser_stores : getContext('__svelte__');
|
||||
|
||||
return {
|
||||
/** @type {typeof page} */
|
||||
page: {
|
||||
subscribe: stores.page.subscribe
|
||||
},
|
||||
/** @type {typeof navigating} */
|
||||
navigating: {
|
||||
subscribe: stores.navigating.subscribe
|
||||
},
|
||||
/** @type {typeof updated} */
|
||||
updated: stores.updated
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated Use `page` from `$app/state` instead (requires Svelte 5, [see docs for more info](https://svelte.dev/docs/kit/migrating-to-sveltekit-2#SvelteKit-2.12:-$app-stores-deprecated))
|
||||
* @type {import('svelte/store').Readable<import('@sveltejs/kit').Page>}
|
||||
*/
|
||||
export const page = {
|
||||
subscribe(fn) {
|
||||
const store = DEV ? get_store('page') : getStores().page;
|
||||
return store.subscribe(fn);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated Use `navigating` from `$app/state` instead (requires Svelte 5, [see docs for more info](https://svelte.dev/docs/kit/migrating-to-sveltekit-2#SvelteKit-2.12:-$app-stores-deprecated))
|
||||
* @type {import('svelte/store').Readable<import('@sveltejs/kit').Navigation | null>}
|
||||
*/
|
||||
export const navigating = {
|
||||
subscribe(fn) {
|
||||
const store = DEV ? get_store('navigating') : getStores().navigating;
|
||||
return store.subscribe(fn);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A readable store whose initial value is `false`. If [`version.pollInterval`](https://svelte.dev/docs/kit/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.
|
||||
*
|
||||
* @deprecated Use `updated` from `$app/state` instead (requires Svelte 5, [see docs for more info](https://svelte.dev/docs/kit/migrating-to-sveltekit-2#SvelteKit-2.12:-$app-stores-deprecated))
|
||||
* @type {import('svelte/store').Readable<boolean> & { check(): Promise<boolean> }}
|
||||
*/
|
||||
export const updated = {
|
||||
subscribe(fn) {
|
||||
const store = DEV ? get_store('updated') : getStores().updated;
|
||||
|
||||
if (BROWSER) {
|
||||
updated.check = store.check;
|
||||
}
|
||||
|
||||
return store.subscribe(fn);
|
||||
},
|
||||
check: () => {
|
||||
throw new Error(
|
||||
BROWSER
|
||||
? 'Cannot check updated store before subscribing'
|
||||
: 'Can only check updated store in browser'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @template {keyof ReturnType<typeof getStores>} Name
|
||||
* @param {Name} name
|
||||
* @returns {ReturnType<typeof getStores>[Name]}
|
||||
*/
|
||||
function get_store(name) {
|
||||
try {
|
||||
return getStores()[name];
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Cannot subscribe to '${name}' store on the server outside of a Svelte component, as it is bound to the current request via component context. This prevents state from leaking between users.` +
|
||||
'For more information, see https://svelte.dev/docs/kit/state-management#avoid-shared-state-on-the-server'
|
||||
);
|
||||
}
|
||||
}
|
||||
17
frontend/node_modules/@sveltejs/kit/src/runtime/client/bundle.js
generated
vendored
Normal file
17
frontend/node_modules/@sveltejs/kit/src/runtime/client/bundle.js
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* if `bundleStrategy` is 'single' or 'inline', this file is used as the entry point */
|
||||
|
||||
import * as kit from './entry.js';
|
||||
|
||||
// @ts-expect-error
|
||||
import * as app from '__sveltekit/manifest';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {import('./types.js').HydrateOptions} options
|
||||
*/
|
||||
export function start(element, options) {
|
||||
void kit.start(app, element, options);
|
||||
}
|
||||
|
||||
export { app };
|
||||
3309
frontend/node_modules/@sveltejs/kit/src/runtime/client/client.js
generated
vendored
Normal file
3309
frontend/node_modules/@sveltejs/kit/src/runtime/client/client.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
16
frontend/node_modules/@sveltejs/kit/src/runtime/client/constants.js
generated
vendored
Normal file
16
frontend/node_modules/@sveltejs/kit/src/runtime/client/constants.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export const SNAPSHOT_KEY = 'sveltekit:snapshot';
|
||||
export const SCROLL_KEY = 'sveltekit:scroll';
|
||||
export const STATES_KEY = 'sveltekit:states';
|
||||
export const PAGE_URL_KEY = 'sveltekit:pageurl';
|
||||
|
||||
export const HISTORY_INDEX = 'sveltekit:history';
|
||||
export const NAVIGATION_INDEX = 'sveltekit:navigation';
|
||||
|
||||
export const PRELOAD_PRIORITIES = /** @type {const} */ ({
|
||||
tap: 1,
|
||||
hover: 2,
|
||||
viewport: 3,
|
||||
eager: 4,
|
||||
off: -1,
|
||||
false: -1
|
||||
});
|
||||
3
frontend/node_modules/@sveltejs/kit/src/runtime/client/entry.js
generated
vendored
Normal file
3
frontend/node_modules/@sveltejs/kit/src/runtime/client/entry.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// we expose this as a separate entry point (rather than treating client.js as the entry point)
|
||||
// so that everything other than `start`/`load_css` can be treeshaken
|
||||
export { start, load_css } from './client.js';
|
||||
178
frontend/node_modules/@sveltejs/kit/src/runtime/client/fetcher.js
generated
vendored
Normal file
178
frontend/node_modules/@sveltejs/kit/src/runtime/client/fetcher.js
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { BROWSER, DEV } from 'esm-env';
|
||||
import { hash } from '../../utils/hash.js';
|
||||
import { base64_decode } from '../utils.js';
|
||||
|
||||
let loading = 0;
|
||||
|
||||
/** @type {typeof fetch} */
|
||||
const native_fetch = BROWSER ? window.fetch : /** @type {any} */ (() => {});
|
||||
|
||||
export function lock_fetch() {
|
||||
loading += 1;
|
||||
}
|
||||
|
||||
export function unlock_fetch() {
|
||||
loading -= 1;
|
||||
}
|
||||
|
||||
if (DEV && BROWSER) {
|
||||
let can_inspect_stack_trace = false;
|
||||
|
||||
// detect whether async stack traces work
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const check_stack_trace = async () => {
|
||||
const stack = /** @type {string} */ (new Error().stack);
|
||||
can_inspect_stack_trace = stack.includes('check_stack_trace');
|
||||
};
|
||||
|
||||
void check_stack_trace();
|
||||
|
||||
/**
|
||||
* @param {RequestInfo | URL} input
|
||||
* @param {RequestInit & Record<string, any> | undefined} init
|
||||
*/
|
||||
window.fetch = (input, init) => {
|
||||
// Check if fetch was called via load_node. the lock method only checks if it was called at the
|
||||
// same time, but not necessarily if it was called from `load`.
|
||||
// We use just the filename as the method name sometimes does not appear on the CI.
|
||||
const url = input instanceof Request ? input.url : input.toString();
|
||||
const stack_array = /** @type {string} */ (new Error().stack).split('\n');
|
||||
// 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 cutoff = stack_array.findIndex((a) => a.includes('load@') || a.includes('at load'));
|
||||
const stack = stack_array.slice(0, cutoff + 2).join('\n');
|
||||
|
||||
const in_load_heuristic = can_inspect_stack_trace
|
||||
? stack.includes('src/runtime/client/client.js')
|
||||
: loading;
|
||||
|
||||
// This flag is set in initial_fetch and subsequent_fetch
|
||||
const used_kit_fetch = init?.__sveltekit_fetch__;
|
||||
|
||||
if (in_load_heuristic && !used_kit_fetch) {
|
||||
console.warn(
|
||||
`Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://svelte.dev/docs/kit/load#making-fetch-requests`
|
||||
);
|
||||
}
|
||||
|
||||
const method = input instanceof Request ? input.method : init?.method || 'GET';
|
||||
|
||||
if (method !== 'GET') {
|
||||
cache.delete(build_selector(input));
|
||||
}
|
||||
|
||||
return native_fetch(input, init);
|
||||
};
|
||||
} else if (BROWSER) {
|
||||
window.fetch = (input, init) => {
|
||||
const method = input instanceof Request ? input.method : init?.method || 'GET';
|
||||
|
||||
if (method !== 'GET') {
|
||||
cache.delete(build_selector(input));
|
||||
}
|
||||
|
||||
return native_fetch(input, init);
|
||||
};
|
||||
}
|
||||
|
||||
const cache = new Map();
|
||||
|
||||
/**
|
||||
* Should be called on the initial run of load functions that hydrate the page.
|
||||
* Saves any requests with cache-control max-age to the cache.
|
||||
* @param {URL | string} resource
|
||||
* @param {RequestInit} [opts]
|
||||
*/
|
||||
export function initial_fetch(resource, opts) {
|
||||
const selector = build_selector(resource, opts);
|
||||
|
||||
const script = document.querySelector(selector);
|
||||
if (script?.textContent) {
|
||||
script.remove(); // In case multiple script tags match the same selector
|
||||
let { body, ...init } = JSON.parse(script.textContent);
|
||||
|
||||
const ttl = script.getAttribute('data-ttl');
|
||||
if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
|
||||
const b64 = script.getAttribute('data-b64');
|
||||
if (b64 !== null) {
|
||||
// Can't use native_fetch('data:...;base64,${body}')
|
||||
// csp can block the request
|
||||
body = base64_decode(body);
|
||||
}
|
||||
|
||||
return Promise.resolve(new Response(body, init));
|
||||
}
|
||||
|
||||
return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get the response from the cache, if max-age allows it, else does a fetch.
|
||||
* @param {URL | string} resource
|
||||
* @param {string} resolved
|
||||
* @param {RequestInit} [opts]
|
||||
*/
|
||||
export function subsequent_fetch(resource, resolved, opts) {
|
||||
if (cache.size > 0) {
|
||||
const selector = build_selector(resource, opts);
|
||||
const cached = cache.get(selector);
|
||||
if (cached) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Request/cache#value
|
||||
if (
|
||||
performance.now() < cached.ttl &&
|
||||
['default', 'force-cache', 'only-if-cached', undefined].includes(opts?.cache)
|
||||
) {
|
||||
return new Response(cached.body, cached.init);
|
||||
}
|
||||
|
||||
cache.delete(selector);
|
||||
}
|
||||
}
|
||||
|
||||
return DEV ? dev_fetch(resolved, opts) : window.fetch(resolved, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {RequestInfo | URL} resource
|
||||
* @param {RequestInit & Record<string, any> | undefined} opts
|
||||
*/
|
||||
export function dev_fetch(resource, opts) {
|
||||
const patched_opts = { ...opts };
|
||||
// This assigns the __sveltekit_fetch__ flag and makes it non-enumerable
|
||||
Object.defineProperty(patched_opts, '__sveltekit_fetch__', {
|
||||
value: true,
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
return window.fetch(resource, patched_opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the cache key for a given request
|
||||
* @param {URL | RequestInfo} resource
|
||||
* @param {RequestInit} [opts]
|
||||
*/
|
||||
function build_selector(resource, opts) {
|
||||
const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
|
||||
|
||||
let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
|
||||
|
||||
if (opts?.headers || opts?.body) {
|
||||
/** @type {import('types').StrictBody[]} */
|
||||
const values = [];
|
||||
|
||||
if (opts.headers) {
|
||||
values.push([...new Headers(opts.headers)].join(','));
|
||||
}
|
||||
|
||||
if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
|
||||
values.push(opts.body);
|
||||
}
|
||||
|
||||
selector += `[data-hash="${hash(...values)}"]`;
|
||||
}
|
||||
|
||||
return selector;
|
||||
}
|
||||
77
frontend/node_modules/@sveltejs/kit/src/runtime/client/parse.js
generated
vendored
Normal file
77
frontend/node_modules/@sveltejs/kit/src/runtime/client/parse.js
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { exec, parse_route_id } from '../../utils/routing.js';
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').SvelteKitApp} app
|
||||
* @returns {import('types').CSRRoute[]}
|
||||
*/
|
||||
export function parse({ nodes, server_loads, dictionary, matchers }) {
|
||||
const layouts_with_server_load = new Set(server_loads);
|
||||
|
||||
return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
|
||||
const { pattern, params } = parse_route_id(id);
|
||||
|
||||
/** @type {import('types').CSRRoute} */
|
||||
const route = {
|
||||
id,
|
||||
/** @param {string} path */
|
||||
exec: (path) => {
|
||||
const match = pattern.exec(path);
|
||||
if (match) return exec(match, params, matchers);
|
||||
},
|
||||
errors: [1, ...(errors || [])].map((n) => nodes[n]),
|
||||
layouts: [0, ...(layouts || [])].map(create_layout_loader),
|
||||
leaf: create_leaf_loader(leaf)
|
||||
};
|
||||
|
||||
// bit of a hack, but ensures that layout/error node lists are the same
|
||||
// length, without which the wrong data will be applied if the route
|
||||
// manifest looks like `[[a, b], [c,], d]`
|
||||
route.errors.length = route.layouts.length = Math.max(
|
||||
route.errors.length,
|
||||
route.layouts.length
|
||||
);
|
||||
|
||||
return route;
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {number} id
|
||||
* @returns {[boolean, import('types').CSRPageNodeLoader]}
|
||||
*/
|
||||
function create_leaf_loader(id) {
|
||||
// whether or not the route uses the server data is
|
||||
// encoded using the ones' complement, to save space
|
||||
const uses_server_data = id < 0;
|
||||
if (uses_server_data) id = ~id;
|
||||
return [uses_server_data, nodes[id]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number | undefined} id
|
||||
* @returns {[boolean, import('types').CSRPageNodeLoader] | undefined}
|
||||
*/
|
||||
function create_layout_loader(id) {
|
||||
// whether or not the layout uses the server data is
|
||||
// encoded in the layouts array, to save space
|
||||
return id === undefined ? id : [layouts_with_server_load.has(id), nodes[id]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').CSRRouteServer} input
|
||||
* @param {import('types').CSRPageNodeLoader[]} app_nodes Will be modified if a new node is loaded that's not already in the array
|
||||
* @returns {import('types').CSRRoute}
|
||||
*/
|
||||
export function parse_server_route({ nodes, id, leaf, layouts, errors }, app_nodes) {
|
||||
return {
|
||||
id,
|
||||
exec: () => ({}), // dummy function; exec already happened on the server
|
||||
// By writing to app_nodes only when a loader at that index is not already defined,
|
||||
// we ensure that loaders have referential equality when they load the same node.
|
||||
// Code elsewhere in client.js relies on this referential equality to determine
|
||||
// if a loader is different and should therefore (re-)run.
|
||||
errors: errors.map((n) => (n ? (app_nodes[n] ||= nodes[n]) : undefined)),
|
||||
layouts: layouts.map((n) => (n ? [n[0], (app_nodes[n[1]] ||= nodes[n[1]])] : undefined)),
|
||||
leaf: [leaf[0], (app_nodes[leaf[1]] ||= nodes[leaf[1]])]
|
||||
};
|
||||
}
|
||||
96
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/command.svelte.js
generated
vendored
Normal file
96
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/command.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/** @import { RemoteCommand, RemoteQueryOverride } from '@sveltejs/kit' */
|
||||
/** @import { RemoteFunctionResponse } from 'types' */
|
||||
/** @import { Query } from './query.svelte.js' */
|
||||
import { app_dir, base } from '$app/paths/internal/client';
|
||||
import * as devalue from 'devalue';
|
||||
import { HttpError } from '@sveltejs/kit/internal';
|
||||
import { app } from '../client.js';
|
||||
import { stringify_remote_arg } from '../../shared.js';
|
||||
import { get_remote_request_headers, refresh_queries, release_overrides } from './shared.svelte.js';
|
||||
|
||||
/**
|
||||
* Client-version of the `command` function from `$app/server`.
|
||||
* @param {string} id
|
||||
* @returns {RemoteCommand<any, any>}
|
||||
*/
|
||||
export function command(id) {
|
||||
/** @type {number} */
|
||||
let pending_count = $state(0);
|
||||
|
||||
// Careful: This function MUST be synchronous (can't use the async keyword) because the return type has to be a promise with an updates() method.
|
||||
// If we make it async, the return type will be a promise that resolves to a promise with an updates() method, which is not what we want.
|
||||
/** @type {RemoteCommand<any, any>} */
|
||||
const command_function = (arg) => {
|
||||
/** @type {Array<Query<any> | RemoteQueryOverride>} */
|
||||
let updates = [];
|
||||
|
||||
// Increment pending count when command starts
|
||||
pending_count++;
|
||||
|
||||
// Noone should call commands during rendering but belts and braces.
|
||||
// Do this here, after await Svelte' reactivity context is gone.
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...get_remote_request_headers()
|
||||
};
|
||||
|
||||
/** @type {Promise<any> & { updates: (...args: any[]) => any }} */
|
||||
const promise = (async () => {
|
||||
try {
|
||||
// Wait a tick to give room for the `updates` method to be called
|
||||
await Promise.resolve();
|
||||
|
||||
const response = await fetch(`${base}/${app_dir}/remote/${id}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
payload: stringify_remote_arg(arg, app.hooks.transport),
|
||||
refreshes: updates.map((u) => u._key)
|
||||
}),
|
||||
headers
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
release_overrides(updates);
|
||||
// We only end up here in case of a network error or if the server has an internal error
|
||||
// (which shouldn't happen because we handle errors on the server and always send a 200 response)
|
||||
throw new Error('Failed to execute remote function');
|
||||
}
|
||||
|
||||
const result = /** @type {RemoteFunctionResponse} */ (await response.json());
|
||||
if (result.type === 'redirect') {
|
||||
release_overrides(updates);
|
||||
throw new Error(
|
||||
'Redirects are not allowed in commands. Return a result instead and use goto on the client'
|
||||
);
|
||||
} else if (result.type === 'error') {
|
||||
release_overrides(updates);
|
||||
throw new HttpError(result.status ?? 500, result.error);
|
||||
} else {
|
||||
if (result.refreshes) {
|
||||
refresh_queries(result.refreshes, updates);
|
||||
}
|
||||
|
||||
return devalue.parse(result.result, app.decoders);
|
||||
}
|
||||
} finally {
|
||||
// Decrement pending count when command completes
|
||||
pending_count--;
|
||||
}
|
||||
})();
|
||||
|
||||
promise.updates = (/** @type {any} */ ...args) => {
|
||||
updates = args;
|
||||
// @ts-expect-error Don't allow updates to be called multiple times
|
||||
delete promise.updates;
|
||||
return promise;
|
||||
};
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
Object.defineProperty(command_function, 'pending', {
|
||||
get: () => pending_count
|
||||
});
|
||||
|
||||
return command_function;
|
||||
}
|
||||
650
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/form.svelte.js
generated
vendored
Normal file
650
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/form.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
/** @import { RemoteFormInput, RemoteForm, RemoteQueryOverride } from '@sveltejs/kit' */
|
||||
/** @import { InternalRemoteFormIssue, RemoteFunctionResponse } from 'types' */
|
||||
/** @import { Query } from './query.svelte.js' */
|
||||
import { app_dir, base } from '$app/paths/internal/client';
|
||||
import * as devalue from 'devalue';
|
||||
import { DEV } from 'esm-env';
|
||||
import { HttpError } from '@sveltejs/kit/internal';
|
||||
import { app, remote_responses, _goto, set_nearest_error_page, invalidateAll } from '../client.js';
|
||||
import { tick } from 'svelte';
|
||||
import { refresh_queries, release_overrides } from './shared.svelte.js';
|
||||
import { createAttachmentKey } from 'svelte/attachments';
|
||||
import {
|
||||
convert_formdata,
|
||||
flatten_issues,
|
||||
create_field_proxy,
|
||||
deep_set,
|
||||
set_nested_value,
|
||||
throw_on_old_property_access,
|
||||
build_path_string,
|
||||
normalize_issue,
|
||||
serialize_binary_form,
|
||||
BINARY_FORM_CONTENT_TYPE
|
||||
} from '../../form-utils.js';
|
||||
|
||||
/**
|
||||
* Merge client issues into server issues. Server issues are persisted unless
|
||||
* a client-issue exists for the same path, in which case the client-issue overrides it.
|
||||
* @param {FormData} form_data
|
||||
* @param {InternalRemoteFormIssue[]} current_issues
|
||||
* @param {InternalRemoteFormIssue[]} client_issues
|
||||
* @returns {InternalRemoteFormIssue[]}
|
||||
*/
|
||||
function merge_with_server_issues(form_data, current_issues, client_issues) {
|
||||
const merged = [
|
||||
...current_issues.filter(
|
||||
(issue) => issue.server && !client_issues.some((i) => i.name === issue.name)
|
||||
),
|
||||
...client_issues
|
||||
];
|
||||
|
||||
const keys = Array.from(form_data.keys());
|
||||
|
||||
return merged.sort((a, b) => keys.indexOf(a.name) - keys.indexOf(b.name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-version of the `form` function from `$app/server`.
|
||||
* @template {RemoteFormInput} T
|
||||
* @template U
|
||||
* @param {string} id
|
||||
* @returns {RemoteForm<T, U>}
|
||||
*/
|
||||
export function form(id) {
|
||||
/** @type {Map<any, { count: number, instance: RemoteForm<T, U> }>} */
|
||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- we don't need reactivity for this
|
||||
const instances = new Map();
|
||||
|
||||
/** @param {string | number | boolean} [key] */
|
||||
function create_instance(key) {
|
||||
const action_id_without_key = id;
|
||||
const action_id = id + (key != undefined ? `/${JSON.stringify(key)}` : '');
|
||||
const action = '?/remote=' + encodeURIComponent(action_id);
|
||||
|
||||
/**
|
||||
* @type {Record<string, string | string[] | File | File[]>}
|
||||
*/
|
||||
let input = $state({});
|
||||
|
||||
/** @type {InternalRemoteFormIssue[]} */
|
||||
let raw_issues = $state.raw([]);
|
||||
|
||||
const issues = $derived(flatten_issues(raw_issues));
|
||||
|
||||
/** @type {any} */
|
||||
let result = $state.raw(remote_responses[action_id]);
|
||||
|
||||
/** @type {number} */
|
||||
let pending_count = $state(0);
|
||||
|
||||
/** @type {StandardSchemaV1 | undefined} */
|
||||
let preflight_schema = undefined;
|
||||
|
||||
/** @type {HTMLFormElement | null} */
|
||||
let element = null;
|
||||
|
||||
/** @type {Record<string, boolean>} */
|
||||
let touched = {};
|
||||
|
||||
let submitted = false;
|
||||
|
||||
/**
|
||||
* @param {FormData} form_data
|
||||
* @returns {Record<string, any>}
|
||||
*/
|
||||
function convert(form_data) {
|
||||
const data = convert_formdata(form_data);
|
||||
if (key !== undefined && !form_data.has('id')) {
|
||||
data.id = key;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLFormElement} form
|
||||
* @param {FormData} form_data
|
||||
* @param {Parameters<RemoteForm<any, any>['enhance']>[0]} callback
|
||||
*/
|
||||
async function handle_submit(form, form_data, callback) {
|
||||
const data = convert(form_data);
|
||||
|
||||
submitted = true;
|
||||
|
||||
// Increment pending count immediately so that `pending` reflects
|
||||
// the in-progress state during async preflight validation
|
||||
pending_count++;
|
||||
|
||||
const validated = await preflight_schema?.['~standard'].validate(data);
|
||||
|
||||
if (validated?.issues) {
|
||||
raw_issues = merge_with_server_issues(
|
||||
form_data,
|
||||
raw_issues,
|
||||
validated.issues.map((issue) => normalize_issue(issue, false))
|
||||
);
|
||||
pending_count--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Preflight passed - clear stale client-side preflight issues
|
||||
if (preflight_schema) {
|
||||
raw_issues = raw_issues.filter((issue) => issue.server);
|
||||
}
|
||||
|
||||
// TODO 3.0 remove this warning
|
||||
if (DEV) {
|
||||
const error = () => {
|
||||
throw new Error(
|
||||
'Remote form functions no longer get passed a FormData object. The payload is now a POJO. See https://kit.svelte.dev/docs/remote-functions#form for details.'
|
||||
);
|
||||
};
|
||||
for (const key of [
|
||||
'append',
|
||||
'delete',
|
||||
'entries',
|
||||
'forEach',
|
||||
'get',
|
||||
'getAll',
|
||||
'has',
|
||||
'keys',
|
||||
'set',
|
||||
'values'
|
||||
]) {
|
||||
if (!(key in data)) {
|
||||
Object.defineProperty(data, key, { get: error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await callback({
|
||||
form,
|
||||
data,
|
||||
submit: () => submit(form_data)
|
||||
});
|
||||
} catch (e) {
|
||||
const error = e instanceof HttpError ? e.body : { message: /** @type {any} */ (e).message };
|
||||
const status = e instanceof HttpError ? e.status : 500;
|
||||
void set_nearest_error_page(error, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FormData} data
|
||||
* @returns {Promise<any> & { updates: (...args: any[]) => any }}
|
||||
*/
|
||||
function submit(data) {
|
||||
// Store a reference to the current instance and increment the usage count for the duration
|
||||
// of the request. This ensures that the instance is not deleted in case of an optimistic update
|
||||
// (e.g. when deleting an item in a list) that fails and wants to surface an error to the user afterwards.
|
||||
// If the instance would be deleted in the meantime, the error property would be assigned to the old,
|
||||
// no-longer-visible instance, so it would never be shown to the user.
|
||||
const entry = instances.get(key);
|
||||
if (entry) {
|
||||
entry.count++;
|
||||
}
|
||||
|
||||
/** @type {Array<Query<any> | RemoteQueryOverride>} */
|
||||
let updates = [];
|
||||
|
||||
/** @type {Promise<any> & { updates: (...args: any[]) => any }} */
|
||||
const promise = (async () => {
|
||||
try {
|
||||
await Promise.resolve();
|
||||
|
||||
const { blob } = serialize_binary_form(convert(data), {
|
||||
remote_refreshes: updates.map((u) => u._key)
|
||||
});
|
||||
|
||||
const response = await fetch(`${base}/${app_dir}/remote/${action_id_without_key}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': BINARY_FORM_CONTENT_TYPE,
|
||||
// Forms cannot be called during rendering, so it's save to use location here
|
||||
'x-sveltekit-pathname': location.pathname,
|
||||
'x-sveltekit-search': location.search
|
||||
},
|
||||
body: blob
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// We only end up here in case of a network error or if the server has an internal error
|
||||
// (which shouldn't happen because we handle errors on the server and always send a 200 response)
|
||||
throw new Error('Failed to execute remote function');
|
||||
}
|
||||
|
||||
const form_result = /** @type { RemoteFunctionResponse} */ (await response.json());
|
||||
|
||||
// reset issues in case it's a redirect or error (but issues passed in that case)
|
||||
raw_issues = [];
|
||||
|
||||
if (form_result.type === 'result') {
|
||||
({ issues: raw_issues = [], result } = devalue.parse(form_result.result, app.decoders));
|
||||
|
||||
if (issues.$) {
|
||||
release_overrides(updates);
|
||||
} else {
|
||||
if (form_result.refreshes) {
|
||||
refresh_queries(form_result.refreshes, updates);
|
||||
} else {
|
||||
void invalidateAll();
|
||||
}
|
||||
}
|
||||
} else if (form_result.type === 'redirect') {
|
||||
const refreshes = form_result.refreshes ?? '';
|
||||
const invalidateAll = !refreshes && updates.length === 0;
|
||||
if (!invalidateAll) {
|
||||
refresh_queries(refreshes, updates);
|
||||
}
|
||||
// Use internal version to allow redirects to external URLs
|
||||
void _goto(form_result.location, { invalidateAll }, 0);
|
||||
} else {
|
||||
throw new HttpError(form_result.status ?? 500, form_result.error);
|
||||
}
|
||||
} catch (e) {
|
||||
result = undefined;
|
||||
release_overrides(updates);
|
||||
throw e;
|
||||
} finally {
|
||||
// Decrement pending count when submission completes
|
||||
pending_count--;
|
||||
|
||||
void tick().then(() => {
|
||||
if (entry) {
|
||||
entry.count--;
|
||||
if (entry.count === 0) {
|
||||
instances.delete(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
promise.updates = (...args) => {
|
||||
updates = args;
|
||||
return promise;
|
||||
};
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/** @type {RemoteForm<T, U>} */
|
||||
const instance = {};
|
||||
|
||||
instance.method = 'POST';
|
||||
instance.action = action;
|
||||
|
||||
/** @param {Parameters<RemoteForm<any, any>['enhance']>[0]} callback */
|
||||
const form_onsubmit = (callback) => {
|
||||
/** @param {SubmitEvent} event */
|
||||
return async (event) => {
|
||||
const form = /** @type {HTMLFormElement} */ (event.target);
|
||||
const method = event.submitter?.hasAttribute('formmethod')
|
||||
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formMethod
|
||||
: clone(form).method;
|
||||
|
||||
if (method !== 'post') return;
|
||||
|
||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
||||
const action = new URL(
|
||||
// We can't do submitter.formAction directly because that property is always set
|
||||
event.submitter?.hasAttribute('formaction')
|
||||
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
|
||||
: clone(form).action
|
||||
);
|
||||
|
||||
if (action.searchParams.get('/remote') !== action_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.submitter?.hasAttribute('formtarget')
|
||||
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formTarget
|
||||
: clone(form).target;
|
||||
|
||||
if (target === '_blank') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const form_data = new FormData(form, event.submitter);
|
||||
|
||||
if (DEV) {
|
||||
validate_form_data(form_data, clone(form).enctype);
|
||||
}
|
||||
|
||||
await handle_submit(form, form_data, callback);
|
||||
};
|
||||
};
|
||||
|
||||
/** @param {(event: SubmitEvent) => void} onsubmit */
|
||||
function create_attachment(onsubmit) {
|
||||
return (/** @type {HTMLFormElement} */ form) => {
|
||||
if (element) {
|
||||
let message = `A form object can only be attached to a single \`<form>\` element`;
|
||||
if (DEV && !key) {
|
||||
const name = id.split('/').pop();
|
||||
message += `. To create multiple instances, use \`${name}.for(key)\``;
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
element = form;
|
||||
|
||||
touched = {};
|
||||
|
||||
form.addEventListener('submit', onsubmit);
|
||||
|
||||
/** @param {Event} e */
|
||||
const handle_input = (e) => {
|
||||
// strictly speaking it can be an HTMLTextAreaElement or HTMLSelectElement
|
||||
// but that makes the types unnecessarily awkward
|
||||
const element = /** @type {HTMLInputElement} */ (e.target);
|
||||
|
||||
let name = element.name;
|
||||
if (!name) return;
|
||||
|
||||
const is_array = name.endsWith('[]');
|
||||
if (is_array) name = name.slice(0, -2);
|
||||
|
||||
const is_file = element.type === 'file';
|
||||
|
||||
touched[name] = true;
|
||||
|
||||
if (is_array) {
|
||||
let value;
|
||||
|
||||
if (element.tagName === 'SELECT') {
|
||||
value = Array.from(
|
||||
element.querySelectorAll('option:checked'),
|
||||
(e) => /** @type {HTMLOptionElement} */ (e).value
|
||||
);
|
||||
} else {
|
||||
const elements = /** @type {HTMLInputElement[]} */ (
|
||||
Array.from(form.querySelectorAll(`[name="${name}[]"]`))
|
||||
);
|
||||
|
||||
if (DEV) {
|
||||
for (const e of elements) {
|
||||
if ((e.type === 'file') !== is_file) {
|
||||
throw new Error(
|
||||
`Cannot mix and match file and non-file inputs under the same name ("${element.name}")`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = is_file
|
||||
? elements.map((input) => Array.from(input.files ?? [])).flat()
|
||||
: elements.map((element) => element.value);
|
||||
if (element.type === 'checkbox') {
|
||||
value = /** @type {string[]} */ (value.filter((_, i) => elements[i].checked));
|
||||
}
|
||||
}
|
||||
|
||||
set_nested_value(input, name, value);
|
||||
} else if (is_file) {
|
||||
if (DEV && element.multiple) {
|
||||
throw new Error(
|
||||
`Can only use the \`multiple\` attribute when \`name\` includes a \`[]\` suffix — consider changing "${name}" to "${name}[]"`
|
||||
);
|
||||
}
|
||||
|
||||
const file = /** @type {HTMLInputElement & { files: FileList }} */ (element).files[0];
|
||||
|
||||
if (file) {
|
||||
set_nested_value(input, name, file);
|
||||
} else {
|
||||
// Remove the property by setting to undefined and clean up
|
||||
const path_parts = name.split(/\.|\[|\]/).filter(Boolean);
|
||||
let current = /** @type {any} */ (input);
|
||||
for (let i = 0; i < path_parts.length - 1; i++) {
|
||||
if (current[path_parts[i]] == null) return;
|
||||
current = current[path_parts[i]];
|
||||
}
|
||||
delete current[path_parts[path_parts.length - 1]];
|
||||
}
|
||||
} else {
|
||||
set_nested_value(
|
||||
input,
|
||||
name,
|
||||
element.type === 'checkbox' && !element.checked ? null : element.value
|
||||
);
|
||||
}
|
||||
|
||||
name = name.replace(/^[nb]:/, '');
|
||||
|
||||
touched[name] = true;
|
||||
};
|
||||
|
||||
form.addEventListener('input', handle_input);
|
||||
|
||||
const handle_reset = async () => {
|
||||
// need to wait a moment, because the `reset` event occurs before
|
||||
// the inputs are actually updated (so that it can be cancelled)
|
||||
await tick();
|
||||
|
||||
input = convert_formdata(new FormData(form));
|
||||
};
|
||||
|
||||
form.addEventListener('reset', handle_reset);
|
||||
|
||||
return () => {
|
||||
form.removeEventListener('submit', onsubmit);
|
||||
form.removeEventListener('input', handle_input);
|
||||
form.removeEventListener('reset', handle_reset);
|
||||
element = null;
|
||||
preflight_schema = undefined;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
instance[createAttachmentKey()] = create_attachment(
|
||||
form_onsubmit(({ submit, form }) =>
|
||||
submit().then(() => {
|
||||
if (!issues.$) {
|
||||
form.reset();
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
let validate_id = 0;
|
||||
|
||||
// TODO 3.0 remove
|
||||
if (DEV) {
|
||||
throw_on_old_property_access(instance);
|
||||
|
||||
Object.defineProperty(instance, 'buttonProps', {
|
||||
get() {
|
||||
throw new Error(
|
||||
'`form.buttonProps` has been removed: Instead of `<button {...form.buttonProps}>, use `<button {...form.fields.action.as("submit", "value")}>`.' +
|
||||
' See the PR for more info: https://github.com/sveltejs/kit/pull/14622'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Object.defineProperties(instance, {
|
||||
fields: {
|
||||
get: () =>
|
||||
create_field_proxy(
|
||||
{},
|
||||
() => input,
|
||||
(path, value) => {
|
||||
if (path.length === 0) {
|
||||
input = value;
|
||||
} else {
|
||||
deep_set(input, path.map(String), value);
|
||||
|
||||
const key = build_path_string(path);
|
||||
touched[key] = true;
|
||||
}
|
||||
},
|
||||
() => issues
|
||||
)
|
||||
},
|
||||
result: {
|
||||
get: () => result
|
||||
},
|
||||
pending: {
|
||||
get: () => pending_count
|
||||
},
|
||||
preflight: {
|
||||
/** @type {RemoteForm<T, U>['preflight']} */
|
||||
value: (schema) => {
|
||||
preflight_schema = schema;
|
||||
return instance;
|
||||
}
|
||||
},
|
||||
validate: {
|
||||
/** @type {RemoteForm<any, any>['validate']} */
|
||||
value: async ({ includeUntouched = false, preflightOnly = false } = {}) => {
|
||||
if (!element) return;
|
||||
|
||||
const id = ++validate_id;
|
||||
|
||||
// wait a tick in case the user is calling validate() right after set() which takes time to propagate
|
||||
await tick();
|
||||
|
||||
const default_submitter = /** @type {HTMLElement | undefined} */ (
|
||||
element.querySelector('button:not([type]), [type="submit"]')
|
||||
);
|
||||
|
||||
const form_data = new FormData(element, default_submitter);
|
||||
|
||||
/** @type {InternalRemoteFormIssue[]} */
|
||||
let array = [];
|
||||
|
||||
const data = convert(form_data);
|
||||
|
||||
const validated = await preflight_schema?.['~standard'].validate(data);
|
||||
|
||||
if (validate_id !== id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (validated?.issues) {
|
||||
array = validated.issues.map((issue) => normalize_issue(issue, false));
|
||||
} else if (!preflightOnly) {
|
||||
const response = await fetch(`${base}/${app_dir}/remote/${action_id_without_key}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': BINARY_FORM_CONTENT_TYPE,
|
||||
// Validation should not be and will not be called during rendering, so it's save to use location here
|
||||
'x-sveltekit-pathname': location.pathname,
|
||||
'x-sveltekit-search': location.search
|
||||
},
|
||||
body: serialize_binary_form(data, {
|
||||
validate_only: true
|
||||
}).blob
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (validate_id !== id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.type === 'result') {
|
||||
array = /** @type {InternalRemoteFormIssue[]} */ (
|
||||
devalue.parse(result.result, app.decoders)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!includeUntouched && !submitted) {
|
||||
array = array.filter((issue) => touched[issue.name]);
|
||||
}
|
||||
|
||||
const is_server_validation = !validated?.issues && !preflightOnly;
|
||||
|
||||
raw_issues = is_server_validation
|
||||
? array
|
||||
: merge_with_server_issues(form_data, raw_issues, array);
|
||||
}
|
||||
},
|
||||
enhance: {
|
||||
/** @type {RemoteForm<any, any>['enhance']} */
|
||||
value: (callback) => {
|
||||
return {
|
||||
method: 'POST',
|
||||
action,
|
||||
[createAttachmentKey()]: create_attachment(form_onsubmit(callback))
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
const instance = create_instance();
|
||||
|
||||
Object.defineProperty(instance, 'for', {
|
||||
/** @type {RemoteForm<T, U>['for']} */
|
||||
value: (key) => {
|
||||
const entry = instances.get(key) ?? { count: 0, instance: create_instance(key) };
|
||||
|
||||
try {
|
||||
$effect.pre(() => {
|
||||
return () => {
|
||||
entry.count--;
|
||||
|
||||
void tick().then(() => {
|
||||
if (entry.count === 0) {
|
||||
instances.delete(key);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
entry.count += 1;
|
||||
instances.set(key, entry);
|
||||
} catch {
|
||||
// not in an effect context
|
||||
}
|
||||
|
||||
return entry.instance;
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FormData} form_data
|
||||
* @param {string} enctype
|
||||
*/
|
||||
function validate_form_data(form_data, enctype) {
|
||||
for (const key of form_data.keys()) {
|
||||
if (/^\$[.[]?/.test(key)) {
|
||||
throw new Error(
|
||||
'`$` is used to collect all FormData validation issues and cannot be used as the `name` of a form control'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (enctype !== 'multipart/form-data') {
|
||||
for (const value of form_data.values()) {
|
||||
if (value instanceof File) {
|
||||
throw new Error(
|
||||
'Your form contains <input type="file"> fields, but is missing the necessary `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.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/index.js
generated
vendored
Normal file
4
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { command } from './command.svelte.js';
|
||||
export { form } from './form.svelte.js';
|
||||
export { prerender } from './prerender.svelte.js';
|
||||
export { query, query_batch } from './query.svelte.js';
|
||||
184
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/prerender.svelte.js
generated
vendored
Normal file
184
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/prerender.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import { app_dir, base } from '$app/paths/internal/client';
|
||||
import { version } from '__sveltekit/environment';
|
||||
import * as devalue from 'devalue';
|
||||
import { DEV } from 'esm-env';
|
||||
import { app, remote_responses } from '../client.js';
|
||||
import {
|
||||
create_remote_function,
|
||||
get_remote_request_headers,
|
||||
remote_request
|
||||
} from './shared.svelte.js';
|
||||
|
||||
// Initialize Cache API for prerender functions
|
||||
const CACHE_NAME = DEV ? `sveltekit:${Date.now()}` : `sveltekit:${version}`;
|
||||
/** @type {Cache | undefined} */
|
||||
let prerender_cache;
|
||||
|
||||
const prerender_cache_ready = (async () => {
|
||||
if (typeof caches !== 'undefined') {
|
||||
try {
|
||||
prerender_cache = await caches.open(CACHE_NAME);
|
||||
|
||||
// Clean up old cache versions
|
||||
const cache_names = await caches.keys();
|
||||
for (const cache_name of cache_names) {
|
||||
if (cache_name.startsWith('sveltekit:') && cache_name !== CACHE_NAME) {
|
||||
await caches.delete(cache_name);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to initialize SvelteKit cache:', error);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @implements {Partial<Promise<T>>}
|
||||
*/
|
||||
class Prerender {
|
||||
/** @type {Promise<T>} */
|
||||
#promise;
|
||||
|
||||
#loading = $state(true);
|
||||
#ready = $state(false);
|
||||
|
||||
/** @type {T | undefined} */
|
||||
#current = $state.raw();
|
||||
|
||||
#error = $state.raw(undefined);
|
||||
|
||||
/**
|
||||
* @param {() => Promise<T>} fn
|
||||
*/
|
||||
constructor(fn) {
|
||||
this.#promise = fn().then(
|
||||
(value) => {
|
||||
this.#loading = false;
|
||||
this.#ready = true;
|
||||
this.#current = value;
|
||||
return value;
|
||||
},
|
||||
(error) => {
|
||||
this.#loading = false;
|
||||
this.#error = error;
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {((value: any) => any) | null | undefined} onfulfilled
|
||||
* @param {((reason: any) => any) | null | undefined} [onrejected]
|
||||
* @returns
|
||||
*/
|
||||
then(onfulfilled, onrejected) {
|
||||
return this.#promise.then(onfulfilled, onrejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {((reason: any) => any) | null | undefined} onrejected
|
||||
*/
|
||||
catch(onrejected) {
|
||||
return this.#promise.catch(onrejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(() => any) | null | undefined} onfinally
|
||||
*/
|
||||
finally(onfinally) {
|
||||
return this.#promise.finally(onfinally);
|
||||
}
|
||||
|
||||
get current() {
|
||||
return this.#current;
|
||||
}
|
||||
|
||||
get error() {
|
||||
return this.#error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the resource is loading.
|
||||
*/
|
||||
get loading() {
|
||||
return this.#loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once the resource has been loaded.
|
||||
*/
|
||||
get ready() {
|
||||
return this.#ready;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {string} encoded
|
||||
*/
|
||||
function put(url, encoded) {
|
||||
return /** @type {Cache} */ (prerender_cache)
|
||||
.put(
|
||||
url,
|
||||
// We need to create a new response because the original response is already consumed
|
||||
new Response(encoded, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch(() => {
|
||||
// Nothing we can do here
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function prerender(id) {
|
||||
return create_remote_function(id, (cache_key, payload) => {
|
||||
return new Prerender(async () => {
|
||||
await prerender_cache_ready;
|
||||
|
||||
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
|
||||
|
||||
if (Object.hasOwn(remote_responses, cache_key)) {
|
||||
const data = remote_responses[cache_key];
|
||||
|
||||
if (prerender_cache) {
|
||||
void put(url, devalue.stringify(data, app.encoders));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Do this here, after await Svelte' reactivity context is gone.
|
||||
const headers = get_remote_request_headers();
|
||||
|
||||
// Check the Cache API first
|
||||
if (prerender_cache) {
|
||||
try {
|
||||
const cached_response = await prerender_cache.match(url);
|
||||
|
||||
if (cached_response) {
|
||||
const cached_result = await cached_response.text();
|
||||
return devalue.parse(cached_result, app.decoders);
|
||||
}
|
||||
} catch {
|
||||
// Nothing we can do here
|
||||
}
|
||||
}
|
||||
|
||||
const encoded = await remote_request(url, headers);
|
||||
|
||||
// For successful prerender requests, save to cache
|
||||
if (prerender_cache) {
|
||||
void put(url, encoded);
|
||||
}
|
||||
|
||||
return devalue.parse(encoded, app.decoders);
|
||||
});
|
||||
});
|
||||
}
|
||||
341
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/query.svelte.js
generated
vendored
Normal file
341
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/query.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/** @import { RemoteQueryFunction } from '@sveltejs/kit' */
|
||||
/** @import { RemoteFunctionResponse } from 'types' */
|
||||
import { app_dir, base } from '$app/paths/internal/client';
|
||||
import { app, goto, query_map, remote_responses } from '../client.js';
|
||||
import { tick } from 'svelte';
|
||||
import {
|
||||
create_remote_function,
|
||||
get_remote_request_headers,
|
||||
remote_request
|
||||
} from './shared.svelte.js';
|
||||
import * as devalue from 'devalue';
|
||||
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
||||
import { DEV } from 'esm-env';
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {RemoteQueryFunction<any, any>}
|
||||
*/
|
||||
export function query(id) {
|
||||
if (DEV) {
|
||||
// If this reruns as part of HMR, refresh the query
|
||||
for (const [key, entry] of query_map) {
|
||||
if (key === id || key.startsWith(id + '/')) {
|
||||
// use optional chaining in case a prerender function was turned into a query
|
||||
entry.resource.refresh?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return create_remote_function(id, (cache_key, payload) => {
|
||||
return new Query(cache_key, async () => {
|
||||
if (Object.hasOwn(remote_responses, cache_key)) {
|
||||
return remote_responses[cache_key];
|
||||
}
|
||||
|
||||
const url = `${base}/${app_dir}/remote/${id}${payload ? `?payload=${payload}` : ''}`;
|
||||
|
||||
const result = await remote_request(url, get_remote_request_headers());
|
||||
return devalue.parse(result, app.decoders);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {(arg: any) => Query<any>}
|
||||
*/
|
||||
export function query_batch(id) {
|
||||
/** @type {Map<string, Array<{resolve: (value: any) => void, reject: (error: any) => void}>>} */
|
||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- we don't need reactivity for this
|
||||
let batching = new Map();
|
||||
|
||||
return create_remote_function(id, (cache_key, payload) => {
|
||||
return new Query(cache_key, () => {
|
||||
if (Object.hasOwn(remote_responses, cache_key)) {
|
||||
return remote_responses[cache_key];
|
||||
}
|
||||
|
||||
// Collect all the calls to the same query in the same macrotask,
|
||||
// then execute them as one backend request.
|
||||
return new Promise((resolve, reject) => {
|
||||
// create_remote_function caches identical calls, but in case a refresh to the same query is called multiple times this function
|
||||
// is invoked multiple times with the same payload, so we need to deduplicate here
|
||||
const entry = batching.get(payload) ?? [];
|
||||
entry.push({ resolve, reject });
|
||||
batching.set(payload, entry);
|
||||
|
||||
if (batching.size > 1) return;
|
||||
|
||||
// Do this here, after await Svelte' reactivity context is gone.
|
||||
// TODO is it possible to have batches of the same key
|
||||
// but in different forks/async contexts and in the same macrotask?
|
||||
// If so this would potentially be buggy
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...get_remote_request_headers()
|
||||
};
|
||||
|
||||
// Wait for the next macrotask - don't use microtask as Svelte runtime uses these to collect changes and flush them,
|
||||
// and flushes could reveal more queries that should be batched.
|
||||
setTimeout(async () => {
|
||||
const batched = batching;
|
||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
||||
batching = new Map();
|
||||
|
||||
try {
|
||||
const response = await fetch(`${base}/${app_dir}/remote/${id}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
payloads: Array.from(batched.keys())
|
||||
}),
|
||||
headers
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to execute batch query');
|
||||
}
|
||||
|
||||
const result = /** @type {RemoteFunctionResponse} */ (await response.json());
|
||||
if (result.type === 'error') {
|
||||
throw new HttpError(result.status ?? 500, result.error);
|
||||
}
|
||||
|
||||
if (result.type === 'redirect') {
|
||||
await goto(result.location);
|
||||
throw new Redirect(307, result.location);
|
||||
}
|
||||
|
||||
const results = devalue.parse(result.result, app.decoders);
|
||||
|
||||
// Resolve individual queries
|
||||
// Maps guarantee insertion order so we can do it like this
|
||||
let i = 0;
|
||||
|
||||
for (const resolvers of batched.values()) {
|
||||
for (const { resolve, reject } of resolvers) {
|
||||
if (results[i].type === 'error') {
|
||||
reject(new HttpError(results[i].status, results[i].error));
|
||||
} else {
|
||||
resolve(results[i].data);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} catch (error) {
|
||||
// Reject all queries in the batch
|
||||
for (const resolver of batched.values()) {
|
||||
for (const { reject } of resolver) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @implements {Partial<Promise<T>>}
|
||||
*/
|
||||
export class Query {
|
||||
/** @type {string} */
|
||||
_key;
|
||||
|
||||
#init = false;
|
||||
/** @type {() => Promise<T>} */
|
||||
#fn;
|
||||
#loading = $state(true);
|
||||
/** @type {Array<() => void>} */
|
||||
#latest = [];
|
||||
|
||||
/** @type {boolean} */
|
||||
#ready = $state(false);
|
||||
/** @type {T | undefined} */
|
||||
#raw = $state.raw();
|
||||
/** @type {Promise<void>} */
|
||||
#promise;
|
||||
/** @type {Array<(old: T) => T>} */
|
||||
#overrides = $state([]);
|
||||
|
||||
/** @type {T | undefined} */
|
||||
#current = $derived.by(() => {
|
||||
// don't reduce undefined value
|
||||
if (!this.#ready) return undefined;
|
||||
|
||||
return this.#overrides.reduce((v, r) => r(v), /** @type {T} */ (this.#raw));
|
||||
});
|
||||
|
||||
#error = $state.raw(undefined);
|
||||
|
||||
/** @type {Promise<T>['then']} */
|
||||
// @ts-expect-error TS doesn't understand that the promise returns something
|
||||
#then = $derived.by(() => {
|
||||
const p = this.#promise;
|
||||
this.#overrides.length;
|
||||
|
||||
return (resolve, reject) => {
|
||||
const result = (async () => {
|
||||
await p;
|
||||
// svelte-ignore await_reactivity_loss
|
||||
await tick();
|
||||
return /** @type {T} */ (this.#current);
|
||||
})();
|
||||
|
||||
if (resolve || reject) {
|
||||
return result.then(resolve, reject);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {() => Promise<T>} fn
|
||||
*/
|
||||
constructor(key, fn) {
|
||||
this._key = key;
|
||||
this.#fn = fn;
|
||||
this.#promise = $state.raw(this.#run());
|
||||
}
|
||||
|
||||
#run() {
|
||||
// Prevent state_unsafe_mutation error on first run when the resource is created within the template
|
||||
if (this.#init) {
|
||||
this.#loading = true;
|
||||
} else {
|
||||
this.#init = true;
|
||||
}
|
||||
|
||||
// Don't use Promise.withResolvers, it's too new still
|
||||
/** @type {() => void} */
|
||||
let resolve;
|
||||
/** @type {(e?: any) => void} */
|
||||
let reject;
|
||||
/** @type {Promise<void>} */
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
this.#latest.push(
|
||||
// @ts-expect-error it's defined at this point
|
||||
resolve
|
||||
);
|
||||
|
||||
Promise.resolve(this.#fn())
|
||||
.then((value) => {
|
||||
// Skip the response if resource was refreshed with a later promise while we were waiting for this one to resolve
|
||||
const idx = this.#latest.indexOf(resolve);
|
||||
if (idx === -1) return;
|
||||
|
||||
this.#latest.splice(0, idx).forEach((r) => r());
|
||||
this.#ready = true;
|
||||
this.#loading = false;
|
||||
this.#raw = value;
|
||||
this.#error = undefined;
|
||||
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => {
|
||||
const idx = this.#latest.indexOf(resolve);
|
||||
if (idx === -1) return;
|
||||
|
||||
this.#latest.splice(0, idx).forEach((r) => r());
|
||||
this.#error = e;
|
||||
this.#loading = false;
|
||||
reject(e);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
get then() {
|
||||
return this.#then;
|
||||
}
|
||||
|
||||
get catch() {
|
||||
this.#then;
|
||||
return (/** @type {any} */ reject) => {
|
||||
return this.#then(undefined, reject);
|
||||
};
|
||||
}
|
||||
|
||||
get finally() {
|
||||
this.#then;
|
||||
return (/** @type {any} */ fn) => {
|
||||
return this.#then(
|
||||
(value) => {
|
||||
fn();
|
||||
return value;
|
||||
},
|
||||
(error) => {
|
||||
fn();
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
get current() {
|
||||
return this.#current;
|
||||
}
|
||||
|
||||
get error() {
|
||||
return this.#error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the resource is loading or reloading.
|
||||
*/
|
||||
get loading() {
|
||||
return this.#loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once the resource has been loaded for the first time.
|
||||
*/
|
||||
get ready() {
|
||||
return this.#ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
refresh() {
|
||||
delete remote_responses[this._key];
|
||||
return (this.#promise = this.#run());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
*/
|
||||
set(value) {
|
||||
this.#ready = true;
|
||||
this.#loading = false;
|
||||
this.#error = undefined;
|
||||
this.#raw = value;
|
||||
this.#promise = Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(old: T) => T} fn
|
||||
*/
|
||||
withOverride(fn) {
|
||||
this.#overrides.push(fn);
|
||||
|
||||
return {
|
||||
_key: this._key,
|
||||
release: () => {
|
||||
const i = this.#overrides.indexOf(fn);
|
||||
|
||||
if (i !== -1) {
|
||||
this.#overrides.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
154
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/shared.svelte.js
generated
vendored
Normal file
154
frontend/node_modules/@sveltejs/kit/src/runtime/client/remote-functions/shared.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/** @import { RemoteQueryOverride } from '@sveltejs/kit' */
|
||||
/** @import { RemoteFunctionResponse } from 'types' */
|
||||
/** @import { Query } from './query.svelte.js' */
|
||||
import * as devalue from 'devalue';
|
||||
import { app, goto, query_map, remote_responses } from '../client.js';
|
||||
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
||||
import { tick, untrack } from 'svelte';
|
||||
import { create_remote_key, stringify_remote_arg } from '../../shared.js';
|
||||
import { page } from '../state.svelte.js';
|
||||
|
||||
/**
|
||||
* @returns {{ 'x-sveltekit-pathname': string, 'x-sveltekit-search': string }}
|
||||
*/
|
||||
export function get_remote_request_headers() {
|
||||
// This will be the correct value of the current or soon-current url,
|
||||
// even in forks because it's state-based - therefore not using window.location.
|
||||
// Use untrack(...) to Avoid accidental reactive dependency on pathname/search
|
||||
return untrack(() => ({
|
||||
'x-sveltekit-pathname': page.url.pathname,
|
||||
'x-sveltekit-search': page.url.search
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {HeadersInit} headers
|
||||
*/
|
||||
export async function remote_request(url, headers) {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new HttpError(500, 'Failed to execute remote function');
|
||||
}
|
||||
|
||||
const result = /** @type {RemoteFunctionResponse} */ (await response.json());
|
||||
|
||||
if (result.type === 'redirect') {
|
||||
await goto(result.location);
|
||||
throw new Redirect(307, result.location);
|
||||
}
|
||||
|
||||
if (result.type === 'error') {
|
||||
throw new HttpError(result.status ?? 500, result.error);
|
||||
}
|
||||
|
||||
return result.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-version of the `query`/`prerender`/`cache` function from `$app/server`.
|
||||
* @param {string} id
|
||||
* @param {(key: string, args: string) => any} create
|
||||
*/
|
||||
export function create_remote_function(id, create) {
|
||||
return (/** @type {any} */ arg) => {
|
||||
const payload = stringify_remote_arg(arg, app.hooks.transport);
|
||||
const cache_key = create_remote_key(id, payload);
|
||||
let entry = query_map.get(cache_key);
|
||||
|
||||
let tracking = true;
|
||||
try {
|
||||
$effect.pre(() => {
|
||||
if (entry) entry.count++;
|
||||
return () => {
|
||||
const entry = query_map.get(cache_key);
|
||||
if (entry) {
|
||||
entry.count--;
|
||||
void tick().then(() => {
|
||||
if (!entry.count && entry === query_map.get(cache_key)) {
|
||||
query_map.delete(cache_key);
|
||||
delete remote_responses[cache_key];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
} catch {
|
||||
tracking = false;
|
||||
}
|
||||
|
||||
let resource = entry?.resource;
|
||||
if (!resource) {
|
||||
resource = create(cache_key, payload);
|
||||
|
||||
Object.defineProperty(resource, '_key', {
|
||||
value: cache_key
|
||||
});
|
||||
|
||||
query_map.set(
|
||||
cache_key,
|
||||
(entry = {
|
||||
count: tracking ? 1 : 0,
|
||||
resource
|
||||
})
|
||||
);
|
||||
|
||||
resource
|
||||
.then(() => {
|
||||
void tick().then(() => {
|
||||
if (
|
||||
!(/** @type {NonNullable<typeof entry>} */ (entry).count) &&
|
||||
entry === query_map.get(cache_key)
|
||||
) {
|
||||
// If no one is tracking this resource anymore, we can delete it from the cache
|
||||
query_map.delete(cache_key);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// error delete the resource from the cache
|
||||
// TODO is that correct?
|
||||
query_map.delete(cache_key);
|
||||
});
|
||||
}
|
||||
|
||||
return resource;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<Query<any> | RemoteQueryOverride>} updates
|
||||
*/
|
||||
export function release_overrides(updates) {
|
||||
for (const update of updates) {
|
||||
if ('release' in update) {
|
||||
update.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} stringified_refreshes
|
||||
* @param {Array<Query<any> | RemoteQueryOverride>} updates
|
||||
*/
|
||||
export function refresh_queries(stringified_refreshes, updates = []) {
|
||||
const refreshes = Object.entries(devalue.parse(stringified_refreshes, app.decoders));
|
||||
|
||||
// `refreshes` is a superset of `updates`
|
||||
for (const [key, value] of refreshes) {
|
||||
// If there was an optimistic update, release it right before we update the query
|
||||
const update = updates.find((u) => u._key === key);
|
||||
if (update && 'release' in update) {
|
||||
update.release();
|
||||
}
|
||||
// Update the query with the new value
|
||||
const entry = query_map.get(key);
|
||||
entry?.resource.set(value);
|
||||
}
|
||||
}
|
||||
28
frontend/node_modules/@sveltejs/kit/src/runtime/client/session-storage.js
generated
vendored
Normal file
28
frontend/node_modules/@sveltejs/kit/src/runtime/client/session-storage.js
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Read a value from `sessionStorage`
|
||||
* @param {string} key
|
||||
* @param {(value: string) => any} parse
|
||||
*/
|
||||
/*@__NO_SIDE_EFFECTS__*/
|
||||
export function get(key, parse = JSON.parse) {
|
||||
try {
|
||||
return parse(sessionStorage[key]);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to `sessionStorage`
|
||||
* @param {string} key
|
||||
* @param {any} value
|
||||
* @param {(value: any) => string} stringify
|
||||
*/
|
||||
export function set(key, value, stringify = JSON.stringify) {
|
||||
const data = stringify(value);
|
||||
try {
|
||||
sessionStorage[key] = data;
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
59
frontend/node_modules/@sveltejs/kit/src/runtime/client/state.svelte.js
generated
vendored
Normal file
59
frontend/node_modules/@sveltejs/kit/src/runtime/client/state.svelte.js
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { updated_listener } from './utils.js';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Page} */
|
||||
export let page;
|
||||
|
||||
/** @type {{ current: import('@sveltejs/kit').Navigation | null }} */
|
||||
export let navigating;
|
||||
|
||||
/** @type {{ current: boolean }} */
|
||||
export let updated;
|
||||
|
||||
// this is a bootleg way to tell if we're in old svelte or new svelte
|
||||
const is_legacy =
|
||||
onMount.toString().includes('$$') || /function \w+\(\) \{\}/.test(onMount.toString());
|
||||
|
||||
const placeholder_url = 'a:';
|
||||
|
||||
if (is_legacy) {
|
||||
page = {
|
||||
data: {},
|
||||
form: null,
|
||||
error: null,
|
||||
params: {},
|
||||
route: { id: null },
|
||||
state: {},
|
||||
status: -1,
|
||||
url: new URL(placeholder_url)
|
||||
};
|
||||
navigating = { current: null };
|
||||
updated = { current: false };
|
||||
} else {
|
||||
page = new (class Page {
|
||||
data = $state.raw({});
|
||||
form = $state.raw(null);
|
||||
error = $state.raw(null);
|
||||
params = $state.raw({});
|
||||
route = $state.raw({ id: null });
|
||||
state = $state.raw({});
|
||||
status = $state.raw(-1);
|
||||
url = $state.raw(new URL(placeholder_url));
|
||||
})();
|
||||
|
||||
navigating = new (class Navigating {
|
||||
current = $state.raw(null);
|
||||
})();
|
||||
|
||||
updated = new (class Updated {
|
||||
current = $state.raw(false);
|
||||
})();
|
||||
updated_listener.v = () => (updated.current = true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@sveltejs/kit').Page} new_page
|
||||
*/
|
||||
export function update(new_page) {
|
||||
Object.assign(page, new_page);
|
||||
}
|
||||
133
frontend/node_modules/@sveltejs/kit/src/runtime/client/types.d.ts
generated
vendored
Normal file
133
frontend/node_modules/@sveltejs/kit/src/runtime/client/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import { SvelteComponent } from 'svelte';
|
||||
import {
|
||||
ClientHooks,
|
||||
CSRPageNode,
|
||||
CSRPageNodeLoader,
|
||||
CSRRoute,
|
||||
CSRRouteServer,
|
||||
ServerDataNode,
|
||||
TrailingSlash,
|
||||
Uses
|
||||
} from 'types';
|
||||
import { Page, ParamMatcher } from '@sveltejs/kit';
|
||||
|
||||
export interface SvelteKitApp {
|
||||
/**
|
||||
* A list of all the error/layout/page nodes used in the app.
|
||||
* - In case of router.resolution=client, this is filled completely upfront.
|
||||
* - In case of router.resolution=server, this is filled with the root layout and root error page
|
||||
* at the beginning and then filled up as the user navigates around the app, loading new nodes
|
||||
*/
|
||||
nodes: CSRPageNodeLoader[];
|
||||
|
||||
/**
|
||||
* A list of all layout node ids that have a server load function.
|
||||
* Pages are not present because it's shorter to encode it on the leaf itself.
|
||||
*
|
||||
* In case of router.resolution=server, this only contains one entry for the root layout.
|
||||
*/
|
||||
server_loads: number[];
|
||||
|
||||
/**
|
||||
* A map of `[routeId: string]: [leaf, layouts, errors]` tuples, which
|
||||
* is parsed into an array of routes on startup. The numbers refer to the indices in `nodes`.
|
||||
* If the leaf number is negative, it means it does use a server load function and the complement is the node index.
|
||||
* The route layout and error nodes are not referenced, they are always number 0 and 1 and always apply.
|
||||
*
|
||||
* In case of router.resolution=server, this object is empty, as resolution happens on the server.
|
||||
*/
|
||||
dictionary: Record<string, [leaf: number, layouts: number[], errors?: number[]]>;
|
||||
|
||||
/**
|
||||
* A map of `[matcherName: string]: (..) => boolean`, which is used to match route parameters.
|
||||
*
|
||||
* In case of router.resolution=server, this object is empty, as resolution happens on the server.
|
||||
*/
|
||||
matchers: Record<string, ParamMatcher>;
|
||||
|
||||
hooks: ClientHooks;
|
||||
|
||||
decode: (type: string, value: any) => any;
|
||||
|
||||
decoders: Record<string, (data: any) => any>;
|
||||
encoders: Record<string, (data: any) => any>;
|
||||
|
||||
/**
|
||||
* Whether or not we're using hash-based routing
|
||||
*/
|
||||
hash: boolean;
|
||||
|
||||
root: typeof SvelteComponent;
|
||||
}
|
||||
|
||||
export type NavigationIntent = {
|
||||
/** `url.pathname + url.search` */
|
||||
id: string;
|
||||
/** Whether we are invalidating or navigating */
|
||||
invalidating: boolean;
|
||||
/** The route parameters */
|
||||
params: Record<string, string>;
|
||||
/** The route that matches `path` */
|
||||
route: CSRRoute;
|
||||
/** The destination URL */
|
||||
url: URL;
|
||||
};
|
||||
|
||||
export type NavigationResult = NavigationRedirect | NavigationFinished;
|
||||
|
||||
export type NavigationRedirect = {
|
||||
type: 'redirect';
|
||||
location: string;
|
||||
};
|
||||
|
||||
export type NavigationFinished = {
|
||||
type: 'loaded';
|
||||
state: NavigationState;
|
||||
props: {
|
||||
constructors: Array<typeof SvelteComponent>;
|
||||
errors?: Array<typeof SvelteComponent | undefined>;
|
||||
components?: SvelteComponent[];
|
||||
page: Page;
|
||||
form?: Record<string, any> | null;
|
||||
error?: App.Error;
|
||||
[key: `data_${number}`]: Record<string, any>;
|
||||
};
|
||||
};
|
||||
|
||||
export type BranchNode = {
|
||||
node: CSRPageNode;
|
||||
loader: CSRPageNodeLoader;
|
||||
server: DataNode | null;
|
||||
universal: DataNode | null;
|
||||
data: Record<string, any> | null;
|
||||
slash?: TrailingSlash;
|
||||
};
|
||||
|
||||
export interface DataNode {
|
||||
type: 'data';
|
||||
data: Record<string, any> | null;
|
||||
uses: Uses;
|
||||
slash?: TrailingSlash;
|
||||
}
|
||||
|
||||
export interface NavigationState {
|
||||
branch: Array<BranchNode | undefined>;
|
||||
error: App.Error | null;
|
||||
params: Record<string, string>;
|
||||
route: CSRRoute | null;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
export interface HydrateOptions {
|
||||
status: number;
|
||||
error: App.Error | null;
|
||||
node_ids: number[];
|
||||
params: Record<string, string>;
|
||||
route: { id: string | null };
|
||||
/** Only used when `router.resolution=server`; can then still be undefined in case of 404 */
|
||||
server_route?: CSRRouteServer;
|
||||
data: Array<ServerDataNode | null>;
|
||||
form: Record<string, any> | null;
|
||||
/** The results of all remote functions executed during SSR so that they can be reused during hydration */
|
||||
remote: Record<string, any>;
|
||||
}
|
||||
365
frontend/node_modules/@sveltejs/kit/src/runtime/client/utils.js
generated
vendored
Normal file
365
frontend/node_modules/@sveltejs/kit/src/runtime/client/utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
import { BROWSER, DEV } from 'esm-env';
|
||||
import { writable } from 'svelte/store';
|
||||
import { assets } from '$app/paths';
|
||||
import { version } from '__sveltekit/environment';
|
||||
import { PRELOAD_PRIORITIES } from './constants.js';
|
||||
|
||||
/* global __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
|
||||
|
||||
export const origin = BROWSER ? location.origin : '';
|
||||
|
||||
/** @param {string | URL} url */
|
||||
export function resolve_url(url) {
|
||||
if (url instanceof URL) return url;
|
||||
|
||||
let baseURI = document.baseURI;
|
||||
|
||||
if (!baseURI) {
|
||||
const baseTags = document.getElementsByTagName('base');
|
||||
baseURI = baseTags.length ? baseTags[0].href : document.URL;
|
||||
}
|
||||
|
||||
return new URL(url, baseURI);
|
||||
}
|
||||
|
||||
export function scroll_state() {
|
||||
return {
|
||||
x: pageXOffset,
|
||||
y: pageYOffset
|
||||
};
|
||||
}
|
||||
|
||||
const warned = new WeakSet();
|
||||
|
||||
/** @typedef {keyof typeof valid_link_options} LinkOptionName */
|
||||
|
||||
const valid_link_options = /** @type {const} */ ({
|
||||
'preload-code': ['', 'off', 'false', 'tap', 'hover', 'viewport', 'eager'],
|
||||
'preload-data': ['', 'off', 'false', 'tap', 'hover'],
|
||||
keepfocus: ['', 'true', 'off', 'false'],
|
||||
noscroll: ['', 'true', 'off', 'false'],
|
||||
reload: ['', 'true', 'off', 'false'],
|
||||
replacestate: ['', 'true', 'off', 'false']
|
||||
});
|
||||
|
||||
/**
|
||||
* @template {LinkOptionName} T
|
||||
* @typedef {typeof valid_link_options[T][number]} ValidLinkOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {LinkOptionName} T
|
||||
* @param {Element} element
|
||||
* @param {T} name
|
||||
*/
|
||||
function link_option(element, name) {
|
||||
const value = /** @type {ValidLinkOptions<T> | null} */ (
|
||||
element.getAttribute(`data-sveltekit-${name}`)
|
||||
);
|
||||
|
||||
if (DEV) {
|
||||
validate_link_option(element, name, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {LinkOptionName} T
|
||||
* @template {ValidLinkOptions<T> | null} U
|
||||
* @param {Element} element
|
||||
* @param {T} name
|
||||
* @param {U} value
|
||||
*/
|
||||
function validate_link_option(element, name, value) {
|
||||
if (value === null) return;
|
||||
|
||||
// @ts-expect-error - includes is dumb
|
||||
if (!warned.has(element) && !valid_link_options[name].includes(value)) {
|
||||
console.error(
|
||||
`Unexpected value for ${name} — should be one of ${valid_link_options[name]
|
||||
.map((option) => JSON.stringify(option))
|
||||
.join(', ')}`,
|
||||
element
|
||||
);
|
||||
|
||||
warned.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
const levels = {
|
||||
...PRELOAD_PRIORITIES,
|
||||
'': PRELOAD_PRIORITIES.hover
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
* @returns {Element | null}
|
||||
*/
|
||||
function parent_element(element) {
|
||||
let parent = element.assignedSlot ?? element.parentNode;
|
||||
|
||||
// @ts-expect-error handle shadow roots
|
||||
if (parent?.nodeType === 11) parent = parent.host;
|
||||
|
||||
return /** @type {Element} */ (parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
* @param {Element} target
|
||||
*/
|
||||
export function find_anchor(element, target) {
|
||||
while (element && element !== target) {
|
||||
if (element.nodeName.toUpperCase() === 'A' && element.hasAttribute('href')) {
|
||||
return /** @type {HTMLAnchorElement | SVGAElement} */ (element);
|
||||
}
|
||||
|
||||
element = /** @type {Element} */ (parent_element(element));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLAnchorElement | SVGAElement} a
|
||||
* @param {string} base
|
||||
* @param {boolean} uses_hash_router
|
||||
*/
|
||||
export function get_link_info(a, base, uses_hash_router) {
|
||||
/** @type {URL | undefined} */
|
||||
let url;
|
||||
|
||||
try {
|
||||
url = new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
|
||||
|
||||
// if the hash doesn't start with `#/` then it's probably linking to an id on the current page
|
||||
if (uses_hash_router && url.hash.match(/^#[^/]/)) {
|
||||
const route = location.hash.split('#')[1] || '/';
|
||||
url.hash = `#${route}${url.hash}`;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const target = a instanceof SVGAElement ? a.target.baseVal : a.target;
|
||||
|
||||
const external =
|
||||
!url ||
|
||||
!!target ||
|
||||
is_external_url(url, base, uses_hash_router) ||
|
||||
(a.getAttribute('rel') || '').split(/\s+/).includes('external');
|
||||
|
||||
const download = url?.origin === origin && a.hasAttribute('download');
|
||||
|
||||
return { url, external, target, download };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLFormElement | HTMLAnchorElement | SVGAElement} element
|
||||
*/
|
||||
export function get_router_options(element) {
|
||||
/** @type {ValidLinkOptions<'keepfocus'> | null} */
|
||||
let keepfocus = null;
|
||||
|
||||
/** @type {ValidLinkOptions<'noscroll'> | null} */
|
||||
let noscroll = null;
|
||||
|
||||
/** @type {ValidLinkOptions<'preload-code'> | null} */
|
||||
let preload_code = null;
|
||||
|
||||
/** @type {ValidLinkOptions<'preload-data'> | null} */
|
||||
let preload_data = null;
|
||||
|
||||
/** @type {ValidLinkOptions<'reload'> | null} */
|
||||
let reload = null;
|
||||
|
||||
/** @type {ValidLinkOptions<'replacestate'> | null} */
|
||||
let replace_state = null;
|
||||
|
||||
/** @type {Element} */
|
||||
let el = element;
|
||||
|
||||
while (el && el !== document.documentElement) {
|
||||
if (preload_code === null) preload_code = link_option(el, 'preload-code');
|
||||
if (preload_data === null) preload_data = link_option(el, 'preload-data');
|
||||
if (keepfocus === null) keepfocus = link_option(el, 'keepfocus');
|
||||
if (noscroll === null) noscroll = link_option(el, 'noscroll');
|
||||
if (reload === null) reload = link_option(el, 'reload');
|
||||
if (replace_state === null) replace_state = link_option(el, 'replacestate');
|
||||
|
||||
el = /** @type {Element} */ (parent_element(el));
|
||||
}
|
||||
|
||||
/** @param {string | null} value */
|
||||
function get_option_state(value) {
|
||||
switch (value) {
|
||||
case '':
|
||||
case 'true':
|
||||
return true;
|
||||
case 'off':
|
||||
case 'false':
|
||||
return false;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
preload_code: levels[preload_code ?? 'off'],
|
||||
preload_data: levels[preload_data ?? 'off'],
|
||||
keepfocus: get_option_state(keepfocus),
|
||||
noscroll: get_option_state(noscroll),
|
||||
reload: get_option_state(reload),
|
||||
replace_state: get_option_state(replace_state)
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {any} value */
|
||||
export function notifiable_store(value) {
|
||||
const store = writable(value);
|
||||
let ready = true;
|
||||
|
||||
function notify() {
|
||||
ready = true;
|
||||
store.update((val) => val);
|
||||
}
|
||||
|
||||
/** @param {any} new_value */
|
||||
function set(new_value) {
|
||||
ready = false;
|
||||
store.set(new_value);
|
||||
}
|
||||
|
||||
/** @param {(value: any) => void} run */
|
||||
function subscribe(run) {
|
||||
/** @type {any} */
|
||||
let old_value;
|
||||
return store.subscribe((new_value) => {
|
||||
if (old_value === undefined || (ready && new_value !== old_value)) {
|
||||
run((old_value = new_value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { notify, set, subscribe };
|
||||
}
|
||||
|
||||
export const updated_listener = {
|
||||
v: () => {}
|
||||
};
|
||||
|
||||
export function create_updated_store() {
|
||||
const { set, subscribe } = writable(false);
|
||||
|
||||
if (DEV || !BROWSER) {
|
||||
return {
|
||||
subscribe,
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
check: async () => false
|
||||
};
|
||||
}
|
||||
|
||||
const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__;
|
||||
|
||||
/** @type {NodeJS.Timeout} */
|
||||
let timeout;
|
||||
|
||||
/** @type {() => Promise<boolean>} */
|
||||
async function check() {
|
||||
clearTimeout(timeout);
|
||||
|
||||
if (interval) timeout = setTimeout(check, interval);
|
||||
|
||||
try {
|
||||
const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
|
||||
headers: {
|
||||
pragma: 'no-cache',
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const updated = data.version !== version;
|
||||
|
||||
if (updated) {
|
||||
set(true);
|
||||
updated_listener.v();
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
return updated;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (interval) timeout = setTimeout(check, interval);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
check
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Is external if
|
||||
* - origin different
|
||||
* - path doesn't start with base
|
||||
* - uses hash router and pathname is more than base
|
||||
* @param {URL} url
|
||||
* @param {string} base
|
||||
* @param {boolean} hash_routing
|
||||
*/
|
||||
export function is_external_url(url, base, hash_routing) {
|
||||
if (url.origin !== origin || !url.pathname.startsWith(base)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hash_routing) {
|
||||
return url.pathname !== location.pathname;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @type {Set<string> | null} */
|
||||
let seen = null;
|
||||
|
||||
/**
|
||||
* Used for server-side resolution, to replicate Vite's CSS loading behaviour in production.
|
||||
*
|
||||
* Closely modelled after https://github.com/vitejs/vite/blob/3dd12f4724130fdf8ba44c6d3252ebdff407fd47/packages/vite/src/node/plugins/importAnalysisBuild.ts#L214
|
||||
* (which ideally we could just use directly, but it's not exported)
|
||||
* @param {string[]} deps
|
||||
*/
|
||||
export function load_css(deps) {
|
||||
if (__SVELTEKIT_CLIENT_ROUTING__) return;
|
||||
|
||||
const csp_nonce_meta = /** @type {HTMLMetaElement} */ (
|
||||
document.querySelector('meta[property=csp-nonce]')
|
||||
);
|
||||
const csp_nonce = csp_nonce_meta?.nonce || csp_nonce_meta?.getAttribute('nonce');
|
||||
|
||||
seen ??= new Set(
|
||||
Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map((link) => {
|
||||
return /** @type {HTMLLinkElement} */ (link).href;
|
||||
})
|
||||
);
|
||||
|
||||
for (const dep of deps) {
|
||||
const href = new URL(dep, document.baseURI).href;
|
||||
|
||||
if (seen.has(href)) continue;
|
||||
seen.add(href);
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.crossOrigin = '';
|
||||
link.href = dep;
|
||||
if (csp_nonce) {
|
||||
link.setAttribute('nonce', csp_nonce);
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
}
|
||||
6
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-4/error.svelte
generated
vendored
Normal file
6
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-4/error.svelte
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
</script>
|
||||
|
||||
<h1>{$page.status}</h1>
|
||||
<p>{$page.error?.message}</p>
|
||||
1
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-4/layout.svelte
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-4/layout.svelte
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<slot />
|
||||
6
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte
generated
vendored
Normal file
6
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
import { page } from '$app/state';
|
||||
</script>
|
||||
|
||||
<h1>{page.status}</h1>
|
||||
<p>{page.error?.message}</p>
|
||||
5
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte
generated
vendored
Normal file
5
frontend/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
1
frontend/node_modules/@sveltejs/kit/src/runtime/env/dynamic/private.js
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/runtime/env/dynamic/private.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { private_env as env } from '../../shared-server.js';
|
||||
1
frontend/node_modules/@sveltejs/kit/src/runtime/env/dynamic/public.js
generated
vendored
Normal file
1
frontend/node_modules/@sveltejs/kit/src/runtime/env/dynamic/public.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { public_env as env } from '../../shared-server.js';
|
||||
879
frontend/node_modules/@sveltejs/kit/src/runtime/form-utils.js
generated
vendored
Normal file
879
frontend/node_modules/@sveltejs/kit/src/runtime/form-utils.js
generated
vendored
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
/** @import { RemoteForm } from '@sveltejs/kit' */
|
||||
/** @import { BinaryFormMeta, InternalRemoteFormIssue } from 'types' */
|
||||
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
||||
|
||||
import { DEV } from 'esm-env';
|
||||
import * as devalue from 'devalue';
|
||||
import { text_decoder, text_encoder } from './utils.js';
|
||||
import { SvelteKitError } from '@sveltejs/kit/internal';
|
||||
|
||||
/**
|
||||
* Sets a value in a nested object using a path string, mutating the original object
|
||||
* @param {Record<string, any>} object
|
||||
* @param {string} path_string
|
||||
* @param {any} value
|
||||
*/
|
||||
export function set_nested_value(object, path_string, value) {
|
||||
if (path_string.startsWith('n:')) {
|
||||
path_string = path_string.slice(2);
|
||||
value = value === '' ? undefined : parseFloat(value);
|
||||
} else if (path_string.startsWith('b:')) {
|
||||
path_string = path_string.slice(2);
|
||||
value = value === 'on';
|
||||
}
|
||||
|
||||
deep_set(object, split_path(path_string), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert `FormData` into a POJO
|
||||
* @param {FormData} data
|
||||
*/
|
||||
export function convert_formdata(data) {
|
||||
/** @type {Record<string, any>} */
|
||||
const result = {};
|
||||
|
||||
for (let key of data.keys()) {
|
||||
const is_array = key.endsWith('[]');
|
||||
/** @type {any[]} */
|
||||
let values = data.getAll(key);
|
||||
|
||||
if (is_array) key = key.slice(0, -2);
|
||||
|
||||
if (values.length > 1 && !is_array) {
|
||||
throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
|
||||
}
|
||||
|
||||
// an empty `<input type="file">` will submit a non-existent file, bizarrely
|
||||
values = values.filter(
|
||||
(entry) => typeof entry === 'string' || entry.name !== '' || entry.size > 0
|
||||
);
|
||||
|
||||
if (key.startsWith('n:')) {
|
||||
key = key.slice(2);
|
||||
values = values.map((v) => (v === '' ? undefined : parseFloat(/** @type {string} */ (v))));
|
||||
} else if (key.startsWith('b:')) {
|
||||
key = key.slice(2);
|
||||
values = values.map((v) => v === 'on');
|
||||
}
|
||||
|
||||
set_nested_value(result, key, is_array ? values : values[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export const BINARY_FORM_CONTENT_TYPE = 'application/x-sveltekit-formdata';
|
||||
const BINARY_FORM_VERSION = 0;
|
||||
const HEADER_BYTES = 1 + 4 + 2;
|
||||
/**
|
||||
* The binary format is as follows:
|
||||
* - 1 byte: Format version
|
||||
* - 4 bytes: Length of the header (u32)
|
||||
* - 2 bytes: Length of the file offset table (u16)
|
||||
* - header: devalue.stringify([data, meta])
|
||||
* - file offset table: JSON.stringify([offset1, offset2, ...]) (empty if no files) (offsets start from the end of the table)
|
||||
* - file1, file2, ...
|
||||
* @param {Record<string, any>} data
|
||||
* @param {BinaryFormMeta} meta
|
||||
*/
|
||||
export function serialize_binary_form(data, meta) {
|
||||
/** @type {Array<BlobPart>} */
|
||||
const blob_parts = [new Uint8Array([BINARY_FORM_VERSION])];
|
||||
|
||||
/** @type {Array<[file: File, index: number]>} */
|
||||
const files = [];
|
||||
|
||||
if (!meta.remote_refreshes?.length) {
|
||||
delete meta.remote_refreshes;
|
||||
}
|
||||
|
||||
const encoded_header = devalue.stringify([data, meta], {
|
||||
File: (file) => {
|
||||
if (!(file instanceof File)) return;
|
||||
|
||||
files.push([file, files.length]);
|
||||
return [file.name, file.type, file.size, file.lastModified, files.length - 1];
|
||||
}
|
||||
});
|
||||
|
||||
const encoded_header_buffer = text_encoder.encode(encoded_header);
|
||||
|
||||
let encoded_file_offsets = '';
|
||||
if (files.length) {
|
||||
// Sort small files to the front
|
||||
files.sort(([a], [b]) => a.size - b.size);
|
||||
|
||||
/** @type {Array<number>} */
|
||||
const file_offsets = new Array(files.length);
|
||||
let start = 0;
|
||||
for (const [file, index] of files) {
|
||||
file_offsets[index] = start;
|
||||
start += file.size;
|
||||
}
|
||||
encoded_file_offsets = JSON.stringify(file_offsets);
|
||||
}
|
||||
|
||||
const length_buffer = new Uint8Array(4);
|
||||
const length_view = new DataView(length_buffer.buffer);
|
||||
|
||||
length_view.setUint32(0, encoded_header_buffer.byteLength, true);
|
||||
blob_parts.push(length_buffer.slice());
|
||||
|
||||
length_view.setUint16(0, encoded_file_offsets.length, true);
|
||||
blob_parts.push(length_buffer.slice(0, 2));
|
||||
|
||||
blob_parts.push(encoded_header_buffer);
|
||||
blob_parts.push(encoded_file_offsets);
|
||||
|
||||
for (const [file] of files) {
|
||||
blob_parts.push(file);
|
||||
}
|
||||
|
||||
return {
|
||||
blob: new Blob(blob_parts)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
* @returns {Promise<{ data: Record<string, any>; meta: BinaryFormMeta; form_data: FormData | null }>}
|
||||
*/
|
||||
export async function deserialize_binary_form(request) {
|
||||
if (request.headers.get('content-type') !== BINARY_FORM_CONTENT_TYPE) {
|
||||
const form_data = await request.formData();
|
||||
return { data: convert_formdata(form_data), meta: {}, form_data };
|
||||
}
|
||||
if (!request.body) {
|
||||
throw deserialize_error('no body');
|
||||
}
|
||||
const content_length = parseInt(request.headers.get('content-length') ?? '');
|
||||
if (Number.isNaN(content_length)) {
|
||||
throw deserialize_error('invalid Content-Length header');
|
||||
}
|
||||
|
||||
const reader = request.body.getReader();
|
||||
|
||||
/** @type {Array<Promise<Uint8Array<ArrayBuffer> | undefined>>} */
|
||||
const chunks = [];
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {Promise<Uint8Array<ArrayBuffer> | undefined>}
|
||||
*/
|
||||
function get_chunk(index) {
|
||||
if (index in chunks) return chunks[index];
|
||||
|
||||
let i = chunks.length;
|
||||
while (i <= index) {
|
||||
chunks[i] = reader.read().then((chunk) => chunk.value);
|
||||
i++;
|
||||
}
|
||||
return chunks[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @param {number} length
|
||||
* @returns {Promise<Uint8Array | null>}
|
||||
*/
|
||||
async function get_buffer(offset, length) {
|
||||
/** @type {Uint8Array} */
|
||||
let start_chunk;
|
||||
let chunk_start = 0;
|
||||
/** @type {number} */
|
||||
let chunk_index;
|
||||
for (chunk_index = 0; ; chunk_index++) {
|
||||
const chunk = await get_chunk(chunk_index);
|
||||
if (!chunk) return null;
|
||||
|
||||
const chunk_end = chunk_start + chunk.byteLength;
|
||||
// If this chunk contains the target offset
|
||||
if (offset >= chunk_start && offset < chunk_end) {
|
||||
start_chunk = chunk;
|
||||
break;
|
||||
}
|
||||
chunk_start = chunk_end;
|
||||
}
|
||||
// If the buffer is completely contained in one chunk, do a subarray
|
||||
if (offset + length <= chunk_start + start_chunk.byteLength) {
|
||||
return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start);
|
||||
}
|
||||
// Otherwise, copy the data into a new buffer
|
||||
const chunks = [start_chunk.subarray(offset - chunk_start)];
|
||||
let cursor = start_chunk.byteLength - offset + chunk_start;
|
||||
while (cursor < length) {
|
||||
chunk_index++;
|
||||
let chunk = await get_chunk(chunk_index);
|
||||
if (!chunk) return null;
|
||||
if (chunk.byteLength > length - cursor) {
|
||||
chunk = chunk.subarray(0, length - cursor);
|
||||
}
|
||||
chunks.push(chunk);
|
||||
cursor += chunk.byteLength;
|
||||
}
|
||||
const buffer = new Uint8Array(length);
|
||||
cursor = 0;
|
||||
for (const chunk of chunks) {
|
||||
buffer.set(chunk, cursor);
|
||||
cursor += chunk.byteLength;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const header = await get_buffer(0, HEADER_BYTES);
|
||||
if (!header) throw deserialize_error('too short');
|
||||
|
||||
if (header[0] !== BINARY_FORM_VERSION) {
|
||||
throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`);
|
||||
}
|
||||
const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
||||
const data_length = header_view.getUint32(1, true);
|
||||
|
||||
if (HEADER_BYTES + data_length > content_length) {
|
||||
throw deserialize_error('data overflow');
|
||||
}
|
||||
|
||||
const file_offsets_length = header_view.getUint16(5, true);
|
||||
|
||||
if (HEADER_BYTES + data_length + file_offsets_length > content_length) {
|
||||
throw deserialize_error('file offset table overflow');
|
||||
}
|
||||
|
||||
// Read the form data
|
||||
const data_buffer = await get_buffer(HEADER_BYTES, data_length);
|
||||
if (!data_buffer) throw deserialize_error('data too short');
|
||||
|
||||
/** @type {Array<number | undefined>} */
|
||||
let file_offsets;
|
||||
/** @type {number} */
|
||||
let files_start_offset;
|
||||
if (file_offsets_length > 0) {
|
||||
// Read the file offset table
|
||||
const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
|
||||
if (!file_offsets_buffer) throw deserialize_error('file offset table too short');
|
||||
|
||||
const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
|
||||
|
||||
if (
|
||||
!Array.isArray(parsed_offsets) ||
|
||||
parsed_offsets.some((n) => typeof n !== 'number' || !Number.isInteger(n) || n < 0)
|
||||
) {
|
||||
throw deserialize_error('invalid file offset table');
|
||||
}
|
||||
|
||||
file_offsets = /** @type {Array<number>} */ (parsed_offsets);
|
||||
files_start_offset = HEADER_BYTES + data_length + file_offsets_length;
|
||||
}
|
||||
|
||||
/** @type {Array<{ offset: number, size: number }>} */
|
||||
const file_spans = [];
|
||||
const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), {
|
||||
File: ([name, type, size, last_modified, index]) => {
|
||||
if (
|
||||
typeof name !== 'string' ||
|
||||
typeof type !== 'string' ||
|
||||
typeof size !== 'number' ||
|
||||
typeof last_modified !== 'number' ||
|
||||
typeof index !== 'number'
|
||||
) {
|
||||
throw deserialize_error('invalid file metadata');
|
||||
}
|
||||
|
||||
let offset = file_offsets[index];
|
||||
|
||||
// Check that the file offset table entry has not been already
|
||||
// used. If not, immediately mark it as used.
|
||||
if (offset === undefined) {
|
||||
throw deserialize_error('duplicate file offset table index');
|
||||
}
|
||||
file_offsets[index] = undefined;
|
||||
|
||||
offset += files_start_offset;
|
||||
if (offset + size > content_length) {
|
||||
throw deserialize_error('file data overflow');
|
||||
}
|
||||
|
||||
file_spans.push({ offset, size });
|
||||
|
||||
return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), {
|
||||
getPrototypeOf() {
|
||||
// Trick validators into thinking this is a normal File
|
||||
return File.prototype;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sort file spans in increasing order primarily by offset
|
||||
// and secondarily by size (to allow 0-length files).
|
||||
file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size);
|
||||
|
||||
// Check that file spans do not overlap and there are no gaps between them.
|
||||
for (let i = 1; i < file_spans.length; i++) {
|
||||
const previous = file_spans[i - 1];
|
||||
const current = file_spans[i];
|
||||
|
||||
const previous_end = previous.offset + previous.size;
|
||||
if (previous_end < current.offset) {
|
||||
throw deserialize_error('gaps in file data');
|
||||
}
|
||||
if (previous_end > current.offset) {
|
||||
throw deserialize_error('overlapping file data');
|
||||
}
|
||||
}
|
||||
|
||||
// Read the request body asyncronously so it doesn't stall
|
||||
void (async () => {
|
||||
let has_more = true;
|
||||
while (has_more) {
|
||||
const chunk = await get_chunk(chunks.length);
|
||||
has_more = !!chunk;
|
||||
}
|
||||
})();
|
||||
|
||||
return { data, meta, form_data: null };
|
||||
}
|
||||
/**
|
||||
* @param {string} message
|
||||
*/
|
||||
function deserialize_error(message) {
|
||||
return new SvelteKitError(400, 'Bad Request', `Could not deserialize binary form: ${message}`);
|
||||
}
|
||||
|
||||
/** @implements {File} */
|
||||
class LazyFile {
|
||||
/** @type {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} */
|
||||
#get_chunk;
|
||||
/** @type {number} */
|
||||
#offset;
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} type
|
||||
* @param {number} size
|
||||
* @param {number} last_modified
|
||||
* @param {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} get_chunk
|
||||
* @param {number} offset
|
||||
*/
|
||||
constructor(name, type, size, last_modified, get_chunk, offset) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
this.lastModified = last_modified;
|
||||
this.webkitRelativePath = '';
|
||||
this.#get_chunk = get_chunk;
|
||||
this.#offset = offset;
|
||||
|
||||
// TODO - hacky, required for private members to be accessed on proxy
|
||||
this.arrayBuffer = this.arrayBuffer.bind(this);
|
||||
this.bytes = this.bytes.bind(this);
|
||||
this.slice = this.slice.bind(this);
|
||||
this.stream = this.stream.bind(this);
|
||||
this.text = this.text.bind(this);
|
||||
}
|
||||
/** @type {ArrayBuffer | undefined} */
|
||||
#buffer;
|
||||
async arrayBuffer() {
|
||||
this.#buffer ??= await new Response(this.stream()).arrayBuffer();
|
||||
return this.#buffer;
|
||||
}
|
||||
async bytes() {
|
||||
return new Uint8Array(await this.arrayBuffer());
|
||||
}
|
||||
/**
|
||||
* @param {number=} start
|
||||
* @param {number=} end
|
||||
* @param {string=} contentType
|
||||
*/
|
||||
slice(start = 0, end = this.size, contentType = this.type) {
|
||||
// https://github.com/nodejs/node/blob/a5f3cd8cb5ba9e7911d93c5fd3ebc6d781220dd8/lib/internal/blob.js#L240
|
||||
if (start < 0) {
|
||||
start = Math.max(this.size + start, 0);
|
||||
} else {
|
||||
start = Math.min(start, this.size);
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
end = Math.max(this.size + end, 0);
|
||||
} else {
|
||||
end = Math.min(end, this.size);
|
||||
}
|
||||
const size = Math.max(end - start, 0);
|
||||
const file = new LazyFile(
|
||||
this.name,
|
||||
contentType,
|
||||
size,
|
||||
this.lastModified,
|
||||
this.#get_chunk,
|
||||
this.#offset + start
|
||||
);
|
||||
|
||||
return file;
|
||||
}
|
||||
stream() {
|
||||
let cursor = 0;
|
||||
let chunk_index = 0;
|
||||
return new ReadableStream({
|
||||
start: async (controller) => {
|
||||
let chunk_start = 0;
|
||||
/** @type {Uint8Array} */
|
||||
let start_chunk;
|
||||
for (chunk_index = 0; ; chunk_index++) {
|
||||
const chunk = await this.#get_chunk(chunk_index);
|
||||
if (!chunk) return null;
|
||||
|
||||
const chunk_end = chunk_start + chunk.byteLength;
|
||||
// If this chunk contains the target offset
|
||||
if (this.#offset >= chunk_start && this.#offset < chunk_end) {
|
||||
start_chunk = chunk;
|
||||
break;
|
||||
}
|
||||
chunk_start = chunk_end;
|
||||
}
|
||||
// If the buffer is completely contained in one chunk, do a subarray
|
||||
if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) {
|
||||
controller.enqueue(
|
||||
start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start)
|
||||
);
|
||||
controller.close();
|
||||
} else {
|
||||
controller.enqueue(start_chunk.subarray(this.#offset - chunk_start));
|
||||
cursor = start_chunk.byteLength - this.#offset + chunk_start;
|
||||
}
|
||||
},
|
||||
pull: async (controller) => {
|
||||
chunk_index++;
|
||||
let chunk = await this.#get_chunk(chunk_index);
|
||||
if (!chunk) {
|
||||
controller.error('incomplete file data');
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
if (chunk.byteLength > this.size - cursor) {
|
||||
chunk = chunk.subarray(0, this.size - cursor);
|
||||
}
|
||||
controller.enqueue(chunk);
|
||||
cursor += chunk.byteLength;
|
||||
if (cursor >= this.size) {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async text() {
|
||||
return text_decoder.decode(await this.arrayBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/;
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
export function split_path(path) {
|
||||
if (!path_regex.test(path)) {
|
||||
throw new Error(`Invalid path ${path}`);
|
||||
}
|
||||
|
||||
return path.split(/\.|\[|\]/).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property key is dangerous and could lead to prototype pollution
|
||||
* @param {string} key
|
||||
*/
|
||||
function check_prototype_pollution(key) {
|
||||
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||
throw new Error(
|
||||
`Invalid key "${key}"` +
|
||||
(DEV ? ': This key is not allowed to prevent prototype pollution.' : '')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in a nested object using an array of keys, mutating the original object.
|
||||
* @param {Record<string, any>} object
|
||||
* @param {string[]} keys
|
||||
* @param {any} value
|
||||
*/
|
||||
export function deep_set(object, keys, value) {
|
||||
let current = object;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i += 1) {
|
||||
const key = keys[i];
|
||||
|
||||
check_prototype_pollution(key);
|
||||
|
||||
const is_array = /^\d+$/.test(keys[i + 1]);
|
||||
const exists = Object.hasOwn(current, key);
|
||||
const inner = current[key];
|
||||
|
||||
if (exists && is_array !== Array.isArray(inner)) {
|
||||
throw new Error(`Invalid array key ${keys[i + 1]}`);
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
current[key] = is_array ? [] : {};
|
||||
}
|
||||
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
const final_key = keys[keys.length - 1];
|
||||
check_prototype_pollution(final_key);
|
||||
current[final_key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StandardSchemaV1.Issue} issue
|
||||
* @param {boolean} server Whether this issue came from server validation
|
||||
*/
|
||||
export function normalize_issue(issue, server = false) {
|
||||
/** @type {InternalRemoteFormIssue} */
|
||||
const normalized = { name: '', path: [], message: issue.message, server };
|
||||
|
||||
if (issue.path !== undefined) {
|
||||
let name = '';
|
||||
|
||||
for (const segment of issue.path) {
|
||||
const key = /** @type {string | number} */ (
|
||||
typeof segment === 'object' ? segment.key : segment
|
||||
);
|
||||
|
||||
normalized.path.push(key);
|
||||
|
||||
if (typeof key === 'number') {
|
||||
name += `[${key}]`;
|
||||
} else if (typeof key === 'string') {
|
||||
name += name === '' ? key : '.' + key;
|
||||
}
|
||||
}
|
||||
|
||||
normalized.name = name;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {InternalRemoteFormIssue[]} issues
|
||||
*/
|
||||
export function flatten_issues(issues) {
|
||||
/** @type {Record<string, InternalRemoteFormIssue[]>} */
|
||||
const result = {};
|
||||
|
||||
for (const issue of issues) {
|
||||
(result.$ ??= []).push(issue);
|
||||
|
||||
let name = '';
|
||||
|
||||
if (issue.path !== undefined) {
|
||||
for (const key of issue.path) {
|
||||
if (typeof key === 'number') {
|
||||
name += `[${key}]`;
|
||||
} else if (typeof key === 'string') {
|
||||
name += name === '' ? key : '.' + key;
|
||||
}
|
||||
|
||||
(result[name] ??= []).push(issue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a nested value from an object using a path array
|
||||
* @param {Record<string, any>} object
|
||||
* @param {(string | number)[]} path
|
||||
* @returns {any}
|
||||
*/
|
||||
|
||||
export function deep_get(object, path) {
|
||||
let current = object;
|
||||
for (const key of path) {
|
||||
if (current == null || typeof current !== 'object') {
|
||||
return current;
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proxy-based field accessor for form data
|
||||
* @param {any} target - Function or empty POJO
|
||||
* @param {() => Record<string, any>} get_input - Function to get current input data
|
||||
* @param {(path: (string | number)[], value: any) => void} set_input - Function to set input data
|
||||
* @param {() => Record<string, InternalRemoteFormIssue[]>} get_issues - Function to get current issues
|
||||
* @param {(string | number)[]} path - Current access path
|
||||
* @returns {any} Proxy object with name(), value(), and issues() methods
|
||||
*/
|
||||
export function create_field_proxy(target, get_input, set_input, get_issues, path = []) {
|
||||
const get_value = () => {
|
||||
return deep_get(get_input(), path);
|
||||
};
|
||||
|
||||
return new Proxy(target, {
|
||||
get(target, prop) {
|
||||
if (typeof prop === 'symbol') return target[prop];
|
||||
|
||||
// Handle array access like jobs[0]
|
||||
if (/^\d+$/.test(prop)) {
|
||||
return create_field_proxy({}, get_input, set_input, get_issues, [
|
||||
...path,
|
||||
parseInt(prop, 10)
|
||||
]);
|
||||
}
|
||||
|
||||
const key = build_path_string(path);
|
||||
|
||||
if (prop === 'set') {
|
||||
const set_func = function (/** @type {any} */ newValue) {
|
||||
set_input(path, newValue);
|
||||
return newValue;
|
||||
};
|
||||
return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]);
|
||||
}
|
||||
|
||||
if (prop === 'value') {
|
||||
return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]);
|
||||
}
|
||||
|
||||
if (prop === 'issues' || prop === 'allIssues') {
|
||||
const issues_func = () => {
|
||||
const all_issues = get_issues()[key === '' ? '$' : key];
|
||||
|
||||
if (prop === 'allIssues') {
|
||||
return all_issues?.map((issue) => ({
|
||||
path: issue.path,
|
||||
message: issue.message
|
||||
}));
|
||||
}
|
||||
|
||||
return all_issues
|
||||
?.filter((issue) => issue.name === key)
|
||||
?.map((issue) => ({
|
||||
path: issue.path,
|
||||
message: issue.message
|
||||
}));
|
||||
};
|
||||
|
||||
return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]);
|
||||
}
|
||||
|
||||
if (prop === 'as') {
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {string} [input_value]
|
||||
*/
|
||||
const as_func = (type, input_value) => {
|
||||
const is_array =
|
||||
type === 'file multiple' ||
|
||||
type === 'select multiple' ||
|
||||
(type === 'checkbox' && typeof input_value === 'string');
|
||||
|
||||
const prefix =
|
||||
type === 'number' || type === 'range'
|
||||
? 'n:'
|
||||
: type === 'checkbox' && !is_array
|
||||
? 'b:'
|
||||
: '';
|
||||
|
||||
// Base properties for all input types
|
||||
/** @type {Record<string, any>} */
|
||||
const base_props = {
|
||||
name: prefix + key + (is_array ? '[]' : ''),
|
||||
get 'aria-invalid'() {
|
||||
const issues = get_issues();
|
||||
return key in issues ? 'true' : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Add type attribute only for non-text inputs and non-select elements
|
||||
if (type !== 'text' && type !== 'select' && type !== 'select multiple') {
|
||||
base_props.type = type === 'file multiple' ? 'file' : type;
|
||||
}
|
||||
|
||||
// Handle submit and hidden inputs
|
||||
if (type === 'submit' || type === 'hidden') {
|
||||
if (DEV) {
|
||||
if (!input_value) {
|
||||
throw new Error(`\`${type}\` inputs must have a value`);
|
||||
}
|
||||
}
|
||||
|
||||
return Object.defineProperties(base_props, {
|
||||
value: { value: input_value, enumerable: true }
|
||||
});
|
||||
}
|
||||
|
||||
// Handle select inputs
|
||||
if (type === 'select' || type === 'select multiple') {
|
||||
return Object.defineProperties(base_props, {
|
||||
multiple: { value: is_array, enumerable: true },
|
||||
value: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
return get_value();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle checkbox inputs
|
||||
if (type === 'checkbox' || type === 'radio') {
|
||||
if (DEV) {
|
||||
if (type === 'radio' && !input_value) {
|
||||
throw new Error('Radio inputs must have a value');
|
||||
}
|
||||
|
||||
if (type === 'checkbox' && is_array && !input_value) {
|
||||
throw new Error('Checkbox array inputs must have a value');
|
||||
}
|
||||
}
|
||||
|
||||
return Object.defineProperties(base_props, {
|
||||
value: { value: input_value ?? 'on', enumerable: true },
|
||||
checked: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
const value = get_value();
|
||||
|
||||
if (type === 'radio') {
|
||||
return value === input_value;
|
||||
}
|
||||
|
||||
if (is_array) {
|
||||
return (value ?? []).includes(input_value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle file inputs
|
||||
if (type === 'file' || type === 'file multiple') {
|
||||
return Object.defineProperties(base_props, {
|
||||
multiple: { value: is_array, enumerable: true },
|
||||
files: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
const value = get_value();
|
||||
|
||||
// Convert File/File[] to FileList-like object
|
||||
if (value instanceof File) {
|
||||
// In browsers, we can create a proper FileList using DataTransfer
|
||||
if (typeof DataTransfer !== 'undefined') {
|
||||
const fileList = new DataTransfer();
|
||||
fileList.items.add(value);
|
||||
return fileList.files;
|
||||
}
|
||||
// Fallback for environments without DataTransfer
|
||||
return { 0: value, length: 1 };
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.every((f) => f instanceof File)) {
|
||||
if (typeof DataTransfer !== 'undefined') {
|
||||
const fileList = new DataTransfer();
|
||||
value.forEach((file) => fileList.items.add(file));
|
||||
return fileList.files;
|
||||
}
|
||||
// Fallback for environments without DataTransfer
|
||||
/** @type {any} */
|
||||
const fileListLike = { length: value.length };
|
||||
value.forEach((file, index) => {
|
||||
fileListLike[index] = file;
|
||||
});
|
||||
return fileListLike;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle all other input types (text, number, etc.)
|
||||
return Object.defineProperties(base_props, {
|
||||
value: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
const value = get_value();
|
||||
return value != null ? String(value) : '';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, 'as']);
|
||||
}
|
||||
|
||||
// Handle property access (nested fields)
|
||||
return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a path string from an array of path segments
|
||||
* @param {(string | number)[]} path
|
||||
* @returns {string}
|
||||
*/
|
||||
export function build_path_string(path) {
|
||||
let result = '';
|
||||
|
||||
for (const segment of path) {
|
||||
if (typeof segment === 'number') {
|
||||
result += `[${segment}]`;
|
||||
} else {
|
||||
result += result === '' ? segment : '.' + segment;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {RemoteForm<any, any>} instance
|
||||
* @deprecated remove in 3.0
|
||||
*/
|
||||
export function throw_on_old_property_access(instance) {
|
||||
Object.defineProperty(instance, 'field', {
|
||||
value: (/** @type {string} */ name) => {
|
||||
const new_name = name.endsWith('[]') ? name.slice(0, -2) : name;
|
||||
throw new Error(
|
||||
`\`form.field\` has been removed: Instead of \`<input name={form.field('${name}')} />\` do \`<input {...form.fields.${new_name}.as(type)} />\``
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
for (const property of ['input', 'issues']) {
|
||||
Object.defineProperty(instance, property, {
|
||||
get() {
|
||||
const new_name = property === 'issues' ? 'issues' : 'value';
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, prop) {
|
||||
const prop_string = typeof prop === 'string' ? prop : String(prop);
|
||||
const old =
|
||||
prop_string.includes('[') || prop_string.includes('.')
|
||||
? `['${prop_string}']`
|
||||
: `.${prop_string}`;
|
||||
const replacement = `.${prop_string}.${new_name}()`;
|
||||
throw new Error(
|
||||
`\`form.${property}\` has been removed: Instead of \`form.${property}${old}\` write \`form.fields${replacement}\``
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
49
frontend/node_modules/@sveltejs/kit/src/runtime/pathname.js
generated
vendored
Normal file
49
frontend/node_modules/@sveltejs/kit/src/runtime/pathname.js
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
const DATA_SUFFIX = '/__data.json';
|
||||
const HTML_DATA_SUFFIX = '.html__data.json';
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function has_data_suffix(pathname) {
|
||||
return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX);
|
||||
}
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function add_data_suffix(pathname) {
|
||||
if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX);
|
||||
return pathname.replace(/\/$/, '') + DATA_SUFFIX;
|
||||
}
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function strip_data_suffix(pathname) {
|
||||
if (pathname.endsWith(HTML_DATA_SUFFIX)) {
|
||||
return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html';
|
||||
}
|
||||
|
||||
return pathname.slice(0, -DATA_SUFFIX.length);
|
||||
}
|
||||
|
||||
const ROUTE_SUFFIX = '/__route.js';
|
||||
|
||||
/**
|
||||
* @param {string} pathname
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function has_resolution_suffix(pathname) {
|
||||
return pathname.endsWith(ROUTE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a regular URL to a route to send to SvelteKit's server-side route resolution endpoint
|
||||
* @param {string} pathname
|
||||
* @returns {string}
|
||||
*/
|
||||
export function add_resolution_suffix(pathname) {
|
||||
return pathname.replace(/\/$/, '') + ROUTE_SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pathname
|
||||
* @returns {string}
|
||||
*/
|
||||
export function strip_resolution_suffix(pathname) {
|
||||
return pathname.slice(0, -ROUTE_SUFFIX.length);
|
||||
}
|
||||
4
frontend/node_modules/@sveltejs/kit/src/runtime/server/ambient.d.ts
generated
vendored
Normal file
4
frontend/node_modules/@sveltejs/kit/src/runtime/server/ambient.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
declare module '__SERVER__/internal.js' {
|
||||
export const options: import('types').SSROptions;
|
||||
export const get_hooks: () => Promise<Partial<import('types').ServerHooks>>;
|
||||
}
|
||||
9
frontend/node_modules/@sveltejs/kit/src/runtime/server/app.js
generated
vendored
Normal file
9
frontend/node_modules/@sveltejs/kit/src/runtime/server/app.js
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/** @type {{ decoders: Record<string, (data: any) => any> }} */
|
||||
export let app;
|
||||
|
||||
/**
|
||||
* @param {{ decoders: Record<string, (data: any) => any> }} value
|
||||
*/
|
||||
export function set_app(value) {
|
||||
app = value;
|
||||
}
|
||||
4
frontend/node_modules/@sveltejs/kit/src/runtime/server/constants.js
generated
vendored
Normal file
4
frontend/node_modules/@sveltejs/kit/src/runtime/server/constants.js
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const NULL_BODY_STATUS = [101, 103, 204, 205, 304];
|
||||
|
||||
// eslint-disable-next-line n/prefer-global/process
|
||||
export const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer;
|
||||
334
frontend/node_modules/@sveltejs/kit/src/runtime/server/cookie.js
generated
vendored
Normal file
334
frontend/node_modules/@sveltejs/kit/src/runtime/server/cookie.js
generated
vendored
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
import { parse, serialize } from 'cookie';
|
||||
import { DEV } from 'esm-env';
|
||||
import { normalize_path, resolve } from '../../utils/url.js';
|
||||
import { add_data_suffix } from '../pathname.js';
|
||||
import { text_encoder } from '../utils.js';
|
||||
|
||||
// eslint-disable-next-line no-control-regex -- control characters are invalid in cookie names
|
||||
const INVALID_COOKIE_CHARACTER_REGEX = /[\x00-\x1F\x7F()<>@,;:"/[\]?={} \t]/;
|
||||
|
||||
/**
|
||||
* Tracks all cookies set during dev mode so we can emit warnings
|
||||
* when we detect that there's likely cookie misusage due to wrong paths
|
||||
*
|
||||
* @type {Record<string, Set<string>>} */
|
||||
const cookie_paths = {};
|
||||
|
||||
/**
|
||||
* Cookies that are larger than this size (including the name and other
|
||||
* attributes) are discarded by browsers
|
||||
*/
|
||||
const MAX_COOKIE_SIZE = 4129;
|
||||
|
||||
// TODO 3.0 remove this check
|
||||
/** @param {import('./page/types.js').Cookie['options']} options */
|
||||
function validate_options(options) {
|
||||
if (options?.path === undefined) {
|
||||
throw new Error('You must specify a `path` when setting, deleting or serializing cookies');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique key for a cookie based on its domain, path, and name in
|
||||
* the format: `<domain>/<path>?<name>`.
|
||||
* If domain is undefined, it will be omitted.
|
||||
* For example: `/?name`, `example.com/foo?name`.
|
||||
*
|
||||
* @param {string | undefined} domain
|
||||
* @param {string} path
|
||||
* @param {string} name
|
||||
* @returns {string}
|
||||
*/
|
||||
function generate_cookie_key(domain, path, name) {
|
||||
return `${domain || ''}${path}?${encodeURIComponent(name)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
* @param {URL} url
|
||||
*/
|
||||
export function get_cookies(request, url) {
|
||||
const header = request.headers.get('cookie') ?? '';
|
||||
const initial_cookies = parse(header, { decode: (value) => value });
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let normalized_url;
|
||||
|
||||
/** @type {Map<string, import('./page/types.js').Cookie>} */
|
||||
const new_cookies = new Map();
|
||||
|
||||
/** @type {import('cookie').CookieSerializeOptions} */
|
||||
const defaults = {
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
secure: url.hostname === 'localhost' && url.protocol === 'http:' ? false : true
|
||||
};
|
||||
|
||||
/** @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('@sveltejs/kit').Cookies}` above is not
|
||||
// sufficient to do so.
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {import('cookie').CookieParseOptions} [opts]
|
||||
*/
|
||||
get(name, opts) {
|
||||
// Look for the most specific matching cookie from new_cookies
|
||||
const best_match = Array.from(new_cookies.values())
|
||||
.filter((c) => {
|
||||
return (
|
||||
c.name === name &&
|
||||
domain_matches(url.hostname, c.options.domain) &&
|
||||
path_matches(url.pathname, c.options.path)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => b.options.path.length - a.options.path.length)[0];
|
||||
|
||||
if (best_match) {
|
||||
return best_match.options.maxAge === 0 ? undefined : best_match.value;
|
||||
}
|
||||
|
||||
const req_cookies = parse(header, { decode: opts?.decode });
|
||||
const cookie = req_cookies[name]; // the decoded string or undefined
|
||||
|
||||
// in development, if the cookie was set during this session with `cookies.set`,
|
||||
// but at a different path, warn the user. (ignore cookies from request headers,
|
||||
// since we don't know which path they were set at)
|
||||
if (DEV && !cookie) {
|
||||
const paths = Array.from(cookie_paths[name] ?? []).filter((path) => {
|
||||
// we only care about paths that are _more_ specific than the current path
|
||||
return path_matches(path, url.pathname) && path !== url.pathname;
|
||||
});
|
||||
|
||||
if (paths.length > 0) {
|
||||
console.warn(
|
||||
// prettier-ignore
|
||||
`'${name}' cookie does not exist for ${url.pathname}, but was previously set at ${conjoin([...paths])}. Did you mean to set its 'path' to '/' instead?`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return cookie;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {import('cookie').CookieParseOptions} [opts]
|
||||
*/
|
||||
getAll(opts) {
|
||||
const cookies = parse(header, { decode: opts?.decode });
|
||||
|
||||
// Group cookies by name and find the most specific one for each name
|
||||
const lookup = new Map();
|
||||
|
||||
for (const c of new_cookies.values()) {
|
||||
if (
|
||||
domain_matches(url.hostname, c.options.domain) &&
|
||||
path_matches(url.pathname, c.options.path)
|
||||
) {
|
||||
const existing = lookup.get(c.name);
|
||||
|
||||
// If no existing cookie or this one has a more specific (longer) path, use this one
|
||||
if (!existing || c.options.path.length > existing.options.path.length) {
|
||||
lookup.set(c.name, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the most specific cookies to the result
|
||||
for (const c of lookup.values()) {
|
||||
cookies[c.name] = c.value;
|
||||
}
|
||||
|
||||
return Object.entries(cookies).map(([name, value]) => ({ name, value }));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @param {import('./page/types.js').Cookie['options']} options
|
||||
*/
|
||||
set(name, value, options) {
|
||||
// TODO: remove this check in 3.0
|
||||
const illegal_characters = name.match(INVALID_COOKIE_CHARACTER_REGEX);
|
||||
if (illegal_characters) {
|
||||
console.warn(
|
||||
`The cookie name "${name}" will be invalid in SvelteKit 3.0 as it contains ${illegal_characters.join(
|
||||
' and '
|
||||
)}. See RFC 2616 for more details https://datatracker.ietf.org/doc/html/rfc2616#section-2.2`
|
||||
);
|
||||
}
|
||||
|
||||
validate_options(options);
|
||||
set_internal(name, value, { ...defaults, ...options });
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {import('./page/types.js').Cookie['options']} options
|
||||
*/
|
||||
delete(name, options) {
|
||||
validate_options(options);
|
||||
cookies.set(name, '', { ...options, maxAge: 0 });
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @param {import('./page/types.js').Cookie['options']} options
|
||||
*/
|
||||
serialize(name, value, options) {
|
||||
validate_options(options);
|
||||
|
||||
let path = options.path;
|
||||
|
||||
if (!options.domain || options.domain === url.hostname) {
|
||||
if (!normalized_url) {
|
||||
throw new Error('Cannot serialize cookies until after the route is determined');
|
||||
}
|
||||
path = resolve(normalized_url, path);
|
||||
}
|
||||
|
||||
return serialize(name, value, { ...defaults, ...options, path });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {URL} destination
|
||||
* @param {string | null} header
|
||||
*/
|
||||
function get_cookie_header(destination, header) {
|
||||
/** @type {Record<string, string>} */
|
||||
const combined_cookies = {
|
||||
// cookies sent by the user agent have lowest precedence
|
||||
...initial_cookies
|
||||
};
|
||||
|
||||
// cookies previous set during this event with cookies.set have higher precedence
|
||||
for (const cookie of new_cookies.values()) {
|
||||
if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
|
||||
if (!path_matches(destination.pathname, cookie.options.path)) continue;
|
||||
|
||||
const encoder = cookie.options.encode || encodeURIComponent;
|
||||
combined_cookies[cookie.name] = encoder(cookie.value);
|
||||
}
|
||||
|
||||
// explicit header has highest precedence
|
||||
if (header) {
|
||||
const parsed = parse(header, { decode: (value) => value });
|
||||
for (const name in parsed) {
|
||||
combined_cookies[name] = parsed[name];
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(combined_cookies)
|
||||
.map(([name, value]) => `${name}=${value}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
/** @type {Array<() => void>} */
|
||||
const internal_queue = [];
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @param {import('./page/types.js').Cookie['options']} options
|
||||
*/
|
||||
function set_internal(name, value, options) {
|
||||
if (!normalized_url) {
|
||||
internal_queue.push(() => set_internal(name, value, options));
|
||||
return;
|
||||
}
|
||||
|
||||
let path = options.path;
|
||||
|
||||
if (!options.domain || options.domain === url.hostname) {
|
||||
path = resolve(normalized_url, path);
|
||||
}
|
||||
|
||||
// Generate unique key for cookie storage
|
||||
const cookie_key = generate_cookie_key(options.domain, path, name);
|
||||
const cookie = { name, value, options: { ...options, path } };
|
||||
new_cookies.set(cookie_key, cookie);
|
||||
|
||||
if (DEV) {
|
||||
const serialized = serialize(name, value, cookie.options);
|
||||
if (text_encoder.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('types').TrailingSlash} trailing_slash
|
||||
*/
|
||||
function set_trailing_slash(trailing_slash) {
|
||||
normalized_url = normalize_path(url.pathname, trailing_slash);
|
||||
internal_queue.forEach((fn) => fn());
|
||||
}
|
||||
|
||||
return { cookies, new_cookies, get_cookie_header, set_internal, set_trailing_slash };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hostname
|
||||
* @param {string} [constraint]
|
||||
*/
|
||||
export function domain_matches(hostname, constraint) {
|
||||
if (!constraint) return true;
|
||||
|
||||
const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
|
||||
|
||||
if (hostname === normalized) return true;
|
||||
return hostname.endsWith('.' + normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} [constraint]
|
||||
*/
|
||||
export function path_matches(path, constraint) {
|
||||
if (!constraint) return true;
|
||||
|
||||
const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
|
||||
|
||||
if (path === normalized) return true;
|
||||
return path.startsWith(normalized + '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Headers} headers
|
||||
* @param {MapIterator<import('./page/types.js').Cookie>} cookies
|
||||
*/
|
||||
export function add_cookies_to_headers(headers, cookies) {
|
||||
for (const new_cookie of cookies) {
|
||||
const { name, value, options } = new_cookie;
|
||||
headers.append('set-cookie', serialize(name, value, options));
|
||||
|
||||
// special case — for routes ending with .html, the route data lives in a sibling
|
||||
// `.html__data.json` file rather than a child `/__data.json` file, which means
|
||||
// we need to duplicate the cookie
|
||||
if (options.path.endsWith('.html')) {
|
||||
const path = add_data_suffix(options.path);
|
||||
headers.append('set-cookie', serialize(name, value, { ...options, path }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} array
|
||||
*/
|
||||
function conjoin(array) {
|
||||
if (array.length <= 2) return array.join(' and ');
|
||||
return `${array.slice(0, -1).join(', ')} and ${array.at(-1)}`;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue