@sveltejs/kit 2.60.1 → 2.61.1
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 +8 -9
- package/src/core/postbuild/analyse.js +1 -3
- package/src/core/sync/create_manifest_data/conflict.js +72 -0
- package/src/core/sync/create_manifest_data/index.js +1 -65
- package/src/core/sync/write_non_ambient.js +2 -2
- package/src/core/sync/write_types/index.js +1 -1
- package/src/exports/public.d.ts +23 -30
- package/src/exports/vite/build/build_server.js +36 -13
- package/src/exports/vite/dev/index.js +4 -2
- package/src/exports/vite/index.js +18 -16
- package/src/runtime/app/server/index.js +1 -2
- package/src/runtime/app/server/remote/form.js +10 -0
- package/src/runtime/app/server/remote/query.js +100 -36
- package/src/runtime/client/client.js +13 -8
- package/src/runtime/client/remote-functions/cache.svelte.js +157 -0
- package/src/runtime/client/remote-functions/form.svelte.js +235 -196
- package/src/runtime/client/remote-functions/index.js +2 -2
- package/src/runtime/client/remote-functions/prerender.svelte.js +1 -2
- package/src/runtime/client/remote-functions/query/cache.js +4 -0
- package/src/runtime/client/remote-functions/query/index.js +48 -0
- package/src/runtime/client/remote-functions/query/instance.svelte.js +249 -0
- package/src/runtime/client/remote-functions/query/proxy.js +156 -0
- package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query-live/cache.js +4 -0
- package/src/runtime/client/remote-functions/query-live/index.js +31 -0
- package/src/runtime/client/remote-functions/{query-live.svelte.js → query-live/instance.svelte.js} +61 -310
- package/src/runtime/client/remote-functions/query-live/iterator.js +91 -0
- package/src/runtime/client/remote-functions/query-live/proxy.js +144 -0
- package/src/runtime/client/remote-functions/shared.svelte.js +53 -6
- package/src/runtime/client/utils.js +1 -1
- package/src/runtime/form-utils.js +7 -16
- package/src/runtime/server/index.js +2 -3
- package/src/runtime/server/page/actions.js +2 -9
- package/src/runtime/server/page/csp.js +3 -4
- package/src/runtime/server/page/render.js +13 -14
- package/src/runtime/server/respond.js +60 -36
- package/src/runtime/server/utils.js +23 -3
- package/src/types/global-private.d.ts +5 -0
- package/src/types/internal.d.ts +29 -6
- package/src/utils/routing.js +3 -1
- package/src/utils/shared-iterator.js +213 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +27 -31
- package/types/index.d.ts.map +1 -1
- package/src/runtime/client/remote-functions/query.svelte.js +0 -512
|
@@ -6,7 +6,6 @@ import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
|
6
6
|
import { untrack } from 'svelte';
|
|
7
7
|
import { create_remote_key, split_remote_key } from '../../shared.js';
|
|
8
8
|
import { navigating, page } from '../state.svelte.js';
|
|
9
|
-
import { noop } from '../../../utils/functions.js';
|
|
10
9
|
|
|
11
10
|
/** Indicates a query function, as opposed to a query instance */
|
|
12
11
|
export const QUERY_FUNCTION_ID = Symbol('sveltekit.query_function_id');
|
|
@@ -16,17 +15,65 @@ export const QUERY_OVERRIDE_KEY = Symbol('sveltekit.query_override_key');
|
|
|
16
15
|
export const QUERY_RESOURCE_KEY = Symbol('sveltekit.query_resource_key');
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
|
-
*
|
|
18
|
+
* If we're inside a reactive context, pin a cache entry for as long as the
|
|
19
|
+
* surrounding effect is alive. Without this, a transiently-referenced
|
|
20
|
+
* `QueryProxy`/`LiveQueryProxy` (e.g. one produced by `{await fn()}` in a
|
|
21
|
+
* template or by `$derived(await fn())`) would be eligible for GC as soon as
|
|
22
|
+
* the awaited value has been read, after which the FinalizationRegistry
|
|
23
|
+
* would evict the cache entry — even though the consuming effect is still
|
|
24
|
+
* alive and may rely on the entry being refreshed (e.g. via `refreshAll()`
|
|
25
|
+
* or a server-initiated single-flight refresh).
|
|
26
|
+
*
|
|
27
|
+
* @template TResource
|
|
28
|
+
* @param {Map<string, Map<string, { resource: TResource }>>} cache_map
|
|
29
|
+
* @param {{ manual_ref: (entry: any, id: string, payload: string) => () => void }} cache
|
|
30
|
+
* @param {string} id
|
|
31
|
+
* @param {string} payload
|
|
20
32
|
*/
|
|
21
|
-
export function
|
|
33
|
+
export function pin_in_effect(cache_map, cache, id, payload) {
|
|
22
34
|
try {
|
|
23
|
-
$effect.pre(
|
|
24
|
-
|
|
35
|
+
$effect.pre(() => {
|
|
36
|
+
const entry = cache_map.get(id)?.get(payload);
|
|
37
|
+
if (!entry) return;
|
|
38
|
+
return cache.manual_ref(entry, id, payload);
|
|
39
|
+
});
|
|
25
40
|
} catch {
|
|
26
|
-
|
|
41
|
+
// not in an effect context — nothing to pin
|
|
27
42
|
}
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Wrap a proxy's `then`/`catch`/`finally` function so that the underlying
|
|
47
|
+
* cache entry stays pinned for the lifetime of the awaited promise. Without
|
|
48
|
+
* this, a proxy awaited outside any effect (e.g. in an event handler) could
|
|
49
|
+
* be GC'd between the `.then` getter returning the thenable and the
|
|
50
|
+
* underlying promise settling, causing the FinalizationRegistry to evict the
|
|
51
|
+
* cache entry mid-flight and the awaited value to resolve to Svelte's
|
|
52
|
+
* `UNINITIALIZED` sentinel from a torn-down `$derived`.
|
|
53
|
+
*
|
|
54
|
+
* @template TResource
|
|
55
|
+
* @template {(...args: any[]) => Promise<any>} TThen
|
|
56
|
+
* @param {Map<string, Map<string, { resource: TResource }>>} cache_map
|
|
57
|
+
* @param {{ manual_ref: (entry: any, id: string, payload: string) => () => void }} cache
|
|
58
|
+
* @param {string} id
|
|
59
|
+
* @param {string} payload
|
|
60
|
+
* @param {TThen} then
|
|
61
|
+
* @returns {TThen}
|
|
62
|
+
*/
|
|
63
|
+
export function pin_while_resolving(cache_map, cache, id, payload, then) {
|
|
64
|
+
return /** @type {TThen} */ (
|
|
65
|
+
(...a) => {
|
|
66
|
+
const entry = cache_map.get(id)?.get(payload);
|
|
67
|
+
const release = entry ? cache.manual_ref(entry, id, payload) : undefined;
|
|
68
|
+
const promise = then(...a);
|
|
69
|
+
if (release) {
|
|
70
|
+
promise.then(release, release);
|
|
71
|
+
}
|
|
72
|
+
return promise;
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
30
77
|
/**
|
|
31
78
|
* @returns {{ 'x-sveltekit-pathname': string, 'x-sveltekit-search': string }}
|
|
32
79
|
*/
|
|
@@ -249,7 +249,7 @@ export const updated_listener = {
|
|
|
249
249
|
export function create_updated_store() {
|
|
250
250
|
const { set, subscribe } = writable(false);
|
|
251
251
|
|
|
252
|
-
if (
|
|
252
|
+
if (__SVELTEKIT_DEV__ || !BROWSER) {
|
|
253
253
|
return {
|
|
254
254
|
subscribe,
|
|
255
255
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -145,10 +145,6 @@ export async function deserialize_binary_form(request) {
|
|
|
145
145
|
if (!request.body) {
|
|
146
146
|
throw deserialize_error('no body');
|
|
147
147
|
}
|
|
148
|
-
const content_length = parseInt(request.headers.get('content-length') ?? '');
|
|
149
|
-
if (Number.isNaN(content_length)) {
|
|
150
|
-
throw deserialize_error('invalid Content-Length header');
|
|
151
|
-
}
|
|
152
148
|
|
|
153
149
|
const reader = request.body.getReader();
|
|
154
150
|
|
|
@@ -228,16 +224,11 @@ export async function deserialize_binary_form(request) {
|
|
|
228
224
|
}
|
|
229
225
|
const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
|
230
226
|
const data_length = header_view.getUint32(1, true);
|
|
231
|
-
|
|
232
|
-
if (HEADER_BYTES + data_length > content_length) {
|
|
233
|
-
throw deserialize_error('data overflow');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
227
|
const file_offsets_length = header_view.getUint16(5, true);
|
|
237
228
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
229
|
+
// Validation uses embedded binary header fields (data_length, file_offsets_length)
|
|
230
|
+
// rather than Content-Length, which proxies/middleboxes may strip or corrupt.
|
|
231
|
+
// See: https://github.com/sveltejs/kit/issues/15299
|
|
241
232
|
|
|
242
233
|
// Read the form data
|
|
243
234
|
const data_buffer = await get_buffer(HEADER_BYTES, data_length);
|
|
@@ -289,9 +280,6 @@ export async function deserialize_binary_form(request) {
|
|
|
289
280
|
file_offsets[index] = undefined;
|
|
290
281
|
|
|
291
282
|
offset += files_start_offset;
|
|
292
|
-
if (offset + size > content_length) {
|
|
293
|
-
throw deserialize_error('file data overflow');
|
|
294
|
-
}
|
|
295
283
|
|
|
296
284
|
file_spans.push({ offset, size });
|
|
297
285
|
|
|
@@ -716,8 +704,11 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
|
|
|
716
704
|
}
|
|
717
705
|
}
|
|
718
706
|
|
|
707
|
+
const value =
|
|
708
|
+
typeof input_value === 'boolean' ? (input_value ? 'on' : 'off') : input_value;
|
|
709
|
+
|
|
719
710
|
return Object.defineProperties(base_props, {
|
|
720
|
-
value: { value
|
|
711
|
+
value: { value, enumerable: true }
|
|
721
712
|
});
|
|
722
713
|
}
|
|
723
714
|
|
|
@@ -5,7 +5,6 @@ import { IN_WEBCONTAINER } from './constants.js';
|
|
|
5
5
|
import { respond } from './respond.js';
|
|
6
6
|
import { set_private_env, set_public_env } from '../shared-server.js';
|
|
7
7
|
import { options, get_hooks } from '__SERVER__/internal.js';
|
|
8
|
-
import { DEV } from 'esm-env';
|
|
9
8
|
import { filter_env } from '../../utils/env.js';
|
|
10
9
|
import { format_server_error } from './utils.js';
|
|
11
10
|
import { set_read_implementation, set_manifest } from '__sveltekit/server';
|
|
@@ -102,7 +101,7 @@ export class Server {
|
|
|
102
101
|
set_read_implementation(wrapped_read);
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
// During
|
|
104
|
+
// During dev and for some adapters this function might be called in quick succession,
|
|
106
105
|
// so we need to make sure we're not invoking this logic (most notably the init hook) multiple times
|
|
107
106
|
await (init_promise ??= (async () => {
|
|
108
107
|
try {
|
|
@@ -141,7 +140,7 @@ export class Server {
|
|
|
141
140
|
await module.init();
|
|
142
141
|
}
|
|
143
142
|
} catch (e) {
|
|
144
|
-
if (
|
|
143
|
+
if (__SVELTEKIT_DEV__) {
|
|
145
144
|
this.#options.hooks = {
|
|
146
145
|
handle: () => {
|
|
147
146
|
throw e;
|
|
@@ -7,7 +7,7 @@ import { HttpError, Redirect, ActionFailure, SvelteKitError } from '@sveltejs/ki
|
|
|
7
7
|
import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
|
|
8
8
|
import { get_status, normalize_error } from '../../../utils/error.js';
|
|
9
9
|
import { is_form_content_type, negotiate } from '../../../utils/http.js';
|
|
10
|
-
import { handle_error_and_jsonify } from '../utils.js';
|
|
10
|
+
import { create_replacer, handle_error_and_jsonify } from '../utils.js';
|
|
11
11
|
import { record_span } from '../../telemetry/record_span.js';
|
|
12
12
|
|
|
13
13
|
/** @param {RequestEvent} event */
|
|
@@ -301,14 +301,7 @@ function validate_action_return(data) {
|
|
|
301
301
|
* @param {ServerHooks['transport']} transport
|
|
302
302
|
*/
|
|
303
303
|
export function uneval_action_response(data, route_id, transport) {
|
|
304
|
-
const replacer = (
|
|
305
|
-
for (const key in transport) {
|
|
306
|
-
const encoded = transport[key].encode(thing);
|
|
307
|
-
if (encoded) {
|
|
308
|
-
return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
};
|
|
304
|
+
const replacer = create_replacer(transport);
|
|
312
305
|
|
|
313
306
|
return try_serialize(data, (value) => devalue.uneval(value, replacer), route_id);
|
|
314
307
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DEV } from 'esm-env';
|
|
2
1
|
import { escape_html } from '../../../utils/escape.js';
|
|
3
2
|
import { sha256 } from './crypto.js';
|
|
4
3
|
|
|
@@ -87,7 +86,7 @@ class BaseProvider {
|
|
|
87
86
|
*/
|
|
88
87
|
constructor(use_hashes, directives, nonce) {
|
|
89
88
|
this.#use_hashes = use_hashes;
|
|
90
|
-
this.#directives =
|
|
89
|
+
this.#directives = __SVELTEKIT_DEV__ ? { ...directives } : directives; // clone in dev so we can safely mutate
|
|
91
90
|
|
|
92
91
|
const d = this.#directives;
|
|
93
92
|
|
|
@@ -103,7 +102,7 @@ class BaseProvider {
|
|
|
103
102
|
const style_src_attr = d['style-src-attr'];
|
|
104
103
|
const style_src_elem = d['style-src-elem'];
|
|
105
104
|
|
|
106
|
-
if (
|
|
105
|
+
if (__SVELTEKIT_DEV__) {
|
|
107
106
|
// remove strict-dynamic in dev...
|
|
108
107
|
// TODO reinstate this if we can figure out how to make strict-dynamic work
|
|
109
108
|
// if (d['default-src']) {
|
|
@@ -164,7 +163,7 @@ class BaseProvider {
|
|
|
164
163
|
|
|
165
164
|
this.#script_needs_csp = this.#script_src_needs_csp || this.#script_src_elem_needs_csp;
|
|
166
165
|
this.#style_needs_csp =
|
|
167
|
-
!
|
|
166
|
+
!__SVELTEKIT_DEV__ &&
|
|
168
167
|
(this.#style_src_needs_csp ||
|
|
169
168
|
this.#style_src_attr_needs_csp ||
|
|
170
169
|
this.#style_src_elem_needs_csp);
|
|
@@ -15,7 +15,12 @@ import { create_server_routing_response, generate_route_object } from './server_
|
|
|
15
15
|
import { add_resolution_suffix } from '../../pathname.js';
|
|
16
16
|
import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
|
|
17
17
|
import { text_encoder } from '../../utils.js';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
count_non_ssi_comments,
|
|
20
|
+
create_replacer,
|
|
21
|
+
get_global_name,
|
|
22
|
+
handle_error_and_jsonify
|
|
23
|
+
} from '../utils.js';
|
|
19
24
|
import { create_remote_key } from '../../shared.js';
|
|
20
25
|
import { get_status } from '../../../utils/error.js';
|
|
21
26
|
|
|
@@ -302,13 +307,15 @@ export async function render_response({
|
|
|
302
307
|
return `${assets}/${path}`;
|
|
303
308
|
};
|
|
304
309
|
|
|
305
|
-
// inline styles can come from `bundleStrategy: 'inline'` or `inlineStyleThreshold`
|
|
306
310
|
const style = client.inline
|
|
307
311
|
? client.inline?.style
|
|
308
312
|
: Array.from(inline_styles.values()).join('\n');
|
|
309
313
|
|
|
310
314
|
if (style) {
|
|
311
|
-
|
|
315
|
+
// We always inline all styles to avoid FOUC during development.
|
|
316
|
+
// Once that's accomplished, we find and remove the style node using the
|
|
317
|
+
// `data-sveltekit` attribute once CSR kicks in
|
|
318
|
+
const attributes = __SVELTEKIT_DEV__ ? ['data-sveltekit'] : [];
|
|
312
319
|
if (csp.style_needs_nonce) attributes.push(`nonce="${csp.nonce}"`);
|
|
313
320
|
csp.add_style(style);
|
|
314
321
|
head.add_style(style, attributes);
|
|
@@ -557,15 +564,7 @@ export async function render_response({
|
|
|
557
564
|
}
|
|
558
565
|
}
|
|
559
566
|
|
|
560
|
-
|
|
561
|
-
const replacer = (/** @type {any} */ thing) => {
|
|
562
|
-
for (const key in options.hooks.transport) {
|
|
563
|
-
const encoded = options.hooks.transport[key].encode(thing);
|
|
564
|
-
if (encoded) {
|
|
565
|
-
return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
};
|
|
567
|
+
const replacer = create_replacer(options.hooks.transport);
|
|
569
568
|
|
|
570
569
|
if (Object.keys(query).length > 0) {
|
|
571
570
|
serialized_query_data = `${global}.query = ${devalue.uneval(query, replacer)};\n\n\t\t\t\t\t\t`;
|
|
@@ -605,10 +604,10 @@ export async function render_response({
|
|
|
605
604
|
}
|
|
606
605
|
|
|
607
606
|
if (options.service_worker) {
|
|
608
|
-
let opts =
|
|
607
|
+
let opts = __SVELTEKIT_DEV__ ? ", { type: 'module' }" : '';
|
|
609
608
|
if (options.service_worker_options != null) {
|
|
610
609
|
const service_worker_options = { ...options.service_worker_options };
|
|
611
|
-
if (
|
|
610
|
+
if (__SVELTEKIT_DEV__) {
|
|
612
611
|
service_worker_options.type = 'module';
|
|
613
612
|
}
|
|
614
613
|
opts = `, ${s(service_worker_options)}`;
|
|
@@ -40,8 +40,6 @@ import { get_remote_id, handle_remote_call } from './remote.js';
|
|
|
40
40
|
import { record_span } from '../telemetry/record_span.js';
|
|
41
41
|
import { otel } from '../telemetry/otel.js';
|
|
42
42
|
|
|
43
|
-
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
44
|
-
|
|
45
43
|
/** @type {import('types').RequiredResolveOptions['transformPageChunk']} */
|
|
46
44
|
const default_transform = ({ html }) => html;
|
|
47
45
|
|
|
@@ -153,7 +151,8 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
153
151
|
refreshes: null,
|
|
154
152
|
requested: null,
|
|
155
153
|
reconnects: null,
|
|
156
|
-
batches: null
|
|
154
|
+
batches: null,
|
|
155
|
+
live_iterators: null
|
|
157
156
|
},
|
|
158
157
|
is_in_remote_function: false,
|
|
159
158
|
is_in_render: false,
|
|
@@ -228,6 +227,7 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
228
227
|
});
|
|
229
228
|
}
|
|
230
229
|
|
|
230
|
+
/** @type {string | null} */
|
|
231
231
|
let resolved_path = url.pathname;
|
|
232
232
|
|
|
233
233
|
if (!remote_id) {
|
|
@@ -249,10 +249,24 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
/** @type {import('types').RequiredResolveOptions} */
|
|
253
|
+
let resolve_opts = {
|
|
254
|
+
transformPageChunk: default_transform,
|
|
255
|
+
filterSerializedResponseHeaders: default_filter,
|
|
256
|
+
preload: default_preload
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/** @type {import('types').TrailingSlash} */
|
|
260
|
+
let trailing_slash = 'never';
|
|
261
|
+
|
|
262
|
+
/** @type {PageNodes | undefined} */
|
|
263
|
+
let page_nodes;
|
|
264
|
+
|
|
252
265
|
try {
|
|
253
266
|
resolved_path = decode_pathname(resolved_path);
|
|
254
267
|
} catch {
|
|
255
|
-
|
|
268
|
+
resolved_path = null;
|
|
269
|
+
return await handle();
|
|
256
270
|
}
|
|
257
271
|
|
|
258
272
|
// try to serve the rerouted prerendered resource if it exists
|
|
@@ -326,19 +340,8 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
326
340
|
}
|
|
327
341
|
}
|
|
328
342
|
|
|
329
|
-
/** @type {import('types').RequiredResolveOptions} */
|
|
330
|
-
let resolve_opts = {
|
|
331
|
-
transformPageChunk: default_transform,
|
|
332
|
-
filterSerializedResponseHeaders: default_filter,
|
|
333
|
-
preload: default_preload
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
/** @type {import('types').TrailingSlash} */
|
|
337
|
-
let trailing_slash = 'never';
|
|
338
|
-
|
|
339
343
|
try {
|
|
340
|
-
|
|
341
|
-
const page_nodes = route?.page
|
|
344
|
+
page_nodes = route?.page
|
|
342
345
|
? new PageNodes(await load_page_nodes(route.page, manifest))
|
|
343
346
|
: undefined;
|
|
344
347
|
|
|
@@ -393,16 +396,36 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
393
396
|
prerender = page_nodes.prerender();
|
|
394
397
|
}
|
|
395
398
|
|
|
396
|
-
if (state.before_handle) {
|
|
397
|
-
state.before_handle(event, config, prerender);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
399
|
if (state.emulator?.platform) {
|
|
401
400
|
event.platform = await state.emulator.platform({ config, prerender });
|
|
402
401
|
}
|
|
402
|
+
|
|
403
|
+
if (state.before_handle) {
|
|
404
|
+
return await state.before_handle(event, config, prerender, handle);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return await handle();
|
|
410
|
+
} catch (e) {
|
|
411
|
+
if (e instanceof Redirect) {
|
|
412
|
+
try {
|
|
413
|
+
const response =
|
|
414
|
+
is_data_request || remote_id
|
|
415
|
+
? redirect_json_response(e)
|
|
416
|
+
: route?.page && is_action_json_request(event)
|
|
417
|
+
? action_json_redirect(e)
|
|
418
|
+
: redirect_response(e.status, e.location);
|
|
419
|
+
add_cookies_to_headers(response.headers, new_cookies.values());
|
|
420
|
+
return response;
|
|
421
|
+
} catch (err) {
|
|
422
|
+
return await handle_fatal_error(event, event_state, options, err);
|
|
403
423
|
}
|
|
404
424
|
}
|
|
425
|
+
return await handle_fatal_error(event, event_state, options, e);
|
|
426
|
+
}
|
|
405
427
|
|
|
428
|
+
async function handle() {
|
|
406
429
|
set_trailing_slash(trailing_slash);
|
|
407
430
|
|
|
408
431
|
if (state.prerendering && !state.prerendering.fallback && !state.prerendering.inside_reroute) {
|
|
@@ -518,22 +541,6 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
518
541
|
}
|
|
519
542
|
|
|
520
543
|
return response;
|
|
521
|
-
} catch (e) {
|
|
522
|
-
if (e instanceof Redirect) {
|
|
523
|
-
try {
|
|
524
|
-
const response =
|
|
525
|
-
is_data_request || remote_id
|
|
526
|
-
? redirect_json_response(e)
|
|
527
|
-
: route?.page && is_action_json_request(event)
|
|
528
|
-
? action_json_redirect(e)
|
|
529
|
-
: redirect_response(e.status, e.location);
|
|
530
|
-
add_cookies_to_headers(response.headers, new_cookies.values());
|
|
531
|
-
return response;
|
|
532
|
-
} catch (err) {
|
|
533
|
-
return await handle_fatal_error(event, event_state, options, err);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
return await handle_fatal_error(event, event_state, options, e);
|
|
537
544
|
}
|
|
538
545
|
|
|
539
546
|
/**
|
|
@@ -551,6 +558,23 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
551
558
|
};
|
|
552
559
|
}
|
|
553
560
|
|
|
561
|
+
if (resolved_path === null) {
|
|
562
|
+
return await respond_with_error({
|
|
563
|
+
event,
|
|
564
|
+
event_state,
|
|
565
|
+
options,
|
|
566
|
+
manifest,
|
|
567
|
+
state,
|
|
568
|
+
status: 400,
|
|
569
|
+
error: new SvelteKitError(
|
|
570
|
+
400,
|
|
571
|
+
'Malformed URI',
|
|
572
|
+
`Failed to decode URI: ${event.url.pathname}`
|
|
573
|
+
),
|
|
574
|
+
resolve_opts
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
554
578
|
if (options.hash_routing || state.prerendering?.fallback) {
|
|
555
579
|
return await render_response({
|
|
556
580
|
event,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @import { ServerHooks } from 'types' */
|
|
2
|
+
import * as devalue from 'devalue';
|
|
1
3
|
import { DEV } from 'esm-env';
|
|
2
4
|
import { json, text } from '@sveltejs/kit';
|
|
3
5
|
import { HttpError } from '@sveltejs/kit/internal';
|
|
@@ -40,7 +42,7 @@ export function allowed_methods(mod) {
|
|
|
40
42
|
* @param {import('types').SSROptions} options
|
|
41
43
|
*/
|
|
42
44
|
export function get_global_name(options) {
|
|
43
|
-
return
|
|
45
|
+
return __SVELTEKIT_DEV__ ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
@@ -53,7 +55,7 @@ export function get_global_name(options) {
|
|
|
53
55
|
export function static_error_page(options, status, message) {
|
|
54
56
|
let page = options.templates.error({ status, message: escape_html(message) });
|
|
55
57
|
|
|
56
|
-
if (
|
|
58
|
+
if (__SVELTEKIT_DEV__) {
|
|
57
59
|
// inject Vite HMR client, for easier debugging
|
|
58
60
|
page = page.replace('</head>', '<script type="module" src="/@vite/client"></script></head>');
|
|
59
61
|
}
|
|
@@ -103,7 +105,7 @@ export async function handle_error_and_jsonify(event, state, options, error) {
|
|
|
103
105
|
return { message: 'Unknown Error', ...error.body };
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
if (
|
|
108
|
+
if (__SVELTEKIT_DEV__ && typeof error == 'object') {
|
|
107
109
|
fix_stack_trace(error);
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -261,3 +263,21 @@ export function get_node_type(node_id) {
|
|
|
261
263
|
export function count_non_ssi_comments(str) {
|
|
262
264
|
return (str.match(/<!--(?!#)/g) ?? []).length;
|
|
263
265
|
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Creates a serialiser for non-arbitrary POJOs using the app's transport hook
|
|
269
|
+
* @param {ServerHooks['transport']} transport
|
|
270
|
+
* @returns {(thing: unknown) => string | undefined}
|
|
271
|
+
*/
|
|
272
|
+
export function create_replacer(transport) {
|
|
273
|
+
/** @param {unknown} thing */
|
|
274
|
+
const replacer = (thing) => {
|
|
275
|
+
for (const key in transport) {
|
|
276
|
+
const encoded = transport[key].encode(thing);
|
|
277
|
+
if (encoded) {
|
|
278
|
+
return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
return replacer;
|
|
283
|
+
}
|
|
@@ -3,6 +3,11 @@ declare global {
|
|
|
3
3
|
const __SVELTEKIT_APP_DIR__: string;
|
|
4
4
|
const __SVELTEKIT_APP_VERSION_FILE__: string;
|
|
5
5
|
const __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: number;
|
|
6
|
+
/**
|
|
7
|
+
* True if the user ran `vite dev`. This is different from `esm-env` because
|
|
8
|
+
* it is influenced by `NODE_ENV` which can still be true during `vite preview`
|
|
9
|
+
*/
|
|
10
|
+
const __SVELTEKIT_DEV__: boolean;
|
|
6
11
|
const __SVELTEKIT_EMBEDDED__: boolean;
|
|
7
12
|
const __SVELTEKIT_PATHS_ASSETS__: string;
|
|
8
13
|
const __SVELTEKIT_PATHS_BASE__: string;
|
package/src/types/internal.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
} from './private.js';
|
|
36
36
|
import { Span } from '@opentelemetry/api';
|
|
37
37
|
import type { PageOptions } from '../exports/vite/static_analysis/index.js';
|
|
38
|
+
import { SharedIterator } from '../utils/shared-iterator.js';
|
|
38
39
|
|
|
39
40
|
export interface ServerModule {
|
|
40
41
|
Server: typeof InternalServer;
|
|
@@ -179,9 +180,15 @@ export class InternalServer extends Server {
|
|
|
179
180
|
request: Request,
|
|
180
181
|
options: RequestOptions & {
|
|
181
182
|
prerendering?: PrerenderOptions;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
/** @internal for saving dependencies during prerendering and generating fallback pages */
|
|
184
|
+
read: (file: string) => Buffer<ArrayBuffer>;
|
|
185
|
+
/** @internal used during development to check feature availability depending on the current route */
|
|
186
|
+
before_handle?: (
|
|
187
|
+
event: RequestEvent,
|
|
188
|
+
config: any,
|
|
189
|
+
prerender: PrerenderOption,
|
|
190
|
+
handle: () => Promise<Response>
|
|
191
|
+
) => Promise<Response>;
|
|
185
192
|
emulator?: Emulator;
|
|
186
193
|
}
|
|
187
194
|
): Promise<Response>;
|
|
@@ -469,7 +476,10 @@ export interface SSRNode {
|
|
|
469
476
|
universal_id?: string;
|
|
470
477
|
server_id?: string;
|
|
471
478
|
|
|
472
|
-
/**
|
|
479
|
+
/**
|
|
480
|
+
* During development, all styles are inlined for the page to avoid FOUC.
|
|
481
|
+
* But in production, this stores styles that are below the inline threshold
|
|
482
|
+
*/
|
|
473
483
|
inline_styles?(): MaybePromise<
|
|
474
484
|
Record<string, string | ((assets: string, base: string) => string)>
|
|
475
485
|
>;
|
|
@@ -565,12 +575,18 @@ export interface SSRState {
|
|
|
565
575
|
* prerender option is inherited by the endpoint, unless overridden.
|
|
566
576
|
*/
|
|
567
577
|
prerender_default?: PrerenderOption;
|
|
568
|
-
|
|
578
|
+
/** @internal reads from the filesystem when user code tries to fetch a static asset */
|
|
579
|
+
read?: (file: string) => Buffer<ArrayBuffer>;
|
|
569
580
|
/**
|
|
570
581
|
* Used to set up `__SVELTEKIT_TRACK__` which checks if a used feature is supported.
|
|
571
582
|
* E.g. if `read` from `$app/server` is used, it checks whether the route's config is compatible.
|
|
572
583
|
*/
|
|
573
|
-
before_handle?: (
|
|
584
|
+
before_handle?: (
|
|
585
|
+
event: RequestEvent,
|
|
586
|
+
config: any,
|
|
587
|
+
prerender: PrerenderOption,
|
|
588
|
+
handle: () => Promise<Response>
|
|
589
|
+
) => Promise<Response>;
|
|
574
590
|
emulator?: Emulator;
|
|
575
591
|
}
|
|
576
592
|
|
|
@@ -718,6 +734,13 @@ export interface RequestState {
|
|
|
718
734
|
}
|
|
719
735
|
>
|
|
720
736
|
>;
|
|
737
|
+
/**
|
|
738
|
+
* A per-request cache of shared live-query iterators, keyed by remote id
|
|
739
|
+
* and stringified argument payload. Used so that multiple `for await`
|
|
740
|
+
* consumers of the same `query.live` call within one request multiplex a
|
|
741
|
+
* single underlying user generator.
|
|
742
|
+
*/
|
|
743
|
+
live_iterators: null | Map<string, SharedIterator<any>>;
|
|
721
744
|
};
|
|
722
745
|
readonly is_in_remote_function: boolean;
|
|
723
746
|
readonly is_in_render: boolean;
|
package/src/utils/routing.js
CHANGED
|
@@ -3,6 +3,8 @@ import { decode_params } from './url.js';
|
|
|
3
3
|
|
|
4
4
|
const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
|
|
5
5
|
|
|
6
|
+
const root_group_pattern = /^\/\((?:[^)]+)\)$/;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Creates the regex pattern, extracts parameter names, and generates types for a route
|
|
8
10
|
* @param {string} id
|
|
@@ -12,7 +14,7 @@ export function parse_route_id(id) {
|
|
|
12
14
|
const params = [];
|
|
13
15
|
|
|
14
16
|
const pattern =
|
|
15
|
-
id === '/'
|
|
17
|
+
id === '/' || root_group_pattern.test(id)
|
|
16
18
|
? /^\/$/
|
|
17
19
|
: new RegExp(
|
|
18
20
|
`^${get_route_segments(id)
|