@sveltejs/kit 1.0.0-next.29 → 1.0.0-next.292
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/assets/app/env.js +20 -0
- package/assets/app/navigation.js +24 -0
- package/assets/app/paths.js +1 -0
- package/assets/app/stores.js +97 -0
- package/assets/client/singletons.js +13 -0
- package/assets/client/start.js +1523 -0
- package/assets/components/error.svelte +18 -2
- package/assets/env.js +8 -0
- package/assets/paths.js +13 -0
- package/assets/server/index.js +2797 -0
- package/dist/chunks/amp_hook.js +56 -0
- package/dist/chunks/cert.js +28154 -0
- package/dist/chunks/constants.js +663 -0
- package/dist/chunks/filesystem.js +110 -0
- package/dist/chunks/index.js +508 -0
- package/dist/chunks/index2.js +1307 -0
- package/dist/chunks/index3.js +118 -0
- package/dist/chunks/index4.js +196 -0
- package/dist/chunks/index5.js +242 -0
- package/dist/chunks/index6.js +15585 -0
- package/dist/chunks/index7.js +4207 -0
- package/dist/chunks/misc.js +3 -0
- package/dist/chunks/multipart-parser.js +449 -0
- package/dist/chunks/object.js +83 -0
- package/dist/chunks/sync.js +999 -0
- package/dist/chunks/url.js +56 -0
- package/dist/cli.js +1018 -85
- package/dist/hooks.js +28 -0
- package/dist/install-fetch.js +6518 -0
- package/dist/node.js +94 -0
- package/package.json +92 -54
- package/svelte-kit.js +2 -0
- package/types/ambient.d.ts +303 -0
- package/types/index.d.ts +166 -0
- package/types/internal.d.ts +318 -0
- package/types/private.d.ts +357 -0
- package/CHANGELOG.md +0 -332
- package/assets/runtime/app/navigation.js +0 -23
- package/assets/runtime/app/navigation.js.map +0 -1
- package/assets/runtime/app/paths.js +0 -2
- package/assets/runtime/app/paths.js.map +0 -1
- package/assets/runtime/app/stores.js +0 -78
- package/assets/runtime/app/stores.js.map +0 -1
- package/assets/runtime/internal/singletons.js +0 -15
- package/assets/runtime/internal/singletons.js.map +0 -1
- package/assets/runtime/internal/start.js +0 -591
- package/assets/runtime/internal/start.js.map +0 -1
- package/assets/runtime/utils-85ebcc60.js +0 -18
- package/assets/runtime/utils-85ebcc60.js.map +0 -1
- package/dist/api.js +0 -44
- package/dist/api.js.map +0 -1
- package/dist/build.js +0 -246
- package/dist/build.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/colors.js +0 -37
- package/dist/colors.js.map +0 -1
- package/dist/create_app.js +0 -580
- package/dist/create_app.js.map +0 -1
- package/dist/index.js +0 -368
- package/dist/index.js.map +0 -1
- package/dist/index2.js +0 -12035
- package/dist/index2.js.map +0 -1
- package/dist/index3.js +0 -549
- package/dist/index3.js.map +0 -1
- package/dist/index4.js +0 -74
- package/dist/index4.js.map +0 -1
- package/dist/index5.js +0 -464
- package/dist/index5.js.map +0 -1
- package/dist/index6.js +0 -735
- package/dist/index6.js.map +0 -1
- package/dist/logging.js +0 -43
- package/dist/logging.js.map +0 -1
- package/dist/package.js +0 -432
- package/dist/package.js.map +0 -1
- package/dist/renderer.js +0 -2425
- package/dist/renderer.js.map +0 -1
- package/dist/standard.js +0 -101
- package/dist/standard.js.map +0 -1
- package/dist/utils.js +0 -58
- package/dist/utils.js.map +0 -1
- package/svelte-kit +0 -3
|
@@ -0,0 +1,1523 @@
|
|
|
1
|
+
import { onMount, tick } from 'svelte';
|
|
2
|
+
import { writable } from 'svelte/store';
|
|
3
|
+
import { base, set_paths } from '../paths.js';
|
|
4
|
+
import Root from '__GENERATED__/root.svelte';
|
|
5
|
+
import { routes, fallback } from '__GENERATED__/manifest.js';
|
|
6
|
+
import { init } from './singletons.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {unknown} err
|
|
10
|
+
* @return {Error}
|
|
11
|
+
*/
|
|
12
|
+
function coalesce_to_error(err) {
|
|
13
|
+
return err instanceof Error ||
|
|
14
|
+
(err && /** @type {any} */ (err).name && /** @type {any} */ (err).message)
|
|
15
|
+
? /** @type {Error} */ (err)
|
|
16
|
+
: new Error(JSON.stringify(err));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {import('types').LoadOutput} loaded
|
|
21
|
+
* @returns {import('types').NormalizedLoadOutput}
|
|
22
|
+
*/
|
|
23
|
+
function normalize(loaded) {
|
|
24
|
+
const has_error_status =
|
|
25
|
+
loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
|
|
26
|
+
if (loaded.error || has_error_status) {
|
|
27
|
+
const status = loaded.status;
|
|
28
|
+
|
|
29
|
+
if (!loaded.error && has_error_status) {
|
|
30
|
+
return {
|
|
31
|
+
status: status || 500,
|
|
32
|
+
error: new Error()
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
|
|
37
|
+
|
|
38
|
+
if (!(error instanceof Error)) {
|
|
39
|
+
return {
|
|
40
|
+
status: 500,
|
|
41
|
+
error: new Error(
|
|
42
|
+
`"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
|
|
43
|
+
)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!status || status < 400 || status > 599) {
|
|
48
|
+
console.warn('"error" returned from load() without a valid status code — defaulting to 500');
|
|
49
|
+
return { status: 500, error };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { status, error };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (loaded.redirect) {
|
|
56
|
+
if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
|
|
57
|
+
return {
|
|
58
|
+
status: 500,
|
|
59
|
+
error: new Error(
|
|
60
|
+
'"redirect" property returned from load() must be accompanied by a 3xx status code'
|
|
61
|
+
)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof loaded.redirect !== 'string') {
|
|
66
|
+
return {
|
|
67
|
+
status: 500,
|
|
68
|
+
error: new Error('"redirect" property returned from load() must be a string')
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// TODO remove before 1.0
|
|
74
|
+
if (/** @type {any} */ (loaded).context) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
'You are returning "context" from a load function. ' +
|
|
77
|
+
'"context" was renamed to "stuff", please adjust your code accordingly.'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return /** @type {import('types').NormalizedLoadOutput} */ (loaded);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} path
|
|
86
|
+
* @param {import('types').TrailingSlash} trailing_slash
|
|
87
|
+
*/
|
|
88
|
+
function normalize_path(path, trailing_slash) {
|
|
89
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
90
|
+
|
|
91
|
+
if (trailing_slash === 'never') {
|
|
92
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
93
|
+
} else if (trailing_slash === 'always' && /\/[^./]+$/.test(path)) {
|
|
94
|
+
return path + '/';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Hash using djb2
|
|
102
|
+
* @param {import('types').StrictBody} value
|
|
103
|
+
*/
|
|
104
|
+
function hash(value) {
|
|
105
|
+
let hash = 5381;
|
|
106
|
+
let i = value.length;
|
|
107
|
+
|
|
108
|
+
if (typeof value === 'string') {
|
|
109
|
+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
110
|
+
} else {
|
|
111
|
+
while (i) hash = (hash * 33) ^ value[--i];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (hash >>> 0).toString(36);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @param {HTMLDocument} doc */
|
|
118
|
+
function get_base_uri(doc) {
|
|
119
|
+
let baseURI = doc.baseURI;
|
|
120
|
+
|
|
121
|
+
if (!baseURI) {
|
|
122
|
+
const baseTags = doc.getElementsByTagName('base');
|
|
123
|
+
baseURI = baseTags.length ? baseTags[0].href : doc.URL;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return baseURI;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function scroll_state() {
|
|
130
|
+
return {
|
|
131
|
+
x: pageXOffset,
|
|
132
|
+
y: pageYOffset
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** @param {Event} event */
|
|
137
|
+
function find_anchor(event) {
|
|
138
|
+
const node = event
|
|
139
|
+
.composedPath()
|
|
140
|
+
.find((e) => e instanceof Node && e.nodeName.toUpperCase() === 'A'); // SVG <a> elements have a lowercase name
|
|
141
|
+
return /** @type {HTMLAnchorElement | SVGAElement | undefined} */ (node);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @param {HTMLAnchorElement | SVGAElement} node */
|
|
145
|
+
function get_href(node) {
|
|
146
|
+
return node instanceof SVGAElement
|
|
147
|
+
? new URL(node.href.baseVal, document.baseURI)
|
|
148
|
+
: new URL(node.href);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @param {any} value */
|
|
152
|
+
function notifiable_store(value) {
|
|
153
|
+
const store = writable(value);
|
|
154
|
+
let ready = true;
|
|
155
|
+
|
|
156
|
+
function notify() {
|
|
157
|
+
ready = true;
|
|
158
|
+
store.update((val) => val);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** @param {any} new_value */
|
|
162
|
+
function set(new_value) {
|
|
163
|
+
ready = false;
|
|
164
|
+
store.set(new_value);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** @param {(value: any) => void} run */
|
|
168
|
+
function subscribe(run) {
|
|
169
|
+
/** @type {any} */
|
|
170
|
+
let old_value;
|
|
171
|
+
return store.subscribe((new_value) => {
|
|
172
|
+
if (old_value === undefined || (ready && new_value !== old_value)) {
|
|
173
|
+
run((old_value = new_value));
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { notify, set, subscribe };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function create_updated_store() {
|
|
182
|
+
const { set, subscribe } = writable(false);
|
|
183
|
+
|
|
184
|
+
const interval = +(
|
|
185
|
+
/** @type {string} */ (import.meta.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL)
|
|
186
|
+
);
|
|
187
|
+
const initial = import.meta.env.VITE_SVELTEKIT_APP_VERSION;
|
|
188
|
+
|
|
189
|
+
/** @type {NodeJS.Timeout} */
|
|
190
|
+
let timeout;
|
|
191
|
+
|
|
192
|
+
async function check() {
|
|
193
|
+
if (import.meta.env.DEV || import.meta.env.SSR) return false;
|
|
194
|
+
|
|
195
|
+
clearTimeout(timeout);
|
|
196
|
+
|
|
197
|
+
if (interval) timeout = setTimeout(check, interval);
|
|
198
|
+
|
|
199
|
+
const file = import.meta.env.VITE_SVELTEKIT_APP_VERSION_FILE;
|
|
200
|
+
|
|
201
|
+
const res = await fetch(`${base}/${file}`, {
|
|
202
|
+
headers: {
|
|
203
|
+
pragma: 'no-cache',
|
|
204
|
+
'cache-control': 'no-cache'
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (res.ok) {
|
|
209
|
+
const { version } = await res.json();
|
|
210
|
+
const updated = version !== initial;
|
|
211
|
+
|
|
212
|
+
if (updated) {
|
|
213
|
+
set(true);
|
|
214
|
+
clearTimeout(timeout);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return updated;
|
|
218
|
+
} else {
|
|
219
|
+
throw new Error(`Version check failed: ${res.status}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (interval) timeout = setTimeout(check, interval);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
subscribe,
|
|
227
|
+
check
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {RequestInfo} resource
|
|
233
|
+
* @param {RequestInit} [opts]
|
|
234
|
+
*/
|
|
235
|
+
function initial_fetch(resource, opts) {
|
|
236
|
+
const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
|
|
237
|
+
|
|
238
|
+
let selector = `script[sveltekit\\:data-type="data"][sveltekit\\:data-url=${url}]`;
|
|
239
|
+
|
|
240
|
+
if (opts && typeof opts.body === 'string') {
|
|
241
|
+
selector += `[sveltekit\\:data-body="${hash(opts.body)}"]`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const script = document.querySelector(selector);
|
|
245
|
+
if (script && script.textContent) {
|
|
246
|
+
const { body, ...init } = JSON.parse(script.textContent);
|
|
247
|
+
return Promise.resolve(new Response(body, init));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return fetch(resource, opts);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const SCROLL_KEY = 'sveltekit:scroll';
|
|
254
|
+
const INDEX_KEY = 'sveltekit:index';
|
|
255
|
+
|
|
256
|
+
// We track the scroll position associated with each history entry in sessionStorage,
|
|
257
|
+
// rather than on history.state itself, because when navigation is driven by
|
|
258
|
+
// popstate it's too late to update the scroll position associated with the
|
|
259
|
+
// state we're navigating from
|
|
260
|
+
|
|
261
|
+
/** @typedef {{ x: number, y: number }} ScrollPosition */
|
|
262
|
+
/** @type {Record<number, ScrollPosition>} */
|
|
263
|
+
let scroll_positions = {};
|
|
264
|
+
try {
|
|
265
|
+
scroll_positions = JSON.parse(sessionStorage[SCROLL_KEY]);
|
|
266
|
+
} catch {
|
|
267
|
+
// do nothing
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** @param {number} index */
|
|
271
|
+
function update_scroll_positions(index) {
|
|
272
|
+
scroll_positions[index] = scroll_state();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {{
|
|
277
|
+
* target: Element;
|
|
278
|
+
* session: App.Session;
|
|
279
|
+
* base: string;
|
|
280
|
+
* trailing_slash: import('types').TrailingSlash;
|
|
281
|
+
* }} opts
|
|
282
|
+
* @returns {import('./types').Client}
|
|
283
|
+
*/
|
|
284
|
+
function create_client({ target, session, base, trailing_slash }) {
|
|
285
|
+
/** @type {Map<string, import('./types').NavigationResult>} */
|
|
286
|
+
const cache = new Map();
|
|
287
|
+
|
|
288
|
+
/** @type {Set<string>} */
|
|
289
|
+
const invalidated = new Set();
|
|
290
|
+
|
|
291
|
+
const stores = {
|
|
292
|
+
url: notifiable_store({}),
|
|
293
|
+
page: notifiable_store({}),
|
|
294
|
+
navigating: writable(/** @type {import('types').Navigation | null} */ (null)),
|
|
295
|
+
session: writable(session),
|
|
296
|
+
updated: create_updated_store()
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/** @type {{id: string | null, promise: Promise<import('./types').NavigationResult | undefined> | null}} */
|
|
300
|
+
const load_cache = {
|
|
301
|
+
id: null,
|
|
302
|
+
promise: null
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const callbacks = {
|
|
306
|
+
/** @type {Array<(opts: { from: URL, to: URL | null, cancel: () => void }) => void>} */
|
|
307
|
+
before_navigate: [],
|
|
308
|
+
|
|
309
|
+
/** @type {Array<(opts: { from: URL | null, to: URL }) => void>} */
|
|
310
|
+
after_navigate: []
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/** @type {import('./types').NavigationState} */
|
|
314
|
+
let current = {
|
|
315
|
+
// @ts-ignore - we need the initial value to be null
|
|
316
|
+
url: null,
|
|
317
|
+
session_id: 0,
|
|
318
|
+
branch: []
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
let started = false;
|
|
322
|
+
let autoscroll = true;
|
|
323
|
+
let updating = false;
|
|
324
|
+
let session_id = 1;
|
|
325
|
+
|
|
326
|
+
/** @type {Promise<void> | null} */
|
|
327
|
+
let invalidating = null;
|
|
328
|
+
|
|
329
|
+
/** @type {import('svelte').SvelteComponent} */
|
|
330
|
+
let root;
|
|
331
|
+
|
|
332
|
+
/** @type {App.Session} */
|
|
333
|
+
let $session;
|
|
334
|
+
|
|
335
|
+
let ready = false;
|
|
336
|
+
stores.session.subscribe(async (value) => {
|
|
337
|
+
$session = value;
|
|
338
|
+
|
|
339
|
+
if (!ready) return;
|
|
340
|
+
session_id += 1;
|
|
341
|
+
|
|
342
|
+
const intent = get_navigation_intent(new URL(location.href));
|
|
343
|
+
update(intent, [], true);
|
|
344
|
+
});
|
|
345
|
+
ready = true;
|
|
346
|
+
|
|
347
|
+
/** Keeps tracks of multiple navigations caused by redirects during rendering */
|
|
348
|
+
let navigating = 0;
|
|
349
|
+
|
|
350
|
+
let router_enabled = true;
|
|
351
|
+
|
|
352
|
+
// keeping track of the history index in order to prevent popstate navigation events if needed
|
|
353
|
+
let current_history_index = history.state?.[INDEX_KEY] ?? 0;
|
|
354
|
+
|
|
355
|
+
if (current_history_index === 0) {
|
|
356
|
+
// create initial history entry, so we can return here
|
|
357
|
+
history.replaceState({ ...history.state, [INDEX_KEY]: 0 }, '', location.href);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// if we reload the page, or Cmd-Shift-T back to it,
|
|
361
|
+
// recover scroll position
|
|
362
|
+
const scroll = scroll_positions[current_history_index];
|
|
363
|
+
if (scroll) scrollTo(scroll.x, scroll.y);
|
|
364
|
+
|
|
365
|
+
let hash_navigating = false;
|
|
366
|
+
|
|
367
|
+
/** @type {import('types').Page} */
|
|
368
|
+
let page;
|
|
369
|
+
|
|
370
|
+
/** @type {{}} */
|
|
371
|
+
let token;
|
|
372
|
+
|
|
373
|
+
/** @type {{}} */
|
|
374
|
+
let navigating_token;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* @param {string} href
|
|
378
|
+
* @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any }} opts
|
|
379
|
+
* @param {string[]} redirect_chain
|
|
380
|
+
*/
|
|
381
|
+
async function goto(
|
|
382
|
+
href,
|
|
383
|
+
{ noscroll = false, replaceState = false, keepfocus = false, state = {} },
|
|
384
|
+
redirect_chain
|
|
385
|
+
) {
|
|
386
|
+
const url = new URL(href, get_base_uri(document));
|
|
387
|
+
|
|
388
|
+
if (router_enabled) {
|
|
389
|
+
return navigate({
|
|
390
|
+
url,
|
|
391
|
+
scroll: noscroll ? scroll_state() : null,
|
|
392
|
+
keepfocus,
|
|
393
|
+
redirect_chain,
|
|
394
|
+
details: {
|
|
395
|
+
state,
|
|
396
|
+
replaceState
|
|
397
|
+
},
|
|
398
|
+
accepted: () => {},
|
|
399
|
+
blocked: () => {}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
await native_navigation(url);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** @param {URL} url */
|
|
407
|
+
async function prefetch(url) {
|
|
408
|
+
if (!owns(url)) {
|
|
409
|
+
throw new Error('Attempted to prefetch a URL that does not belong to this app');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const intent = get_navigation_intent(url);
|
|
413
|
+
|
|
414
|
+
load_cache.promise = get_navigation_result(intent, false);
|
|
415
|
+
load_cache.id = intent.id;
|
|
416
|
+
|
|
417
|
+
return load_cache.promise;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @param {import('./types').NavigationIntent} intent
|
|
422
|
+
* @param {string[]} redirect_chain
|
|
423
|
+
* @param {boolean} no_cache
|
|
424
|
+
* @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean, details: { replaceState: boolean, state: any } | null}} [opts]
|
|
425
|
+
*/
|
|
426
|
+
async function update(intent, redirect_chain, no_cache, opts) {
|
|
427
|
+
const current_token = (token = {});
|
|
428
|
+
let navigation_result = await get_navigation_result(intent, no_cache);
|
|
429
|
+
|
|
430
|
+
if (!navigation_result && intent.url.pathname === location.pathname) {
|
|
431
|
+
// this could happen in SPA fallback mode if the user navigated to
|
|
432
|
+
// `/non-existent-page`. if we fall back to reloading the page, it
|
|
433
|
+
// will create an infinite loop. so whereas we normally handle
|
|
434
|
+
// unknown routes by going to the server, in this special case
|
|
435
|
+
// we render a client-side error page instead
|
|
436
|
+
navigation_result = await load_root_error_page({
|
|
437
|
+
status: 404,
|
|
438
|
+
error: new Error(`Not found: ${intent.url.pathname}`),
|
|
439
|
+
url: intent.url
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (!navigation_result) {
|
|
444
|
+
await native_navigation(intent.url);
|
|
445
|
+
return; // unnecessary, but TypeScript prefers it this way
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// abort if user navigated during update
|
|
449
|
+
if (token !== current_token) return;
|
|
450
|
+
|
|
451
|
+
invalidated.clear();
|
|
452
|
+
|
|
453
|
+
if (navigation_result.redirect) {
|
|
454
|
+
if (redirect_chain.length > 10 || redirect_chain.includes(intent.url.pathname)) {
|
|
455
|
+
navigation_result = await load_root_error_page({
|
|
456
|
+
status: 500,
|
|
457
|
+
error: new Error('Redirect loop'),
|
|
458
|
+
url: intent.url
|
|
459
|
+
});
|
|
460
|
+
} else {
|
|
461
|
+
if (router_enabled) {
|
|
462
|
+
goto(new URL(navigation_result.redirect, intent.url).href, {}, [
|
|
463
|
+
...redirect_chain,
|
|
464
|
+
intent.url.pathname
|
|
465
|
+
]);
|
|
466
|
+
} else {
|
|
467
|
+
await native_navigation(new URL(navigation_result.redirect, location.href));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
} else if (navigation_result.props?.page?.status >= 400) {
|
|
473
|
+
const updated = await stores.updated.check();
|
|
474
|
+
if (updated) {
|
|
475
|
+
await native_navigation(intent.url);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
updating = true;
|
|
480
|
+
|
|
481
|
+
if (opts && opts.details) {
|
|
482
|
+
const { details } = opts;
|
|
483
|
+
const change = details.replaceState ? 0 : 1;
|
|
484
|
+
details.state[INDEX_KEY] = current_history_index += change;
|
|
485
|
+
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', intent.url);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (started) {
|
|
489
|
+
current = navigation_result.state;
|
|
490
|
+
|
|
491
|
+
root.$set(navigation_result.props);
|
|
492
|
+
} else {
|
|
493
|
+
initialize(navigation_result);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// opts must be passed if we're navigating
|
|
497
|
+
if (opts) {
|
|
498
|
+
const { scroll, keepfocus } = opts;
|
|
499
|
+
|
|
500
|
+
if (!keepfocus) {
|
|
501
|
+
// Reset page selection and focus
|
|
502
|
+
// We try to mimic browsers' behaviour as closely as possible by targeting the
|
|
503
|
+
// first scrollable region, but unfortunately it's not a perfect match — e.g.
|
|
504
|
+
// shift-tabbing won't immediately cycle up from the end of the page on Chromium
|
|
505
|
+
// See https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area
|
|
506
|
+
const root = document.body;
|
|
507
|
+
const tabindex = root.getAttribute('tabindex');
|
|
508
|
+
|
|
509
|
+
getSelection()?.removeAllRanges();
|
|
510
|
+
root.tabIndex = -1;
|
|
511
|
+
root.focus();
|
|
512
|
+
|
|
513
|
+
// restore `tabindex` as to prevent `root` from stealing input from elements
|
|
514
|
+
if (tabindex !== null) {
|
|
515
|
+
root.setAttribute('tabindex', tabindex);
|
|
516
|
+
} else {
|
|
517
|
+
root.removeAttribute('tabindex');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// need to render the DOM before we can scroll to the rendered elements
|
|
522
|
+
await tick();
|
|
523
|
+
|
|
524
|
+
if (autoscroll) {
|
|
525
|
+
const deep_linked = intent.url.hash && document.getElementById(intent.url.hash.slice(1));
|
|
526
|
+
if (scroll) {
|
|
527
|
+
scrollTo(scroll.x, scroll.y);
|
|
528
|
+
} else if (deep_linked) {
|
|
529
|
+
// Here we use `scrollIntoView` on the element instead of `scrollTo`
|
|
530
|
+
// because it natively supports the `scroll-margin` and `scroll-behavior`
|
|
531
|
+
// CSS properties.
|
|
532
|
+
deep_linked.scrollIntoView();
|
|
533
|
+
} else {
|
|
534
|
+
scrollTo(0, 0);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
// in this case we're simply invalidating
|
|
539
|
+
await tick();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
load_cache.promise = null;
|
|
543
|
+
load_cache.id = null;
|
|
544
|
+
autoscroll = true;
|
|
545
|
+
updating = false;
|
|
546
|
+
|
|
547
|
+
if (navigation_result.props.page) {
|
|
548
|
+
page = navigation_result.props.page;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1];
|
|
552
|
+
router_enabled = leaf_node?.module.router !== false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/** @param {import('./types').NavigationResult} result */
|
|
556
|
+
function initialize(result) {
|
|
557
|
+
current = result.state;
|
|
558
|
+
|
|
559
|
+
const style = document.querySelector('style[data-svelte]');
|
|
560
|
+
if (style) style.remove();
|
|
561
|
+
|
|
562
|
+
page = result.props.page;
|
|
563
|
+
|
|
564
|
+
root = new Root({
|
|
565
|
+
target,
|
|
566
|
+
props: { ...result.props, stores },
|
|
567
|
+
hydrate: true
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
started = true;
|
|
571
|
+
|
|
572
|
+
if (router_enabled) {
|
|
573
|
+
const navigation = { from: null, to: new URL(location.href) };
|
|
574
|
+
callbacks.after_navigate.forEach((fn) => fn(navigation));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* @param {import('./types').NavigationIntent} intent
|
|
580
|
+
* @param {boolean} no_cache
|
|
581
|
+
*/
|
|
582
|
+
async function get_navigation_result(intent, no_cache) {
|
|
583
|
+
if (load_cache.id === intent.id && load_cache.promise) {
|
|
584
|
+
return load_cache.promise;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
for (let i = 0; i < intent.routes.length; i += 1) {
|
|
588
|
+
const route = intent.routes[i];
|
|
589
|
+
|
|
590
|
+
// load code for subsequent routes immediately, if they are as
|
|
591
|
+
// likely to match the current path/query as the current one
|
|
592
|
+
let j = i + 1;
|
|
593
|
+
while (j < intent.routes.length) {
|
|
594
|
+
const next = intent.routes[j];
|
|
595
|
+
if (next[0].toString() === route[0].toString()) {
|
|
596
|
+
next[1].forEach((loader) => loader());
|
|
597
|
+
j += 1;
|
|
598
|
+
} else {
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const result = await load_route(route, intent, no_cache);
|
|
604
|
+
if (result) return result;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
*
|
|
610
|
+
* @param {{
|
|
611
|
+
* url: URL;
|
|
612
|
+
* params: Record<string, string>;
|
|
613
|
+
* stuff: Record<string, any>;
|
|
614
|
+
* branch: Array<import('./types').BranchNode | undefined>;
|
|
615
|
+
* status: number;
|
|
616
|
+
* error?: Error;
|
|
617
|
+
* }} opts
|
|
618
|
+
*/
|
|
619
|
+
async function get_navigation_result_from_branch({ url, params, stuff, branch, status, error }) {
|
|
620
|
+
const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
|
|
621
|
+
const redirect = filtered.find((f) => f.loaded?.redirect);
|
|
622
|
+
|
|
623
|
+
/** @type {import('./types').NavigationResult} */
|
|
624
|
+
const result = {
|
|
625
|
+
redirect: redirect?.loaded?.redirect,
|
|
626
|
+
state: {
|
|
627
|
+
url,
|
|
628
|
+
params,
|
|
629
|
+
branch,
|
|
630
|
+
session_id
|
|
631
|
+
},
|
|
632
|
+
props: {
|
|
633
|
+
components: filtered.map((node) => node.module.default)
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
for (let i = 0; i < filtered.length; i += 1) {
|
|
638
|
+
const loaded = filtered[i].loaded;
|
|
639
|
+
result.props[`props_${i}`] = loaded ? await loaded.props : null;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (!current.url || url.href !== current.url.href) {
|
|
643
|
+
result.props.page = { url, params, status, error, stuff };
|
|
644
|
+
|
|
645
|
+
// TODO remove this for 1.0
|
|
646
|
+
/**
|
|
647
|
+
* @param {string} property
|
|
648
|
+
* @param {string} replacement
|
|
649
|
+
*/
|
|
650
|
+
const print_error = (property, replacement) => {
|
|
651
|
+
Object.defineProperty(result.props.page, property, {
|
|
652
|
+
get: () => {
|
|
653
|
+
throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
print_error('origin', 'origin');
|
|
659
|
+
print_error('path', 'pathname');
|
|
660
|
+
print_error('query', 'searchParams');
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const leaf = filtered[filtered.length - 1];
|
|
664
|
+
const maxage = leaf.loaded && leaf.loaded.maxage;
|
|
665
|
+
|
|
666
|
+
if (maxage) {
|
|
667
|
+
const key = url.pathname + url.search; // omit hash
|
|
668
|
+
let ready = false;
|
|
669
|
+
|
|
670
|
+
const clear = () => {
|
|
671
|
+
if (cache.get(key) === result) {
|
|
672
|
+
cache.delete(key);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
unsubscribe();
|
|
676
|
+
clearTimeout(timeout);
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
const timeout = setTimeout(clear, maxage * 1000);
|
|
680
|
+
|
|
681
|
+
const unsubscribe = stores.session.subscribe(() => {
|
|
682
|
+
if (ready) clear();
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
ready = true;
|
|
686
|
+
|
|
687
|
+
cache.set(key, result);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* @param {{
|
|
695
|
+
* status?: number;
|
|
696
|
+
* error?: Error;
|
|
697
|
+
* module: import('types').CSRComponent;
|
|
698
|
+
* url: URL;
|
|
699
|
+
* params: Record<string, string>;
|
|
700
|
+
* stuff: Record<string, any>;
|
|
701
|
+
* props?: Record<string, any>;
|
|
702
|
+
* }} options
|
|
703
|
+
*/
|
|
704
|
+
async function load_node({ status, error, module, url, params, stuff, props }) {
|
|
705
|
+
/** @type {import('./types').BranchNode} */
|
|
706
|
+
const node = {
|
|
707
|
+
module,
|
|
708
|
+
uses: {
|
|
709
|
+
params: new Set(),
|
|
710
|
+
url: false,
|
|
711
|
+
session: false,
|
|
712
|
+
stuff: false,
|
|
713
|
+
dependencies: new Set()
|
|
714
|
+
},
|
|
715
|
+
loaded: null,
|
|
716
|
+
stuff
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
if (props) {
|
|
720
|
+
// shadow endpoint props means we need to mark this URL as a dependency of itself
|
|
721
|
+
node.uses.dependencies.add(url.href);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** @type {Record<string, string>} */
|
|
725
|
+
const uses_params = {};
|
|
726
|
+
for (const key in params) {
|
|
727
|
+
Object.defineProperty(uses_params, key, {
|
|
728
|
+
get() {
|
|
729
|
+
node.uses.params.add(key);
|
|
730
|
+
return params[key];
|
|
731
|
+
},
|
|
732
|
+
enumerable: true
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const session = $session;
|
|
737
|
+
|
|
738
|
+
if (module.load) {
|
|
739
|
+
/** @type {import('types').LoadInput | import('types').ErrorLoadInput} */
|
|
740
|
+
const load_input = {
|
|
741
|
+
params: uses_params,
|
|
742
|
+
props: props || {},
|
|
743
|
+
get url() {
|
|
744
|
+
node.uses.url = true;
|
|
745
|
+
return url;
|
|
746
|
+
},
|
|
747
|
+
get session() {
|
|
748
|
+
node.uses.session = true;
|
|
749
|
+
return session;
|
|
750
|
+
},
|
|
751
|
+
get stuff() {
|
|
752
|
+
node.uses.stuff = true;
|
|
753
|
+
return { ...stuff };
|
|
754
|
+
},
|
|
755
|
+
fetch(resource, info) {
|
|
756
|
+
const requested = typeof resource === 'string' ? resource : resource.url;
|
|
757
|
+
const { href } = new URL(requested, url);
|
|
758
|
+
node.uses.dependencies.add(href);
|
|
759
|
+
|
|
760
|
+
return started ? fetch(resource, info) : initial_fetch(resource, info);
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
if (import.meta.env.DEV) {
|
|
765
|
+
// TODO remove this for 1.0
|
|
766
|
+
Object.defineProperty(load_input, 'page', {
|
|
767
|
+
get: () => {
|
|
768
|
+
throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (error) {
|
|
774
|
+
/** @type {import('types').ErrorLoadInput} */ (load_input).status = status;
|
|
775
|
+
/** @type {import('types').ErrorLoadInput} */ (load_input).error = error;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const loaded = await module.load.call(null, load_input);
|
|
779
|
+
|
|
780
|
+
if (!loaded) {
|
|
781
|
+
throw new Error('load function must return a value');
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
node.loaded = normalize(loaded);
|
|
785
|
+
if (node.loaded.stuff) node.stuff = node.loaded.stuff;
|
|
786
|
+
} else if (props) {
|
|
787
|
+
node.loaded = normalize({ props });
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return node;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* @param {import('types').CSRRoute} route
|
|
795
|
+
* @param {import('./types').NavigationIntent} intent
|
|
796
|
+
* @param {boolean} no_cache
|
|
797
|
+
*/
|
|
798
|
+
async function load_route(route, { id, url, path }, no_cache) {
|
|
799
|
+
if (!no_cache) {
|
|
800
|
+
const cached = cache.get(id);
|
|
801
|
+
if (cached) return cached;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const [pattern, a, b, get_params, shadow_key] = route;
|
|
805
|
+
const params = get_params
|
|
806
|
+
? // the pattern is for the route which we've already matched to this path
|
|
807
|
+
get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
|
|
808
|
+
: {};
|
|
809
|
+
|
|
810
|
+
const changed = current.url && {
|
|
811
|
+
url: id !== current.url.pathname + current.url.search,
|
|
812
|
+
params: Object.keys(params).filter((key) => current.params[key] !== params[key]),
|
|
813
|
+
session: session_id !== current.session_id
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
/** @type {Array<import('./types').BranchNode | undefined>} */
|
|
817
|
+
let branch = [];
|
|
818
|
+
|
|
819
|
+
/** @type {Record<string, any>} */
|
|
820
|
+
let stuff = {};
|
|
821
|
+
let stuff_changed = false;
|
|
822
|
+
|
|
823
|
+
/** @type {number | undefined} */
|
|
824
|
+
let status = 200;
|
|
825
|
+
|
|
826
|
+
/** @type {Error | undefined} */
|
|
827
|
+
let error;
|
|
828
|
+
|
|
829
|
+
// preload modules
|
|
830
|
+
a.forEach((loader) => loader());
|
|
831
|
+
|
|
832
|
+
load: for (let i = 0; i < a.length; i += 1) {
|
|
833
|
+
/** @type {import('./types').BranchNode | undefined} */
|
|
834
|
+
let node;
|
|
835
|
+
|
|
836
|
+
try {
|
|
837
|
+
if (!a[i]) continue;
|
|
838
|
+
|
|
839
|
+
const module = await a[i]();
|
|
840
|
+
const previous = current.branch[i];
|
|
841
|
+
|
|
842
|
+
const changed_since_last_render =
|
|
843
|
+
!previous ||
|
|
844
|
+
module !== previous.module ||
|
|
845
|
+
(changed.url && previous.uses.url) ||
|
|
846
|
+
changed.params.some((param) => previous.uses.params.has(param)) ||
|
|
847
|
+
(changed.session && previous.uses.session) ||
|
|
848
|
+
Array.from(previous.uses.dependencies).some((dep) => invalidated.has(dep)) ||
|
|
849
|
+
(stuff_changed && previous.uses.stuff);
|
|
850
|
+
|
|
851
|
+
if (changed_since_last_render) {
|
|
852
|
+
/** @type {Record<string, any>} */
|
|
853
|
+
let props = {};
|
|
854
|
+
|
|
855
|
+
const is_shadow_page = shadow_key !== undefined && i === a.length - 1;
|
|
856
|
+
|
|
857
|
+
if (is_shadow_page) {
|
|
858
|
+
const res = await fetch(
|
|
859
|
+
`${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`,
|
|
860
|
+
{
|
|
861
|
+
headers: {
|
|
862
|
+
'x-sveltekit-load': /** @type {string} */ (shadow_key)
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
if (res.status === 204) {
|
|
868
|
+
// fallthrough
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (res.ok) {
|
|
873
|
+
const redirect = res.headers.get('x-sveltekit-location');
|
|
874
|
+
|
|
875
|
+
if (redirect) {
|
|
876
|
+
return {
|
|
877
|
+
redirect,
|
|
878
|
+
props: {},
|
|
879
|
+
state: current
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
props = await res.json();
|
|
884
|
+
} else {
|
|
885
|
+
status = res.status;
|
|
886
|
+
error = new Error('Failed to load data');
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
if (!error) {
|
|
891
|
+
node = await load_node({
|
|
892
|
+
module,
|
|
893
|
+
url,
|
|
894
|
+
params,
|
|
895
|
+
props,
|
|
896
|
+
stuff
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
if (node) {
|
|
901
|
+
if (is_shadow_page) {
|
|
902
|
+
node.uses.url = true;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (node.loaded) {
|
|
906
|
+
if (node.loaded.fallthrough) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (node.loaded.error) {
|
|
910
|
+
status = node.loaded.status;
|
|
911
|
+
error = node.loaded.error;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (node.loaded.redirect) {
|
|
915
|
+
return {
|
|
916
|
+
redirect: node.loaded.redirect,
|
|
917
|
+
props: {},
|
|
918
|
+
state: current
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (node.loaded.stuff) {
|
|
923
|
+
stuff_changed = true;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
node = previous;
|
|
929
|
+
}
|
|
930
|
+
} catch (e) {
|
|
931
|
+
status = 500;
|
|
932
|
+
error = coalesce_to_error(e);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (error) {
|
|
936
|
+
while (i--) {
|
|
937
|
+
if (b[i]) {
|
|
938
|
+
let error_loaded;
|
|
939
|
+
|
|
940
|
+
/** @type {import('./types').BranchNode | undefined} */
|
|
941
|
+
let node_loaded;
|
|
942
|
+
let j = i;
|
|
943
|
+
while (!(node_loaded = branch[j])) {
|
|
944
|
+
j -= 1;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
try {
|
|
948
|
+
error_loaded = await load_node({
|
|
949
|
+
status,
|
|
950
|
+
error,
|
|
951
|
+
module: await b[i](),
|
|
952
|
+
url,
|
|
953
|
+
params,
|
|
954
|
+
stuff: node_loaded.stuff
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
if (error_loaded?.loaded?.error) {
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (error_loaded?.loaded?.stuff) {
|
|
962
|
+
stuff = {
|
|
963
|
+
...stuff,
|
|
964
|
+
...error_loaded.loaded.stuff
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
branch = branch.slice(0, j + 1).concat(error_loaded);
|
|
969
|
+
break load;
|
|
970
|
+
} catch (e) {
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return await load_root_error_page({
|
|
977
|
+
status,
|
|
978
|
+
error,
|
|
979
|
+
url
|
|
980
|
+
});
|
|
981
|
+
} else {
|
|
982
|
+
if (node?.loaded?.stuff) {
|
|
983
|
+
stuff = {
|
|
984
|
+
...stuff,
|
|
985
|
+
...node.loaded.stuff
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
branch.push(node);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return await get_navigation_result_from_branch({
|
|
994
|
+
url,
|
|
995
|
+
params,
|
|
996
|
+
stuff,
|
|
997
|
+
branch,
|
|
998
|
+
status,
|
|
999
|
+
error
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* @param {{
|
|
1005
|
+
* status: number;
|
|
1006
|
+
* error: Error;
|
|
1007
|
+
* url: URL;
|
|
1008
|
+
* }} opts
|
|
1009
|
+
*/
|
|
1010
|
+
async function load_root_error_page({ status, error, url }) {
|
|
1011
|
+
/** @type {Record<string, string>} */
|
|
1012
|
+
const params = {}; // error page does not have params
|
|
1013
|
+
|
|
1014
|
+
const root_layout = await load_node({
|
|
1015
|
+
module: await fallback[0],
|
|
1016
|
+
url,
|
|
1017
|
+
params,
|
|
1018
|
+
stuff: {}
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
const root_error = await load_node({
|
|
1022
|
+
status,
|
|
1023
|
+
error,
|
|
1024
|
+
module: await fallback[1],
|
|
1025
|
+
url,
|
|
1026
|
+
params,
|
|
1027
|
+
stuff: (root_layout && root_layout.loaded && root_layout.loaded.stuff) || {}
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
return await get_navigation_result_from_branch({
|
|
1031
|
+
url,
|
|
1032
|
+
params,
|
|
1033
|
+
stuff: {
|
|
1034
|
+
...root_layout?.loaded?.stuff,
|
|
1035
|
+
...root_error?.loaded?.stuff
|
|
1036
|
+
},
|
|
1037
|
+
branch: [root_layout, root_error],
|
|
1038
|
+
status,
|
|
1039
|
+
error
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/** @param {URL} url */
|
|
1044
|
+
function owns(url) {
|
|
1045
|
+
return url.origin === location.origin && url.pathname.startsWith(base);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/** @param {URL} url */
|
|
1049
|
+
function get_navigation_intent(url) {
|
|
1050
|
+
const path = decodeURI(url.pathname.slice(base.length) || '/');
|
|
1051
|
+
|
|
1052
|
+
/** @type {import('./types').NavigationIntent} */
|
|
1053
|
+
const intent = {
|
|
1054
|
+
id: url.pathname + url.search,
|
|
1055
|
+
routes: routes.filter(([pattern]) => pattern.test(path)),
|
|
1056
|
+
url,
|
|
1057
|
+
path
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
return intent;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* @param {{
|
|
1065
|
+
* url: URL;
|
|
1066
|
+
* scroll: { x: number, y: number } | null;
|
|
1067
|
+
* keepfocus: boolean;
|
|
1068
|
+
* redirect_chain: string[];
|
|
1069
|
+
* details: {
|
|
1070
|
+
* replaceState: boolean;
|
|
1071
|
+
* state: any;
|
|
1072
|
+
* } | null;
|
|
1073
|
+
* accepted: () => void;
|
|
1074
|
+
* blocked: () => void;
|
|
1075
|
+
* }} opts
|
|
1076
|
+
*/
|
|
1077
|
+
async function navigate({ url, scroll, keepfocus, redirect_chain, details, accepted, blocked }) {
|
|
1078
|
+
const from = current.url;
|
|
1079
|
+
let should_block = false;
|
|
1080
|
+
|
|
1081
|
+
const navigation = {
|
|
1082
|
+
from,
|
|
1083
|
+
to: url,
|
|
1084
|
+
cancel: () => (should_block = true)
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
callbacks.before_navigate.forEach((fn) => fn(navigation));
|
|
1088
|
+
|
|
1089
|
+
if (should_block) {
|
|
1090
|
+
blocked();
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (!owns(url)) {
|
|
1095
|
+
await native_navigation(url);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const pathname = normalize_path(url.pathname, trailing_slash);
|
|
1099
|
+
url = new URL(url.origin + pathname + url.search + url.hash);
|
|
1100
|
+
|
|
1101
|
+
const intent = get_navigation_intent(url);
|
|
1102
|
+
|
|
1103
|
+
update_scroll_positions(current_history_index);
|
|
1104
|
+
|
|
1105
|
+
accepted();
|
|
1106
|
+
|
|
1107
|
+
navigating++;
|
|
1108
|
+
|
|
1109
|
+
const current_navigating_token = (navigating_token = {});
|
|
1110
|
+
|
|
1111
|
+
if (started) {
|
|
1112
|
+
stores.navigating.set({
|
|
1113
|
+
from: current.url,
|
|
1114
|
+
to: intent.url
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
await update(intent, redirect_chain, false, {
|
|
1119
|
+
scroll,
|
|
1120
|
+
keepfocus,
|
|
1121
|
+
details
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
navigating--;
|
|
1125
|
+
|
|
1126
|
+
// navigation was aborted
|
|
1127
|
+
if (navigating_token !== current_navigating_token) return;
|
|
1128
|
+
|
|
1129
|
+
if (!navigating) {
|
|
1130
|
+
const navigation = { from, to: url };
|
|
1131
|
+
callbacks.after_navigate.forEach((fn) => fn(navigation));
|
|
1132
|
+
|
|
1133
|
+
stores.navigating.set(null);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Loads `href` the old-fashioned way, with a full page reload.
|
|
1139
|
+
* Returns a `Promise` that never resolves (to prevent any
|
|
1140
|
+
* subsequent work, e.g. history manipulation, from happening)
|
|
1141
|
+
* @param {URL} url
|
|
1142
|
+
*/
|
|
1143
|
+
function native_navigation(url) {
|
|
1144
|
+
location.href = url.href;
|
|
1145
|
+
return new Promise(() => {});
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
return {
|
|
1149
|
+
after_navigate: (fn) => {
|
|
1150
|
+
onMount(() => {
|
|
1151
|
+
callbacks.after_navigate.push(fn);
|
|
1152
|
+
|
|
1153
|
+
return () => {
|
|
1154
|
+
const i = callbacks.after_navigate.indexOf(fn);
|
|
1155
|
+
callbacks.after_navigate.splice(i, 1);
|
|
1156
|
+
};
|
|
1157
|
+
});
|
|
1158
|
+
},
|
|
1159
|
+
|
|
1160
|
+
before_navigate: (fn) => {
|
|
1161
|
+
onMount(() => {
|
|
1162
|
+
callbacks.before_navigate.push(fn);
|
|
1163
|
+
|
|
1164
|
+
return () => {
|
|
1165
|
+
const i = callbacks.before_navigate.indexOf(fn);
|
|
1166
|
+
callbacks.before_navigate.splice(i, 1);
|
|
1167
|
+
};
|
|
1168
|
+
});
|
|
1169
|
+
},
|
|
1170
|
+
|
|
1171
|
+
disable_scroll_handling: () => {
|
|
1172
|
+
if (import.meta.env.DEV && started && !updating) {
|
|
1173
|
+
throw new Error('Can only disable scroll handling during navigation');
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
if (updating || !started) {
|
|
1177
|
+
autoscroll = false;
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
|
|
1181
|
+
goto: (href, opts = {}) => goto(href, opts, []),
|
|
1182
|
+
|
|
1183
|
+
invalidate: (resource) => {
|
|
1184
|
+
const { href } = new URL(resource, location.href);
|
|
1185
|
+
|
|
1186
|
+
invalidated.add(href);
|
|
1187
|
+
|
|
1188
|
+
if (!invalidating) {
|
|
1189
|
+
invalidating = Promise.resolve().then(async () => {
|
|
1190
|
+
const intent = get_navigation_intent(new URL(location.href));
|
|
1191
|
+
await update(intent, [], true);
|
|
1192
|
+
|
|
1193
|
+
invalidating = null;
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
return invalidating;
|
|
1198
|
+
},
|
|
1199
|
+
|
|
1200
|
+
prefetch: async (href) => {
|
|
1201
|
+
const url = new URL(href, get_base_uri(document));
|
|
1202
|
+
await prefetch(url);
|
|
1203
|
+
},
|
|
1204
|
+
|
|
1205
|
+
// TODO rethink this API
|
|
1206
|
+
prefetch_routes: async (pathnames) => {
|
|
1207
|
+
const matching = pathnames
|
|
1208
|
+
? routes.filter((route) => pathnames.some((pathname) => route[0].test(pathname)))
|
|
1209
|
+
: routes;
|
|
1210
|
+
|
|
1211
|
+
const promises = matching.map((r) => Promise.all(r[1].map((load) => load())));
|
|
1212
|
+
|
|
1213
|
+
await Promise.all(promises);
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
_start_router: () => {
|
|
1217
|
+
history.scrollRestoration = 'manual';
|
|
1218
|
+
|
|
1219
|
+
// Adopted from Nuxt.js
|
|
1220
|
+
// Reset scrollRestoration to auto when leaving page, allowing page reload
|
|
1221
|
+
// and back-navigation from other pages to use the browser to restore the
|
|
1222
|
+
// scrolling position.
|
|
1223
|
+
addEventListener('beforeunload', (e) => {
|
|
1224
|
+
let should_block = false;
|
|
1225
|
+
|
|
1226
|
+
const navigation = {
|
|
1227
|
+
from: current.url,
|
|
1228
|
+
to: null,
|
|
1229
|
+
cancel: () => (should_block = true)
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
callbacks.before_navigate.forEach((fn) => fn(navigation));
|
|
1233
|
+
|
|
1234
|
+
if (should_block) {
|
|
1235
|
+
e.preventDefault();
|
|
1236
|
+
e.returnValue = '';
|
|
1237
|
+
} else {
|
|
1238
|
+
history.scrollRestoration = 'auto';
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
addEventListener('visibilitychange', () => {
|
|
1243
|
+
if (document.visibilityState === 'hidden') {
|
|
1244
|
+
update_scroll_positions(current_history_index);
|
|
1245
|
+
|
|
1246
|
+
try {
|
|
1247
|
+
sessionStorage[SCROLL_KEY] = JSON.stringify(scroll_positions);
|
|
1248
|
+
} catch {
|
|
1249
|
+
// do nothing
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
/** @param {Event} event */
|
|
1255
|
+
const trigger_prefetch = (event) => {
|
|
1256
|
+
const a = find_anchor(event);
|
|
1257
|
+
if (a && a.href && a.hasAttribute('sveltekit:prefetch')) {
|
|
1258
|
+
prefetch(get_href(a));
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
/** @type {NodeJS.Timeout} */
|
|
1263
|
+
let mousemove_timeout;
|
|
1264
|
+
|
|
1265
|
+
/** @param {MouseEvent|TouchEvent} event */
|
|
1266
|
+
const handle_mousemove = (event) => {
|
|
1267
|
+
clearTimeout(mousemove_timeout);
|
|
1268
|
+
mousemove_timeout = setTimeout(() => {
|
|
1269
|
+
// event.composedPath(), which is used in find_anchor, will be empty if the event is read in a timeout
|
|
1270
|
+
// add a layer of indirection to address that
|
|
1271
|
+
event.target?.dispatchEvent(
|
|
1272
|
+
new CustomEvent('sveltekit:trigger_prefetch', { bubbles: true })
|
|
1273
|
+
);
|
|
1274
|
+
}, 20);
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
addEventListener('touchstart', trigger_prefetch);
|
|
1278
|
+
addEventListener('mousemove', handle_mousemove);
|
|
1279
|
+
addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
|
|
1280
|
+
|
|
1281
|
+
/** @param {MouseEvent} event */
|
|
1282
|
+
addEventListener('click', (event) => {
|
|
1283
|
+
if (!router_enabled) return;
|
|
1284
|
+
|
|
1285
|
+
// Adapted from https://github.com/visionmedia/page.js
|
|
1286
|
+
// MIT license https://github.com/visionmedia/page.js#license
|
|
1287
|
+
if (event.button || event.which !== 1) return;
|
|
1288
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
1289
|
+
if (event.defaultPrevented) return;
|
|
1290
|
+
|
|
1291
|
+
const a = find_anchor(event);
|
|
1292
|
+
if (!a) return;
|
|
1293
|
+
|
|
1294
|
+
if (!a.href) return;
|
|
1295
|
+
|
|
1296
|
+
const is_svg_a_element = a instanceof SVGAElement;
|
|
1297
|
+
const url = get_href(a);
|
|
1298
|
+
|
|
1299
|
+
// Ignore if url does not have origin (e.g. `mailto:`, `tel:`.)
|
|
1300
|
+
// MEMO: Without this condition, firefox will open mailer twice.
|
|
1301
|
+
// See: https://github.com/sveltejs/kit/issues/4045
|
|
1302
|
+
if (!is_svg_a_element && url.origin === 'null') return;
|
|
1303
|
+
|
|
1304
|
+
// Ignore if tag has
|
|
1305
|
+
// 1. 'download' attribute
|
|
1306
|
+
// 2. 'rel' attribute includes external
|
|
1307
|
+
const rel = (a.getAttribute('rel') || '').split(/\s+/);
|
|
1308
|
+
|
|
1309
|
+
if (a.hasAttribute('download') || rel.includes('external')) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Ignore if <a> has a target
|
|
1314
|
+
if (is_svg_a_element ? a.target.baseVal : a.target) return;
|
|
1315
|
+
|
|
1316
|
+
if (url.href === location.href) {
|
|
1317
|
+
if (!location.hash) event.preventDefault();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// Check if new url only differs by hash and use the browser default behavior in that case
|
|
1322
|
+
// This will ensure the `hashchange` event is fired
|
|
1323
|
+
// Removing the hash does a full page navigation in the browser, so make sure a hash is present
|
|
1324
|
+
const [base, hash] = url.href.split('#');
|
|
1325
|
+
if (hash !== undefined && base === location.href.split('#')[0]) {
|
|
1326
|
+
// set this flag to distinguish between navigations triggered by
|
|
1327
|
+
// clicking a hash link and those triggered by popstate
|
|
1328
|
+
hash_navigating = true;
|
|
1329
|
+
|
|
1330
|
+
update_scroll_positions(current_history_index);
|
|
1331
|
+
|
|
1332
|
+
stores.page.set({ ...page, url });
|
|
1333
|
+
stores.page.notify();
|
|
1334
|
+
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
navigate({
|
|
1339
|
+
url,
|
|
1340
|
+
scroll: a.hasAttribute('sveltekit:noscroll') ? scroll_state() : null,
|
|
1341
|
+
keepfocus: false,
|
|
1342
|
+
redirect_chain: [],
|
|
1343
|
+
details: {
|
|
1344
|
+
state: {},
|
|
1345
|
+
replaceState: false
|
|
1346
|
+
},
|
|
1347
|
+
accepted: () => event.preventDefault(),
|
|
1348
|
+
blocked: () => event.preventDefault()
|
|
1349
|
+
});
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
addEventListener('popstate', (event) => {
|
|
1353
|
+
if (event.state && router_enabled) {
|
|
1354
|
+
// if a popstate-driven navigation is cancelled, we need to counteract it
|
|
1355
|
+
// with history.go, which means we end up back here, hence this check
|
|
1356
|
+
if (event.state[INDEX_KEY] === current_history_index) return;
|
|
1357
|
+
|
|
1358
|
+
navigate({
|
|
1359
|
+
url: new URL(location.href),
|
|
1360
|
+
scroll: scroll_positions[event.state[INDEX_KEY]],
|
|
1361
|
+
keepfocus: false,
|
|
1362
|
+
redirect_chain: [],
|
|
1363
|
+
details: null,
|
|
1364
|
+
accepted: () => {
|
|
1365
|
+
current_history_index = event.state[INDEX_KEY];
|
|
1366
|
+
},
|
|
1367
|
+
blocked: () => {
|
|
1368
|
+
const delta = current_history_index - event.state[INDEX_KEY];
|
|
1369
|
+
history.go(delta);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
addEventListener('hashchange', () => {
|
|
1376
|
+
// if the hashchange happened as a result of clicking on a link,
|
|
1377
|
+
// we need to update history, otherwise we have to leave it alone
|
|
1378
|
+
if (hash_navigating) {
|
|
1379
|
+
hash_navigating = false;
|
|
1380
|
+
history.replaceState(
|
|
1381
|
+
{ ...history.state, [INDEX_KEY]: ++current_history_index },
|
|
1382
|
+
'',
|
|
1383
|
+
location.href
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
},
|
|
1388
|
+
|
|
1389
|
+
_hydrate: async ({ status, error, nodes, params }) => {
|
|
1390
|
+
const url = new URL(location.href);
|
|
1391
|
+
|
|
1392
|
+
/** @type {Array<import('./types').BranchNode | undefined>} */
|
|
1393
|
+
const branch = [];
|
|
1394
|
+
|
|
1395
|
+
/** @type {Record<string, any>} */
|
|
1396
|
+
let stuff = {};
|
|
1397
|
+
|
|
1398
|
+
/** @type {import('./types').NavigationResult | undefined} */
|
|
1399
|
+
let result;
|
|
1400
|
+
|
|
1401
|
+
let error_args;
|
|
1402
|
+
|
|
1403
|
+
try {
|
|
1404
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
1405
|
+
const is_leaf = i === nodes.length - 1;
|
|
1406
|
+
|
|
1407
|
+
let props;
|
|
1408
|
+
|
|
1409
|
+
if (is_leaf) {
|
|
1410
|
+
const serialized = document.querySelector('script[sveltekit\\:data-type="props"]');
|
|
1411
|
+
if (serialized) {
|
|
1412
|
+
props = JSON.parse(/** @type {string} */ (serialized.textContent));
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
const node = await load_node({
|
|
1417
|
+
module: await nodes[i],
|
|
1418
|
+
url,
|
|
1419
|
+
params,
|
|
1420
|
+
stuff,
|
|
1421
|
+
status: is_leaf ? status : undefined,
|
|
1422
|
+
error: is_leaf ? error : undefined,
|
|
1423
|
+
props
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
if (props) {
|
|
1427
|
+
node.uses.dependencies.add(url.href);
|
|
1428
|
+
node.uses.url = true;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
branch.push(node);
|
|
1432
|
+
|
|
1433
|
+
if (node && node.loaded) {
|
|
1434
|
+
if (node.loaded.error) {
|
|
1435
|
+
if (error) throw node.loaded.error;
|
|
1436
|
+
error_args = {
|
|
1437
|
+
status: node.loaded.status,
|
|
1438
|
+
error: node.loaded.error,
|
|
1439
|
+
url
|
|
1440
|
+
};
|
|
1441
|
+
} else if (node.loaded.stuff) {
|
|
1442
|
+
stuff = {
|
|
1443
|
+
...stuff,
|
|
1444
|
+
...node.loaded.stuff
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
result = error_args
|
|
1451
|
+
? await load_root_error_page(error_args)
|
|
1452
|
+
: await get_navigation_result_from_branch({
|
|
1453
|
+
url,
|
|
1454
|
+
params,
|
|
1455
|
+
stuff,
|
|
1456
|
+
branch,
|
|
1457
|
+
status,
|
|
1458
|
+
error
|
|
1459
|
+
});
|
|
1460
|
+
} catch (e) {
|
|
1461
|
+
if (error) throw e;
|
|
1462
|
+
|
|
1463
|
+
result = await load_root_error_page({
|
|
1464
|
+
status: 500,
|
|
1465
|
+
error: coalesce_to_error(e),
|
|
1466
|
+
url
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (result.redirect) {
|
|
1471
|
+
// this is a real edge case — `load` would need to return
|
|
1472
|
+
// a redirect but only in the browser
|
|
1473
|
+
await native_navigation(new URL(result.redirect, location.href));
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
initialize(result);
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* @param {{
|
|
1483
|
+
* paths: {
|
|
1484
|
+
* assets: string;
|
|
1485
|
+
* base: string;
|
|
1486
|
+
* },
|
|
1487
|
+
* target: Element;
|
|
1488
|
+
* session: any;
|
|
1489
|
+
* route: boolean;
|
|
1490
|
+
* spa: boolean;
|
|
1491
|
+
* trailing_slash: import('types').TrailingSlash;
|
|
1492
|
+
* hydrate: {
|
|
1493
|
+
* status: number;
|
|
1494
|
+
* error: Error;
|
|
1495
|
+
* nodes: Array<Promise<import('types').CSRComponent>>;
|
|
1496
|
+
* params: Record<string, string>;
|
|
1497
|
+
* };
|
|
1498
|
+
* }} opts
|
|
1499
|
+
*/
|
|
1500
|
+
async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
|
|
1501
|
+
const client = create_client({
|
|
1502
|
+
target,
|
|
1503
|
+
session,
|
|
1504
|
+
base: paths.base,
|
|
1505
|
+
trailing_slash
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
init({ client });
|
|
1509
|
+
set_paths(paths);
|
|
1510
|
+
|
|
1511
|
+
if (hydrate) {
|
|
1512
|
+
await client._hydrate(hydrate);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
if (route) {
|
|
1516
|
+
if (spa) client.goto(location.href, { replaceState: true });
|
|
1517
|
+
client._start_router();
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
dispatchEvent(new CustomEvent('sveltekit:start'));
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
export { start };
|