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

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 (77) hide show
  1. package/package.json +11 -11
  2. package/src/cli.js +2 -2
  3. package/src/core/adapt/builder.js +8 -5
  4. package/src/core/generate_manifest/index.js +6 -0
  5. package/src/core/postbuild/entities.js +8 -2
  6. package/src/core/postbuild/fallback.js +2 -1
  7. package/src/core/postbuild/prerender.js +15 -6
  8. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  9. package/src/core/sync/create_manifest_data/index.js +33 -2
  10. package/src/core/sync/write_app_types.js +73 -29
  11. package/src/core/sync/write_tsconfig/index.js +15 -2
  12. package/src/core/sync/write_types/index.js +5 -2
  13. package/src/exports/index.js +23 -12
  14. package/src/exports/internal/shared.js +4 -12
  15. package/src/exports/node/index.js +2 -7
  16. package/src/exports/public.d.ts +61 -12
  17. package/src/exports/vite/build/remote.js +10 -12
  18. package/src/exports/vite/dev/index.js +32 -19
  19. package/src/exports/vite/index.js +162 -133
  20. package/src/exports/vite/utils.js +1 -7
  21. package/src/runtime/app/server/remote/form.js +9 -1
  22. package/src/runtime/app/server/remote/prerender.js +13 -9
  23. package/src/runtime/app/server/remote/query.js +0 -6
  24. package/src/runtime/app/server/remote/requested.js +4 -4
  25. package/src/runtime/app/state/client.js +3 -0
  26. package/src/runtime/app/state/index.js +2 -2
  27. package/src/runtime/app/state/server.js +3 -0
  28. package/src/runtime/client/client.js +731 -342
  29. package/src/runtime/client/constants.js +2 -6
  30. package/src/runtime/client/fetcher.js +24 -25
  31. package/src/runtime/client/remote-functions/form.svelte.js +42 -14
  32. package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
  33. package/src/runtime/client/remote-functions/query/index.js +1 -1
  34. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  35. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -2
  36. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  37. package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
  38. package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
  39. package/src/runtime/client/remote-functions/shared.svelte.js +12 -11
  40. package/src/runtime/client/state.svelte.js +10 -22
  41. package/src/runtime/client/types.d.ts +1 -2
  42. package/src/runtime/client/utils.js +22 -14
  43. package/src/runtime/components/root.svelte +4 -14
  44. package/src/runtime/form-utils.js +4 -6
  45. package/src/runtime/pathname.js +37 -0
  46. package/src/runtime/props.svelte.js +71 -0
  47. package/src/runtime/server/fetch.js +9 -14
  48. package/src/runtime/server/index.js +10 -24
  49. package/src/runtime/server/page/crypto.js +2 -2
  50. package/src/runtime/server/page/csp.js +88 -104
  51. package/src/runtime/server/page/index.js +1 -2
  52. package/src/runtime/server/page/load_data.js +16 -33
  53. package/src/runtime/server/page/render.js +46 -25
  54. package/src/runtime/server/page/respond_with_error.js +1 -3
  55. package/src/runtime/server/page/serialize_data.js +2 -13
  56. package/src/runtime/server/page/server_routing.js +58 -9
  57. package/src/runtime/server/respond.js +20 -29
  58. package/src/runtime/server/state.js +43 -0
  59. package/src/runtime/shared.js +4 -2
  60. package/src/runtime/utils.js +3 -0
  61. package/src/types/ambient-private.d.ts +2 -2
  62. package/src/types/ambient.d.ts +59 -8
  63. package/src/types/global-private.d.ts +1 -1
  64. package/src/types/internal.d.ts +4 -8
  65. package/src/types/private.d.ts +3 -12
  66. package/src/utils/hash.js +21 -0
  67. package/src/utils/http.js +8 -7
  68. package/src/utils/import.js +2 -1
  69. package/src/utils/params.js +1 -1
  70. package/src/utils/regex.js +9 -0
  71. package/src/utils/routing.js +52 -27
  72. package/src/utils/url.js +1 -0
  73. package/src/version.js +1 -1
  74. package/types/index.d.ts +165 -66
  75. package/types/index.d.ts.map +2 -1
  76. package/src/exports/node/polyfills.js +0 -30
  77. 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,
