@sveltejs/kit 3.0.0-next.7 → 3.0.0-next.8
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/package.json +2 -2
- package/src/core/config/options.js +1 -1
- package/src/core/env.js +1 -0
- package/src/core/sync/create_manifest_data/index.js +1 -1
- package/src/core/sync/sync.js +0 -2
- package/src/core/sync/write_client_manifest.js +0 -7
- package/src/core/sync/write_non_ambient.js +2 -2
- package/src/core/sync/write_server.js +0 -3
- package/src/core/sync/write_types/index.js +17 -14
- package/src/core/utils.js +2 -2
- package/src/exports/node/index.js +4 -8
- package/src/exports/public.d.ts +21 -14
- package/src/exports/vite/dev/index.js +3 -3
- package/src/exports/vite/index.js +64 -44
- package/src/exports/vite/preview/index.js +13 -14
- package/src/exports/vite/utils.js +5 -5
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/forms.js +2 -2
- package/src/runtime/app/paths/internal/client.js +4 -2
- package/src/runtime/app/paths/internal/server.js +2 -23
- package/src/runtime/app/paths/server.js +2 -2
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +216 -171
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/payload.js +17 -0
- package/src/runtime/client/remote-functions/form.svelte.js +6 -6
- package/src/runtime/client/types.d.ts +2 -6
- package/src/runtime/components/root.svelte +66 -0
- package/src/runtime/form-utils.js +1 -4
- package/src/runtime/server/cookie.js +17 -7
- package/src/runtime/server/page/index.js +7 -14
- package/src/runtime/server/page/render.js +71 -78
- package/src/runtime/server/remote.js +2 -1
- package/src/runtime/server/utils.js +28 -5
- package/src/runtime/types.d.ts +8 -0
- package/src/types/global-private.d.ts +10 -17
- package/src/types/internal.d.ts +21 -25
- package/src/utils/import.js +6 -1
- package/src/utils/routing.js +6 -6
- package/src/version.js +1 -1
- package/types/index.d.ts +34 -43
- package/types/index.d.ts.map +3 -2
- package/src/core/sync/write_root.js +0 -127
|
@@ -1,3 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* in development or if `bundleStrategy` is 'split', this file is used as the entry point */
|
|
2
|
+
|
|
3
|
+
import { set_payload } from './payload.js';
|
|
4
|
+
|
|
5
|
+
/** @type {Promise<typeof import('./client-entry.js')>} */
|
|
6
|
+
let client;
|
|
7
|
+
|
|
8
|
+
/** @param {import('types').SvelteKitPayload} payload */
|
|
9
|
+
export function init(payload) {
|
|
10
|
+
set_payload(payload);
|
|
11
|
+
// Importing the client after setting the payload ensures that modules such as
|
|
12
|
+
// `$app/paths` can read it during initialization.
|
|
13
|
+
client ??= import('./client-entry.js');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @param {Parameters<import('./client-entry.js').start>} args */
|
|
17
|
+
export async function start(...args) {
|
|
18
|
+
return (await client).start(...args);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @param {Parameters<import('./client-entry.js').load_css>} args */
|
|
22
|
+
export async function load_css(...args) {
|
|
23
|
+
return (await client).load_css(...args);
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @import {SvelteKitPayload} from 'types'; */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code inside the SvelteKit client runtime should only use this, not the global,
|
|
5
|
+
* so that the file hashes stay stable between rebuilds as long as the SvelteKit runtime doesn't change
|
|
6
|
+
*/
|
|
7
|
+
export let payload = __SVELTEKIT_PAYLOAD__ ?? /** @type {SvelteKitPayload} */ ({});
|
|
8
|
+
|
|
9
|
+
/** @param {SvelteKitPayload} value */
|
|
10
|
+
export function set_payload(value) {
|
|
11
|
+
payload = value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// this makes Vite inject its dev client code as this module's first dependency
|
|
15
|
+
// so that global constant replacements are done before any other module evaluates.
|
|
16
|
+
// For build, it's inert.
|
|
17
|
+
import.meta.hot;
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
query_responses,
|
|
9
9
|
_goto,
|
|
10
10
|
set_nearest_error_page,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
handle_error,
|
|
12
|
+
refreshAll
|
|
13
13
|
} from '../client.js';
|
|
14
14
|
import { tick } from 'svelte';
|
|
15
15
|
import { categorize_updates, remote_request } from './shared.svelte.js';
|
|
@@ -227,14 +227,14 @@ export function form(id) {
|
|
|
227
227
|
|
|
228
228
|
// if the developer took control of updates via `.updates(...)` (even with
|
|
229
229
|
// no arguments), or the server performed explicit refreshes, don't invalidateAll
|
|
230
|
-
const
|
|
230
|
+
const should_refresh = refreshes === null && !response.r;
|
|
231
231
|
|
|
232
232
|
if (response.redirect) {
|
|
233
233
|
// Use internal version to allow redirects to external URLs
|
|
234
234
|
void _goto(
|
|
235
235
|
response.redirect,
|
|
236
236
|
{
|
|
237
|
-
|
|
237
|
+
refreshAll: should_refresh
|
|
238
238
|
},
|
|
239
239
|
0
|
|
240
240
|
);
|
|
@@ -244,8 +244,8 @@ export function form(id) {
|
|
|
244
244
|
const succeeded = raw_issues.length === 0;
|
|
245
245
|
|
|
246
246
|
if (succeeded) {
|
|
247
|
-
if (
|
|
248
|
-
void
|
|
247
|
+
if (should_refresh) {
|
|
248
|
+
void refreshAll();
|
|
249
249
|
}
|
|
250
250
|
} else {
|
|
251
251
|
if (DEV) {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Uses
|
|
11
11
|
} from 'types';
|
|
12
12
|
import { Page, ParamMatcher } from '@sveltejs/kit';
|
|
13
|
+
import { RenderNode } from '../types.js';
|
|
13
14
|
|
|
14
15
|
export interface SvelteKitApp {
|
|
15
16
|
/**
|
|
@@ -57,8 +58,6 @@ export interface SvelteKitApp {
|
|
|
57
58
|
*/
|
|
58
59
|
hash: boolean;
|
|
59
60
|
|
|
60
|
-
root: typeof SvelteComponent;
|
|
61
|
-
|
|
62
61
|
/**
|
|
63
62
|
* Lazily loads the contents of src/error.html, used as a last-resort
|
|
64
63
|
* error page when the root layout's load function throws during client-side rendering.
|
|
@@ -91,13 +90,10 @@ export type NavigationFinished = {
|
|
|
91
90
|
type: 'loaded';
|
|
92
91
|
state: NavigationState;
|
|
93
92
|
props: {
|
|
94
|
-
constructors: Array<typeof SvelteComponent>;
|
|
95
|
-
errors?: Array<typeof SvelteComponent | undefined>;
|
|
96
|
-
components?: SvelteComponent[];
|
|
97
93
|
page: Page;
|
|
98
94
|
form?: Record<string, any> | null;
|
|
99
95
|
error?: App.Error;
|
|
100
|
-
|
|
96
|
+
tree: RenderNode;
|
|
101
97
|
};
|
|
102
98
|
};
|
|
103
99
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { afterNavigate } from '$app/navigation';
|
|
3
|
+
import type { Page } from '@sveltejs/kit';
|
|
4
|
+
import type { RenderNode } from '../types.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
page: Page;
|
|
8
|
+
tree: RenderNode;
|
|
9
|
+
components: any[];
|
|
10
|
+
resetters: Array<(() => void) | undefined>;
|
|
11
|
+
form?: any;
|
|
12
|
+
error?: App.Error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { page, components, resetters, tree, form, error }: Props = $props();
|
|
16
|
+
|
|
17
|
+
let mounted = $state(false);
|
|
18
|
+
let navigated = $state(false);
|
|
19
|
+
let title = $state('');
|
|
20
|
+
|
|
21
|
+
afterNavigate(() => {
|
|
22
|
+
if (mounted) {
|
|
23
|
+
navigated = true;
|
|
24
|
+
title = document.title || 'untitled page';
|
|
25
|
+
} else {
|
|
26
|
+
mounted = true;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
{#snippet node(n: RenderNode, depth: number)}
|
|
32
|
+
{const Component = $derived(n.component)}
|
|
33
|
+
{const Error = $derived(n.error)}
|
|
34
|
+
{const data = $derived(n.data)}
|
|
35
|
+
|
|
36
|
+
<svelte:boundary onerror={(_, reset) => (resetters[depth] = reset)}>
|
|
37
|
+
{#if n.child}
|
|
38
|
+
<!-- svelte-ignore binding_property_non_reactive -->
|
|
39
|
+
<Component bind:this={components[depth]} {data} {form} params={page.params}>
|
|
40
|
+
{@render node(n.child, depth + 1)}
|
|
41
|
+
</Component>
|
|
42
|
+
{:else}
|
|
43
|
+
<!-- svelte-ignore binding_property_non_reactive -->
|
|
44
|
+
<Component bind:this={components[depth]} {data} {form} params={page.params} {error} />
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
{#snippet failed(error)}
|
|
48
|
+
<Error {error} />
|
|
49
|
+
{/snippet}
|
|
50
|
+
</svelte:boundary>
|
|
51
|
+
{/snippet}
|
|
52
|
+
|
|
53
|
+
{@render node(tree, 0)}
|
|
54
|
+
|
|
55
|
+
{#if mounted}
|
|
56
|
+
<div
|
|
57
|
+
id="svelte-announcer"
|
|
58
|
+
aria-live="assertive"
|
|
59
|
+
aria-atomic="true"
|
|
60
|
+
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"
|
|
61
|
+
>
|
|
62
|
+
{#if navigated}
|
|
63
|
+
{title}
|
|
64
|
+
{/if}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
@@ -150,10 +150,7 @@ export async function deserialize_binary_form(request) {
|
|
|
150
150
|
throw deserialize_error('no body');
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
const reader = /** @type {ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */ (
|
|
155
|
-
request.body.getReader()
|
|
156
|
-
);
|
|
153
|
+
const reader = request.body.getReader();
|
|
157
154
|
|
|
158
155
|
/** @type {Array<Promise<Uint8Array<ArrayBuffer> | undefined>>} */
|
|
159
156
|
const chunks = [];
|
|
@@ -12,10 +12,11 @@ import { text_encoder } from '../utils.js';
|
|
|
12
12
|
const cookie_paths = {};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Cookies
|
|
16
|
-
*
|
|
15
|
+
* Cookies whose name and value combined are larger than this size are
|
|
16
|
+
* discarded by browsers. This is the limit codified for the name/value pair
|
|
17
|
+
* in RFC 6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-20#section-5.6-7.5.1
|
|
17
18
|
*/
|
|
18
|
-
const MAX_COOKIE_SIZE =
|
|
19
|
+
const MAX_COOKIE_SIZE = 4096;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Generates a unique key for a cookie based on its domain, path, and name in
|
|
@@ -124,7 +125,13 @@ export function get_cookies(request, url) {
|
|
|
124
125
|
|
|
125
126
|
// Add the most specific cookies to the result
|
|
126
127
|
for (const c of lookup.values()) {
|
|
127
|
-
cookies
|
|
128
|
+
// tombstones (deleted cookies) shadow request-header cookies,
|
|
129
|
+
// mirroring the behavior of `get()`
|
|
130
|
+
if (c.options.maxAge === 0) {
|
|
131
|
+
delete cookies[c.name];
|
|
132
|
+
} else {
|
|
133
|
+
cookies[c.name] = c.value;
|
|
134
|
+
}
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
return /** @type {Array<{ name: string; value: string }>} */ (
|
|
@@ -219,9 +226,12 @@ export function get_cookies(request, url) {
|
|
|
219
226
|
new_cookies.set(cookie_key, cookie);
|
|
220
227
|
|
|
221
228
|
if (DEV) {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
const size =
|
|
230
|
+
// only the name/value pair counts towards MAX_COOKIE_SIZE, not the other attributes
|
|
231
|
+
text_encoder.encode(name).byteLength +
|
|
232
|
+
text_encoder.encode((options.encode ?? encodeURIComponent)(value)).byteLength;
|
|
233
|
+
|
|
234
|
+
if (size > MAX_COOKIE_SIZE) {
|
|
225
235
|
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
226
236
|
}
|
|
227
237
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
/** @import { Component } from 'svelte' */
|
|
1
2
|
/** @import { ActionResult, RequestEvent, SSRManifest } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode,
|
|
3
|
+
/** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode, SSRNode, SSROptions, SSRState } from 'types' */
|
|
3
4
|
import { text } from '@sveltejs/kit';
|
|
4
5
|
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
5
6
|
import { compact } from '../../../utils/array.js';
|
|
@@ -316,13 +317,7 @@ export async function render_page(
|
|
|
316
317
|
},
|
|
317
318
|
status,
|
|
318
319
|
error,
|
|
319
|
-
error_components: await load_error_components(
|
|
320
|
-
options,
|
|
321
|
-
ssr,
|
|
322
|
-
error_branch,
|
|
323
|
-
page,
|
|
324
|
-
manifest
|
|
325
|
-
),
|
|
320
|
+
error_components: await load_error_components(ssr, error_branch, page, manifest),
|
|
326
321
|
branch: error_branch,
|
|
327
322
|
fetched,
|
|
328
323
|
data_serializer
|
|
@@ -374,7 +369,7 @@ export async function render_page(
|
|
|
374
369
|
action_result,
|
|
375
370
|
fetched,
|
|
376
371
|
data_serializer: !ssr ? server_data_serializer(event, event_state, options) : data_serializer,
|
|
377
|
-
error_components: await load_error_components(
|
|
372
|
+
error_components: await load_error_components(ssr, branch, page, manifest)
|
|
378
373
|
});
|
|
379
374
|
} catch (e) {
|
|
380
375
|
// a remote function could have thrown a redirect during render
|
|
@@ -398,18 +393,16 @@ export async function render_page(
|
|
|
398
393
|
}
|
|
399
394
|
|
|
400
395
|
/**
|
|
401
|
-
*
|
|
402
|
-
* @param {SSROptions} options
|
|
403
396
|
* @param {boolean} ssr
|
|
404
397
|
* @param {Array<import('./types.js').Loaded | null>} branch
|
|
405
398
|
* @param {PageNodeIndexes} page
|
|
406
399
|
* @param {SSRManifest} manifest
|
|
407
400
|
*/
|
|
408
|
-
async function load_error_components(
|
|
409
|
-
/** @type {Array<
|
|
401
|
+
async function load_error_components(ssr, branch, page, manifest) {
|
|
402
|
+
/** @type {Array<Component | undefined> | undefined} */
|
|
410
403
|
let error_components;
|
|
411
404
|
|
|
412
|
-
if (
|
|
405
|
+
if (ssr) {
|
|
413
406
|
let last_idx = -1;
|
|
414
407
|
error_components = await Promise.all(
|
|
415
408
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
/** @import { RenderNode } from '../../types.js' */
|
|
1
2
|
import * as devalue from 'devalue';
|
|
2
|
-
import { readable, writable } from 'svelte/store';
|
|
3
3
|
import { DEV } from 'esm-env';
|
|
4
4
|
import { isRedirect, text } from '@sveltejs/kit';
|
|
5
5
|
import * as paths from '$app/paths/internal/server';
|
|
@@ -22,14 +22,11 @@ import {
|
|
|
22
22
|
} from '../utils.js';
|
|
23
23
|
import * as env from '__sveltekit/env';
|
|
24
24
|
import { collect_remote_data } from '../remote.js';
|
|
25
|
+
import Root from '../../components/root.svelte';
|
|
26
|
+
import { render } from 'svelte/server';
|
|
25
27
|
|
|
26
28
|
// TODO rename this function/module
|
|
27
29
|
|
|
28
|
-
const updated = {
|
|
29
|
-
...readable(false),
|
|
30
|
-
check: () => false
|
|
31
|
-
};
|
|
32
|
-
|
|
33
30
|
/**
|
|
34
31
|
* Creates the HTML response.
|
|
35
32
|
* @param {{
|
|
@@ -46,7 +43,7 @@ const updated = {
|
|
|
46
43
|
* resolve_opts: import('types').RequiredResolveOptions;
|
|
47
44
|
* action_result?: import('@sveltejs/kit').ActionResult;
|
|
48
45
|
* data_serializer: import('./types.js').ServerDataSerializer;
|
|
49
|
-
* error_components?: Array<import('
|
|
46
|
+
* error_components?: Array<import('svelte').Component | undefined>
|
|
50
47
|
* }} opts
|
|
51
48
|
*/
|
|
52
49
|
export async function render_response({
|
|
@@ -91,7 +88,8 @@ export async function render_response({
|
|
|
91
88
|
// TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold
|
|
92
89
|
const inline_styles = new Map();
|
|
93
90
|
|
|
94
|
-
|
|
91
|
+
// TODO `svelte/server` should expose `RenderOutput`
|
|
92
|
+
/** @type {{ head: string, body: string, hashes: { script: string[] } }} */
|
|
95
93
|
let rendered;
|
|
96
94
|
|
|
97
95
|
const form_value =
|
|
@@ -142,49 +140,47 @@ export async function render_response({
|
|
|
142
140
|
if (page_config.ssr) {
|
|
143
141
|
/** @type {Record<string, any>} */
|
|
144
142
|
const props = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
form: form_value
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
if (error_components) {
|
|
163
|
-
if (error) {
|
|
164
|
-
props.error = error;
|
|
143
|
+
components: [],
|
|
144
|
+
resetters: [],
|
|
145
|
+
form: form_value,
|
|
146
|
+
tree: /** @type {RenderNode} */ ({}),
|
|
147
|
+
error,
|
|
148
|
+
page: {
|
|
149
|
+
error,
|
|
150
|
+
params: /** @type {Record<string, any>} */ (event.params),
|
|
151
|
+
route: event.route,
|
|
152
|
+
status,
|
|
153
|
+
url: event.url,
|
|
154
|
+
data: {},
|
|
155
|
+
form: form_value,
|
|
156
|
+
state: {}
|
|
165
157
|
}
|
|
166
|
-
|
|
167
|
-
}
|
|
158
|
+
};
|
|
168
159
|
|
|
169
|
-
let
|
|
160
|
+
let current_node = props.tree;
|
|
161
|
+
let data = props.page.data;
|
|
170
162
|
|
|
171
|
-
// props_n (instead of props[n]) makes it easy to avoid
|
|
172
|
-
// unnecessary updates for layout components
|
|
173
163
|
for (let i = 0; i < branch.length; i += 1) {
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
const node = branch[i];
|
|
165
|
+
|
|
166
|
+
data = { ...data, ...node.data };
|
|
167
|
+
|
|
168
|
+
// TODO this is undefined sometimes... where does the default error component come from?
|
|
169
|
+
const error = error_components?.slice(0, i + 1).findLast((x) => x);
|
|
170
|
+
|
|
171
|
+
current_node.error = error;
|
|
172
|
+
current_node.component = await node.node.component?.();
|
|
173
|
+
current_node.data = data;
|
|
174
|
+
|
|
175
|
+
if (i < branch.length - 1) {
|
|
176
|
+
current_node.child = /** @type {import('../../types.js').RenderNode} */ ({});
|
|
177
|
+
current_node = current_node.child;
|
|
178
|
+
}
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
props.page =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
route: event.route,
|
|
182
|
-
status,
|
|
183
|
-
url: event.url,
|
|
184
|
-
data,
|
|
185
|
-
form: form_value,
|
|
186
|
-
state: {}
|
|
187
|
-
};
|
|
181
|
+
props.page.data = data;
|
|
182
|
+
|
|
183
|
+
const render_state = { ...event_state, is_in_render: true };
|
|
188
184
|
|
|
189
185
|
const render_opts = {
|
|
190
186
|
context: new Map([
|
|
@@ -197,15 +193,28 @@ export async function render_response({
|
|
|
197
193
|
]),
|
|
198
194
|
csp: csp.script_needs_nonce ? { nonce: csp.nonce } : { hash: csp.script_needs_hash },
|
|
199
195
|
transformError: error_components
|
|
200
|
-
? /** @param {unknown} e */
|
|
196
|
+
? /** @param {unknown} e */ (e) => {
|
|
201
197
|
if (isRedirect(e)) {
|
|
202
198
|
throw e;
|
|
203
199
|
}
|
|
204
200
|
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
const handled = handle_error_and_jsonify(event, render_state, options, e);
|
|
202
|
+
|
|
203
|
+
// TODO 4.0 make this an async function and await `handled`
|
|
204
|
+
if (handled instanceof Promise) {
|
|
205
|
+
return handled.then((e) => {
|
|
206
|
+
error = e;
|
|
207
|
+
props.page.error = error;
|
|
208
|
+
props.page.status = status = error.status;
|
|
209
|
+
return error;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
error = handled;
|
|
214
|
+
props.page.error = error;
|
|
215
|
+
props.page.status = status = error.status;
|
|
216
|
+
|
|
217
|
+
return error;
|
|
209
218
|
}
|
|
210
219
|
: undefined
|
|
211
220
|
};
|
|
@@ -231,44 +240,27 @@ export async function render_response({
|
|
|
231
240
|
};
|
|
232
241
|
}
|
|
233
242
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
rendered = await with_request_store({ event, state }, async () => {
|
|
237
|
-
// use relative paths during rendering, so that the resulting HTML is as
|
|
238
|
-
// portable as possible, but reset afterwards
|
|
239
|
-
if (paths.relative) paths.override({ base, assets });
|
|
240
|
-
|
|
243
|
+
rendered = await with_request_store({ event, state: render_state }, async () => {
|
|
241
244
|
// We have to invoke .then eagerly here in order to kick off rendering: it's only starting on access,
|
|
242
245
|
// and `await maybe_promise` would eagerly access the .then property but call its function only after a tick, which is too late
|
|
243
246
|
// for the paths.reset() below and for any eager getRequestEvent() calls during rendering without AsyncLocalStorage available.
|
|
244
|
-
|
|
245
|
-
const rendered = options.root.render(props, render_opts).then((r) => r);
|
|
246
|
-
|
|
247
|
-
// we reset this synchronously, rather than after async rendering is complete,
|
|
248
|
-
// to avoid cross-talk between requests. This is a breaking change for
|
|
249
|
-
// anyone who opts into async SSR, since `base` and `assets` will no
|
|
250
|
-
// longer be relative to the current pathname.
|
|
251
|
-
// TODO 3.0 remove `base` and `assets` in favour of `resolve(...)` and `asset(...)`
|
|
252
|
-
paths.reset();
|
|
247
|
+
const rendered = render(Root, { ...render_opts, props });
|
|
253
248
|
|
|
254
|
-
|
|
255
|
-
const { head, html: body, css, hashes } = await rendered;
|
|
249
|
+
const { head, body, hashes } = await rendered;
|
|
256
250
|
|
|
257
251
|
if (hashes) {
|
|
258
252
|
csp.add_script_hashes(hashes.script);
|
|
259
253
|
}
|
|
260
254
|
|
|
261
|
-
return { head, body,
|
|
255
|
+
return { head, body, hashes };
|
|
262
256
|
});
|
|
263
257
|
} finally {
|
|
264
258
|
if (DEV) {
|
|
265
259
|
globalThis.fetch = fetch;
|
|
266
260
|
}
|
|
267
|
-
|
|
268
|
-
paths.reset(); // just in case `options.root.render(...)` failed
|
|
269
261
|
}
|
|
270
262
|
} else {
|
|
271
|
-
rendered = { head: '', body: '',
|
|
263
|
+
rendered = { head: '', body: '', hashes: { script: [] } };
|
|
272
264
|
}
|
|
273
265
|
|
|
274
266
|
for (const { node } of branch) {
|
|
@@ -412,7 +404,7 @@ export async function render_response({
|
|
|
412
404
|
|
|
413
405
|
const blocks = [];
|
|
414
406
|
|
|
415
|
-
const properties = [`base: ${base_expression}`];
|
|
407
|
+
const properties = [`base: ${base_expression}`, `version: ${s(__SVELTEKIT_APP_VERSION__)}`];
|
|
416
408
|
|
|
417
409
|
if (paths.assets) {
|
|
418
410
|
properties.push(`assets: ${s(paths.assets)}`);
|
|
@@ -435,7 +427,9 @@ export async function render_response({
|
|
|
435
427
|
if (client.inline) {
|
|
436
428
|
app_declaration = `const app = ${global}.app.app;`;
|
|
437
429
|
} else if (client.app) {
|
|
438
|
-
app_declaration = `const
|
|
430
|
+
app_declaration = `const kit = await import(${s(prefixed(client.start))});
|
|
431
|
+
kit.init(${global});
|
|
432
|
+
const app = await import(${s(prefixed(client.app))});`;
|
|
439
433
|
} else {
|
|
440
434
|
app_declaration = `const { app } = await import(${s(prefixed(client.start))});`;
|
|
441
435
|
}
|
|
@@ -529,10 +523,9 @@ export async function render_response({
|
|
|
529
523
|
|
|
530
524
|
${serialized_data}${global}.app.start(${args.join(', ')});`
|
|
531
525
|
: client.app
|
|
532
|
-
? `
|
|
533
|
-
|
|
534
|
-
import(${s(prefixed(client.app))})
|
|
535
|
-
]).then(([kit, app]) => {
|
|
526
|
+
? `import(${s(prefixed(client.start))}).then(async (kit) => {
|
|
527
|
+
kit.init(${global});
|
|
528
|
+
const app = await import(${s(prefixed(client.app))});
|
|
536
529
|
${serialized_data}kit.start(app, ${args.join(', ')});
|
|
537
530
|
});`
|
|
538
531
|
: `import(${s(prefixed(client.start))}).then((app) => {
|
|
@@ -361,7 +361,8 @@ export async function collect_remote_data(data, event, state, options) {
|
|
|
361
361
|
* @returns {Promise<App.Error>}
|
|
362
362
|
*/
|
|
363
363
|
function convert_error(error) {
|
|
364
|
-
|
|
364
|
+
// TODO 4.0 remove the `Promise.resolve(...)`
|
|
365
|
+
return Promise.resolve(handle_error_and_jsonify(event, state, options, error));
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
/** @type {Promise<any>[]} */
|
|
@@ -98,9 +98,9 @@ export async function handle_fatal_error(event, state, options, error) {
|
|
|
98
98
|
* @param {import('types').RequestState} state
|
|
99
99
|
* @param {import('types').SSROptions} options
|
|
100
100
|
* @param {any} error
|
|
101
|
-
* @returns {Promise<App.Error>}
|
|
101
|
+
* @returns {App.Error | Promise<App.Error>}
|
|
102
102
|
*/
|
|
103
|
-
export
|
|
103
|
+
export function handle_error_and_jsonify(event, state, options, error) {
|
|
104
104
|
if (error instanceof HttpError) {
|
|
105
105
|
// @ts-expect-error custom user errors may not have a message field if App.Error is overwritten
|
|
106
106
|
return { message: 'Unknown Error', ...error.body };
|
|
@@ -113,11 +113,34 @@ export async function handle_error_and_jsonify(event, state, options, error) {
|
|
|
113
113
|
const status = get_status(error);
|
|
114
114
|
const message = get_message(error);
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
// TODO 4.0 await this, rather than handling the non-Promise case
|
|
117
|
+
const result = with_request_store({ event, state }, () =>
|
|
117
118
|
options.hooks.handleError({ error, event, status, message })
|
|
118
|
-
)
|
|
119
|
+
) ?? { status, message };
|
|
120
|
+
|
|
121
|
+
if (result instanceof Promise) {
|
|
122
|
+
if (!__SVELTEKIT_SUPPORTS_ASYNC__ && state.is_in_render) {
|
|
123
|
+
console.warn(
|
|
124
|
+
`To use an async \`handleError\` hook to handle errors that occur during rendering, you must enable \`compilerOptions.experimental.async\` in the SvelteKit plugin of your Vite config. The returned error has been replaced with a generic object`
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// we're discarding the result, but we still need to prevent an unhandled
|
|
128
|
+
// rejection if the user's async `handleError` hook rejects
|
|
129
|
+
result.catch(() => {});
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
status,
|
|
133
|
+
message: 'Internal Error'
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result.then((body) => {
|
|
138
|
+
body ??= { status, message };
|
|
139
|
+
return { ...body, status: get_status(body, error) };
|
|
140
|
+
});
|
|
141
|
+
}
|
|
119
142
|
|
|
120
|
-
return { ...
|
|
143
|
+
return { ...result, status: get_status(result, error) };
|
|
121
144
|
}
|
|
122
145
|
|
|
123
146
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SvelteKitPayload } from 'types';
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
const __SVELTEKIT_ADAPTER_NAME__: string;
|
|
@@ -33,26 +33,20 @@ declare global {
|
|
|
33
33
|
* Used for treeshaking universal load code from client bundles when no universal loads exist.
|
|
34
34
|
*/
|
|
35
35
|
const __SVELTEKIT_HAS_UNIVERSAL_LOAD__: boolean;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assets?: string;
|
|
42
|
-
/** Public environment variables */
|
|
43
|
-
env?: Record<string, string>;
|
|
44
|
-
/** Serialized data from query/form/command functions */
|
|
45
|
-
data?: RemoteFunctionData;
|
|
46
|
-
/** Create a placeholder promise */
|
|
47
|
-
defer?: (id: number) => Promise<any>;
|
|
48
|
-
/** Resolve a placeholder promise */
|
|
49
|
-
resolve?: (data: { id: number; data: any; error: any }) => void;
|
|
50
|
-
};
|
|
36
|
+
/**
|
|
37
|
+
* The `__sveltekit_abc123` object in the init `<script>`.
|
|
38
|
+
* Should only be used when bundleStrategy !== 'inline' to avoid SvelteKit runtime changing on every build, preventing cacheability.
|
|
39
|
+
*/
|
|
40
|
+
const __SVELTEKIT_PAYLOAD__: SvelteKitPayload;
|
|
51
41
|
/**
|
|
52
42
|
* The Vite `root` setting used to construct paths to nodes and components
|
|
53
43
|
* for the SSR manifest during development
|
|
54
44
|
*/
|
|
55
45
|
const __SVELTEKIT_ROOT__: string;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the `experimental.async` flag is applied
|
|
48
|
+
*/
|
|
49
|
+
const __SVELTEKIT_SUPPORTS_ASYNC__: boolean;
|
|
56
50
|
/**
|
|
57
51
|
* This makes the use of specific features visible at both dev and build time, in such a
|
|
58
52
|
* way that we can error when they are not supported by the target platform.
|
|
@@ -68,7 +62,6 @@ declare global {
|
|
|
68
62
|
* to throw an error if the feature would fail in production.
|
|
69
63
|
*/
|
|
70
64
|
var __SVELTEKIT_TRACK__: (label: string) => void;
|
|
71
|
-
var __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: boolean;
|
|
72
65
|
var Bun: object;
|
|
73
66
|
var Deno: object;
|
|
74
67
|
}
|