@sveltejs/kit 3.0.0-next.12 → 3.0.0-next.13

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 (46) hide show
  1. package/package.json +10 -10
  2. package/src/core/postbuild/prerender.js +3 -3
  3. package/src/core/sync/write_app_types.js +1 -2
  4. package/src/core/sync/write_tsconfig/index.js +13 -1
  5. package/src/exports/index.js +23 -12
  6. package/src/exports/internal/shared.js +4 -12
  7. package/src/exports/public.d.ts +59 -11
  8. package/src/exports/vite/build/remote.js +10 -12
  9. package/src/exports/vite/dev/index.js +32 -19
  10. package/src/exports/vite/index.js +118 -123
  11. package/src/runtime/app/server/remote/form.js +9 -1
  12. package/src/runtime/app/server/remote/prerender.js +13 -9
  13. package/src/runtime/app/server/remote/requested.js +4 -4
  14. package/src/runtime/app/state/client.js +3 -0
  15. package/src/runtime/app/state/index.js +2 -2
  16. package/src/runtime/app/state/server.js +3 -0
  17. package/src/runtime/client/client.js +584 -305
  18. package/src/runtime/client/constants.js +2 -6
  19. package/src/runtime/client/fetcher.js +27 -17
  20. package/src/runtime/client/remote-functions/form.svelte.js +39 -7
  21. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  22. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  23. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  24. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  25. package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
  26. package/src/runtime/client/remote-functions/shared.svelte.js +11 -10
  27. package/src/runtime/client/state.svelte.js +10 -22
  28. package/src/runtime/client/types.d.ts +1 -2
  29. package/src/runtime/client/utils.js +21 -14
  30. package/src/runtime/components/root.svelte +4 -14
  31. package/src/runtime/props.svelte.js +71 -0
  32. package/src/runtime/server/fetch.js +4 -6
  33. package/src/runtime/server/page/csp.js +86 -103
  34. package/src/runtime/server/page/index.js +1 -2
  35. package/src/runtime/server/page/load_data.js +15 -5
  36. package/src/runtime/server/page/render.js +27 -24
  37. package/src/runtime/server/page/respond_with_error.js +1 -3
  38. package/src/runtime/server/respond.js +0 -2
  39. package/src/types/ambient-private.d.ts +1 -1
  40. package/src/types/ambient.d.ts +1 -1
  41. package/src/types/internal.d.ts +2 -7
  42. package/src/types/private.d.ts +3 -12
  43. package/src/version.js +1 -1
  44. package/types/index.d.ts +92 -57
  45. package/types/index.d.ts.map +2 -1
  46. package/src/runtime/types.d.ts +0 -8
@@ -1,10 +1,6 @@
1
1
  export const SNAPSHOT_KEY = 'sveltekit:snapshot';
2
- export const SCROLL_KEY = 'sveltekit:scroll';
3
- export const STATES_KEY = 'sveltekit:states';
4
- export const PAGE_URL_KEY = 'sveltekit:pageurl';
5
-
6
- export const HISTORY_INDEX = 'sveltekit:history';
7
- export const NAVIGATION_INDEX = 'sveltekit:navigation';
2
+ export const HISTORY_INFO_KEY = 'sveltekit:history-info';
3
+ export const HISTORY_METADATA_KEY = 'sveltekit:metadata';
8
4
 