@@ -1,6 +1,6 @@
1
1
  import { BROWSER, DEV } from 'esm-env';
2
2
  import { noop } from '../../utils/functions.js';
3
- import { hash } from '../../utils/hash.js';
3
+ import { hash_request } from '../../utils/hash.js';
4
4
  import { base64_decode } from '../utils.js';
5
5
 
6
6
  let loading = 0;
@@ -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,18 +165,14 @@ 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) {
165
- /** @type {import('types').StrictBody[]} */
166
- const values = [];
167
-
168
- if (opts.headers) {
169
- values.push([...new Headers(opts.headers)].join(','));
170
- }
168
+ const body = opts.body;
171
169
 
172
- if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
173
- values.push(opts.body);
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;
174
173
  }
175
174
 
176
- selector += `[data-hash="${hash(...values)}"]`;
175
+ selector += `[data-hash="${hash_request(opts.headers, body)}"]`;
177
176
  }
178
177
 
179
178
  return selector;
@@ -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
@@ -233,13 +262,9 @@ export function form(id) {
233
262
 
234
263
  if (response.redirect) {
235
264
  // Use internal version to allow redirects to external URLs
236
- void _goto(
237
- response.redirect,
238
- {
239
- refreshAll: should_refresh
240
- },
241
- 0
242
- );
265
+ void _goto(response.redirect, {
266
+ refreshAll: should_refresh
267
+ });
243
268
  return true;
244
269
  }
245
270
 
@@ -355,7 +380,10 @@ export function form(id) {
355
380
  const instance = {};
356
381
 
357
382
  instance.method = 'POST';
358
- instance.action = action;
383
+ Object.defineProperty(instance, 'action', {
384
+ get: get_action,
385
+ enumerable: true
386
+ });
359
387
 
360
388
  instance[createAttachmentKey()] = (/** @type {HTMLFormElement} */ form) => {
361
389
  if (element) {
@@ -100,7 +100,7 @@ export function prerender(id) {
100
100
 
101
101
  if (result.redirect) {
102
102
  // Use internal version to allow redirects to external URLs
103
- void _goto(result.redirect, {}, 0);
103
+ void _goto(result.redirect);
104
104
  return;
105
105
  }
106
106
 
@@ -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
 
@@ -30,7 +30,7 @@ export function query(id) {
30
30
 
31
31
  if (result.redirect) {
32
32
  // Use internal version to allow redirects to external URLs
33
- await _goto(result.redirect, {}, 0);
33
+ await _goto(result.redirect);
34
34
  }
35
35
  });
36
36
  };
@@ -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;
@@ -50,7 +50,7 @@ export function query_batch(id) {
50
50
 
51
51
  if (response.redirect) {
52
52
  // Use internal version to allow redirects to external URLs
53
- await _goto(response.redirect, {}, 0);
53
+ await _goto(response.redirect);
54
54
 
55
55
  // settle all batched promises (with `undefined`, like a redirect
56
56
  // from a non-batched query) so that callers don't hang forever
@@ -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 {
@@ -74,16 +74,6 @@ export class LiveQueryProxy {
74
74
  return this.#get_cached_query().done;
75
75
  }
76
76
 
77
- /**
78
- * @deprecated Use `for await (const value of liveQuery())` instead.
79
- * @returns {AsyncGenerator<T>}
80
- */
81
- run() {
82
- throw new Error(
83
- '`.run()` has been removed from live queries. Use `for await (const value of liveQuery())` instead.'
84
- );
85
- }
86
-
87
77
  reconnect() {
88
78
  return this.#get_cached_query().reconnect();
89
79
  }
@@ -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
  }
@@ -196,12 +197,12 @@ export async function remote_request(url, init) {
196
197
  export async function handle_side_channel_response(response) {
197
198
  if (response.type === 'redirect') {
198
199
  // Use internal version to allow redirects to external URLs
199
- await _goto(response.location, {}, 0);
200
+ await _goto(response.location);
200
201
  throw new Redirect(307, response.location);
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
  /**
@@ -123,6 +122,7 @@ export function get_link_info(a, base, uses_hash_router) {
123
122
  /** @type {URL | undefined} */
124
123
  let url;
125
124
 
125
+ // TODO replace the try/catch with `URL.parse` when browser support allows (Chrome 126, Firefox 126, Safari 18)
126
126
  try {
127
127
  url = new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
128
128
 
@@ -150,12 +150,6 @@ export function get_link_info(a, base, uses_hash_router) {
150
150
  * @param {HTMLFormElement | HTMLAnchorElement | SVGAElement} element
151
151
  */
152
152
  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
153
  /** @type {ValidLinkOptions<'preload-code'> | null} */
160
154
  let preload_code = null;
161
155
 
@@ -168,16 +162,31 @@ export function get_router_options(element) {
168
162
  /** @type {ValidLinkOptions<'replacestate'> | null} */
169
163
  let replace_state = null;
170
164
 
165
+ /** @type {ValidLinkOptions<'reset'> | null} */
166
+ let reset = null;
167
+
171
168
  /** @type {Element} */
172
169
  let el = element;
173
170
 
174
171
  while (el && el !== document.documentElement) {
172
+ if (DEV) {
173
+ for (const name of ['keepfocus', 'noscroll']) {
174
+ const value = el.getAttribute(`data-sveltekit-${name}`);
175
+ if (value !== null && !warned.has(el)) {
176
+ warned.add(el);
177
+ console.warn(
178
+ `\`data-sveltekit-${name}="true"\` has been replaced with \`data-sveltekit-reset="false"\``
179
+ );
180
+ console.log(el);
181
+ }
182
+ }
183
+ }
184
+
175
185
  if (preload_code === null) preload_code = link_option(el, 'preload-code');
176
186
  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
187
  if (reload === null) reload = link_option(el, 'reload');
180
188
  if (replace_state === null) replace_state = link_option(el, 'replacestate');
189
+ if (reset === null) reset = link_option(el, 'reset');
181
190
 
182
191
  el = /** @type {Element} */ (parent_element(el));
183
192
  }
@@ -198,10 +207,9 @@ export function get_router_options(element) {
198
207
  return {
199
208
  preload_code: levels[preload_code ?? 'false'],
200
209
  preload_data: levels[preload_data ?? 'false'],
201
- keepfocus: get_option_state(keepfocus),
202
- noscroll: get_option_state(noscroll),
203
210
  reload: get_option_state(reload),
204
- replace_state: get_option_state(replace_state)
211
+ replace_state: get_option_state(replace_state),
212
+ reset: get_option_state(reset) ?? true
205
213
  };
206
214
  }
207
215
 
@@ -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>
@@ -3,12 +3,10 @@
3
3
 
4
4
  import { DEV } from 'esm-env';
5
5
  import * as devalue from 'devalue';
6
- import { text_encoder } from './utils.js';
6
+ import { text_decoder, text_encoder } from './utils.js';
7
7
  import { noop } from '../utils/functions.js';
8
8
  import { SvelteKitError } from '@sveltejs/kit/internal';
9
9
 
10
- const decoder = new TextDecoder();
11
-
12
10
  /**
13
11
  * Sets a parsed form field value in a nested object, mutating the original object.
14
12
  * @param {Record<string, any>} object
@@ -283,7 +281,7 @@ export async function deserialize_binary_form(request, form_id) {
283
281
  const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
284
282
  if (!file_offsets_buffer) throw deserialize_error('file offset table too short');
285
283
 
286
- const parsed_offsets = JSON.parse(decoder.decode(file_offsets_buffer));
284
+ const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
287
285
 
288
286
  if (
289
287
  !Array.isArray(parsed_offsets) ||
@@ -298,7 +296,7 @@ export async function deserialize_binary_form(request, form_id) {
298
296
 
299
297
  /** @type {Array<{ offset: number, size: number }>} */
300
298
  const file_spans = [];
301
- const [data, meta] = devalue.parse(decoder.decode(data_buffer), {
299
+ const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), {
302
300
  File: ([name, type, size, last_modified, index]) => {
303
301
  if (
304
302
  typeof name !== 'string' ||
@@ -488,7 +486,7 @@ class LazyFile {
488
486
  });
489
487
  }
490
488
  async text() {
491
- return decoder.decode(await this.arrayBuffer());
489
+ return text_decoder.decode(await this.arrayBuffer());
492
490
  }
493
491
  }
494
492