@sveltejs/kit 1.0.0-next.405 → 1.0.0-next.406

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 (99) hide show
  1. package/package.json +23 -25
  2. package/{dist → src}/cli.js +19 -18
  3. package/{dist/chunks/index3.js → src/core/adapt/builder.js} +6 -59
  4. package/src/core/adapt/index.js +19 -0
  5. package/src/core/config/index.js +86 -0
  6. package/{dist/chunks/index.js → src/core/config/options.js} +7 -194
  7. package/src/core/config/types.d.ts +1 -0
  8. package/src/core/constants.js +3 -0
  9. package/src/core/generate_manifest/index.js +99 -0
  10. package/src/core/prerender/crawl.js +194 -0
  11. package/src/core/prerender/prerender.js +378 -0
  12. package/src/core/prerender/queue.js +80 -0
  13. package/src/core/sync/create_manifest_data/index.js +492 -0
  14. package/src/core/sync/create_manifest_data/types.d.ts +40 -0
  15. package/src/core/sync/sync.js +59 -0
  16. package/src/core/sync/utils.js +97 -0
  17. package/src/core/sync/write_ambient.js +87 -0
  18. package/src/core/sync/write_client_manifest.js +82 -0
  19. package/src/core/sync/write_matchers.js +25 -0
  20. package/src/core/sync/write_root.js +88 -0
  21. package/{dist/chunks → src/core/sync}/write_tsconfig.js +24 -108
  22. package/src/core/sync/write_types.js +738 -0
  23. package/src/core/utils.js +58 -0
  24. package/{dist → src}/hooks.js +1 -3
  25. package/src/index/index.js +45 -0
  26. package/src/index/private.js +31 -0
  27. package/src/node/index.js +145 -0
  28. package/src/node/polyfills.js +40 -0
  29. package/src/packaging/index.js +218 -0
  30. package/src/packaging/types.d.ts +8 -0
  31. package/src/packaging/typescript.js +150 -0
  32. package/src/packaging/utils.js +138 -0
  33. package/{assets → src/runtime}/app/env.js +3 -5
  34. package/src/runtime/app/navigation.js +22 -0
  35. package/src/runtime/app/paths.js +1 -0
  36. package/{assets → src/runtime}/app/stores.js +6 -9
  37. package/src/runtime/client/ambient.d.ts +17 -0
  38. package/{assets/client/start.js → src/runtime/client/client.js} +302 -878
  39. package/src/runtime/client/fetcher.js +60 -0
  40. package/src/runtime/client/parse.js +36 -0
  41. package/{assets → src/runtime}/client/singletons.js +2 -4
  42. package/src/runtime/client/start.js +48 -0
  43. package/src/runtime/client/types.d.ts +106 -0
  44. package/src/runtime/client/utils.js +113 -0
  45. package/src/runtime/components/error.svelte +16 -0
  46. package/{assets → src/runtime}/components/layout.svelte +0 -0
  47. package/{assets → src/runtime}/env/dynamic/private.js +0 -0
  48. package/{assets → src/runtime}/env/dynamic/public.js +0 -0
  49. package/{assets → src/runtime}/env-private.js +2 -4
  50. package/{assets → src/runtime}/env-public.js +2 -4
  51. package/src/runtime/env.js +6 -0
  52. package/src/runtime/hash.js +16 -0
  53. package/{assets → src/runtime}/paths.js +3 -5
  54. package/src/runtime/server/endpoint.js +42 -0
  55. package/src/runtime/server/index.js +434 -0
  56. package/src/runtime/server/page/cookie.js +25 -0
  57. package/src/runtime/server/page/crypto.js +239 -0
  58. package/src/runtime/server/page/csp.js +249 -0
  59. package/src/runtime/server/page/fetch.js +265 -0
  60. package/src/runtime/server/page/index.js +423 -0
  61. package/src/runtime/server/page/load_data.js +94 -0
  62. package/src/runtime/server/page/render.js +357 -0
  63. package/src/runtime/server/page/respond_with_error.js +105 -0
  64. package/src/runtime/server/page/types.d.ts +44 -0
  65. package/src/runtime/server/utils.js +116 -0
  66. package/src/utils/error.js +22 -0
  67. package/src/utils/escape.js +104 -0
  68. package/{dist/chunks → src/utils}/filesystem.js +22 -24
  69. package/src/utils/http.js +55 -0
  70. package/src/utils/misc.js +1 -0
  71. package/src/utils/routing.js +107 -0
  72. package/src/utils/url.js +97 -0
  73. package/src/vite/build/build_server.js +333 -0
  74. package/src/vite/build/build_service_worker.js +90 -0
  75. package/src/vite/build/utils.js +152 -0
  76. package/src/vite/dev/index.js +565 -0
  77. package/src/vite/index.js +536 -0
  78. package/src/vite/preview/index.js +186 -0
  79. package/src/vite/types.d.ts +3 -0
  80. package/src/vite/utils.js +335 -0
  81. package/svelte-kit.js +1 -10
  82. package/types/ambient.d.ts +5 -12
  83. package/types/index.d.ts +86 -44
  84. package/types/internal.d.ts +50 -72
  85. package/types/private.d.ts +2 -1
  86. package/assets/app/navigation.js +0 -24
  87. package/assets/app/paths.js +0 -1
  88. package/assets/components/error.svelte +0 -29
  89. package/assets/env.js +0 -8
  90. package/assets/server/index.js +0 -3589
  91. package/dist/chunks/error.js +0 -12
  92. package/dist/chunks/index2.js +0 -15745
  93. package/dist/chunks/multipart-parser.js +0 -458
  94. package/dist/chunks/sync.js +0 -1366
  95. package/dist/chunks/utils.js +0 -66
  96. package/dist/node/polyfills.js +0 -17928
  97. package/dist/node.js +0 -348
  98. package/dist/prerender.js +0 -788
  99. package/dist/vite.js +0 -2513
@@ -1,486 +1,32 @@
1
1
  import { onMount, tick } from 'svelte';
