@sveltejs/kit 1.0.0-next.49 → 1.0.0-next.491
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/package.json +93 -66
- package/scripts/special-types/$env+dynamic+private.md +10 -0
- package/scripts/special-types/$env+dynamic+public.md +8 -0
- package/scripts/special-types/$env+static+private.md +19 -0
- package/scripts/special-types/$env+static+public.md +7 -0
- package/scripts/special-types/$lib.md +5 -0
- package/src/cli.js +112 -0
- package/src/constants.js +7 -0
- package/src/core/adapt/builder.js +206 -0
- package/src/core/adapt/index.js +31 -0
- package/src/core/config/default-error.html +56 -0
- package/src/core/config/index.js +110 -0
- package/src/core/config/options.js +504 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/env.js +121 -0
- package/src/core/generate_manifest/index.js +93 -0
- package/src/core/prerender/crawl.js +198 -0
- package/src/core/prerender/entities.js +2252 -0
- package/src/core/prerender/prerender.js +431 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/index.js +470 -0
- package/src/core/sync/create_manifest_data/types.d.ts +37 -0
- package/src/core/sync/sync.js +59 -0
- package/src/core/sync/utils.js +33 -0
- package/src/core/sync/write_ambient.js +53 -0
- package/src/core/sync/write_client_manifest.js +106 -0
- package/src/core/sync/write_matchers.js +25 -0
- package/src/core/sync/write_root.js +91 -0
- package/src/core/sync/write_tsconfig.js +195 -0
- package/src/core/sync/write_types/index.js +678 -0
- package/src/core/utils.js +70 -0
- package/src/exports/hooks/index.js +1 -0
- package/src/exports/hooks/sequence.js +44 -0
- package/src/exports/index.js +45 -0
- package/src/exports/node/index.js +168 -0
- package/src/exports/node/polyfills.js +41 -0
- package/src/exports/vite/build/build_server.js +378 -0
- package/src/exports/vite/build/build_service_worker.js +90 -0
- package/src/exports/vite/build/utils.js +180 -0
- package/src/exports/vite/dev/index.js +576 -0
- package/src/exports/vite/graph_analysis/index.js +277 -0
- package/src/exports/vite/graph_analysis/types.d.ts +5 -0
- package/src/exports/vite/graph_analysis/utils.js +30 -0
- package/src/exports/vite/index.js +598 -0
- package/src/exports/vite/preview/index.js +189 -0
- package/src/exports/vite/types.d.ts +3 -0
- package/src/exports/vite/utils.js +157 -0
- package/src/runtime/app/env.js +1 -0
- package/src/runtime/app/environment.js +11 -0
- package/src/runtime/app/forms.js +108 -0
- package/src/runtime/app/navigation.js +23 -0
- package/src/runtime/app/paths.js +1 -0
- package/src/runtime/app/stores.js +102 -0
- package/src/runtime/client/ambient.d.ts +26 -0
- package/src/runtime/client/client.js +1581 -0
- package/src/runtime/client/fetcher.js +107 -0
- package/src/runtime/client/parse.js +60 -0
- package/src/runtime/client/singletons.js +21 -0
- package/src/runtime/client/start.js +37 -0
- package/src/runtime/client/types.d.ts +85 -0
- package/src/runtime/client/utils.js +159 -0
- package/src/runtime/components/error.svelte +16 -0
- package/{assets → src/runtime}/components/layout.svelte +0 -0
- package/src/runtime/control.js +98 -0
- package/src/runtime/env/dynamic/private.js +1 -0
- package/src/runtime/env/dynamic/public.js +1 -0
- package/src/runtime/env-private.js +6 -0
- package/src/runtime/env-public.js +6 -0
- package/src/runtime/env.js +6 -0
- package/src/runtime/hash.js +16 -0
- package/src/runtime/paths.js +11 -0
- package/src/runtime/server/cookie.js +115 -0
- package/src/runtime/server/data/index.js +136 -0
- package/src/runtime/server/endpoint.js +83 -0
- package/src/runtime/server/index.js +337 -0
- package/src/runtime/server/page/actions.js +243 -0
- package/src/runtime/server/page/crypto.js +239 -0
- package/src/runtime/server/page/csp.js +249 -0
- package/src/runtime/server/page/fetch.js +288 -0
- package/src/runtime/server/page/index.js +304 -0
- package/src/runtime/server/page/load_data.js +124 -0
- package/src/runtime/server/page/render.js +342 -0
- package/src/runtime/server/page/respond_with_error.js +104 -0
- package/src/runtime/server/page/serialize_data.js +87 -0
- package/src/runtime/server/page/types.d.ts +41 -0
- package/src/runtime/server/utils.js +179 -0
- package/src/utils/array.js +9 -0
- package/src/utils/error.js +22 -0
- package/src/utils/escape.js +46 -0
- package/src/utils/filesystem.js +137 -0
- package/src/utils/functions.js +16 -0
- package/src/utils/http.js +55 -0
- package/src/utils/misc.js +1 -0
- package/src/utils/routing.js +117 -0
- package/src/utils/unit_test.js +11 -0
- package/src/utils/url.js +142 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +426 -0
- package/types/index.d.ts +428 -0
- package/types/internal.d.ts +378 -0
- package/types/private.d.ts +209 -0
- package/CHANGELOG.md +0 -476
- package/assets/components/error.svelte +0 -13
- package/assets/runtime/app/env.js +0 -5
- package/assets/runtime/app/navigation.js +0 -44
- package/assets/runtime/app/paths.js +0 -1
- package/assets/runtime/app/stores.js +0 -93
- package/assets/runtime/chunks/utils.js +0 -22
- package/assets/runtime/internal/singletons.js +0 -23
- package/assets/runtime/internal/start.js +0 -773
- package/assets/runtime/paths.js +0 -12
- package/dist/chunks/index.js +0 -3517
- package/dist/chunks/index2.js +0 -587
- package/dist/chunks/index3.js +0 -246
- package/dist/chunks/index4.js +0 -530
- package/dist/chunks/index5.js +0 -763
- package/dist/chunks/index6.js +0 -322
- package/dist/chunks/standard.js +0 -99
- package/dist/chunks/utils.js +0 -83
- package/dist/cli.js +0 -553
- package/dist/ssr.js +0 -2584
- package/types.d.ts +0 -32
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { parse, serialize } from 'cookie';
|
|
2
|
+
|
|
3
|
+
/** @type {import('cookie').CookieSerializeOptions} */
|
|
4
|
+
const DEFAULT_SERIALIZE_OPTIONS = {
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
secure: true,
|
|
7
|
+
sameSite: 'lax'
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {Request} request
|
|
12
|
+
* @param {URL} url
|
|
13
|
+
*/
|
|
14
|
+
export function get_cookies(request, url) {
|
|
15
|
+
/** @type {Map<string, import('./page/types').Cookie>} */
|
|
16
|
+
const new_cookies = new Map();
|
|
17
|
+
|
|
18
|
+
/** @type {import('types').Cookies} */
|
|
19
|
+
const cookies = {
|
|
20
|
+
// The JSDoc param annotations appearing below for get, set and delete
|
|
21
|
+
// are necessary to expose the `cookie` library types to
|
|
22
|
+
// typescript users. `@type {import('types').Cookies}` above is not
|
|
23
|
+
// sufficient to do so.
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} name
|
|
27
|
+
* @param {import('cookie').CookieParseOptions} opts
|
|
28
|
+
*/
|
|
29
|
+
get(name, opts) {
|
|
30
|
+
const c = new_cookies.get(name);
|
|
31
|
+
if (
|
|
32
|
+
c &&
|
|
33
|
+
domain_matches(url.hostname, c.options.domain) &&
|
|
34
|
+
path_matches(url.pathname, c.options.path)
|
|
35
|
+
) {
|
|
36
|
+
return c.value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const decode = opts?.decode || decodeURIComponent;
|
|
40
|
+
const req_cookies = parse(request.headers.get('cookie') ?? '', { decode });
|
|
41
|
+
return req_cookies[name]; // the decoded string or undefined
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} name
|
|
46
|
+
* @param {string} value
|
|
47
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
48
|
+
*/
|
|
49
|
+
set(name, value, opts = {}) {
|
|
50
|
+
new_cookies.set(name, {
|
|
51
|
+
name,
|
|
52
|
+
value,
|
|
53
|
+
options: {
|
|
54
|
+
...DEFAULT_SERIALIZE_OPTIONS,
|
|
55
|
+
...opts
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} name
|
|
62
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
63
|
+
*/
|
|
64
|
+
delete(name, opts = {}) {
|
|
65
|
+
new_cookies.set(name, {
|
|
66
|
+
name,
|
|
67
|
+
value: '',
|
|
68
|
+
options: {
|
|
69
|
+
...DEFAULT_SERIALIZE_OPTIONS,
|
|
70
|
+
...opts,
|
|
71
|
+
maxAge: 0
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return { cookies, new_cookies };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} hostname
|
|
82
|
+
* @param {string} [constraint]
|
|
83
|
+
*/
|
|
84
|
+
export function domain_matches(hostname, constraint) {
|
|
85
|
+
if (!constraint) return true;
|
|
86
|
+
|
|
87
|
+
const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
|
|
88
|
+
|
|
89
|
+
if (hostname === normalized) return true;
|
|
90
|
+
return hostname.endsWith('.' + normalized);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} path
|
|
95
|
+
* @param {string} [constraint]
|
|
96
|
+
*/
|
|
97
|
+
export function path_matches(path, constraint) {
|
|
98
|
+
if (!constraint) return true;
|
|
99
|
+
|
|
100
|
+
const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
|
|
101
|
+
|
|
102
|
+
if (path === normalized) return true;
|
|
103
|
+
return path.startsWith(normalized + '/');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {Headers} headers
|
|
108
|
+
* @param {import('./page/types').Cookie[]} cookies
|
|
109
|
+
*/
|
|
110
|
+
export function add_cookies_to_headers(headers, cookies) {
|
|
111
|
+
for (const new_cookie of cookies) {
|
|
112
|
+
const { name, value, options } = new_cookie;
|
|
113
|
+
headers.append('set-cookie', serialize(name, value, options));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { HttpError, Redirect } from '../../control.js';
|
|
2
|
+
import { normalize_error } from '../../../utils/error.js';
|
|
3
|
+
import { once } from '../../../utils/functions.js';
|
|
4
|
+
import { load_server_data } from '../page/load_data.js';
|
|
5
|
+
import { data_response, handle_error_and_jsonify } from '../utils.js';
|
|
6
|
+
import { normalize_path } from '../../../utils/url.js';
|
|
7
|
+
import { DATA_SUFFIX } from '../../../constants.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('types').RequestEvent} event
|
|
11
|
+
* @param {import('types').SSRRoute} route
|
|
12
|
+
* @param {import('types').SSROptions} options
|
|
13
|
+
* @param {import('types').SSRState} state
|
|
14
|
+
* @returns {Promise<Response>}
|
|
15
|
+
*/
|
|
16
|
+
export async function render_data(event, route, options, state) {
|
|
17
|
+
if (!route.page) {
|
|
18
|
+
// requesting /__data.js should fail for a +server.js
|
|
19
|
+
return new Response(undefined, {
|
|
20
|
+
status: 404
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const node_ids = [...route.page.layouts, route.page.leaf];
|
|
26
|
+
|
|
27
|
+
const invalidated =
|
|
28
|
+
event.url.searchParams
|
|
29
|
+
.get('__invalid')
|
|
30
|
+
?.split('')
|
|
31
|
+
.map((x) => x === 'y') ?? node_ids.map(() => true);
|
|
32
|
+
|
|
33
|
+
let aborted = false;
|
|
34
|
+
|
|
35
|
+
const url = new URL(event.url);
|
|
36
|
+
url.pathname = normalize_path(
|
|
37
|
+
url.pathname.slice(0, -DATA_SUFFIX.length),
|
|
38
|
+
options.trailing_slash
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
url.searchParams.delete('__invalid');
|
|
42
|
+
url.searchParams.delete('__id');
|
|
43
|
+
|
|
44
|
+
const new_event = { ...event, url };
|
|
45
|
+
|
|
46
|
+
const functions = node_ids.map((n, i) => {
|
|
47
|
+
return once(async () => {
|
|
48
|
+
try {
|
|
49
|
+
if (aborted) {
|
|
50
|
+
return /** @type {import('types').ServerDataSkippedNode} */ ({
|
|
51
|
+
type: 'skip'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// == because it could be undefined (in dev) or null (in build, because of JSON.stringify)
|
|
56
|
+
const node = n == undefined ? n : await options.manifest._.nodes[n]();
|
|
57
|
+
return load_server_data({
|
|
58
|
+
event: new_event,
|
|
59
|
+
state,
|
|
60
|
+
node,
|
|
61
|
+
parent: async () => {
|
|
62
|
+
/** @type {Record<string, any>} */
|
|
63
|
+
const data = {};
|
|
64
|
+
for (let j = 0; j < i; j += 1) {
|
|
65
|
+
const parent = /** @type {import('types').ServerDataNode | null} */ (
|
|
66
|
+
await functions[j]()
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (parent) {
|
|
70
|
+
Object.assign(data, parent.data);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} catch (e) {
|
|
77
|
+
aborted = true;
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const promises = functions.map(async (fn, i) => {
|
|
84
|
+
if (!invalidated[i]) {
|
|
85
|
+
return /** @type {import('types').ServerDataSkippedNode} */ ({
|
|
86
|
+
type: 'skip'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return fn();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
let length = promises.length;
|
|
94
|
+
const nodes = await Promise.all(
|
|
95
|
+
promises.map((p, i) =>
|
|
96
|
+
p.catch((error) => {
|
|
97
|
+
if (error instanceof Redirect) {
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Math.min because array isn't guaranteed to resolve in order
|
|
102
|
+
length = Math.min(length, i + 1);
|
|
103
|
+
|
|
104
|
+
return /** @type {import('types').ServerErrorNode} */ ({
|
|
105
|
+
type: 'error',
|
|
106
|
+
error: handle_error_and_jsonify(event, options, error),
|
|
107
|
+
status: error instanceof HttpError ? error.status : undefined
|
|
108
|
+
});
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
/** @type {import('types').ServerData} */
|
|
114
|
+
const server_data = {
|
|
115
|
+
type: 'data',
|
|
116
|
+
nodes: nodes.slice(0, length)
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return data_response(server_data);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
const error = normalize_error(e);
|
|
122
|
+
|
|
123
|
+
if (error instanceof Redirect) {
|
|
124
|
+
/** @type {import('types').ServerData} */
|
|
125
|
+
const server_data = {
|
|
126
|
+
type: 'redirect',
|
|
127
|
+
location: error.location
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return data_response(server_data);
|
|
131
|
+
} else {
|
|
132
|
+
// TODO make it clearer that this was an unexpected error
|
|
133
|
+
return data_response(handle_error_and_jsonify(event, options, error));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { json } from '../../exports/index.js';
|
|
2
|
+
import { negotiate } from '../../utils/http.js';
|
|
3
|
+
import { Redirect, ValidationError } from '../control.js';
|
|
4
|
+
import { check_method_names, method_not_allowed } from './utils.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('types').RequestEvent} event
|
|
8
|
+
* @param {import('types').SSREndpoint} mod
|
|
9
|
+
* @param {import('types').SSRState} state
|
|
10
|
+
* @returns {Promise<Response>}
|
|
11
|
+
*/
|
|
12
|
+
export async function render_endpoint(event, mod, state) {
|
|
13
|
+
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
|
|
14
|
+
|
|
15
|
+
// TODO: Remove for 1.0
|
|
16
|
+
check_method_names(mod);
|
|
17
|
+
|
|
18
|
+
let handler = mod[method];
|
|
19
|
+
|
|
20
|
+
if (!handler && method === 'HEAD') {
|
|
21
|
+
handler = mod.GET;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!handler) {
|
|
25
|
+
return method_not_allowed(mod, method);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const prerender = mod.prerender ?? state.prerender_default;
|
|
29
|
+
|
|
30
|
+
if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
|
|
31
|
+
throw new Error('Cannot prerender endpoints that have mutative methods');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (state.prerendering && !prerender) {
|
|
35
|
+
throw new Error(`${event.routeId} is not prerenderable`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await handler(
|
|
40
|
+
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!(response instanceof Response)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (state.prerendering) {
|
|
50
|
+
response.headers.set('x-sveltekit-routeid', /** @type {string} */ (event.routeId));
|
|
51
|
+
response.headers.set('x-sveltekit-prerender', String(prerender));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return response;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (error instanceof Redirect) {
|
|
57
|
+
return new Response(undefined, {
|
|
58
|
+
status: error.status,
|
|
59
|
+
headers: { location: error.location }
|
|
60
|
+
});
|
|
61
|
+
} else if (error instanceof ValidationError) {
|
|
62
|
+
return json(error.data, { status: error.status });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('types').RequestEvent} event
|
|
71
|
+
*/
|
|
72
|
+
export function is_endpoint_request(event) {
|
|
73
|
+
const { method } = event.request;
|
|
74
|
+
|
|
75
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
76
|
+
// These methods exist exclusively for endpoints
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// GET/POST requests may be for endpoints or pages. We prefer endpoints if this isn't a text/html request
|
|
81
|
+
const accept = event.request.headers.get('accept') ?? '*/*';
|
|
82
|
+
return negotiate(accept, ['*', 'text/html']) !== 'text/html';
|
|
83
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { is_endpoint_request, render_endpoint } from './endpoint.js';
|
|
2
|
+
import { render_page } from './page/index.js';
|
|
3
|
+
import { render_response } from './page/render.js';
|
|
4
|
+
import { respond_with_error } from './page/respond_with_error.js';
|
|
5
|
+
import { coalesce_to_error } from '../../utils/error.js';
|
|
6
|
+
import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
|
|
7
|
+
import { decode_params, disable_search, normalize_path } from '../../utils/url.js';
|
|
8
|
+
import { exec } from '../../utils/routing.js';
|
|
9
|
+
import { render_data } from './data/index.js';
|
|
10
|
+
import { DATA_SUFFIX } from '../../constants.js';
|
|
11
|
+
import { add_cookies_to_headers, get_cookies } from './cookie.js';
|
|
12
|
+
import { HttpError } from '../control.js';
|
|
13
|
+
|
|
14
|
+
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
15
|
+
|
|
16
|
+
/** @param {{ html: string }} opts */
|
|
17
|
+
const default_transform = ({ html }) => html;
|
|
18
|
+
|
|
19
|
+
const default_filter = () => false;
|
|
20
|
+
|
|
21
|
+
/** @type {import('types').Respond} */
|
|
22
|
+
export async function respond(request, options, state) {
|
|
23
|
+
let url = new URL(request.url);
|
|
24
|
+
|
|
25
|
+
if (options.csrf.check_origin) {
|
|
26
|
+
const type = request.headers.get('content-type')?.split(';')[0];
|
|
27
|
+
|
|
28
|
+
const forbidden =
|
|
29
|
+
request.method === 'POST' &&
|
|
30
|
+
request.headers.get('origin') !== url.origin &&
|
|
31
|
+
(type === 'application/x-www-form-urlencoded' || type === 'multipart/form-data');
|
|
32
|
+
|
|
33
|
+
if (forbidden) {
|
|
34
|
+
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
|
|
35
|
+
status: 403
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let decoded;
|
|
41
|
+
try {
|
|
42
|
+
decoded = decodeURI(url.pathname);
|
|
43
|
+
} catch {
|
|
44
|
+
return new Response('Malformed URI', { status: 400 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @type {import('types').SSRRoute | null} */
|
|
48
|
+
let route = null;
|
|
49
|
+
|
|
50
|
+
/** @type {Record<string, string>} */
|
|
51
|
+
let params = {};
|
|
52
|
+
|
|
53
|
+
if (options.paths.base && !state.prerendering?.fallback) {
|
|
54
|
+
if (!decoded.startsWith(options.paths.base)) {
|
|
55
|
+
return new Response('Not found', { status: 404 });
|
|
56
|
+
}
|
|
57
|
+
decoded = decoded.slice(options.paths.base.length) || '/';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const is_data_request = decoded.endsWith(DATA_SUFFIX);
|
|
61
|
+
if (is_data_request) decoded = decoded.slice(0, -DATA_SUFFIX.length) || '/';
|
|
62
|
+
|
|
63
|
+
if (!state.prerendering?.fallback) {
|
|
64
|
+
const matchers = await options.manifest._.matchers();
|
|
65
|
+
|
|
66
|
+
for (const candidate of options.manifest._.routes) {
|
|
67
|
+
const match = candidate.pattern.exec(decoded);
|
|
68
|
+
if (!match) continue;
|
|
69
|
+
|
|
70
|
+
const matched = exec(match, candidate.names, candidate.types, matchers);
|
|
71
|
+
if (matched) {
|
|
72
|
+
route = candidate;
|
|
73
|
+
params = decode_params(matched);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (route?.page && !is_data_request) {
|
|
80
|
+
const normalized = normalize_path(url.pathname, options.trailing_slash);
|
|
81
|
+
|
|
82
|
+
if (normalized !== url.pathname && !state.prerendering?.fallback) {
|
|
83
|
+
return new Response(undefined, {
|
|
84
|
+
status: 301,
|
|
85
|
+
headers: {
|
|
86
|
+
'x-sveltekit-normalize': '1',
|
|
87
|
+
location:
|
|
88
|
+
// ensure paths starting with '//' are not treated as protocol-relative
|
|
89
|
+
(normalized.startsWith('//') ? url.origin + normalized : normalized) +
|
|
90
|
+
(url.search === '?' ? '' : url.search)
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** @type {Record<string, string>} */
|
|
97
|
+
const headers = {};
|
|
98
|
+
|
|
99
|
+
const { cookies, new_cookies } = get_cookies(request, url);
|
|
100
|
+
|
|
101
|
+
if (state.prerendering) disable_search(url);
|
|
102
|
+
|
|
103
|
+
/** @type {import('types').RequestEvent} */
|
|
104
|
+
const event = {
|
|
105
|
+
cookies,
|
|
106
|
+
getClientAddress:
|
|
107
|
+
state.getClientAddress ||
|
|
108
|
+
(() => {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`${__SVELTEKIT_ADAPTER_NAME__} does not specify getClientAddress. Please raise an issue`
|
|
111
|
+
);
|
|
112
|
+
}),
|
|
113
|
+
locals: {},
|
|
114
|
+
params,
|
|
115
|
+
platform: state.platform,
|
|
116
|
+
request,
|
|
117
|
+
routeId: route && route.id,
|
|
118
|
+
setHeaders: (new_headers) => {
|
|
119
|
+
for (const key in new_headers) {
|
|
120
|
+
const lower = key.toLowerCase();
|
|
121
|
+
const value = new_headers[key];
|
|
122
|
+
|
|
123
|
+
if (lower === 'set-cookie') {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Use \`event.cookies.set(name, value, options)\` instead of \`event.setHeaders\` to set cookies`
|
|
126
|
+
);
|
|
127
|
+
} else if (lower in headers) {
|
|
128
|
+
throw new Error(`"${key}" header is already set`);
|
|
129
|
+
} else {
|
|
130
|
+
headers[lower] = value;
|
|
131
|
+
|
|
132
|
+
if (state.prerendering && lower === 'cache-control') {
|
|
133
|
+
state.prerendering.cache = /** @type {string} */ (value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
url
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// TODO remove this for 1.0
|
|
142
|
+
/**
|
|
143
|
+
* @param {string} property
|
|
144
|
+
* @param {string} replacement
|
|
145
|
+
* @param {string} suffix
|
|
146
|
+
*/
|
|
147
|
+
const removed = (property, replacement, suffix = '') => ({
|
|
148
|
+
get: () => {
|
|
149
|
+
throw new Error(`event.${property} has been replaced by event.${replacement}` + suffix);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const details = '. See https://github.com/sveltejs/kit/pull/3384 for details';
|
|
154
|
+
|
|
155
|
+
const body_getter = {
|
|
156
|
+
get: () => {
|
|
157
|
+
throw new Error(
|
|
158
|
+
'To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`' +
|
|
159
|
+
details
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
Object.defineProperties(event, {
|
|
165
|
+
clientAddress: removed('clientAddress', 'getClientAddress'),
|
|
166
|
+
method: removed('method', 'request.method', details),
|
|
167
|
+
headers: removed('headers', 'request.headers', details),
|
|
168
|
+
origin: removed('origin', 'url.origin'),
|
|
169
|
+
path: removed('path', 'url.pathname'),
|
|
170
|
+
query: removed('query', 'url.searchParams'),
|
|
171
|
+
body: body_getter,
|
|
172
|
+
rawBody: body_getter
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
/** @type {import('types').RequiredResolveOptions} */
|
|
176
|
+
let resolve_opts = {
|
|
177
|
+
transformPageChunk: default_transform,
|
|
178
|
+
filterSerializedResponseHeaders: default_filter
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param {import('types').RequestEvent} event
|
|
184
|
+
* @param {import('types').ResolveOptions} [opts]
|
|
185
|
+
*/
|
|
186
|
+
async function resolve(event, opts) {
|
|
187
|
+
try {
|
|
188
|
+
if (opts) {
|
|
189
|
+
// TODO remove for 1.0
|
|
190
|
+
if ('transformPage' in opts) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
'transformPage has been replaced by transformPageChunk — see https://github.com/sveltejs/kit/pull/5657 for more information'
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if ('ssr' in opts) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
'ssr has been removed, set it in the appropriate +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
resolve_opts = {
|
|
203
|
+
transformPageChunk: opts.transformPageChunk || default_transform,
|
|
204
|
+
filterSerializedResponseHeaders: opts.filterSerializedResponseHeaders || default_filter
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (state.prerendering?.fallback) {
|
|
209
|
+
return await render_response({
|
|
210
|
+
event,
|
|
211
|
+
options,
|
|
212
|
+
state,
|
|
213
|
+
page_config: { ssr: false, csr: true },
|
|
214
|
+
status: 200,
|
|
215
|
+
error: null,
|
|
216
|
+
branch: [],
|
|
217
|
+
fetched: [],
|
|
218
|
+
cookies: [],
|
|
219
|
+
resolve_opts
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (route) {
|
|
224
|
+
/** @type {Response} */
|
|
225
|
+
let response;
|
|
226
|
+
|
|
227
|
+
if (is_data_request) {
|
|
228
|
+
response = await render_data(event, route, options, state);
|
|
229
|
+
} else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
|
|
230
|
+
response = await render_endpoint(event, await route.endpoint(), state);
|
|
231
|
+
} else if (route.page) {
|
|
232
|
+
response = await render_page(event, route, route.page, options, state, resolve_opts);
|
|
233
|
+
} else {
|
|
234
|
+
// a route will always have a page or an endpoint, but TypeScript
|
|
235
|
+
// doesn't know that
|
|
236
|
+
throw new Error('This should never happen');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!is_data_request) {
|
|
240
|
+
// we only want to set cookies on __data.js requests, we don't
|
|
241
|
+
// want to cache stuff erroneously etc
|
|
242
|
+
for (const key in headers) {
|
|
243
|
+
const value = headers[key];
|
|
244
|
+
response.headers.set(key, /** @type {string} */ (value));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
add_cookies_to_headers(response.headers, Array.from(new_cookies.values()));
|
|
249
|
+
|
|
250
|
+
return response;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (state.initiator === GENERIC_ERROR) {
|
|
254
|
+
return new Response('Internal Server Error', {
|
|
255
|
+
status: 500
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// if this request came direct from the user, rather than
|
|
260
|
+
// via a `fetch` in a `load`, render a 404 page
|
|
261
|
+
if (!state.initiator) {
|
|
262
|
+
return await respond_with_error({
|
|
263
|
+
event,
|
|
264
|
+
options,
|
|
265
|
+
state,
|
|
266
|
+
status: 404,
|
|
267
|
+
error: new Error(`Not found: ${event.url.pathname}`),
|
|
268
|
+
resolve_opts
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (state.prerendering) {
|
|
273
|
+
return new Response('not found', { status: 404 });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// we can't load the endpoint from our own manifest,
|
|
277
|
+
// so we need to make an actual HTTP request
|
|
278
|
+
return await fetch(request);
|
|
279
|
+
} catch (e) {
|
|
280
|
+
// HttpError can come from endpoint - TODO should it be handled there instead?
|
|
281
|
+
const error = e instanceof HttpError ? e : coalesce_to_error(e);
|
|
282
|
+
return handle_fatal_error(event, options, error);
|
|
283
|
+
} finally {
|
|
284
|
+
event.cookies.set = () => {
|
|
285
|
+
throw new Error('Cannot use `cookies.set(...)` after the response has been generated');
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
event.setHeaders = () => {
|
|
289
|
+
throw new Error('Cannot use `setHeaders(...)` after the response has been generated');
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const response = await options.hooks.handle({
|
|
296
|
+
event,
|
|
297
|
+
resolve,
|
|
298
|
+
// TODO remove for 1.0
|
|
299
|
+
// @ts-expect-error
|
|
300
|
+
get request() {
|
|
301
|
+
throw new Error('request in handle has been replaced with event' + details);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// respond with 304 if etag matches
|
|
306
|
+
if (response.status === 200 && response.headers.has('etag')) {
|
|
307
|
+
let if_none_match_value = request.headers.get('if-none-match');
|
|
308
|
+
|
|
309
|
+
// ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
|
|
310
|
+
if (if_none_match_value?.startsWith('W/"')) {
|
|
311
|
+
if_none_match_value = if_none_match_value.substring(2);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const etag = /** @type {string} */ (response.headers.get('etag'));
|
|
315
|
+
|
|
316
|
+
if (if_none_match_value === etag) {
|
|
317
|
+
const headers = new Headers({ etag });
|
|
318
|
+
|
|
319
|
+
// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
|
320
|
+
for (const key of ['cache-control', 'content-location', 'date', 'expires', 'vary']) {
|
|
321
|
+
const value = response.headers.get(key);
|
|
322
|
+
if (value) headers.set(key, value);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return new Response(undefined, {
|
|
326
|
+
status: 304,
|
|
327
|
+
headers
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return response;
|
|
333
|
+
} catch (/** @type {unknown} */ e) {
|
|
334
|
+
const error = coalesce_to_error(e);
|
|
335
|
+
return handle_fatal_error(event, options, error);
|
|
336
|
+
}
|
|
337
|
+
}
|