@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.
- package/package.json +10 -10
- package/src/core/postbuild/prerender.js +3 -3
- package/src/core/sync/write_app_types.js +1 -2
- package/src/core/sync/write_tsconfig/index.js +13 -1
- package/src/exports/index.js +23 -12
- package/src/exports/internal/shared.js +4 -12
- package/src/exports/public.d.ts +59 -11
- package/src/exports/vite/build/remote.js +10 -12
- package/src/exports/vite/dev/index.js +32 -19
- package/src/exports/vite/index.js +118 -123
- package/src/runtime/app/server/remote/form.js +9 -1
- package/src/runtime/app/server/remote/prerender.js +13 -9
- package/src/runtime/app/server/remote/requested.js +4 -4
- package/src/runtime/app/state/client.js +3 -0
- package/src/runtime/app/state/index.js +2 -2
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/client/client.js +584 -305
- package/src/runtime/client/constants.js +2 -6
- package/src/runtime/client/fetcher.js +27 -17
- package/src/runtime/client/remote-functions/form.svelte.js +39 -7
- package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
- package/src/runtime/client/remote-functions/shared.svelte.js +11 -10
- package/src/runtime/client/state.svelte.js +10 -22
- package/src/runtime/client/types.d.ts +1 -2
- package/src/runtime/client/utils.js +21 -14
- package/src/runtime/components/root.svelte +4 -14
- package/src/runtime/props.svelte.js +71 -0
- package/src/runtime/server/fetch.js +4 -6
- package/src/runtime/server/page/csp.js +86 -103
- package/src/runtime/server/page/index.js +1 -2
- package/src/runtime/server/page/load_data.js +15 -5
- package/src/runtime/server/page/render.js +27 -24
- package/src/runtime/server/page/respond_with_error.js +1 -3
- package/src/runtime/server/respond.js +0 -2
- package/src/types/ambient-private.d.ts +1 -1
- package/src/types/ambient.d.ts +1 -1
- package/src/types/internal.d.ts +2 -7
- package/src/types/private.d.ts +3 -12
- package/src/version.js +1 -1
- package/types/index.d.ts +92 -57
- package/types/index.d.ts.map +2 -1
- package/src/runtime/types.d.ts +0 -8
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
export const SNAPSHOT_KEY = 'sveltekit:snapshot';
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
script
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
105
|
+
const ttl = script.getAttribute('data-ttl');
|
|
106
|
+
if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
|
|
106
107
|
|
|
107
|
-
|
|
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 (
|
|
173
|
-
values.push(
|
|
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
|
-
|
|
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) =>
|
|
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 = '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
35
|
-
|
|
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(
|
|
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
|
|
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
|
|
123
|
-
error:
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 '../
|
|
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 {
|
|
4
|
-
import type { RenderNode } from '../types.js';
|
|
3
|
+
import type { Props, RenderNode } from '../props.svelte.js';
|
|
5
4
|
|
|
6
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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);
|