@sveltejs/kit 3.0.0-next.4 → 3.0.0-next.7
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 +23 -18
- package/src/core/adapt/builder.js +29 -43
- package/src/core/adapt/index.js +1 -4
- package/src/core/config/index.js +135 -71
- package/src/core/config/options.js +291 -244
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +120 -5
- package/src/core/generate_manifest/index.js +12 -15
- package/src/core/postbuild/analyse.js +8 -12
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/prerender.js +40 -23
- package/src/core/sync/create_manifest_data/index.js +29 -49
- package/src/core/sync/write_client_manifest.js +14 -14
- package/src/core/sync/write_non_ambient.js +10 -7
- package/src/core/sync/write_root.js +9 -30
- package/src/core/sync/write_server.js +13 -11
- package/src/core/sync/write_tsconfig.js +2 -4
- package/src/core/sync/write_types/index.js +11 -10
- package/src/exports/index.js +34 -12
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +1 -1
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +1 -2
- package/src/exports/internal/server/index.js +33 -0
- package/src/exports/internal/shared.js +89 -0
- package/src/exports/node/index.js +58 -7
- package/src/exports/params.js +63 -0
- package/src/exports/public.d.ts +299 -145
- package/src/exports/url.js +84 -0
- package/src/exports/vite/build/build_server.js +6 -1
- package/src/exports/vite/dev/index.js +38 -37
- package/src/exports/vite/index.js +498 -353
- package/src/exports/vite/preview/index.js +16 -8
- package/src/exports/vite/utils.js +7 -11
- package/src/runtime/app/env/types.d.ts +1 -1
- package/src/runtime/app/forms.js +22 -5
- package/src/runtime/app/paths/client.js +4 -10
- package/src/runtime/app/paths/public.d.ts +0 -28
- package/src/runtime/app/paths/server.js +8 -4
- package/src/runtime/app/server/remote/form.js +10 -3
- package/src/runtime/app/server/remote/query.js +9 -18
- package/src/runtime/app/server/remote/requested.js +8 -4
- package/src/runtime/app/server/remote/shared.js +5 -7
- package/src/runtime/app/state/client.js +1 -2
- package/src/runtime/app/stores.js +13 -76
- package/src/runtime/client/client.js +205 -167
- package/src/runtime/client/fetcher.js +3 -2
- package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
- package/src/runtime/client/remote-functions/form.svelte.js +122 -31
- package/src/runtime/client/remote-functions/prerender.svelte.js +17 -4
- package/src/runtime/client/remote-functions/query/index.js +3 -2
- package/src/runtime/client/remote-functions/query/instance.svelte.js +27 -11
- package/src/runtime/client/remote-functions/query-batch.svelte.js +4 -3
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +42 -15
- package/src/runtime/client/remote-functions/shared.svelte.js +18 -9
- package/src/runtime/client/state.svelte.js +66 -49
- package/src/runtime/client/types.d.ts +10 -2
- package/src/runtime/client/utils.js +0 -96
- package/src/runtime/form-utils.js +99 -18
- package/src/runtime/server/cookie.js +22 -33
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +7 -7
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/index.js +2 -2
- package/src/runtime/server/page/actions.js +41 -26
- package/src/runtime/server/page/index.js +2 -1
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +36 -47
- package/src/runtime/server/page/respond_with_error.js +7 -8
- package/src/runtime/server/page/server_routing.js +13 -9
- package/src/runtime/server/remote.js +85 -39
- package/src/runtime/server/respond.js +92 -58
- package/src/runtime/server/utils.js +7 -7
- package/src/runtime/telemetry/otel.js +1 -1
- package/src/types/ambient.d.ts +10 -5
- package/src/types/global-private.d.ts +4 -4
- package/src/types/internal.d.ts +17 -20
- package/src/types/private.d.ts +33 -1
- package/src/types/synthetic/$lib.md +1 -1
- package/src/utils/error.js +12 -4
- package/src/utils/mime.js +9 -0
- package/src/utils/params.js +66 -0
- package/src/utils/routing.js +84 -38
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +20 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +434 -269
- package/types/index.d.ts.map +19 -15
- package/src/exports/internal/server.js +0 -22
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
/** @import { RemoteFunctionDataNode, ServerNodesResponse, ServerRedirectNode } from 'types' */
|
|
2
|
+
/** @import { NavigationIntent } from './types.js' */
|
|
2
3
|
/** @import { CacheEntry } from './remote-functions/cache.svelte.js' */
|
|
3
4
|
/** @import { Query } from './remote-functions/query/instance.svelte.js' */
|
|
4
5
|
/** @import { LiveQuery } from './remote-functions/query-live/instance.svelte.js' */
|
|
5
6
|
import { BROWSER, DEV } from 'esm-env';
|
|
6
7
|
import * as svelte from 'svelte';
|
|
7
8
|
import { HttpError, Redirect, SvelteKitError } from '@sveltejs/kit/internal';
|
|
8
|
-
|
|
9
|
-
// Svelte 4 and under don't have `untrack`, so we have to fallback if `untrack` is not exported
|
|
10
|
-
const untrack = svelte.untrack ?? ((value) => value());
|
|
11
|
-
import {
|
|
12
|
-
decode_params,
|
|
13
|
-
decode_pathname,
|
|
14
|
-
strip_hash,
|
|
15
|
-
make_trackable,
|
|
16
|
-
normalize_path
|
|
17
|
-
} from '../../utils/url.js';
|
|
9
|
+
import { decode_pathname, strip_hash, make_trackable, normalize_path } from '../../utils/url.js';
|
|
18
10
|
import { dev_fetch, initial_fetch, lock_fetch, subsequent_fetch, unlock_fetch } from './fetcher.js';
|
|
19
11
|
import { parse, parse_server_route } from './parse.js';
|
|
20
12
|
import * as storage from './session-storage.js';
|
|
@@ -26,11 +18,9 @@ import {
|
|
|
26
18
|
is_external_url,
|
|
27
19
|
origin,
|
|
28
20
|
scroll_state,
|
|
29
|
-
notifiable_store,
|
|
30
|
-
create_updated_store,
|
|
31
21
|
load_css
|
|
32
22
|
} from './utils.js';
|
|
33
|
-
import { base } from '$app/paths';
|
|
23
|
+
import { base } from '$app/paths/internal/client';
|
|
34
24
|
import * as devalue from 'devalue';
|
|
35
25
|
import {
|
|
36
26
|
HISTORY_INDEX,
|
|
@@ -52,8 +42,7 @@ import {
|
|
|
52
42
|
validate_load_response
|
|
53
43
|
} from '../shared.js';
|
|
54
44
|
import { get_message, get_status } from '../../utils/error.js';
|
|
55
|
-
import {
|
|
56
|
-
import { page, update, navigating } from './state.svelte.js';
|
|
45
|
+
import { page, update, navigating, updated } from './state.svelte.js';
|
|
57
46
|
import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
|
|
58
47
|
import { noop_span } from '../telemetry/noop.js';
|
|
59
48
|
import { read_ndjson } from './ndjson.js';
|
|
@@ -90,8 +79,6 @@ const snapshots = storage.get(SNAPSHOT_KEY) ?? {};
|
|
|
90
79
|
if (DEV && BROWSER) {
|
|
91
80
|
let warned = false;
|
|
92
81
|
|
|
93
|
-
const current_module_url = import.meta.url.split('?')[0]; // remove query params that vite adds to the URL when it is loaded from node_modules
|
|
94
|
-
|
|
95
82
|
const warn = () => {
|
|
96
83
|
if (warned) return;
|
|
97
84
|
|
|
@@ -100,9 +87,13 @@ if (DEV && BROWSER) {
|
|
|
100
87
|
let stack = new Error().stack?.split('\n');
|
|
101
88
|
if (!stack) return;
|
|
102
89
|
if (!stack[0].includes('https:') && !stack[0].includes('http:')) stack = stack.slice(1); // Chrome includes the error message in the stack
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
90
|
+
|
|
91
|
+
// skip over `warn` and the place where `warn` was called
|
|
92
|
+
const frame = stack[2];
|
|
93
|
+
|
|
94
|
+
// ignore calls that happen inside dependencies, including SvelteKit.
|
|
95
|
+
// `frame` can be falsy if we came from an anonymous function
|
|
96
|
+
if (frame?.includes('node_modules')) return;
|
|
106
97
|
|
|
107
98
|
warned = true;
|
|
108
99
|
|
|
@@ -124,15 +115,6 @@ if (DEV && BROWSER) {
|
|
|
124
115
|
};
|
|
125
116
|
}
|
|
126
117
|
|
|
127
|
-
export const stores = {
|
|
128
|
-
url: /* @__PURE__ */ notifiable_store({}),
|
|
129
|
-
page: /* @__PURE__ */ notifiable_store({}),
|
|
130
|
-
navigating: /* @__PURE__ */ writable(
|
|
131
|
-
/** @type {import('@sveltejs/kit').Navigation | null} */ (null)
|
|
132
|
-
),
|
|
133
|
-
updated: /* @__PURE__ */ create_updated_store()
|
|
134
|
-
};
|
|
135
|
-
|
|
136
118
|
/** @param {number} index */
|
|
137
119
|
function update_scroll_positions(index) {
|
|
138
120
|
scroll_positions[index] = scroll_state();
|
|
@@ -163,7 +145,8 @@ function clear_onward_history(current_history_index, current_navigation_index) {
|
|
|
163
145
|
* Returns a `Promise` that never resolves (to prevent any
|
|
164
146
|
* subsequent work, e.g. history manipulation, from happening)
|
|
165
147
|
* @param {URL} url
|
|
166
|
-
* @param {boolean} [replace]
|
|
148
|
+
* @param {boolean} [replace] if `true`, will replace the current `history` entry rather than creating a new one with `pushState`
|
|
149
|
+
* @returns {Promise<any>} a promise that never resolves
|
|
167
150
|
*/
|
|
168
151
|
function native_navigation(url, replace = false) {
|
|
169
152
|
if (replace) {
|
|
@@ -187,7 +170,7 @@ async function update_service_worker() {
|
|
|
187
170
|
}
|
|
188
171
|
}
|
|
189
172
|
|
|
190
|
-
/** @type {import('types').CSRRoute[]} All routes of the app. Only available when
|
|
173
|
+
/** @type {import('types').CSRRoute[]} All routes of the app. Only available when router.resolution=client */
|
|
191
174
|
let routes;
|
|
192
175
|
/** @type {import('types').CSRPageNodeLoader} */
|
|
193
176
|
let default_layout_loader;
|
|
@@ -292,8 +275,17 @@ let current_history_index;
|
|
|
292
275
|
/** @type {number} */
|
|
293
276
|
let current_navigation_index;
|
|
294
277
|
|
|
295
|
-
/** @type {{}} */
|
|
296
|
-
let
|
|
278
|
+
/** @type {{}} Token for the latest navigation. Updated on new navigations */
|
|
279
|
+
let navigation_token;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @type {{}}
|
|
283
|
+
* The latest invalidate(All) token. Superseeded by both later invalidate(All)s and navigations.
|
|
284
|
+
* This is separate to navigation_token because an invalidate(All) might be triggered while a navigation
|
|
285
|
+
* is in progress, and we want to be able to finish this navigation (unless the invalidation finishes before
|
|
286
|
+
* it and redirects, in which case we will do the redirect triggered by the invalidation).
|
|
287
|
+
*/
|
|
288
|
+
let invalidation_token;
|
|
297
289
|
|
|
298
290
|
/**
|
|
299
291
|
* A set of tokens which are associated to current preloads.
|
|
@@ -301,6 +293,7 @@ let token;
|
|
|
301
293
|
* If a preload token is in the set and the preload errors, the error
|
|
302
294
|
* handling logic (for example reloading) is skipped.
|
|
303
295
|
*/
|
|
296
|
+
/** @type {Set<{}>} */
|
|
304
297
|
const preload_tokens = new Set();
|
|
305
298
|
|
|
306
299
|
/** @type {Promise<void> | null} */
|
|
@@ -418,7 +411,9 @@ async function _invalidate(include_load_functions = true, reset_page_state = tru
|
|
|
418
411
|
if (!pending_invalidate) return;
|
|
419
412
|
pending_invalidate = null;
|
|
420
413
|
|
|
421
|
-
const
|
|
414
|
+
const token = (invalidation_token = {});
|
|
415
|
+
const nav_token = navigation_token;
|
|
416
|
+
const navigating = is_navigating;
|
|
422
417
|
const intent = await get_navigation_intent(current.url, true);
|
|
423
418
|
|
|
424
419
|
// Clear preload, it might be affected by the invalidation.
|
|
@@ -450,17 +445,25 @@ async function _invalidate(include_load_functions = true, reset_page_state = tru
|
|
|
450
445
|
if (include_load_functions) {
|
|
451
446
|
const prev_state = page.state;
|
|
452
447
|
const navigation_result = intent && (await load_route(intent));
|
|
453
|
-
if (!navigation_result || nav_token !==
|
|
448
|
+
if (!navigation_result || token !== invalidation_token || nav_token !== navigation_token) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
454
451
|
|
|
455
452
|
if (navigation_result.type === 'redirect') {
|
|
456
453
|
return _goto(
|
|
457
454
|
new URL(navigation_result.location, current.url).href,
|
|
458
455
|
{ replaceState: true },
|
|
459
456
|
1,
|
|
460
|
-
|
|
457
|
+
token
|
|
461
458
|
);
|
|
462
459
|
}
|
|
463
460
|
|
|
461
|
+
// A navigation started before the invalidation and ended before it finished. The invalidation did not redirect,
|
|
462
|
+
// hence it likely contains outdated data now, so we ignore it.
|
|
463
|
+
if (navigating && !is_navigating) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
464
467
|
// This is a bit hacky but allows us not having to pass that boolean around, making things harder to reason about
|
|
465
468
|
if (!reset_page_state) {
|
|
466
469
|
navigation_result.props.page.state = prev_state;
|
|
@@ -527,8 +530,10 @@ function persist_state() {
|
|
|
527
530
|
* @param {{ replaceState?: boolean; noScroll?: boolean; keepFocus?: boolean; invalidateAll?: boolean; invalidate?: Array<string | URL | ((url: URL) => boolean)>; state?: Record<string, any> }} options
|
|
528
531
|
* @param {number} redirect_count
|
|
529
532
|
* @param {{}} [nav_token]
|
|
533
|
+
* @param {NavigationIntent | undefined} [intent] navigation intent, when already known by the caller (avoids recomputing it)
|
|
534
|
+
* @returns {Promise<void>}
|
|
530
535
|
*/
|
|
531
|
-
export async function _goto(url, options, redirect_count, nav_token) {
|
|
536
|
+
export async function _goto(url, options, redirect_count, nav_token, intent) {
|
|
532
537
|
/** @type {Set<string>} */
|
|
533
538
|
let query_keys;
|
|
534
539
|
/** @type {Set<string>} */
|
|
@@ -549,6 +554,7 @@ export async function _goto(url, options, redirect_count, nav_token) {
|
|
|
549
554
|
state: options.state,
|
|
550
555
|
redirect_count,
|
|
551
556
|
nav_token,
|
|
557
|
+
intent,
|
|
552
558
|
accept: () => {
|
|
553
559
|
if (options.invalidateAll) {
|
|
554
560
|
force_invalidation = true;
|
|
@@ -696,17 +702,18 @@ async function initialize(result, target, hydrate) {
|
|
|
696
702
|
|
|
697
703
|
update(/** @type {import('@sveltejs/kit').Page} */ (result.props.page));
|
|
698
704
|
|
|
705
|
+
// TODO: use mount()
|
|
699
706
|
root = new app.root({
|
|
700
707
|
target,
|
|
701
|
-
props: { ...result.props,
|
|
708
|
+
props: { ...result.props, components },
|
|
702
709
|
hydrate,
|
|
703
|
-
//
|
|
710
|
+
// Svelte 5 specific: asynchronously instantiate the component, i.e. don't call flushSync
|
|
704
711
|
sync: false,
|
|
705
|
-
//
|
|
712
|
+
// Svelte 5 specific: transformError allows to transform errors before they are passed to boundaries
|
|
706
713
|
transformError: __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__
|
|
707
714
|
? /** @param {unknown} e */ async (e) => {
|
|
708
715
|
const error = await handle_error(e, current.nav);
|
|
709
|
-
rendering_error = { error, status:
|
|
716
|
+
rendering_error = { error, status: error.status };
|
|
710
717
|
page.error = error;
|
|
711
718
|
page.status = rendering_error.status;
|
|
712
719
|
return error;
|
|
@@ -718,8 +725,6 @@ async function initialize(result, target, hydrate) {
|
|
|
718
725
|
// which causes component script blocks to run asynchronously
|
|
719
726
|
void (await Promise.resolve());
|
|
720
727
|
|
|
721
|
-
restore_snapshot(current_navigation_index);
|
|
722
|
-
|
|
723
728
|
if (hydrate) {
|
|
724
729
|
/** @type {import('@sveltejs/kit').AfterNavigate} */
|
|
725
730
|
const navigation = {
|
|
@@ -736,6 +741,8 @@ async function initialize(result, target, hydrate) {
|
|
|
736
741
|
after_navigate_callbacks.forEach((fn) => fn(navigation));
|
|
737
742
|
}
|
|
738
743
|
|
|
744
|
+
restore_snapshot(current_navigation_index);
|
|
745
|
+
|
|
739
746
|
started = true;
|
|
740
747
|
}
|
|
741
748
|
|
|
@@ -746,7 +753,7 @@ async function initialize(result, target, hydrate) {
|
|
|
746
753
|
* params: Record<string, string>;
|
|
747
754
|
* branch: Array<import('./types.js').BranchNode | undefined>;
|
|
748
755
|
* errors?: Array<import('types').CSRPageNodeLoader | undefined>;
|
|
749
|
-
* status
|
|
756
|
+
* status?: number;
|
|
750
757
|
* error: App.Error | null;
|
|
751
758
|
* route: import('types').CSRRoute | null;
|
|
752
759
|
* form?: Record<string, any> | null;
|
|
@@ -790,9 +797,8 @@ async function get_navigation_result_from_branch({
|
|
|
790
797
|
route
|
|
791
798
|
},
|
|
792
799
|
props: {
|
|
793
|
-
// @ts-ignore Somehow it's getting SvelteComponent and SvelteComponentDev mixed up
|
|
794
800
|
constructors: compact(branch).map((branch_node) => branch_node.node.component),
|
|
795
|
-
page
|
|
801
|
+
page
|
|
796
802
|
}
|
|
797
803
|
};
|
|
798
804
|
|
|
@@ -863,7 +869,7 @@ async function get_navigation_result_from_branch({
|
|
|
863
869
|
id: route?.id ?? null
|
|
864
870
|
},
|
|
865
871
|
state: {},
|
|
866
|
-
status,
|
|
872
|
+
status: status ?? error?.status ?? 200,
|
|
867
873
|
url: new URL(url),
|
|
868
874
|
form: form ?? null,
|
|
869
875
|
// The whole page store is updated, but this way the object reference stays the same
|
|
@@ -1160,16 +1166,31 @@ function preload_error({ error, url, route, params }) {
|
|
|
1160
1166
|
branch: []
|
|
1161
1167
|
},
|
|
1162
1168
|
props: {
|
|
1163
|
-
page:
|
|
1169
|
+
page: {
|
|
1170
|
+
// we skipped loading the error page, so we have to use the current page
|
|
1171
|
+
// store, but update the status received while preloading
|
|
1172
|
+
...page,
|
|
1173
|
+
status: error.status
|
|
1174
|
+
},
|
|
1164
1175
|
constructors: []
|
|
1165
1176
|
}
|
|
1166
1177
|
};
|
|
1167
1178
|
}
|
|
1168
1179
|
|
|
1169
1180
|
/**
|
|
1170
|
-
* @
|
|
1181
|
+
* @overload
|
|
1182
|
+
* @param {import('./types.js').NavigationIntent} intent
|
|
1183
|
+
* @returns {Promise<import('./types.js').NavigationResult | undefined>}
|
|
1184
|
+
*/
|
|
1185
|
+
/**
|
|
1186
|
+
* @overload
|
|
1187
|
+
* @param {import('./types.js').NavigationIntent & { preload: {} }} intent
|
|
1171
1188
|
* @returns {Promise<import('./types.js').NavigationResult>}
|
|
1172
1189
|
*/
|
|
1190
|
+
/**
|
|
1191
|
+
* @param {import('./types.js').NavigationIntent & { preload?: {} }} intent
|
|
1192
|
+
* @returns {Promise<import('./types.js').NavigationResult | undefined>}
|
|
1193
|
+
*/
|
|
1173
1194
|
async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
1174
1195
|
if (load_cache?.id === id) {
|
|
1175
1196
|
// the preload becomes the real navigation
|
|
@@ -1225,12 +1246,11 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
|
1225
1246
|
} catch (error) {
|
|
1226
1247
|
const handled_error = await handle_error(error, { url, params, route: { id } });
|
|
1227
1248
|
|
|
1228
|
-
if (preload_tokens.has(preload)) {
|
|
1249
|
+
if (preload && preload_tokens.has(preload)) {
|
|
1229
1250
|
return preload_error({ error: handled_error, url, params, route });
|
|
1230
1251
|
}
|
|
1231
1252
|
|
|
1232
1253
|
return load_root_error_page({
|
|
1233
|
-
status: get_status(error),
|
|
1234
1254
|
error: handled_error,
|
|
1235
1255
|
url,
|
|
1236
1256
|
route
|
|
@@ -1273,7 +1293,7 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
|
1273
1293
|
|
|
1274
1294
|
if (server_data_node?.type === 'error') {
|
|
1275
1295
|
// rethrow and catch below
|
|
1276
|
-
throw server_data_node;
|
|
1296
|
+
throw new HttpError(server_data_node.error.status, server_data_node.error);
|
|
1277
1297
|
}
|
|
1278
1298
|
|
|
1279
1299
|
return load_node({
|
|
@@ -1311,34 +1331,33 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
|
1311
1331
|
if (err instanceof Redirect) {
|
|
1312
1332
|
return {
|
|
1313
1333
|
type: 'redirect',
|
|
1334
|
+
status: err.status,
|
|
1314
1335
|
location: err.location
|
|
1315
1336
|
};
|
|
1316
1337
|
}
|
|
1317
1338
|
|
|
1318
|
-
if (preload_tokens.has(preload)) {
|
|
1339
|
+
if (preload && preload_tokens.has(preload)) {
|
|
1340
|
+
const error = await handle_error(err, { params, url, route: { id: route.id } });
|
|
1319
1341
|
return preload_error({
|
|
1320
|
-
error
|
|
1342
|
+
error,
|
|
1321
1343
|
url,
|
|
1322
1344
|
params,
|
|
1323
1345
|
route
|
|
1324
1346
|
});
|
|
1325
1347
|
}
|
|
1326
1348
|
|
|
1327
|
-
let status = get_status(err);
|
|
1328
1349
|
/** @type {App.Error} */
|
|
1329
1350
|
let error;
|
|
1330
1351
|
|
|
1331
1352
|
if (server_data_nodes?.includes(/** @type {import('types').ServerErrorNode} */ (err))) {
|
|
1332
1353
|
// this is the server error rethrown above, reconstruct but don't invoke
|
|
1333
1354
|
// the client error handler; it should've already been handled on the server
|
|
1334
|
-
status = /** @type {import('types').ServerErrorNode} */ (err).status ?? status;
|
|
1335
1355
|
error = /** @type {import('types').ServerErrorNode} */ (err).error;
|
|
1336
1356
|
} else if (err instanceof HttpError) {
|
|
1337
1357
|
error = err.body;
|
|
1338
1358
|
} else {
|
|
1339
1359
|
// Referenced node could have been removed due to redeploy, check
|
|
1340
|
-
|
|
1341
|
-
if (updated) {
|
|
1360
|
+
if (await updated.check()) {
|
|
1342
1361
|
// Before reloading, try to update the service worker if it exists
|
|
1343
1362
|
await update_service_worker();
|
|
1344
1363
|
return await native_navigation(url);
|
|
@@ -1354,12 +1373,11 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
|
1354
1373
|
params,
|
|
1355
1374
|
branch: branch.slice(0, error_load.idx).concat(error_load.node),
|
|
1356
1375
|
errors,
|
|
1357
|
-
status,
|
|
1358
1376
|
error,
|
|
1359
1377
|
route
|
|
1360
1378
|
});
|
|
1361
1379
|
} else {
|
|
1362
|
-
return await server_fallback(url, { id: route.id }, error
|
|
1380
|
+
return await server_fallback(url, { id: route.id }, error);
|
|
1363
1381
|
}
|
|
1364
1382
|
}
|
|
1365
1383
|
} else {
|
|
@@ -1374,7 +1392,6 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
|
|
|
1374
1392
|
params,
|
|
1375
1393
|
branch,
|
|
1376
1394
|
errors,
|
|
1377
|
-
status: 200,
|
|
1378
1395
|
error: null,
|
|
1379
1396
|
route,
|
|
1380
1397
|
// Reset `form` on navigation, but not invalidation
|
|
@@ -1413,14 +1430,13 @@ async function load_nearest_error_page(i, branch, errors) {
|
|
|
1413
1430
|
|
|
1414
1431
|
/**
|
|
1415
1432
|
* @param {{
|
|
1416
|
-
* status: number;
|
|
1417
1433
|
* error: App.Error;
|
|
1418
1434
|
* url: URL;
|
|
1419
1435
|
* route: { id: string | null }
|
|
1420
1436
|
* }} opts
|
|
1421
|
-
* @returns {Promise<import('./types.js').NavigationFinished>}
|
|
1437
|
+
* @returns {Promise<import('./types.js').NavigationFinished | undefined>} returns `undefined` in case of a redirect
|
|
1422
1438
|
*/
|
|
1423
|
-
async function load_root_error_page({
|
|
1439
|
+
async function load_root_error_page({ error, url, route }) {
|
|
1424
1440
|
/** @type {Record<string, string>} */
|
|
1425
1441
|
const params = {}; // error page does not have params
|
|
1426
1442
|
|
|
@@ -1444,11 +1460,15 @@ async function load_root_error_page({ status, error, url, route }) {
|
|
|
1444
1460
|
}
|
|
1445
1461
|
|
|
1446
1462
|
server_data_node = server_data.nodes[0] ?? null;
|
|
1447
|
-
} catch {
|
|
1463
|
+
} catch (e) {
|
|
1448
1464
|
// at this point we have no choice but to fall back to the server, if it wouldn't
|
|
1449
|
-
// bring us right back here, turning this into an endless loop
|
|
1450
|
-
if
|
|
1451
|
-
|
|
1465
|
+
// bring us right back here, turning this into an endless loop.
|
|
1466
|
+
// if __data.json returned 404, the route doesn't exist — don't reload or we loop
|
|
1467
|
+
if (
|
|
1468
|
+
!(e instanceof HttpError && e.status === 404) &&
|
|
1469
|
+
(url.origin !== origin || url.pathname !== location.pathname || hydrated)
|
|
1470
|
+
) {
|
|
1471
|
+
return await native_navigation(url);
|
|
1452
1472
|
}
|
|
1453
1473
|
}
|
|
1454
1474
|
}
|
|
@@ -1477,18 +1497,30 @@ async function load_root_error_page({ status, error, url, route }) {
|
|
|
1477
1497
|
url,
|
|
1478
1498
|
params,
|
|
1479
1499
|
branch: [root_layout, root_error],
|
|
1480
|
-
status,
|
|
1481
1500
|
error,
|
|
1482
1501
|
errors: [],
|
|
1483
1502
|
route: null
|
|
1484
1503
|
});
|
|
1485
1504
|
} catch (error) {
|
|
1505
|
+
// client-side navigation if the root layout loader throws a redirect while
|
|
1506
|
+
// rendering the default error page
|
|
1486
1507
|
if (error instanceof Redirect) {
|
|
1487
|
-
|
|
1488
|
-
return
|
|
1508
|
+
await _goto(new URL(error.location, location.href), {}, 0);
|
|
1509
|
+
return;
|
|
1489
1510
|
}
|
|
1490
1511
|
|
|
1491
|
-
//
|
|
1512
|
+
// otherwise, render the static error page
|
|
1513
|
+
const error_template = await app.get_error_template();
|
|
1514
|
+
const handled = await handle_error(error, { url, params, route });
|
|
1515
|
+
const message = String(handled?.message ?? '')
|
|
1516
|
+
.replace(/&/g, '&')
|
|
1517
|
+
.replace(/</g, '<')
|
|
1518
|
+
.replace(/>/g, '>');
|
|
1519
|
+
const html = error_template({ status: handled.status, message });
|
|
1520
|
+
const parsed = new DOMParser().parseFromString(html, 'text/html');
|
|
1521
|
+
document.documentElement.replaceChild(document.adoptNode(parsed.head), document.head);
|
|
1522
|
+
document.documentElement.replaceChild(document.adoptNode(parsed.body), document.body);
|
|
1523
|
+
|
|
1492
1524
|
throw error;
|
|
1493
1525
|
}
|
|
1494
1526
|
}
|
|
@@ -1578,7 +1610,7 @@ export async function get_navigation_intent(url, invalidating) {
|
|
|
1578
1610
|
id: get_page_key(url),
|
|
1579
1611
|
invalidating,
|
|
1580
1612
|
route,
|
|
1581
|
-
params
|
|
1613
|
+
params,
|
|
1582
1614
|
url
|
|
1583
1615
|
};
|
|
1584
1616
|
}
|
|
@@ -1673,8 +1705,10 @@ function _before_navigate({ url, type, intent, delta, event, scroll }) {
|
|
|
1673
1705
|
* nav_token?: {};
|
|
1674
1706
|
* accept?: () => void;
|
|
1675
1707
|
* block?: () => void;
|
|
1676
|
-
* event?: Event
|
|
1708
|
+
* event?: Event;
|
|
1709
|
+
* intent?: NavigationIntent | undefined
|
|
1677
1710
|
* }} opts
|
|
1711
|
+
* @returns {Promise<void>}
|
|
1678
1712
|
*/
|
|
1679
1713
|
async function navigate({
|
|
1680
1714
|
type,
|
|
@@ -1688,12 +1722,14 @@ async function navigate({
|
|
|
1688
1722
|
nav_token = {},
|
|
1689
1723
|
accept = noop,
|
|
1690
1724
|
block = noop,
|
|
1691
|
-
event
|
|
1725
|
+
event,
|
|
1726
|
+
intent
|
|
1692
1727
|
}) {
|
|
1693
|
-
const prev_token =
|
|
1694
|
-
|
|
1728
|
+
const prev_token = navigation_token;
|
|
1729
|
+
const prev_invalidation_token = invalidation_token;
|
|
1730
|
+
navigation_token = invalidation_token = nav_token;
|
|
1695
1731
|
|
|
1696
|
-
|
|
1732
|
+
intent ??= await get_navigation_intent(url, false);
|
|
1697
1733
|
const nav =
|
|
1698
1734
|
type === 'enter'
|
|
1699
1735
|
? create_navigation(current, intent, url, type)
|
|
@@ -1709,7 +1745,8 @@ async function navigate({
|
|
|
1709
1745
|
|
|
1710
1746
|
if (!nav) {
|
|
1711
1747
|
block();
|
|
1712
|
-
if (
|
|
1748
|
+
if (navigation_token === nav_token) navigation_token = prev_token;
|
|
1749
|
+
if (invalidation_token === nav_token) invalidation_token = prev_invalidation_token;
|
|
1713
1750
|
return;
|
|
1714
1751
|
}
|
|
1715
1752
|
|
|
@@ -1722,7 +1759,7 @@ async function navigate({
|
|
|
1722
1759
|
is_navigating = true;
|
|
1723
1760
|
|
|
1724
1761
|
if (started && nav.navigation.type !== 'enter') {
|
|
1725
|
-
|
|
1762
|
+
navigating.current = nav.navigation;
|
|
1726
1763
|
}
|
|
1727
1764
|
|
|
1728
1765
|
let navigation_result = intent && (await load_route(intent));
|
|
@@ -1747,7 +1784,6 @@ async function navigate({
|
|
|
1747
1784
|
route: { id: null }
|
|
1748
1785
|
}
|
|
1749
1786
|
),
|
|
1750
|
-
404,
|
|
1751
1787
|
replace_state
|
|
1752
1788
|
);
|
|
1753
1789
|
} else {
|
|
@@ -1762,7 +1798,6 @@ async function navigate({
|
|
|
1762
1798
|
params: {},
|
|
1763
1799
|
route: { id: null }
|
|
1764
1800
|
}),
|
|
1765
|
-
404,
|
|
1766
1801
|
replace_state
|
|
1767
1802
|
);
|
|
1768
1803
|
}
|
|
@@ -1773,11 +1808,13 @@ async function navigate({
|
|
|
1773
1808
|
url = intent?.url || url;
|
|
1774
1809
|
|
|
1775
1810
|
// abort if user navigated during update
|
|
1776
|
-
if (
|
|
1811
|
+
if (navigation_token !== nav_token) {
|
|
1777
1812
|
nav.reject(new Error('navigation aborted'));
|
|
1778
|
-
return
|
|
1813
|
+
return;
|
|
1779
1814
|
}
|
|
1780
1815
|
|
|
1816
|
+
if (!navigation_result) return;
|
|
1817
|
+
|
|
1781
1818
|
if (navigation_result.type === 'redirect') {
|
|
1782
1819
|
// whatwg fetch spec https://fetch.spec.whatwg.org/#http-redirect-fetch says to error after 20 redirects
|
|
1783
1820
|
if (redirect_count < 20) {
|
|
@@ -1798,7 +1835,6 @@ async function navigate({
|
|
|
1798
1835
|
}
|
|
1799
1836
|
|
|
1800
1837
|
navigation_result = await load_root_error_page({
|
|
1801
|
-
status: 500,
|
|
1802
1838
|
error: await handle_error(new Error('Redirect loop'), {
|
|
1803
1839
|
url,
|
|
1804
1840
|
params: {},
|
|
@@ -1807,12 +1843,13 @@ async function navigate({
|
|
|
1807
1843
|
url,
|
|
1808
1844
|
route: { id: null }
|
|
1809
1845
|
});
|
|
1846
|
+
|
|
1847
|
+
if (!navigation_result) return;
|
|
1810
1848
|
} else if (/** @type {number} */ (navigation_result.props.page.status) >= 400) {
|
|
1811
|
-
|
|
1812
|
-
if (updated) {
|
|
1849
|
+
if (await updated.check()) {
|
|
1813
1850
|
// Before reloading, try to update the service worker if it exists
|
|
1814
1851
|
await update_service_worker();
|
|
1815
|
-
await native_navigation(url, replace_state);
|
|
1852
|
+
return await native_navigation(url, replace_state);
|
|
1816
1853
|
}
|
|
1817
1854
|
}
|
|
1818
1855
|
|
|
@@ -1907,6 +1944,16 @@ async function navigate({
|
|
|
1907
1944
|
navigation_result.props.page.url = url;
|
|
1908
1945
|
}
|
|
1909
1946
|
|
|
1947
|
+
// Remove focus before updating the component tree, so that blur/focusout
|
|
1948
|
+
// handlers fire while the old component's data is still valid (#14575)
|
|
1949
|
+
if (
|
|
1950
|
+
!keepfocus &&
|
|
1951
|
+
document.activeElement instanceof HTMLElement &&
|
|
1952
|
+
document.activeElement !== document.body
|
|
1953
|
+
) {
|
|
1954
|
+
document.activeElement.blur();
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1910
1957
|
const fork = load_cache_fork && (await load_cache_fork);
|
|
1911
1958
|
|
|
1912
1959
|
if (fork) {
|
|
@@ -1930,18 +1977,12 @@ async function navigate({
|
|
|
1930
1977
|
|
|
1931
1978
|
const { activeElement } = document;
|
|
1932
1979
|
|
|
1933
|
-
await commit_promise;
|
|
1934
|
-
|
|
1935
|
-
// TODO 3.0 remote — the double tick is probably necessary because
|
|
1936
|
-
// of some store shenanigans. `settled()` and `f.commit()`
|
|
1937
|
-
// should resolve after DOM updates in newer versions
|
|
1938
|
-
await svelte.tick();
|
|
1939
|
-
await svelte.tick();
|
|
1980
|
+
await (commit_promise ?? svelte.tick());
|
|
1940
1981
|
|
|
1941
|
-
if (
|
|
1982
|
+
if (navigation_token !== nav_token) {
|
|
1942
1983
|
// a new navigation happened while we were waiting for the DOM to update, so abort
|
|
1943
1984
|
nav.reject(new Error('navigation aborted'));
|
|
1944
|
-
return
|
|
1985
|
+
return;
|
|
1945
1986
|
}
|
|
1946
1987
|
|
|
1947
1988
|
// Check for async rendering error
|
|
@@ -1985,10 +2026,6 @@ async function navigate({
|
|
|
1985
2026
|
|
|
1986
2027
|
is_navigating = false;
|
|
1987
2028
|
|
|
1988
|
-
if (type === 'popstate') {
|
|
1989
|
-
restore_snapshot(current_navigation_index);
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
2029
|
nav.fulfil(undefined);
|
|
1993
2030
|
|
|
1994
2031
|
// Update to.scroll to the actual scroll position after navigation completed
|
|
@@ -2000,7 +2037,11 @@ async function navigate({
|
|
|
2000
2037
|
fn(/** @type {import('@sveltejs/kit').AfterNavigate} */ (nav.navigation))
|
|
2001
2038
|
);
|
|
2002
2039
|
|
|
2003
|
-
|
|
2040
|
+
if (type === 'popstate') {
|
|
2041
|
+
restore_snapshot(current_navigation_index);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
navigating.current = null;
|
|
2004
2045
|
|
|
2005
2046
|
updating = false;
|
|
2006
2047
|
}
|
|
@@ -2010,23 +2051,21 @@ async function navigate({
|
|
|
2010
2051
|
* @param {URL} url
|
|
2011
2052
|
* @param {{ id: string | null }} route
|
|
2012
2053
|
* @param {App.Error} error
|
|
2013
|
-
* @param {number} status
|
|
2014
2054
|
* @param {boolean} [replace_state]
|
|
2015
|
-
* @returns {Promise<import('./types.js').NavigationFinished>}
|
|
2055
|
+
* @returns {Promise<import('./types.js').NavigationFinished | undefined>}
|
|
2016
2056
|
*/
|
|
2017
|
-
async function server_fallback(url, route, error,
|
|
2057
|
+
async function server_fallback(url, route, error, replace_state) {
|
|
2018
2058
|
if (url.origin === origin && url.pathname === location.pathname && !hydrated) {
|
|
2019
2059
|
// We would reload the same page we're currently on, which isn't hydrated,
|
|
2020
2060
|
// which means no SSR, which means we would end up in an endless loop
|
|
2021
2061
|
return await load_root_error_page({
|
|
2022
|
-
status,
|
|
2023
2062
|
error,
|
|
2024
2063
|
url,
|
|
2025
2064
|
route
|
|
2026
2065
|
});
|
|
2027
2066
|
}
|
|
2028
2067
|
|
|
2029
|
-
if (DEV && status !== 404) {
|
|
2068
|
+
if (DEV && error.status !== 404) {
|
|
2030
2069
|
console.error(
|
|
2031
2070
|
'An error occurred while loading the page. This will cause a full page reload. (This message will only appear during development.)'
|
|
2032
2071
|
);
|
|
@@ -2165,9 +2204,9 @@ function setup_preload() {
|
|
|
2165
2204
|
/**
|
|
2166
2205
|
* @param {unknown} error
|
|
2167
2206
|
* @param {import('@sveltejs/kit').NavigationEvent} event
|
|
2168
|
-
* @returns {
|
|
2207
|
+
* @returns {Promise<App.Error>}
|
|
2169
2208
|
*/
|
|
2170
|
-
function handle_error(error, event) {
|
|
2209
|
+
export async function handle_error(error, event) {
|
|
2171
2210
|
if (error instanceof HttpError) {
|
|
2172
2211
|
return error.body;
|
|
2173
2212
|
}
|
|
@@ -2179,10 +2218,9 @@ function handle_error(error, event) {
|
|
|
2179
2218
|
|
|
2180
2219
|
const status = get_status(error);
|
|
2181
2220
|
const message = get_message(error);
|
|
2221
|
+
const app_error = (await app.hooks.handleError({ error, event, status, message })) ?? { message };
|
|
2182
2222
|
|
|
2183
|
-
return (
|
|
2184
|
-
app.hooks.handleError({ error, event, status, message }) ?? /** @type {any} */ ({ message })
|
|
2185
|
-
);
|
|
2223
|
+
return { ...app_error, status: get_status(app_error, error) };
|
|
2186
2224
|
}
|
|
2187
2225
|
|
|
2188
2226
|
/**
|
|
@@ -2191,7 +2229,7 @@ function handle_error(error, event) {
|
|
|
2191
2229
|
* @param {T} callback
|
|
2192
2230
|
*/
|
|
2193
2231
|
function add_navigation_callback(callbacks, callback) {
|
|
2194
|
-
onMount(() => {
|
|
2232
|
+
svelte.onMount(() => {
|
|
2195
2233
|
callbacks.add(callback);
|
|
2196
2234
|
|
|
2197
2235
|
return () => {
|
|
@@ -2266,9 +2304,11 @@ export function disableScrollHandling() {
|
|
|
2266
2304
|
* Allows you to navigate programmatically to a given route, with options such as keeping the current element focused.
|
|
2267
2305
|
* Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
|
|
2268
2306
|
*
|
|
2269
|
-
*
|
|
2307
|
+
* `goto` is intended for navigations to routes that belong to the app.
|
|
2308
|
+
* If the URL does not resolve to a route within the app, the returned promise will reject.
|
|
2309
|
+
* For external URLs, use `window.location = url` to perform a full-page navigation instead of calling `goto(url)`.
|
|
2270
2310
|
*
|
|
2271
|
-
* @param {string | URL} url Where to navigate to. Note that if you've set [`config.
|
|
2311
|
+
* @param {string | URL} url Where to navigate to. Note that if you've set [`config.paths.base`](https://svelte.dev/docs/kit/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
|
|
2272
2312
|
* @param {Object} [opts] Options related to the navigation
|
|
2273
2313
|
* @param {boolean} [opts.replaceState] If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
|
|
2274
2314
|
* @param {boolean} [opts.noScroll] If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
|
|
@@ -2278,7 +2318,7 @@ export function disableScrollHandling() {
|
|
|
2278
2318
|
* @param {App.PageState} [opts.state] An optional object that will be available as `page.state`
|
|
2279
2319
|
* @returns {Promise<void>}
|
|
2280
2320
|
*/
|
|
2281
|
-
export function goto(url, opts = {}) {
|
|
2321
|
+
export async function goto(url, opts = {}) {
|
|
2282
2322
|
if (!BROWSER) {
|
|
2283
2323
|
throw new Error('Cannot call goto(...) on the server');
|
|
2284
2324
|
}
|
|
@@ -2286,16 +2326,24 @@ export function goto(url, opts = {}) {
|
|
|
2286
2326
|
url = new URL(resolve_url(url));
|
|
2287
2327
|
|
|
2288
2328
|
if (url.origin !== origin) {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
: 'goto: invalid URL'
|
|
2294
|
-
)
|
|
2329
|
+
throw new Error(
|
|
2330
|
+
DEV
|
|
2331
|
+
? `Cannot use \`goto\` with an external URL. Use \`window.location = "${url}"\` instead`
|
|
2332
|
+
: 'goto: invalid URL'
|
|
2295
2333
|
);
|
|
2296
2334
|
}
|
|
2297
2335
|
|
|
2298
|
-
|
|
2336
|
+
const intent = await get_navigation_intent(url, false);
|
|
2337
|
+
|
|
2338
|
+
if (!intent) {
|
|
2339
|
+
throw new Error(
|
|
2340
|
+
DEV
|
|
2341
|
+
? `Cannot use \`goto\` with a URL that does not resolve to a route within the app. Use \`window.location = "${url}"\` instead`
|
|
2342
|
+
: 'goto: invalid URL'
|
|
2343
|
+
);
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
return _goto(url, opts, 0, {}, intent);
|
|
2299
2347
|
}
|
|
2300
2348
|
|
|
2301
2349
|
/**
|
|
@@ -2376,7 +2424,7 @@ export function refreshAll({ includeLoadFunctions = true } = {}) {
|
|
|
2376
2424
|
* Returns a Promise that resolves with the result of running the new route's `load` functions once the preload is complete.
|
|
2377
2425
|
*
|
|
2378
2426
|
* @param {string} href Page to preload
|
|
2379
|
-
* @returns {Promise<{ type: 'loaded';
|
|
2427
|
+
* @returns {Promise<({ type: 'loaded'; data: Record<string, any> } | { type: 'redirect'; location: string } | { type: 'error'; error: App.Error }) & { status: number; }>}
|
|
2380
2428
|
*/
|
|
2381
2429
|
export async function preloadData(href) {
|
|
2382
2430
|
if (!BROWSER) {
|
|
@@ -2394,11 +2442,21 @@ export async function preloadData(href) {
|
|
|
2394
2442
|
if (result.type === 'redirect') {
|
|
2395
2443
|
return {
|
|
2396
2444
|
type: result.type,
|
|
2445
|
+
status: result.status,
|
|
2397
2446
|
location: result.location
|
|
2398
2447
|
};
|
|
2399
2448
|
}
|
|
2400
2449
|
|
|
2401
2450
|
const { status, data } = result.props.page ?? page;
|
|
2451
|
+
|
|
2452
|
+
if (result.type === 'loaded' && result.state.error) {
|
|
2453
|
+
return {
|
|
2454
|
+
type: 'error',
|
|
2455
|
+
status,
|
|
2456
|
+
error: result.state.error
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2402
2460
|
return { type: result.type, status, data };
|
|
2403
2461
|
}
|
|
2404
2462
|
|
|
@@ -2419,7 +2477,9 @@ export async function preloadCode(pathname) {
|
|
|
2419
2477
|
throw new Error('Cannot call preloadCode(...) on the server');
|
|
2420
2478
|
}
|
|
2421
2479
|
|
|
2422
|
-
|
|
2480
|
+
// `current.url` is null until the first navigation/hydration completes, so fall back
|
|
2481
|
+
// to `location` to support calling `preloadCode` during initial page load (#13297)
|
|
2482
|
+
const url = new URL(pathname, current.url ?? location.href);
|
|
2423
2483
|
|
|
2424
2484
|
if (DEV) {
|
|
2425
2485
|
if (!pathname.startsWith('/')) {
|
|
@@ -2485,8 +2545,7 @@ export function pushState(url, state) {
|
|
|
2485
2545
|
|
|
2486
2546
|
page.state = state;
|
|
2487
2547
|
root.$set({
|
|
2488
|
-
|
|
2489
|
-
page: untrack(() => clone_page(page))
|
|
2548
|
+
page
|
|
2490
2549
|
});
|
|
2491
2550
|
|
|
2492
2551
|
clear_onward_history(current_history_index, current_navigation_index);
|
|
@@ -2529,7 +2588,7 @@ export function replaceState(url, state) {
|
|
|
2529
2588
|
|
|
2530
2589
|
page.state = state;
|
|
2531
2590
|
root.$set({
|
|
2532
|
-
page
|
|
2591
|
+
page
|
|
2533
2592
|
});
|
|
2534
2593
|
}
|
|
2535
2594
|
|
|
@@ -2547,7 +2606,7 @@ export async function applyAction(result) {
|
|
|
2547
2606
|
}
|
|
2548
2607
|
|
|
2549
2608
|
if (result.type === 'error') {
|
|
2550
|
-
await set_nearest_error_page(result.error
|
|
2609
|
+
await set_nearest_error_page(result.error);
|
|
2551
2610
|
} else if (result.type === 'redirect') {
|
|
2552
2611
|
await _goto(result.location, { invalidateAll: true }, 0);
|
|
2553
2612
|
} else {
|
|
@@ -2559,11 +2618,11 @@ export async function applyAction(result) {
|
|
|
2559
2618
|
// this brings Svelte's view of the world in line with SvelteKit's
|
|
2560
2619
|
// after use:enhance reset the form....
|
|
2561
2620
|
form: null,
|
|
2562
|
-
page
|
|
2621
|
+
page
|
|
2563
2622
|
});
|
|
2564
2623
|
|
|
2565
2624
|
// ...so that setting the `form` prop takes effect and isn't ignored
|
|
2566
|
-
await tick();
|
|
2625
|
+
await svelte.tick();
|
|
2567
2626
|
root.$set({ form: result.data });
|
|
2568
2627
|
|
|
2569
2628
|
if (result.type === 'success') {
|
|
@@ -2574,9 +2633,8 @@ export async function applyAction(result) {
|
|
|
2574
2633
|
|
|
2575
2634
|
/**
|
|
2576
2635
|
* @param {App.Error} error
|
|
2577
|
-
* @param {number} status
|
|
2578
2636
|
*/
|
|
2579
|
-
export async function set_nearest_error_page(error
|
|
2637
|
+
export async function set_nearest_error_page(error) {
|
|
2580
2638
|
const url = new URL(location.href);
|
|
2581
2639
|
|
|
2582
2640
|
const { branch, route } = current;
|
|
@@ -2588,7 +2646,6 @@ export async function set_nearest_error_page(error, status = 500) {
|
|
|
2588
2646
|
url,
|
|
2589
2647
|
params: current.params,
|
|
2590
2648
|
branch: branch.slice(0, error_load.idx).concat(error_load.node),
|
|
2591
|
-
status,
|
|
2592
2649
|
error,
|
|
2593
2650
|
// do not set errors, we haven't changed the page so the previous ones are still current
|
|
2594
2651
|
route
|
|
@@ -2599,7 +2656,7 @@ export async function set_nearest_error_page(error, status = 500) {
|
|
|
2599
2656
|
root.$set(navigation_result.props);
|
|
2600
2657
|
update(navigation_result.props.page);
|
|
2601
2658
|
|
|
2602
|
-
void tick().then(() => reset_focus(current.url));
|
|
2659
|
+
void svelte.tick().then(() => reset_focus(current.url));
|
|
2603
2660
|
}
|
|
2604
2661
|
}
|
|
2605
2662
|
|
|
@@ -2823,7 +2880,7 @@ function _start_router() {
|
|
|
2823
2880
|
|
|
2824
2881
|
if (event.state?.[HISTORY_INDEX]) {
|
|
2825
2882
|
const history_index = event.state[HISTORY_INDEX];
|
|
2826
|
-
|
|
2883
|
+
navigation_token = invalidation_token = {};
|
|
2827
2884
|
|
|
2828
2885
|
// if a popstate-driven navigation is cancelled, we need to counteract it
|
|
2829
2886
|
// with history.go, which means we end up back here, hence this check
|
|
@@ -2872,7 +2929,7 @@ function _start_router() {
|
|
|
2872
2929
|
block: () => {
|
|
2873
2930
|
history.go(-delta);
|
|
2874
2931
|
},
|
|
2875
|
-
nav_token:
|
|
2932
|
+
nav_token: navigation_token,
|
|
2876
2933
|
event
|
|
2877
2934
|
});
|
|
2878
2935
|
} else {
|
|
@@ -2924,7 +2981,7 @@ function _start_router() {
|
|
|
2924
2981
|
// the navigation away from it was successful.
|
|
2925
2982
|
// Info about bfcache here: https://web.dev/bfcache
|
|
2926
2983
|
if (event.persisted) {
|
|
2927
|
-
|
|
2984
|
+
navigating.current = null;
|
|
2928
2985
|
}
|
|
2929
2986
|
});
|
|
2930
2987
|
|
|
@@ -2933,18 +2990,17 @@ function _start_router() {
|
|
|
2933
2990
|
*/
|
|
2934
2991
|
function update_url(url) {
|
|
2935
2992
|
current.url = page.url = url;
|
|
2936
|
-
stores.page.set(clone_page(page));
|
|
2937
|
-
stores.page.notify();
|
|
2938
2993
|
}
|
|
2939
2994
|
}
|
|
2940
2995
|
|
|
2941
2996
|
/**
|
|
2942
2997
|
* @param {HTMLElement} target
|
|
2943
2998
|
* @param {import('./types.js').HydrateOptions} opts
|
|
2999
|
+
* @returns {Promise<void>}
|
|
2944
3000
|
*/
|
|
2945
3001
|
async function _hydrate(
|
|
2946
3002
|
target,
|
|
2947
|
-
{ status
|
|
3003
|
+
{ status, error, node_ids, params, route, server_route, data: server_data_nodes, form }
|
|
2948
3004
|
) {
|
|
2949
3005
|
hydrated = true;
|
|
2950
3006
|
|
|
@@ -3027,13 +3083,13 @@ async function _hydrate(
|
|
|
3027
3083
|
if (error instanceof Redirect) {
|
|
3028
3084
|
// this is a real edge case — `load` would need to return
|
|
3029
3085
|
// a redirect but only in the browser
|
|
3030
|
-
await native_navigation(new URL(error.location, location.href));
|
|
3031
|
-
return;
|
|
3086
|
+
return await native_navigation(new URL(error.location, location.href));
|
|
3032
3087
|
}
|
|
3033
3088
|
|
|
3089
|
+
const handled_error = await handle_error(error, { url, params, route });
|
|
3090
|
+
|
|
3034
3091
|
result = await load_root_error_page({
|
|
3035
|
-
|
|
3036
|
-
error: await handle_error(error, { url, params, route }),
|
|
3092
|
+
error: handled_error,
|
|
3037
3093
|
url,
|
|
3038
3094
|
route
|
|
3039
3095
|
});
|
|
@@ -3042,6 +3098,10 @@ async function _hydrate(
|
|
|
3042
3098
|
hydrate = false;
|
|
3043
3099
|
}
|
|
3044
3100
|
|
|
3101
|
+
// Exit early when we encounter a redirect while loading the root error page.
|
|
3102
|
+
// In this case, `initialize` will be called later on
|
|
3103
|
+
if (!result) return;
|
|
3104
|
+
|
|
3045
3105
|
if (result.props.page) {
|
|
3046
3106
|
result.props.page.state = {};
|
|
3047
3107
|
}
|
|
@@ -3323,28 +3383,6 @@ function create_navigation(current, intent, url, type, target_scroll = null) {
|
|
|
3323
3383
|
};
|
|
3324
3384
|
}
|
|
3325
3385
|
|
|
3326
|
-
/**
|
|
3327
|
-
* TODO: remove this in 3.0 when the page store is also removed
|
|
3328
|
-
*
|
|
3329
|
-
* We need to assign a new page object so that subscribers are correctly notified.
|
|
3330
|
-
* However, spreading `{ ...page }` returns an empty object so we manually
|
|
3331
|
-
* assign to each property instead.
|
|
3332
|
-
*
|
|
3333
|
-
* @param {import('@sveltejs/kit').Page} page
|
|
3334
|
-
*/
|
|
3335
|
-
function clone_page(page) {
|
|
3336
|
-
return {
|
|
3337
|
-
data: page.data,
|
|
3338
|
-
error: page.error,
|
|
3339
|
-
form: page.form,
|
|
3340
|
-
params: page.params,
|
|
3341
|
-
route: page.route,
|
|
3342
|
-
state: page.state,
|
|
3343
|
-
status: page.status,
|
|
3344
|
-
url: page.url
|
|
3345
|
-
};
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
3386
|
/**
|
|
3349
3387
|
* @param {URL} url
|
|
3350
3388
|
* @returns {URL}
|