@sveltejs/kit 1.0.0-next.23 → 1.0.0-next.233

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