@sveltejs/kit 1.0.0-next.31 → 1.0.0-next.310

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