2
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
- export { set_public_env } from '../env-public.js';
8
-
9
- /**
10
- * @param {unknown} err
11
- * @return {Error}
12
- */
13
- function coalesce_to_error(err) {
14
- return err instanceof Error ||
15
- (err && /** @type {any} */ (err).name && /** @type {any} */ (err).message)
16
- ? /** @type {Error} */ (err)
17
- : new Error(JSON.stringify(err));
18
- }
19
-
20
- /**
21
- * @param {import('types').LoadOutput | void} loaded
22
- * @returns {import('types').NormalizedLoadOutput}
23
- */
24
- function normalize(loaded) {
25
- if (!loaded) {
26
- return {};
27
- }
28
-
29
- // TODO remove for 1.0
30
- // @ts-expect-error
31
- if (loaded.fallthrough) {
32
- throw new Error(
33
- 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching'
34
- );
35
- }
36
-
37
- // TODO remove for 1.0
38
- if ('maxage' in loaded) {
39
- throw new Error('maxage should be replaced with cache: { maxage }');
40
- }
41
-
42
- const has_error_status =
43
- loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
44
- if (loaded.error || has_error_status) {
45
- const status = loaded.status;
46
-
47
- if (!loaded.error && has_error_status) {
48
- return {
49
- status: status || 500,
50
- error: new Error(`${status}`)
51
- };
52
- }
53
-
54
- const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
55
-
56
- if (!(error instanceof Error)) {
57
- return {
58
- status: 500,
59
- error: new Error(
60
- `"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
61
- )
62
- };
63
- }
64
-
65
- if (!status || status < 400 || status > 599) {
66
- console.warn('"error" returned from load() without a valid status code — defaulting to 500');
67
- return { status: 500, error };
68
- }
69
-
70
- return { status, error };
71
- }
72
-
73
- if (loaded.redirect) {
74
- if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
75
- throw new Error(
76
- '"redirect" property returned from load() must be accompanied by a 3xx status code'
77
- );
78
- }
79
-
80
- if (typeof loaded.redirect !== 'string') {
81
- throw new Error('"redirect" property returned from load() must be a string');
82
- }
83
- }
84
-
85
- if (loaded.dependencies) {
86
- if (
87
- !Array.isArray(loaded.dependencies) ||
88
- loaded.dependencies.some((dep) => typeof dep !== 'string')
89
- ) {
90
- throw new Error('"dependencies" property returned from load() must be of type string[]');
91
- }
92
- }
93
-
94
- // TODO remove before 1.0
95
- if (/** @type {any} */ (loaded).context) {
96
- throw new Error(
97
- 'You are returning "context" from a load function. ' +
98
- '"context" was renamed to "stuff", please adjust your code accordingly.'
99
- );
100
- }
101
-
102
- return /** @type {import('types').NormalizedLoadOutput} */ (loaded);
103
- }
104
-
105
- /**
106
- * @param {string} path
107
- * @param {import('types').TrailingSlash} trailing_slash
108
- */
109
- function normalize_path(path, trailing_slash) {
110
- if (path === '/' || trailing_slash === 'ignore') return path;
111
-
112
- if (trailing_slash === 'never') {
113
- return path.endsWith('/') ? path.slice(0, -1) : path;
114
- } else if (trailing_slash === 'always' && !path.endsWith('/')) {
115
- return path + '/';
116
- }
117
-
118
- return path;
119
- }
120
-
121
- /** @param {Record<string, string>} params */
122
- function decode_params(params) {
123
- for (const key in params) {
124
- // input has already been decoded by decodeURI
125
- // now handle the rest that decodeURIComponent would do
126
- params[key] = params[key]
127
- .replace(/%23/g, '#')
128
- .replace(/%3[Bb]/g, ';')
129
- .replace(/%2[Cc]/g, ',')
130
- .replace(/%2[Ff]/g, '/')
131
- .replace(/%3[Ff]/g, '?')
132
- .replace(/%3[Aa]/g, ':')
133
- .replace(/%40/g, '@')
134
- .replace(/%26/g, '&')
135
- .replace(/%3[Dd]/g, '=')
136
- .replace(/%2[Bb]/g, '+')
137
- .replace(/%24/g, '$');
138
- }
139
-
140
- return params;
141
- }
142
-
143
- class LoadURL extends URL {
144
- /** @returns {string} */
145
- get hash() {
146
- throw new Error(
147
- 'url.hash is inaccessible from load. Consider accessing hash from the page store within the script tag of your component.'
148
- );
149
- }
150
- }
151
-
152
- /* global __SVELTEKIT_APP_VERSION__, __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
153
-
154
- /** @param {HTMLDocument} doc */
155
- function get_base_uri(doc) {
156
- let baseURI = doc.baseURI;
157
-
158
- if (!baseURI) {
159
- const baseTags = doc.getElementsByTagName('base');
160
- baseURI = baseTags.length ? baseTags[0].href : doc.URL;
161
- }
162
-
163
- return baseURI;
164
- }
165
-
166
- function scroll_state() {
167
- return {
168
- x: pageXOffset,
169
- y: pageYOffset
170
- };
171
- }
172
-
173
- /** @param {Event} event */
174
- function find_anchor(event) {
175
- const node = event
176
- .composedPath()
177
- .find((e) => e instanceof Node && e.nodeName.toUpperCase() === 'A'); // SVG <a> elements have a lowercase name
178
- return /** @type {HTMLAnchorElement | SVGAElement | undefined} */ (node);
179
- }
180
-
181
- /** @param {HTMLAnchorElement | SVGAElement} node */
182
- function get_href(node) {
183
- return node instanceof SVGAElement
184
- ? new URL(node.href.baseVal, document.baseURI)
185
- : new URL(node.href);
186
- }
187
-
188
- /** @param {any} value */
189
- function notifiable_store(value) {
190
- const store = writable(value);
191
- let ready = true;
192
-
193
- function notify() {
194
- ready = true;
195
- store.update((val) => val);
196
- }
197
-
198
- /** @param {any} new_value */
199
- function set(new_value) {
200
- ready = false;
201
- store.set(new_value);
202
- }
203
-
204
- /** @param {(value: any) => void} run */
205
- function subscribe(run) {
206
- /** @type {any} */
207
- let old_value;
208
- return store.subscribe((new_value) => {
209
- if (old_value === undefined || (ready && new_value !== old_value)) {
210
- run((old_value = new_value));
211
- }
212
- });
213
- }
214
-
215
- return { notify, set, subscribe };
216
- }
217
-
218
- function create_updated_store() {
219
- const { set, subscribe } = writable(false);
220
-
221
- const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__;
222
-
223
- /** @type {NodeJS.Timeout} */
224
- let timeout;
3
+ import { normalize_error } from '../../utils/error.js';
4
+ import { LoadURL, decode_params, normalize_path } from '../../utils/url.js';
5
+ import {
6
+ create_updated_store,
7
+ find_anchor,
8
+ get_base_uri,
9
+ get_href,
10
+ notifiable_store,
11
+ scroll_state
12
+ } from './utils.js';
13
+ import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher.js';
14
+ import { parse } from './parse.js';
15
+ import { error } from '../../index/index.js';
225
16
 
226
- async function check() {
227
- if (import.meta.env.DEV || import.meta.env.SSR) return false;
228
-
229
- clearTimeout(timeout);
230
-
231
- if (interval) timeout = setTimeout(check, interval);
232
-
233
- const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
234
- headers: {
235
- pragma: 'no-cache',
236
- 'cache-control': 'no-cache'
237
- }
238
- });
239
-
240
- if (res.ok) {
241
- const { version } = await res.json();
242
- const updated = version !== __SVELTEKIT_APP_VERSION__;
243
-
244
- if (updated) {
245
- set(true);
246
- clearTimeout(timeout);
247
- }
248
-
249
- return updated;
250
- } else {
251
- throw new Error(`Version check failed: ${res.status}`);
252
- }
253
- }
254
-
255
- if (interval) timeout = setTimeout(check, interval);
256
-
257
- return {
258
- subscribe,
259
- check
260
- };
261
- }
262
-
263
- /**
264
- * Hash using djb2
265
- * @param {import('types').StrictBody} value
266
- */
267
- function hash(value) {
268
- let hash = 5381;
269
- let i = value.length;
270
-
271
- if (typeof value === 'string') {
272
- while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
273
- } else {
274
- while (i) hash = (hash * 33) ^ value[--i];
275
- }
276
-
277
- return (hash >>> 0).toString(36);
278
- }
279
-
280
- let loading = 0;
281
-
282
- const native_fetch = window.fetch;
283
-
284
- function lock_fetch() {
285
- loading += 1;
286
- }
287
-
288
- function unlock_fetch() {
289
- loading -= 1;
290
- }
291
-
292
- if (import.meta.env.DEV) {
293
- let can_inspect_stack_trace = false;
294
-
295
- const check_stack_trace = async () => {
296
- const stack = /** @type {string} */ (new Error().stack);
297
- can_inspect_stack_trace = stack.includes('check_stack_trace');
298
- };
299
-
300
- check_stack_trace();
301
-
302
- window.fetch = (input, init) => {
303
- const url = input instanceof Request ? input.url : input.toString();
304
- const stack = /** @type {string} */ (new Error().stack);
305
-
306
- const heuristic = can_inspect_stack_trace ? stack.includes('load_node') : loading;
307
- if (heuristic) {
308
- console.warn(
309
- `Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/loading#input-fetch`
310
- );
311
- }
312
-
313
- return native_fetch(input, init);
314
- };
315
- }
316
-
317
- /**
318
- * @param {RequestInfo} resource
319
- * @param {RequestInit} [opts]
320
- */
321
- function initial_fetch(resource, opts) {
322
- const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
323
-
324
- let selector = `script[sveltekit\\:data-type="data"][sveltekit\\:data-url=${url}]`;
325
-
326
- if (opts && typeof opts.body === 'string') {
327
- selector += `[sveltekit\\:data-body="${hash(opts.body)}"]`;
328
- }
329
-
330
- const script = document.querySelector(selector);
331
- if (script && script.textContent) {
332
- const { body, ...init } = JSON.parse(script.textContent);
333
- return Promise.resolve(new Response(body, init));
334
- }
335
-
336
- return native_fetch(resource, opts);
337
- }
338
-
339
- const param_pattern = /^(\.\.\.)?(\w+)(?:=(\w+))?$/;
340
-
341
- /** @param {string} id */
342
- function parse_route_id(id) {
343
- /** @type {string[]} */
344
- const names = [];
345
-
346
- /** @type {string[]} */
347
- const types = [];
348
-
349
- // `/foo` should get an optional trailing slash, `/foo.json` should not
350
- // const add_trailing_slash = !/\.[a-z]+$/.test(key);
351
- let add_trailing_slash = true;
352
-
353
- const pattern =
354
- id === ''
355
- ? /^\/$/
356
- : new RegExp(
357
- `^${decodeURIComponent(id)
358
- .split(/(?:@[a-zA-Z0-9_-]+)?(?:\/|$)/)
359
- .map((segment, i, segments) => {
360
- // special case — /[...rest]/ could contain zero segments
361
- const match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
362
- if (match) {
363
- names.push(match[1]);
364
- types.push(match[2]);
365
- return '(?:/(.*))?';
366
- }
367
-
368
- const is_last = i === segments.length - 1;
369
-
370
- return (
371
- segment &&
372
- '/' +
373
- segment
374
- .split(/\[(.+?)\]/)
375
- .map((content, i) => {
376
- if (i % 2) {
377
- const match = param_pattern.exec(content);
378
- if (!match) {
379
- throw new Error(
380
- `Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
381
- );
382
- }
383
-
384
- const [, rest, name, type] = match;
385
- names.push(name);
386
- types.push(type);
387
- return rest ? '(.*?)' : '([^/]+?)';
388
- }
389
-
390
- if (is_last && content.includes('.')) add_trailing_slash = false;
391
-
392
- return (
393
- content // allow users to specify characters on the file system in an encoded manner
394
- .normalize()
395
- // We use [ and ] to denote parameters, so users must encode these on the file
396
- // system to match against them. We don't decode all characters since others
397
- // can already be epressed and so that '%' can be easily used directly in filenames
398
- .replace(/%5[Bb]/g, '[')
399
- .replace(/%5[Dd]/g, ']')
400
- // '#', '/', and '?' can only appear in URL path segments in an encoded manner.
401
- // They will not be touched by decodeURI so need to be encoded here, so
402
- // that we can match against them.
403
- // We skip '/' since you can't create a file with it on any OS
404
- .replace(/#/g, '%23')
405
- .replace(/\?/g, '%3F')
406
- // escape characters that have special meaning in regex
407
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
408
- ); // TODO handle encoding
409
- })
410
- .join('')
411
- );
412
- })
413
- .join('')}${add_trailing_slash ? '/?' : ''}$`
414
- );
415
-
416
- return { pattern, names, types };
417
- }
418
-
419
- /**
420
- * @param {RegExpMatchArray} match
421
- * @param {string[]} names
422
- * @param {string[]} types
423
- * @param {Record<string, import('types').ParamMatcher>} matchers
424
- */
425
- function exec(match, names, types, matchers) {
426
- /** @type {Record<string, string>} */
427
- const params = {};
428
-
429
- for (let i = 0; i < names.length; i += 1) {
430
- const name = names[i];
431
- const type = types[i];
432
- const value = match[i + 1] || '';
433
-
434
- if (type) {
435
- const matcher = matchers[type];
436
- if (!matcher) throw new Error(`Missing "${type}" param matcher`); // TODO do this ahead of time?
437
-
438
- if (!matcher(value)) return;
439
- }
440
-
441
- params[name] = value;
442
- }
443
-
444
- return params;
445
- }
446
-
447
- /**
448
- * @param {import('types').CSRComponentLoader[]} components
449
- * @param {Record<string, [number[], number[], 1?]>} dictionary
450
- * @param {Record<string, (param: string) => boolean>} matchers
451
- * @returns {import('types').CSRRoute[]}
452
- */
453
- function parse(components, dictionary, matchers) {
454
- const routes = Object.entries(dictionary).map(([id, [a, b, has_shadow]]) => {
455
- const { pattern, names, types } = parse_route_id(id);
456
-
457
- return {
458
- id,
459
- /** @param {string} path */
460
- exec: (path) => {
461
- const match = pattern.exec(path);
462
- if (match) return exec(match, names, types, matchers);
463
- },
464
- a: a.map((n) => components[n]),
465
- b: b.map((n) => components[n]),
466
- has_shadow: !!has_shadow
467
- };
468
- });
469
-
470
- return routes;
471
- }
17
+ import Root from '__GENERATED__/root.svelte';
18
+ import { nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js';
19
+ import { HttpError, Redirect } from '../../index/private.js';
472
20
 
473
21
  const SCROLL_KEY = 'sveltekit:scroll';
474
22
  const INDEX_KEY = 'sveltekit:index';
475
23
 
476
- const routes = parse(components, dictionary, matchers);
24
+ const routes = parse(nodes, dictionary, matchers);
477
25
 
478
- // we import the root layout/error components eagerly, so that
26
+ // we import the root layout/error nodes eagerly, so that
479
27
  // connectivity errors after initialisation don't nuke the app
480
- const default_layout = components[0]();
481
- const default_error = components[1]();
482
-
483
- const root_stuff = {};
28
+ const default_layout = nodes[0]();
29
+ const default_error = nodes[1]();
484
30
 
485
31
  // We track the scroll position associated with each history entry in sessionStorage,
486
32
  // rather than on history.state itself, because when navigation is driven by
@@ -510,10 +56,7 @@ function update_scroll_positions(index) {
510
56
  * }} opts
511
57
  * @returns {import('./types').Client}
512
58
  */
513
- function create_client({ target, session, base, trailing_slash }) {
514
- /** @type {Map<string, import('./types').NavigationResult>} */
515
- const cache = new Map();
516
-
59
+ export function create_client({ target, session, base, trailing_slash }) {
517
60
  /** @type {Array<((href: string) => boolean)>} */
518
61
  const invalidated = [];
519
62
 
@@ -544,7 +87,6 @@ function create_client({ target, session, base, trailing_slash }) {
544
87
  branch: [],
545
88
  error: null,
546
89
  session_id: 0,
547
- stuff: root_stuff,
548
90
  // @ts-ignore - we need the initial value to be null
549
91
  url: null
550
92
  };
@@ -573,7 +115,7 @@ function create_client({ target, session, base, trailing_slash }) {
573
115
  const current_load_uses_session = current.branch.some((node) => node?.uses.session);
574
116
  if (!current_load_uses_session) return;
575
117
 
576
- update(new URL(location.href), [], true);
118
+ update(new URL(location.href), []);
577
119
  });
578
120
  ready = true;
579
121
 
@@ -651,7 +193,7 @@ function create_client({ target, session, base, trailing_slash }) {
651
193
  throw new Error('Attempted to prefetch a URL that does not belong to this app');
652
194
  }
653
195
 
654
- load_cache.promise = load_route(intent, false);
196
+ load_cache.promise = load_route(intent);
655
197
  load_cache.id = intent.id;
656
198
 
657
199
  return load_cache.promise;
@@ -661,15 +203,14 @@ function create_client({ target, session, base, trailing_slash }) {
661
203
  * Returns `true` if update completes, `false` if it is aborted
662
204
  * @param {URL} url
663
205
  * @param {string[]} redirect_chain
664
- * @param {boolean} no_cache
665
206
  * @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean, details: { replaceState: boolean, state: any } | null}} [opts]
666
207
  * @param {() => void} [callback]
667
208
  */
668
- async function update(url, redirect_chain, no_cache, opts, callback) {
209
+ async function update(url, redirect_chain, opts, callback) {
669
210
  const intent = get_navigation_intent(url);
670
211
 
671
212
  const current_token = (token = {});
672
- let navigation_result = intent && (await load_route(intent, no_cache));
213
+ let navigation_result = intent && (await load_route(intent));
673
214
 
674
215
  if (
675
216
  !navigation_result &&
@@ -703,7 +244,7 @@ function create_client({ target, session, base, trailing_slash }) {
703
244
 
704
245
  invalidated.length = 0;
705
246
 
706
- if (navigation_result.redirect) {
247
+ if (navigation_result.type === 'redirect') {
707
248
  if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
708
249
  navigation_result = await load_root_error_page({
709
250
  status: 500,
@@ -713,12 +254,12 @@ function create_client({ target, session, base, trailing_slash }) {
713
254
  });
714
255
  } else {
715
256
  if (router_enabled) {
716
- goto(new URL(navigation_result.redirect, url).href, {}, [
257
+ goto(new URL(navigation_result.location, url).href, {}, [
717
258
  ...redirect_chain,
718
259
  url.pathname
719
260
  ]);
720
261
  } else {
721
- await native_navigation(new URL(navigation_result.redirect, location.href));
262
+ await native_navigation(new URL(navigation_result.location, location.href));
722
263
  }
723
264
 
724
265
  return false;
@@ -808,15 +349,15 @@ function create_client({ target, session, base, trailing_slash }) {
808
349
  page = navigation_result.props.page;
809
350
  }
810
351
 
811
- const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1];
812
- router_enabled = leaf_node?.module.router !== false;
352
+ const leaf_node = navigation_result.state.branch.at(-1);
353
+ router_enabled = leaf_node?.node.shared?.router !== false;
813
354
 
814
355
  if (callback) callback();
815
356
 
816
357
  updating = false;
817
358
  }
818
359
 
819
- /** @param {import('./types').NavigationResult} result */
360
+ /** @param {import('./types').NavigationFinished} result */
820
361
  function initialize(result) {
821
362
  current = result.state;
822
363
 
@@ -844,57 +385,53 @@ function create_client({ target, session, base, trailing_slash }) {
844
385
  * @param {{
845
386
  * url: URL;
846
387
  * params: Record<string, string>;
847
- * stuff: Record<string, any>;
848
388
  * branch: Array<import('./types').BranchNode | undefined>;
849
389
  * status: number;
850
- * error: Error | null;
390
+ * error: HttpError | Error | null;
851
391
  * routeId: string | null;
852
392
  * }} opts
853
393
  */
854
394
  async function get_navigation_result_from_branch({
855
395
  url,
856
396
  params,
857
- stuff,
858
397
  branch,
859
398
  status,
860
399
  error,
861
400
  routeId
862
401
  }) {
863
402
  const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
864
- const redirect = filtered.find((f) => f.loaded?.redirect);
865
403
 
866
- /** @type {import('./types').NavigationResult} */
404
+ /** @type {import('./types').NavigationFinished} */
867
405
  const result = {
868
- redirect: redirect?.loaded?.redirect,
406
+ type: 'loaded',
869
407
  state: {
870
408
  url,
871
409
  params,
872
410
  branch,
873
411
  error,
874
- stuff,
875
412
  session_id
876
413
  },
877
414
  props: {
878
- components: filtered.map((node) => node.module.default)
415
+ components: filtered.map((branch_node) => branch_node.node.component)
879
416
  }
880
417
  };
881
418
 
419
+ let data = {};
420
+ let data_changed = false;
882
421
  for (let i = 0; i < filtered.length; i += 1) {
422
+ Object.assign(data, filtered[i].data);
883
423
  // Only set props if the node actually updated. This prevents needless rerenders.
884
424
  if (!current.branch.some((node) => node === filtered[i])) {
885
- const loaded = filtered[i].loaded;
886
- result.props[`props_${i}`] = loaded ? await loaded.props : null;
425
+ result.props[`data_${i}`] = filtered[i].data;
426
+ data_changed = true;
887
427
  }
888
428
  }
889
429
 
890
430
  const page_changed =
891
- !current.url ||
892
- url.href !== current.url.href ||
893
- current.error !== error ||
894
- current.stuff !== stuff;
431
+ !current.url || url.href !== current.url.href || current.error !== error || data_changed;
895
432
 
896
433
  if (page_changed) {
897
- result.props.page = { error, params, routeId, status, stuff, url };
434
+ result.props.page = { error, params, routeId, status, url, data };
898
435
 
899
436
  // TODO remove this for 1.0
900
437
  /**
@@ -914,72 +451,49 @@ function create_client({ target, session, base, trailing_slash }) {
914
451
  print_error('query', 'searchParams');
915
452
  }
916
453
 
917
- const leaf = filtered[filtered.length - 1];
918
- const load_cache = leaf?.loaded?.cache;
919
-
920
- if (load_cache) {
921
- const key = url.pathname + url.search; // omit hash
922
- let ready = false;
923
-
924
- const clear = () => {
925
- if (cache.get(key) === result) {
926
- cache.delete(key);
927
- }
928
-
929
- unsubscribe();
930
- clearTimeout(timeout);
931
- };
932
-
933
- const timeout = setTimeout(clear, load_cache.maxage * 1000);
934
-
935
- const unsubscribe = stores.session.subscribe(() => {
936
- if (ready) clear();
937
- });
938
-
939
- ready = true;
940
-
941
- cache.set(key, result);
942
- }
943
-
944
454
  return result;
945
455
  }
946
456
 
947
457
  /**
458
+ * Call the load function of the given node, if it exists.
459
+ * If `server_data` is passed, this is treated as the initial run and the page endpoint is not requested.
460
+ *
948
461
  * @param {{
949
- * status?: number;
950
- * error?: Error;
951
- * module: import('types').CSRComponent;
462
+ * node: import('types').CSRPageNode;
463
+ * parent: () => Promise<Record<string, any>>;
952
464
  * url: URL;
953
465
  * params: Record<string, string>;
954
- * stuff: Record<string, any>;
955
- * props?: Record<string, any>;
956
466
  * routeId: string | null;
467
+ * server_data: import('types').JSONObject | null;
957
468
  * }} options
469
+ * @returns {Promise<import('./types').BranchNode>}
958
470
  */
959
- async function load_node({ status, error, module, url, params, stuff, props, routeId }) {
960
- /** @type {import('./types').BranchNode} */
961
- const node = {
962
- module,
963
- uses: {
964
- params: new Set(),
965
- url: false,
966
- session: false,
967
- stuff: false,
968
- dependencies: new Set()
969
- },
970
- loaded: null,
971
- stuff
471
+ async function load_node({ node, parent, url, params, routeId, server_data }) {
472
+ const uses = {
473
+ params: new Set(),
474
+ url: false,
475
+ session: false,
476
+ dependencies: new Set(),
477
+ parent: false
972
478
  };
973
479
 
974
- /** @param dep {string} */
975
- function add_dependency(dep) {
976
- const { href } = new URL(dep, url);
977
- node.uses.dependencies.add(href);
480
+ /** @param {string[]} deps */
481
+ function depends(...deps) {
482
+ for (const dep of deps) {
483
+ const { href } = new URL(dep, url);
484
+ uses.dependencies.add(href);
485
+ }
978
486
  }
979
487
 
980
- if (props) {
981
- // shadow endpoint props means we need to mark this URL as a dependency of itself
982
- node.uses.dependencies.add(url.href);
488
+ /** @type {Record<string, any> | null} */
489
+ let data = null;
490
+
491
+ if (node.server) {
492
+ // +page|layout.server.js data means we need to mark this URL as a dependency of itself,
493
+ // unless we want to get clever with usage detection on the server, which could
494
+ // be returned to the client either as payload or custom headers
495
+ uses.dependencies.add(url.href);
496
+ uses.url = true;
983
497
  }
984
498
 
985
499
  /** @type {Record<string, string>} */
@@ -987,7 +501,7 @@ function create_client({ target, session, base, trailing_slash }) {
987
501
  for (const key in params) {
988
502
  Object.defineProperty(uses_params, key, {
989
503
  get() {
990
- node.uses.params.add(key);
504
+ uses.params.add(key);
991
505
  return params[key];
992
506
  },
993
507
  enumerable: true
@@ -997,24 +511,20 @@ function create_client({ target, session, base, trailing_slash }) {
997
511
  const session = $session;
998
512
  const load_url = new LoadURL(url);
999
513
 
1000
- if (module.load) {
514
+ if (node.shared?.load) {
1001
515
  /** @type {import('types').LoadEvent} */
1002
516
  const load_input = {
1003
517
  routeId,
1004
518
  params: uses_params,
1005
- props: props || {},
519
+ data: server_data,
1006
520
  get url() {
1007
- node.uses.url = true;
521
+ uses.url = true;
1008
522
  return load_url;
1009
523
  },
1010
524
  get session() {
1011
- node.uses.session = true;
525
+ uses.session = true;
1012
526
  return session;
1013
527
  },
1014
- get stuff() {
1015
- node.uses.stuff = true;
1016
- return { ...stuff };
1017
- },
1018
528
  async fetch(resource, init) {
1019
529
  let requested;
1020
530
 
@@ -1049,61 +559,61 @@ function create_client({ target, session, base, trailing_slash }) {
1049
559
 
1050
560
  // we must fixup relative urls so they are resolved from the target page
1051
561
  const normalized = new URL(requested, url).href;
1052
- add_dependency(normalized);
562
+ depends(normalized);
1053
563
 
1054
564
  // prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be normalized
1055
565
  return started ? native_fetch(normalized, init) : initial_fetch(requested, init);
1056
566
  },
1057
- status: status ?? null,
1058
- error: error ?? null
567
+ setHeaders: () => {}, // noop
568
+ depends,
569
+ get parent() {
570
+ // uses.parent assignment here, not on method inokation, else we wouldn't notice when someone
571
+ // does await parent() inside an if branch which wasn't executed yet.
572
+ uses.parent = true;
573
+ return parent;
574
+ },
575
+ // @ts-expect-error
576
+ get props() {
577
+ throw new Error(
578
+ '@migration task: Replace `props` with `data` stuff https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292693'
579
+ );
580
+ },
581
+ get stuff() {
582
+ throw new Error(
583
+ '@migration task: Remove stuff https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292693'
584
+ );
585
+ }
1059
586
  };
1060
587
 
1061
- if (import.meta.env.DEV) {
1062
- // TODO remove this for 1.0
1063
- Object.defineProperty(load_input, 'page', {
1064
- get: () => {
1065
- throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
1066
- }
1067
- });
1068
- }
1069
-
1070
588
  if (import.meta.env.DEV) {
1071
589
  try {
1072
590
  lock_fetch();
1073
- node.loaded = normalize(await module.load.call(null, load_input));
591
+ data = (await node.shared.load.call(null, load_input)) ?? null;
1074
592
  } finally {
1075
593
  unlock_fetch();
1076
594
  }
1077
595
  } else {
1078
- node.loaded = normalize(await module.load.call(null, load_input));
1079
- }
1080
-
1081
- if (node.loaded.stuff) node.stuff = node.loaded.stuff;
1082
- if (node.loaded.dependencies) {
1083
- node.loaded.dependencies.forEach(add_dependency);
596
+ data = (await node.shared.load.call(null, load_input)) ?? null;
1084
597
  }
1085
- } else if (props) {
1086
- node.loaded = normalize({ props });
1087
598
  }
1088
599
 
1089
- return node;
600
+ return {
601
+ node,
602
+ data: data || server_data,
603
+ uses
604
+ };
1090
605
  }
1091
606
 
1092
607
  /**
1093
608
  * @param {import('./types').NavigationIntent} intent
1094
- * @param {boolean} no_cache
609
+ * @returns {Promise<import('./types').NavigationResult | undefined>}
1095
610
  */
1096
- async function load_route({ id, url, params, route }, no_cache) {
611
+ async function load_route({ id, url, params, route }) {
1097
612
  if (load_cache.id === id && load_cache.promise) {
1098
613
  return load_cache.promise;
1099
614
  }
1100
615
 
1101
- if (!no_cache) {
1102
- const cached = cache.get(id);
1103
- if (cached) return cached;
1104
- }
1105
-
1106
- const { a, b, has_shadow } = route;
616
+ const { errors, layouts, leaf } = route;
1107
617
 
1108
618
  const changed = current.url && {
1109
619
  url: id !== current.url.pathname + current.url.search,
@@ -1111,191 +621,175 @@ function create_client({ target, session, base, trailing_slash }) {
1111
621
  session: session_id !== current.session_id
1112
622
  };
1113
623
 
1114
- /** @type {Array<import('./types').BranchNode | undefined>} */
1115
- let branch = [];
1116
-
1117
- /** @type {Record<string, any>} */
1118
- let stuff = root_stuff;
1119
- let stuff_changed = false;
1120
-
1121
- /** @type {number} */
1122
- let status = 200;
1123
-
1124
- /** @type {Error | null} */
1125
- let error = null;
1126
-
1127
624
  // preload modules to avoid waterfall, but handle rejections
1128
625
  // so they don't get reported to Sentry et al (we don't need
1129
626
  // to act on the failures at this point)
1130
- a.forEach((loader) => loader().catch(() => {}));
627
+ [...errors, ...layouts, leaf].forEach((loader) => loader?.().catch(() => {}));
1131
628
 
1132
- load: for (let i = 0; i < a.length; i += 1) {
1133
- /** @type {import('./types').BranchNode | undefined} */
1134
- let node;
629
+ const nodes = [...layouts, leaf];
1135
630
 
1136
- try {
1137
- if (!a[i]) continue;
1138
-
1139
- const module = await a[i]();
631
+ // To avoid waterfalls when someone awaits a parent, compute as much as possible here already
632
+ /** @type {boolean[]} */
633
+ const nodes_changed_since_last_render = [];
634
+ for (let i = 0; i < nodes.length; i++) {
635
+ if (!nodes[i]) {
636
+ nodes_changed_since_last_render.push(false);
637
+ } else {
1140
638
  const previous = current.branch[i];
1141
-
1142
639
  const changed_since_last_render =
1143
640
  !previous ||
1144
- module !== previous.module ||
1145
641
  (changed.url && previous.uses.url) ||
1146
642
  changed.params.some((param) => previous.uses.params.has(param)) ||
1147
643
  (changed.session && previous.uses.session) ||
1148
644
  Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))) ||
1149
- (stuff_changed && previous.uses.stuff);
645
+ (previous.uses.parent && nodes_changed_since_last_render.includes(true));
646
+ nodes_changed_since_last_render.push(changed_since_last_render);
647
+ }
648
+ }
1150
649
 
1151
- if (changed_since_last_render) {
1152
- /** @type {Record<string, any>} */
1153
- let props = {};
1154
-
1155
- const is_shadow_page = has_shadow && i === a.length - 1;
1156
-
1157
- if (is_shadow_page) {
1158
- const res = await native_fetch(
1159
- `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`,
1160
- {
1161
- headers: {
1162
- 'x-sveltekit-load': 'true'
1163
- }
1164
- }
1165
- );
650
+ /** @type {import('./types').ServerDataPayload | null} */
651
+ let server_data_payload = null;
1166
652
 
1167
- if (res.ok) {
1168
- const redirect = res.headers.get('x-sveltekit-location');
653
+ if (route.uses_server_data) {
654
+ try {
655
+ const res = await native_fetch(
656
+ `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`
657
+ );
1169
658
 
1170
- if (redirect) {
1171
- return {
1172
- redirect,
1173
- props: {},
1174
- state: current
1175
- };
1176
- }
659
+ server_data_payload = /** @type {import('./types').ServerDataPayload} */ (await res.json());
1177
660
 
1178
- props = res.status === 204 ? {} : await res.json();
1179
- } else {
1180
- status = res.status;
1181
- try {
1182
- error = await res.json();
1183
- } catch (e) {
1184
- error = new Error('Failed to load data');
1185
- }
1186
- }
1187
- }
661
+ if (!res.ok) {
662
+ throw server_data_payload;
663
+ }
664
+ } catch (e) {
665
+ throw new Error('TODO render fallback error page');
666
+ }
1188
667
 
1189
- if (!error) {
1190
- node = await load_node({
1191
- module,
1192
- url,
1193
- params,
1194
- props,
1195
- stuff,
1196
- routeId: route.id
1197
- });
1198
- }
668
+ if (server_data_payload.type === 'redirect') {
669
+ return server_data_payload;
670
+ }
671
+ }
1199
672
 
1200
- if (node) {
1201
- if (is_shadow_page) {
1202
- node.uses.url = true;
1203
- }
673
+ const server_data_nodes = server_data_payload?.nodes;
1204
674
 
1205
- if (node.loaded) {
1206
- if (node.loaded.error) {
1207
- status = node.loaded.status ?? 500;
1208
- error = node.loaded.error;
1209
- }
675
+ const branch_promises = nodes.map(async (loader, i) => {
676
+ return Promise.resolve().then(async () => {
677
+ if (!loader) return;
678
+ const node = await loader();
1210
679
 
1211
- if (node.loaded.redirect) {
1212
- return {
1213
- redirect: node.loaded.redirect,
1214
- props: {},
1215
- state: current
1216
- };
1217
- }
680
+ /** @type {import('./types').BranchNode | undefined} */
681
+ const previous = current.branch[i];
682
+ const changed_since_last_render =
683
+ nodes_changed_since_last_render[i] || !previous || node !== previous.node;
1218
684
 
1219
- if (node.loaded.stuff) {
1220
- stuff_changed = true;
1221
- }
1222
- }
685
+ if (changed_since_last_render) {
686
+ const payload = server_data_nodes?.[i];
687
+
688
+ if (payload?.status) {
689
+ throw error(payload.status, payload.message);
690
+ }
691
+
692
+ if (payload?.error) {
693
+ throw payload.error;
1223
694
  }
695
+
696
+ return await load_node({
697
+ node,
698
+ url,
699
+ params,
700
+ routeId: route.id,
701
+ parent: async () => {
702
+ const data = {};
703
+ for (let j = 0; j < i; j += 1) {
704
+ Object.assign(data, (await branch_promises[j])?.data);
705
+ }
706
+ return data;
707
+ },
708
+ server_data: payload?.data ?? null
709
+ });
1224
710
  } else {
1225
- node = previous;
711
+ return previous;
1226
712
  }
1227
- } catch (e) {
1228
- status = 500;
1229
- error = coalesce_to_error(e);
1230
- }
713
+ });
714
+ });
1231
715
 
1232
- if (error) {
1233
- while (i--) {
1234
- if (b[i]) {
1235
- let error_loaded;
716
+ // if we don't do this, rejections will be unhandled
717
+ for (const p of branch_promises) p.catch(() => {});
1236
718
 
1237
- /** @type {import('./types').BranchNode | undefined} */
1238
- let node_loaded;
1239
- let j = i;
1240
- while (!(node_loaded = branch[j])) {
1241
- j -= 1;
1242
- }
719
+ /** @type {Array<import('./types').BranchNode | undefined>} */
720
+ const branch = [];
1243
721
 
1244
- try {
1245
- error_loaded = await load_node({
1246
- status,
1247
- error,
1248
- module: await b[i](),
1249
- url,
1250
- params,
1251
- stuff: node_loaded.stuff,
1252
- routeId: route.id
1253
- });
1254
-
1255
- if (error_loaded?.loaded?.error) {
1256
- continue;
1257
- }
722
+ for (let i = 0; i < nodes.length; i += 1) {
723
+ if (nodes[i]) {
724
+ try {
725
+ branch.push(await branch_promises[i]);
726
+ } catch (e) {
727
+ const error = normalize_error(e);
728
+
729
+ if (error instanceof Redirect) {
730
+ return {
731
+ type: 'redirect',
732
+ location: error.location
733
+ };
734
+ }
735
+
736
+ const status = e instanceof HttpError ? e.status : 500;
737
+
738
+ while (i--) {
739
+ if (errors[i]) {
740
+ /** @type {import('./types').BranchNode | undefined} */
741
+ let error_loaded;
1258
742
 
1259
- if (error_loaded?.loaded?.stuff) {
1260
- stuff = {
1261
- ...stuff,
1262
- ...error_loaded.loaded.stuff
743
+ let j = i;
744
+ while (!branch[j]) j -= 1;
745
+
746
+ try {
747
+ error_loaded = {
748
+ node: await errors[i](),
749
+ data: {},
750
+ uses: {
751
+ params: new Set(),
752
+ url: false,
753
+ session: false,
754
+ dependencies: new Set(),
755
+ parent: false
756
+ }
1263
757
  };
1264
- }
1265
758
 
1266
- branch = branch.slice(0, j + 1).concat(error_loaded);
1267
- break load;
1268
- } catch (e) {
1269
- continue;
759
+ return await get_navigation_result_from_branch({
760
+ url,
761
+ params,
762
+ branch: branch.slice(0, j + 1).concat(error_loaded),
763
+ status,
764
+ error,
765
+ routeId: route.id
766
+ });
767
+ } catch (e) {
768
+ continue;
769
+ }
1270
770
  }
1271
771
  }
1272
- }
1273
772
 
1274
- return await load_root_error_page({
1275
- status,
1276
- error,
1277
- url,
1278
- routeId: route.id
1279
- });
1280
- } else {
1281
- if (node?.loaded?.stuff) {
1282
- stuff = {
1283
- ...stuff,
1284
- ...node.loaded.stuff
1285
- };
773
+ return await load_root_error_page({
774
+ status,
775
+ error,
776
+ url,
777
+ routeId: route.id
778
+ });
1286
779
  }
1287
-
1288
- branch.push(node);
780
+ } else {
781
+ // push an empty slot so we can rewind past gaps to the
782
+ // layout that corresponds with an +error.svelte page
783
+ branch.push(undefined);
1289
784
  }
1290
785
  }
1291
786
 
1292
787
  return await get_navigation_result_from_branch({
1293
788
  url,
1294
789
  params,
1295
- stuff,
1296
790
  branch,
1297
- status,
1298
- error,
791
+ status: 200,
792
+ error: null,
1299
793
  routeId: route.id
1300
794
  });
1301
795
  }
@@ -1303,7 +797,7 @@ function create_client({ target, session, base, trailing_slash }) {
1303
797
  /**
1304
798
  * @param {{
1305
799
  * status: number;
1306
- * error: Error;
800
+ * error: HttpError | Error;
1307
801
  * url: URL;
1308
802
  * routeId: string | null
1309
803
  * }} opts
@@ -1313,30 +807,30 @@ function create_client({ target, session, base, trailing_slash }) {
1313
807
  const params = {}; // error page does not have params
1314
808
 
1315
809
  const root_layout = await load_node({
1316
- module: await default_layout,
810
+ node: await default_layout,
1317
811
  url,
1318
812
  params,
1319
- stuff: {},
1320
- routeId
813
+ routeId,
814
+ parent: () => Promise.resolve({}),
815
+ server_data: null // TODO!!!!!
1321
816
  });
1322
817
 
1323
- const root_error = await load_node({
1324
- status,
1325
- error,
1326
- module: await default_error,
1327
- url,
1328
- params,
1329
- stuff: (root_layout && root_layout.loaded && root_layout.loaded.stuff) || {},
1330
- routeId
1331
- });
818
+ const root_error = {
819
+ node: await default_error,
820
+ data: null,
821
+ // TODO make this unnecessary
822
+ uses: {
823
+ params: new Set(),
824
+ url: false,
825
+ session: false,
826
+ dependencies: new Set(),
827
+ parent: false
828
+ }
829
+ };
1332
830
 
1333
831
  return await get_navigation_result_from_branch({
1334
832
  url,
1335
833
  params,
1336
- stuff: {
1337
- ...root_layout?.loaded?.stuff,
1338
- ...root_error?.loaded?.stuff
1339
- },
1340
834
  branch: [root_layout, root_error],
1341
835
  status,
1342
836
  error,
@@ -1410,7 +904,6 @@ function create_client({ target, session, base, trailing_slash }) {
1410
904
  await update(
1411
905
  url,
1412
906
  redirect_chain,
1413
- false,
1414
907
  {
1415
908
  scroll,
1416
909
  keepfocus,
@@ -1478,7 +971,13 @@ function create_client({ target, session, base, trailing_slash }) {
1478
971
  goto: (href, opts = {}) => goto(href, opts, []),
1479
972
 
1480
973
  invalidate: (resource) => {
1481
- if (typeof resource === 'function') {
974
+ if (resource === undefined) {
975
+ // Force rerun of all load functions, regardless of their dependencies
976
+ for (const node of current.branch) {
977
+ node?.uses.dependencies.add('');
978
+ }
979
+ invalidated.push(() => true);
980
+ } else if (typeof resource === 'function') {
1482
981
  invalidated.push(resource);
1483
982
  } else {
1484
983
  const { href } = new URL(resource, location.href);
@@ -1487,7 +986,7 @@ function create_client({ target, session, base, trailing_slash }) {
1487
986
 
1488
987
  if (!invalidating) {
1489
988
  invalidating = Promise.resolve().then(async () => {
1490
- await update(new URL(location.href), [], true);
989
+ await update(new URL(location.href), []);
1491
990
 
1492
991
  invalidating = null;
1493
992
  });
@@ -1507,7 +1006,9 @@ function create_client({ target, session, base, trailing_slash }) {
1507
1006
  ? routes.filter((route) => pathnames.some((pathname) => route.exec(pathname)))
1508
1007
  : routes;
1509
1008
 
1510
- const promises = matching.map((r) => Promise.all(r.a.map((load) => load())));
1009
+ const promises = matching.map((r) => {
1010
+ return Promise.all([...r.layouts, r.leaf].map((load) => load?.()));
1011
+ });
1511
1012
 
1512
1013
  await Promise.all(promises);
1513
1014
  },
@@ -1703,143 +1204,66 @@ function create_client({ target, session, base, trailing_slash }) {
1703
1204
  });
1704
1205
  },
1705
1206
 
1706
- _hydrate: async ({ status, error, nodes, params, routeId }) => {
1207
+ _hydrate: async ({ status, error, node_ids, params, routeId }) => {
1707
1208
  const url = new URL(location.href);
1708
1209
 
1709
- /** @type {Array<import('./types').BranchNode | undefined>} */
1710
- const branch = [];
1711
-
1712
- /** @type {Record<string, any>} */
1713
- let stuff = {};
1714
-
1715
- /** @type {import('./types').NavigationResult | undefined} */
1210
+ /** @type {import('./types').NavigationFinished | undefined} */
1716
1211
  let result;
1717
1212
 
1718
- let error_args;
1719
-
1720
1213
  try {
1721
- for (let i = 0; i < nodes.length; i += 1) {
1722
- const is_leaf = i === nodes.length - 1;
1723
-
1724
- let props;
1214
+ const script = document.querySelector(`script[sveltekit\\:data-type="server_data"]`);
1215
+ const server_data = script?.textContent ? JSON.parse(script.textContent) : [];
1725
1216
 
1726
- if (is_leaf) {
1727
- const serialized = document.querySelector('script[sveltekit\\:data-type="props"]');
1728
- if (serialized) {
1729
- props = JSON.parse(/** @type {string} */ (serialized.textContent));
1730
- }
1731
- }
1732
-
1733
- const node = await load_node({
1734
- module: await components[nodes[i]](),
1217
+ const branch_promises = node_ids.map(async (n, i) => {
1218
+ return load_node({
1219
+ node: await nodes[n](),
1735
1220
  url,
1736
1221
  params,
1737
- stuff,
1738
- status: is_leaf ? status : undefined,
1739
- error: is_leaf ? error : undefined,
1740
- props,
1741
- routeId
1222
+ routeId,
1223
+ parent: async () => {
1224
+ const data = {};
1225
+ for (let j = 0; j < i; j += 1) {
1226
+ Object.assign(data, (await branch_promises[j]).data);
1227
+ }
1228
+ return data;
1229
+ },
1230
+ server_data: server_data[i] ?? null
1742
1231
  });
1232
+ });
1743
1233
 
1744
- if (props) {
1745
- node.uses.dependencies.add(url.href);
1746
- node.uses.url = true;
1747
- }
1234
+ result = await get_navigation_result_from_branch({
1235
+ url,
1236
+ params,
1237
+ branch: await Promise.all(branch_promises),
1238
+ status,
1239
+ error: /** @type {import('../server/page/types').SerializedHttpError} */ (error)
1240
+ ?.__is_http_error
1241
+ ? new HttpError(
1242
+ /** @type {import('../server/page/types').SerializedHttpError} */ (error).status,
1243
+ error.message
1244
+ )
1245
+ : error,
1246
+ routeId
1247
+ });
1248
+ } catch (e) {
1249
+ const error = normalize_error(e);
1748
1250
 
1749
- branch.push(node);
1750
-
1751
- if (node && node.loaded) {
1752
- if (node.loaded.error) {
1753
- if (error) throw node.loaded.error;
1754
- error_args = {
1755
- status: node.loaded.status ?? 500,
1756
- error: node.loaded.error,
1757
- url,
1758
- routeId
1759
- };
1760
- } else if (node.loaded.stuff) {
1761
- stuff = {
1762
- ...stuff,
1763
- ...node.loaded.stuff
1764
- };
1765
- }
1766
- }
1251
+ if (error instanceof Redirect) {
1252
+ // this is a real edge case — `load` would need to return
1253
+ // a redirect but only in the browser
1254
+ await native_navigation(new URL(/** @type {Redirect} */ (e).location, location.href));
1255
+ return;
1767
1256
  }
1768
1257
 
1769
- result = error_args
1770
- ? await load_root_error_page(error_args)
1771
- : await get_navigation_result_from_branch({
1772
- url,
1773
- params,
1774
- stuff,
1775
- branch,
1776
- status,
1777
- error,
1778
- routeId
1779
- });
1780
- } catch (e) {
1781
- if (error) throw e;
1782
-
1783
1258
  result = await load_root_error_page({
1784
- status: 500,
1785
- error: coalesce_to_error(e),
1259
+ status: error instanceof HttpError ? error.status : 500,
1260
+ error,
1786
1261
  url,
1787
1262
  routeId
1788
1263
  });
1789
1264
  }
1790
1265
 
1791
- if (result.redirect) {
1792
- // this is a real edge case — `load` would need to return
1793
- // a redirect but only in the browser
1794
- await native_navigation(new URL(result.redirect, location.href));
1795
- }
1796
-
1797
1266
  initialize(result);
1798
1267
  }
1799
1268
  };
1800
1269
  }
1801
-
1802
- /**
1803
- * @param {{
1804
- * paths: {
1805
- * assets: string;
1806
- * base: string;
1807
- * },
1808
- * target: Element;
1809
- * session: any;
1810
- * route: boolean;
1811
- * spa: boolean;
1812
- * trailing_slash: import('types').TrailingSlash;
1813
- * hydrate: {
1814
- * status: number;
1815
- * error: Error;
1816
- * nodes: number[];
1817
- * params: Record<string, string>;
1818
- * routeId: string | null;
1819
- * };
1820
- * }} opts
1821
- */
1822
- async function start({ paths, target, session, route, spa, trailing_slash, hydrate }) {
1823
- const client = create_client({
1824
- target,
1825
- session,
1826
- base: paths.base,
1827
- trailing_slash
1828
- });
1829
-
1830
- init({ client });
1831
- set_paths(paths);
1832
-
1833
- if (hydrate) {
1834
- await client._hydrate(hydrate);
1835
- }
1836
-
1837
- if (route) {
1838
- if (spa) client.goto(location.href, { replaceState: true });
1839
- client._start_router();
1840
- }
1841
-
1842
- dispatchEvent(new CustomEvent('sveltekit:start'));
1843
- }
1844
-
1845
- export { start };