9
5
  export const PRELOAD_PRIORITIES = /** @type {const} */ ({
10
6
  tap: 1,
@@ -89,22 +89,24 @@ const cache = new Map();
89
89
  export function initial_fetch(resource, opts) {
90
90
  const selector = build_selector(resource, opts);
91
91
 
92
- const script = document.querySelector(selector);
93
- if (script?.textContent) {
94
- script.remove(); // In case multiple script tags match the same selector
95
- let { body, ...init } = JSON.parse(script.textContent);
96
-
97
- const b64 = script.getAttribute('data-b64');
98
- if (b64 !== null) {
99
- // Can't use native_fetch('data:...;base64,${body}')
100
- // csp can block the request
101
- body = base64_decode(body);
102
- }
92
+ if (selector) {
93
+ const script = document.querySelector(selector);
94
+ if (script?.textContent) {
95
+ script.remove(); // In case multiple script tags match the same selector
96
+ let { body, ...init } = JSON.parse(script.textContent);
97
+
98
+ const b64 = script.getAttribute('data-b64');
99
+ if (b64 !== null) {
100
+ // Can't use native_fetch('data:...;base64,${body}')
101
+ // csp can block the request
102
+ body = base64_decode(body);
103
+ }
103
104
 
104
- const ttl = script.getAttribute('data-ttl');
105
- if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
105
+ const ttl = script.getAttribute('data-ttl');
106
+ if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
106
107
 
107
- return Promise.resolve(new Response(body, init));
108
+ return Promise.resolve(new Response(body, init));
109
+ }
108
110
  }
109
111
 
110
112
  return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts);
@@ -119,7 +121,7 @@ export function initial_fetch(resource, opts) {
119
121
  export function subsequent_fetch(resource, resolved, opts) {
120
122
  if (cache.size > 0) {
121
123
  const selector = build_selector(resource, opts);
122
- const cached = cache.get(selector);
124
+ const cached = selector && cache.get(selector);
123
125
  if (cached) {
124
126
  // https://developer.mozilla.org/en-US/docs/Web/API/Request/cache#value
125
127
  if (
@@ -155,6 +157,7 @@ export function dev_fetch(resource, opts) {
155
157
  * Build the cache key for a given request
156
158
  * @param {URL | RequestInfo} resource
157
159
  * @param {RequestInit} [opts]
160
+ * @returns {string | null} `null` for requests the server never serializes
158
161
  */
159
162
  function build_selector(resource, opts) {
160
163
  const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
@@ -162,6 +165,13 @@ function build_selector(resource, opts) {
162
165
  let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
163
166
 
164
167
  if (opts?.headers || opts?.body) {
168
+ const body = opts.body;
169
+
170
+ if (body && typeof body !== 'string' && !ArrayBuffer.isView(body)) {
171
+ // the server skips serializing these, so a matching script tag belongs to another request
172
+ return null;
173
+ }
174
+
165
175
  /** @type {import('types').StrictBody[]} */
166
176
  const values = [];
167
177
 
@@ -169,8 +179,8 @@ function build_selector(resource, opts) {
169
179
  values.push([...new Headers(opts.headers)].join(','));
170
180
  }
171
181
 
172
- if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
173
- values.push(opts.body);
182
+ if (body) {
183
+ values.push(/** @type {import('types').StrictBody} */ (body));
174
184
  }
175
185
 
176
186
  selector += `[data-hash="${hash(...values)}"]`;
@@ -11,6 +11,7 @@ import {
11
11
  handle_error,
12
12
  refreshAll
13
13
  } from '../client.js';
14
+ import { page } from '../state.svelte.js';
14
15
  import { tick } from 'svelte';
15
16
  import { categorize_updates, remote_request } from './shared.svelte.js';
16
17
  import { createAttachmentKey } from 'svelte/attachments';
@@ -39,16 +40,22 @@ import {
39
40
  * @returns {InternalRemoteFormIssue[]}
40
41
  */
41
42
  function merge_with_server_issues(form_data, current_issues, client_issues) {
43
+ const client_names = new Set(client_issues.map((issue) => issue.name));
44
+
42
45
  const merged = [
43
- ...current_issues.filter(
44
- (issue) => issue.server && !client_issues.some((i) => i.name === issue.name)
45
- ),
46
+ ...current_issues.filter((issue) => issue.server && !client_names.has(issue.name)),
46
47
  ...client_issues
47
48
  ];
48
49
 
49
- const keys = Array.from(form_data.keys());
50
+ /** @type {Map<string, number>} */
51
+ const positions = new Map();
52
+ let i = 0;
53
+ for (const key of form_data.keys()) {
54
+ if (!positions.has(key)) positions.set(key, i);
55
+ i++;
56
+ }
50
57
 
51
- return merged.sort((a, b) => keys.indexOf(a.name) - keys.indexOf(b.name));
58
+ return merged.sort((a, b) => (positions.get(a.name) ?? -1) - (positions.get(b.name) ?? -1));
52
59
  }
53
60
 
54
61
  /**
@@ -69,7 +76,29 @@ export function form(id) {
69
76
  function create_instance(key) {
70
77
  const action_id_without_key = id;
71
78
  const action_id = id + (key != undefined ? `/${JSON.stringify(key)}` : '');
72
- const action = '?/remote=' + encodeURIComponent(action_id);
79
+ const action = '/remote=' + encodeURIComponent(action_id);
80
+
81
+ /** @type {string} */
82
+ let cached_search = '';
83
+ /** @type {string} */
84
+ let cached_query = '';
85
+
86
+ /** @returns {string} */
87
+ function get_action() {
88
+ if (page.url.search !== cached_search) {
89
+ cached_search = page.url.search;
90
+
91
+ if (page.url.search) {
92
+ const params = new URLSearchParams(page.url.search);
93
+ params.delete('/remote');
94
+ cached_query = params.toString();
95
+ } else {
96
+ cached_query = '';
97
+ }
98
+ }
99
+
100
+ return `?${cached_query && `${cached_query}&`}${action}`;
101
+ }
73
102
 
74
103
  // the output of a non-enhanced submission that resulted in this page —
75
104
  // consume it so the form's state survives hydration (form outputs are
@@ -355,7 +384,10 @@ export function form(id) {
355
384
  const instance = {};
356
385
 
357
386
  instance.method = 'POST';
358
- instance.action = action;
387
+ Object.defineProperty(instance, 'action', {
388
+ get: get_action,
389
+ enumerable: true
390
+ });
359
391
 
360
392
  instance[createAttachmentKey()] = (/** @type {HTMLFormElement} */ form) => {
361
393
  if (element) {
@@ -172,7 +172,7 @@ class Prerender {
172
172
  });
173
173
  this.#loading = false;
174
174
  this.#error = error;
175
- throw new HttpError(error.status, error); // so that transformError doesn't transform it again
175
+ throw new HttpError(error); // so that transformError doesn't transform it again
176
176
  }
177
177
  );
178
178
 
@@ -70,7 +70,7 @@ export class Query {
70
70
  delete query_responses[key];
71
71
 
72
72
  if (node.e) {
73
- this.fail(new HttpError(node.e.status, node.e));
73
+ this.fail(new HttpError(node.e));
74
74
  } else {
75
75
  this.set(/** @type {T} */ (node.v));
76
76
  }
@@ -152,7 +152,7 @@ export class Query {
152
152
  this.#loading = false;
153
153
  });
154
154
 
155
- reject(new HttpError(error.status, error)); // so that transformError doesn't transform it again
155
+ reject(new HttpError(error)); // so that transformError doesn't transform it again
156
156
  });
157
157
 
158
158
  return promise;
@@ -71,7 +71,7 @@ export function query_batch(id) {
71
71
 
72
72
  for (const { resolve, reject } of resolvers) {
73
73
  if (result.type === 'error') {
74
- reject(new HttpError(result.error.status, result.error));
74
+ reject(new HttpError(result.error));
75
75
  } else {
76
76
  resolve(result.data);
77
77
  }
@@ -91,7 +91,7 @@ export class LiveQuery {
91
91
  // the query failed during SSR — seed the failed state (mirroring `fail()`,
92
92
  // minus its terminal `#done`), so the main loop still connects as usual
93
93
  // and the query can recover
94
- const error = new HttpError(node.e.status, node.e);
94
+ const error = new HttpError(node.e);
95
95
  this.#loading = false;
96
96
  this.#error = error.body;
97
97
 
@@ -417,7 +417,7 @@ export class LiveQuery {
417
417
  route: { id: null },
418
418
  url: new URL(location.href)
419
419
  });
420
- this.fail(new HttpError(error.status, error));
420
+ this.fail(new HttpError(error));
421
421
  }
422
422
 
423
423
  get [Symbol.toStringTag]() {
@@ -1,3 +1,4 @@
1
+ /** @import { RemoteFunctionResponse } from 'types' */
1
2
  import { app_dir, base } from '$app/paths/internal/client';
2
3
  import { app } from '../../client.js';
3
4
  import { notify_version } from '../../state.svelte.js';
@@ -31,20 +32,21 @@ export async function* create_live_iterator(
31
32
  notify_version(response.headers.get('x-sveltekit-version'));
32
33
 
33
34
  if (!response.ok) {
34
- const result = await response.json().catch(() => ({
35
- type: 'error',
36
- status: response.status,
37
- error: response.statusText
38
- }));
35
+ /** @type {RemoteFunctionResponse | undefined} */
36
+ const result = await response.json().catch(() => undefined);
39
37
 
40
- throw new HttpError(result.status ?? response.status ?? 500, result.error);
38
+ throw new HttpError(
39
+ result?.type === 'error'
40
+ ? result.error
41
+ : { status: response.status, message: response.statusText }
42
+ );
41
43
  }
42
44
 
43
45
  if (response.headers.get('content-type')?.includes('application/json')) {
44
46
  // we can end up here if we e.g. redirect in `handle`
45
47
  const result = await response.json();
46
48
  await handle_side_channel_response(result);
47
- throw new HttpError(500, 'Invalid query.live response');
49
+ throw new HttpError({ status: 500, message: 'Invalid query.live response' });
48
50
  }
49
51
 
50
52
  if (!response.body) {
@@ -63,7 +65,7 @@ export async function* create_live_iterator(
63
65
  }
64
66
 
65
67
  await handle_side_channel_response(node);
66
- throw new HttpError(500, 'Invalid query.live response');
68
+ throw new HttpError({ status: 500, message: 'Invalid query.live response' });
67
69
  }
68
70
  } finally {
69
71
  try {
@@ -86,7 +86,7 @@ export function pin_while_resolving(cache_map, cache, id, payload, then) {
86
86
  */
87
87
  export function unwrap_node(node) {
88
88
  if (node.e) {
89
- throw new HttpError(node.e.status, node.e);
89
+ throw new HttpError(node.e);
90
90
  }
91
91
 
92
92
  return node.v;
@@ -112,6 +112,7 @@ export function get_remote_request_headers() {
112
112
  */
113
113
  export async function remote_request(url, init) {
114
114
  const response = await fetch(url, init);
115
+ const status = response.status;
115
116
 
116
117
  // detect new deployments from the response header
117
118
  notify_version(response.headers.get('x-sveltekit-version'));
@@ -119,20 +120,20 @@ export async function remote_request(url, init) {
119
120
  if (!response.ok) {
120
121
  const result = await response.json().catch(() => ({
121
122
  type: 'error',
122
- status: response.status,
123
- error: response.statusText
123
+ status,
124
+ error: {
125
+ status,
126
+ message: response.statusText
127
+ }
124
128
  }));
125
129
 
126
- throw new HttpError(
127
- result.error?.status ?? result.status ?? response.status ?? 500,
128
- result.error
129
- );
130
+ throw new HttpError({ status, ...result.error });
130
131
  }
131
132
 
132
133
  const result = /** @type {RemoteFunctionResponse} */ (await response.json());
133
134
 
134
135
  if (result.type === 'error') {
135
- throw new HttpError(result.error.status, result.error);
136
+ throw new HttpError(result.error);
136
137
  }
137
138
 
138
139
  const data = /** @type {RemoteFunctionData} */ (
@@ -147,7 +148,7 @@ export async function remote_request(url, init) {
147
148
  function refresh(key, entry, result) {
148
149
  if (entry?.resource) {
149
150
  if (result.e) {
150
- entry.resource.fail(new HttpError(result.e.status, result.e));
151
+ entry.resource.fail(new HttpError(result.e));
151
152
  } else {
152
153
  entry.resource.set(result.v);
153
154
  }
@@ -201,7 +202,7 @@ export async function handle_side_channel_response(response) {
201
202
  }
202
203
 
203
204
  if (response.type === 'error') {
204
- throw new HttpError(response.error.status, response.error);
205
+ throw new HttpError(response.error);
205
206
  }
206
207
 
207
208
  return response;
@@ -9,6 +9,7 @@ export const page = new (class Page {
9
9
  error = $state.raw(null);
10
10
  params = $state.raw({});
11
11
  route = $state.raw({ id: null });
12
+ shallow = $state.raw(null);
12
13
  state = $state.raw({});
13
14
  status = $state.raw(-1);
14
15
  url = $state.raw(new URL('a:'));
@@ -61,12 +62,14 @@ if (!DEV && BROWSER) {
61
62
  }
62
63
 
63
64
  /** @type {() => Promise<boolean>} */
64
- function check() {
65
- if (checking) return checking;
66
-
65
+ updated.check = function check() {
67
66
  window.clearTimeout(timeout);
68
67
 
69
- return (checking = (async () => {
68
+ if (updated.current) {
69
+ return Promise.resolve(true);
70
+ }
71
+
72
+ return (checking ??= (async () => {
70
73
  try {
71
74
  const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
72
75
  headers: {
@@ -79,13 +82,7 @@ if (!DEV && BROWSER) {
79
82
  }
80
83
 
81
84
  const data = await res.json();
82
- const new_update = data.version !== version;
83
-
84
- if (new_update) {
85
- updated.current = true;
86
- }
87
-
88
- return new_update;
85
+ return (updated.current ||= data.version !== version);
89
86
  } catch {
90
87
  return false;
91
88
  } finally {
@@ -93,16 +90,7 @@ if (!DEV && BROWSER) {
93
90
  if (interval && !updated.current) timeout = window.setTimeout(check, interval);
94
91
  }
95
92
  })());
96
- }
93
+ };
97
94
 
98
- if (interval) timeout = window.setTimeout(check, interval);
99
-
100
- updated.check = check;
101
- }
102
-
103
- /**
104
- * @param {import('@sveltejs/kit').Page} new_page
105
- */
106
- export function update(new_page) {
107
- Object.assign(page, new_page);
95
+ if (interval) timeout = window.setTimeout(updated.check, interval);
108
96
  }
@@ -1,4 +1,3 @@
1
- import { SvelteComponent } from 'svelte';
2
1
  import {
3
2
  ClientHooks,
4
3
  CSRPageNode,
@@ -10,7 +9,7 @@ import {
10
9
  Uses
11
10
  } from 'types';
12
11
  import { Page, ParamMatcher } from '@sveltejs/kit';
13
- import { RenderNode } from '../types.js';
12
+ import { RenderNode } from '../props.svelte.js';
14
13
 
15
14
  export interface SvelteKitApp {
16
15
  /**
@@ -31,10 +31,9 @@ const warned = new WeakSet();
31
31
  const valid_link_options = /** @type {const} */ ({
32
32
  'preload-code': ['', 'false', 'tap', 'hover', 'viewport', 'eager'],
33
33
  'preload-data': ['', 'false', 'tap', 'hover'],
34
- keepfocus: ['', 'true', 'false'],
35
- noscroll: ['', 'true', 'false'],
36
34
  reload: ['', 'true', 'false'],
37
- replacestate: ['', 'true', 'false']
35
+ replacestate: ['', 'true', 'false'],
36
+ reset: ['', 'true', 'false']
38
37
  });
39
38
 
40
39
  /**
@@ -150,12 +149,6 @@ export function get_link_info(a, base, uses_hash_router) {
150
149
  * @param {HTMLFormElement | HTMLAnchorElement | SVGAElement} element
151
150
  */
152
151
  export function get_router_options(element) {
153
- /** @type {ValidLinkOptions<'keepfocus'> | null} */
154
- let keepfocus = null;
155
-
156
- /** @type {ValidLinkOptions<'noscroll'> | null} */
157
- let noscroll = null;
158
-
159
152
  /** @type {ValidLinkOptions<'preload-code'> | null} */
160
153
  let preload_code = null;
161
154
 
@@ -168,16 +161,31 @@ export function get_router_options(element) {
168
161
  /** @type {ValidLinkOptions<'replacestate'> | null} */
169
162
  let replace_state = null;
170
163
 
164
+ /** @type {ValidLinkOptions<'reset'> | null} */
165
+ let reset = null;
166
+
171
167
  /** @type {Element} */
172
168
  let el = element;
173
169
 
174
170
  while (el && el !== document.documentElement) {
171
+ if (DEV) {
172
+ for (const name of ['keepfocus', 'noscroll']) {
173
+ const value = el.getAttribute(`data-sveltekit-${name}`);
174
+ if (value !== null && !warned.has(el)) {
175
+ warned.add(el);
176
+ console.warn(
177
+ `\`data-sveltekit-${name}="true"\` has been replaced with \`data-sveltekit-reset="false"\``
178
+ );
179
+ console.log(el);
180
+ }
181
+ }
182
+ }
183
+
175
184
  if (preload_code === null) preload_code = link_option(el, 'preload-code');
176
185
  if (preload_data === null) preload_data = link_option(el, 'preload-data');
177
- if (keepfocus === null) keepfocus = link_option(el, 'keepfocus');
178
- if (noscroll === null) noscroll = link_option(el, 'noscroll');
179
186
  if (reload === null) reload = link_option(el, 'reload');
180
187
  if (replace_state === null) replace_state = link_option(el, 'replacestate');
188
+ if (reset === null) reset = link_option(el, 'reset');
181
189
 
182
190
  el = /** @type {Element} */ (parent_element(el));
183
191
  }
@@ -198,10 +206,9 @@ export function get_router_options(element) {
198
206
  return {
199
207
  preload_code: levels[preload_code ?? 'false'],
200
208
  preload_data: levels[preload_data ?? 'false'],
201
- keepfocus: get_option_state(keepfocus),
202
- noscroll: get_option_state(noscroll),
203
209
  reload: get_option_state(reload),
204
- replace_state: get_option_state(replace_state)
210
+ replace_state: get_option_state(replace_state),
211
+ reset: get_option_state(reset) ?? true
205
212
  };
206
213
  }
207
214
 
@@ -1,18 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { afterNavigate } from '$app/navigation';
3
- import type { Page } from '@sveltejs/kit';
4
- import type { RenderNode } from '../types.js';
3
+ import type { Props, RenderNode } from '../props.svelte.js';
5
4
 
6
- interface Props {
7
- page: Page;
8
- tree: RenderNode;
9
- components: any[];
10
- resetters: Array<(() => void) | undefined>;
11
- form?: any;
12
- error?: App.Error;
13
- }
14
-
15
- const { page, components, resetters, tree, form, error }: Props = $props();
5
+ const { page, components, onerror, tree, form, error }: Props = $props();
16
6
 
17
7
  let mounted = $state(false);
18
8
  let navigated = $state(false);
@@ -33,7 +23,7 @@
33
23
  {const Error = $derived(n.error)}
34
24
  {const data = $derived(n.data)}
35
25
 
36
- <svelte:boundary onerror={(_, reset) => (resetters[depth] = reset)}>
26
+ <svelte:boundary {onerror}>
37
27
  {#if n.child}
38
28
  <!-- svelte-ignore binding_property_non_reactive -->
39
29
  <Component bind:this={components[depth]} {data} {form} params={page.params}>
@@ -44,7 +34,7 @@
44
34
  <Component bind:this={components[depth]} {data} {form} params={page.params} {error} />
45
35
  {/if}
46
36
 
47
- {#snippet failed(error)}
37
+ {#snippet failed(error: unknown)}
48
38
  <Error {error} />
49
39
  {/snippet}
50
40
  </svelte:boundary>
@@ -0,0 +1,71 @@
1
+ /** @import { Component } from 'svelte'; */
2
+ /** @import { Page } from '@sveltejs/kit'; */
3
+
4
+ import { noop } from '../utils/functions.js';
5
+
6
+ export class Props {
7
+ /** @type {Page} */
8
+ page;
9
+
10
+ /**
11
+ * An array of the `+layout.svelte` and `+page.svelte` component instances
12
+ * that currently live on the page — used for capturing and restoring snapshots.
13
+ * It's updated/manipulated through `bind:this` in `Root.svelte`.
14
+ * @type {Array<Record<string, any>>}
15
+ */
16
+ components = [];
17
+
18
+ /** @type {any} */
19
+ form;
20
+
21
+ /** @type {App.Error | undefined} */
22
+ error;
23
+
24
+ /** @type {RenderNode} */
25
+ tree;
26
+
27
+ /** @type {(error: unknown, reset: () => void) => void} */
28
+ onerror;
29
+
30
+ /**
31
+ * @param {{
32
+ * page: Page;
33
+ * tree: RenderNode;
34
+ * form: any;
35
+ * error: App.Error | undefined;
36
+ * onerror?: (error: unknown, reset: () => void) => void;
37
+ * }} props
38
+ */
39
+ constructor({ page, tree, form, error, onerror = noop }) {
40
+ this.page = page;
41
+ this.tree = tree;
42
+ this.onerror = onerror;
43
+
44
+ this.form = $state.raw(form);
45
+ this.error = $state.raw(error);
46
+ }
47
+ }
48
+
49
+ export class RenderNode {
50
+ /** @type {Component} */
51
+ component;
52
+
53
+ /** @type {Component | undefined} */
54
+ error;
55
+
56
+ /** @type {Record<string, any>} */
57
+ data = $state.raw({});
58
+
59
+ /** @type {RenderNode | undefined} */
60
+ child = $state.raw();
61
+
62
+ /**
63
+ *
64
+ * @param {Component} component
65
+ * @param {Component} error
66
+ */
67
+ constructor(component, error) {
68
+ this.component = component;
69
+ this.error = error;
70
+ }
71
+ }
@@ -117,7 +117,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
117
117
  return await fetch(request);
118
118
  }
119
119
 
120
- if (has_prerendered_path(manifest, paths.base + decoded)) {
120
+ if (has_prerendered_path(manifest, decoded)) {
121
121
  // The path of something prerendered could match a different route
122
122
  // that is still in the manifest, leading to the wrong route being loaded.
123
123
  // We therefore bail early here. The prerendered logic is different for
@@ -142,11 +142,9 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
142
142
  request.headers.set('accept', '*/*');
143
143
  }
144
144
 
145
- if (!request.headers.has('accept-language')) {
146
- request.headers.set(
147
- 'accept-language',
148
- /** @type {string} */ (event.request.headers.get('accept-language'))
149
- );
145
+ const accept_language = event.request.headers.get('accept-language');
146
+ if (accept_language && !request.headers.has('accept-language')) {
147
+ request.headers.set('accept-language', accept_language);
150
148
  }
151
149
 
152
150
  const response = await internal_fetch(request, options, manifest, state);