@sveltejs/kit 2.55.0 → 2.56.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 +3 -4
- package/src/core/postbuild/analyse.js +3 -3
- package/src/core/postbuild/prerender.js +9 -6
- package/src/core/sync/write_tsconfig.js +3 -1
- package/src/exports/internal/remote-functions.js +2 -2
- package/src/exports/public.d.ts +39 -13
- package/src/exports/vite/build/build_server.js +24 -4
- package/src/exports/vite/build/build_service_worker.js +16 -6
- package/src/exports/vite/build/utils.js +18 -3
- package/src/exports/vite/index.js +336 -327
- package/src/runtime/app/paths/server.js +1 -1
- package/src/runtime/app/server/index.js +1 -1
- package/src/runtime/app/server/remote/command.js +12 -7
- package/src/runtime/app/server/remote/form.js +14 -14
- package/src/runtime/app/server/remote/index.js +1 -0
- package/src/runtime/app/server/remote/prerender.js +8 -7
- package/src/runtime/app/server/remote/query.js +141 -66
- package/src/runtime/app/server/remote/requested.js +172 -0
- package/src/runtime/app/server/remote/shared.js +32 -10
- package/src/runtime/client/client.js +45 -20
- package/src/runtime/client/remote-functions/command.svelte.js +39 -16
- package/src/runtime/client/remote-functions/form.svelte.js +41 -24
- package/src/runtime/client/remote-functions/prerender.svelte.js +105 -76
- package/src/runtime/client/remote-functions/query.svelte.js +408 -138
- package/src/runtime/client/remote-functions/shared.svelte.js +95 -94
- package/src/runtime/components/svelte-5/error.svelte +2 -0
- package/src/runtime/form-utils.js +3 -7
- package/src/runtime/server/endpoint.js +0 -1
- package/src/runtime/server/page/actions.js +2 -1
- package/src/runtime/server/page/load_data.js +3 -1
- package/src/runtime/server/page/render.js +38 -15
- package/src/runtime/server/remote.js +65 -50
- package/src/runtime/server/respond.js +17 -3
- package/src/runtime/server/utils.js +0 -12
- package/src/runtime/shared.js +233 -5
- package/src/types/global-private.d.ts +4 -4
- package/src/types/internal.d.ts +80 -44
- package/src/utils/css.js +0 -3
- package/src/utils/escape.js +15 -3
- package/src/version.js +1 -1
- package/types/index.d.ts +68 -14
- package/types/index.d.ts.map +6 -1
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
/** @import {
|
|
2
|
-
/** @import {
|
|
3
|
-
/** @import { Query } from './query.svelte.js' */
|
|
1
|
+
/** @import { RemoteFunctionResponse, RemoteRefreshMap } from 'types' */
|
|
2
|
+
/** @import { RemoteQueryUpdate } from '@sveltejs/kit' */
|
|
4
3
|
import * as devalue from 'devalue';
|
|
5
|
-
import { app, goto, query_map
|
|
4
|
+
import { app, goto, query_map } from '../client.js';
|
|
6
5
|
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
7
|
-
import {
|
|
8
|
-
import { create_remote_key,
|
|
9
|
-
import { page } from '../state.svelte.js';
|
|
6
|
+
import { untrack } from 'svelte';
|
|
7
|
+
import { create_remote_key, split_remote_key } from '../../shared.js';
|
|
8
|
+
import { navigating, page } from '../state.svelte.js';
|
|
9
|
+
|
|
10
|
+
/** Indicates a query function, as opposed to a query instance */
|
|
11
|
+
export const QUERY_FUNCTION_ID = Symbol('sveltekit.query_function_id');
|
|
12
|
+
/** Indicates a query override callback, used to release the override */
|
|
13
|
+
export const QUERY_OVERRIDE_KEY = Symbol('sveltekit.query_override_key');
|
|
14
|
+
/** Indicates a query instance */
|
|
15
|
+
export const QUERY_RESOURCE_KEY = Symbol('sveltekit.query_resource_key');
|
|
10
16
|
|
|
11
17
|
/**
|
|
12
18
|
* @returns {{ 'x-sveltekit-pathname': string, 'x-sveltekit-search': string }}
|
|
@@ -15,10 +21,14 @@ export function get_remote_request_headers() {
|
|
|
15
21
|
// This will be the correct value of the current or soon-current url,
|
|
16
22
|
// even in forks because it's state-based - therefore not using window.location.
|
|
17
23
|
// Use untrack(...) to Avoid accidental reactive dependency on pathname/search
|
|
18
|
-
return untrack(() =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
return untrack(() => {
|
|
25
|
+
const url = navigating.current?.to?.url ?? page.url;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
'x-sveltekit-pathname': url.pathname,
|
|
29
|
+
'x-sveltekit-search': url.search
|
|
30
|
+
};
|
|
31
|
+
});
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
/**
|
|
@@ -52,103 +62,94 @@ export async function remote_request(url, headers) {
|
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
65
|
+
* Given an array of updates, which could be query instances, query functions, or query override release functions,
|
|
66
|
+
* categorize them into overrides (which need to be released after the command completes), refreshes (which
|
|
67
|
+
* just need to be refreshed after the command completes), or both.
|
|
68
|
+
*
|
|
69
|
+
* @param {RemoteQueryUpdate[]} updates
|
|
70
|
+
* @returns {{ overrides: Array<() => void>, refreshes: Set<string> }}
|
|
58
71
|
*/
|
|
59
|
-
export function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
export function categorize_updates(updates) {
|
|
73
|
+
/** @type {Set<string>} */
|
|
74
|
+
const override_keys = new Set();
|
|
75
|
+
/** @type {Array<() => void>} */
|
|
76
|
+
const overrides = [];
|
|
77
|
+
/** @type {Set<string>} */
|
|
78
|
+
const refreshes = new Set();
|
|
79
|
+
|
|
80
|
+
for (const update of updates) {
|
|
81
|
+
if (typeof update === 'function') {
|
|
82
|
+
if (Object.hasOwn(update, QUERY_FUNCTION_ID)) {
|
|
83
|
+
// this is a query function (not instance), so we need to find all active instances
|
|
84
|
+
// of this functionand request that they be refreshed by the command handler
|
|
85
|
+
// @ts-expect-error
|
|
86
|
+
const id = /** @type {string} */ (update[QUERY_FUNCTION_ID]);
|
|
87
|
+
const entries = query_map.get(id);
|
|
88
|
+
|
|
89
|
+
if (entries) {
|
|
90
|
+
for (const payload of entries.keys()) {
|
|
91
|
+
refreshes.add(create_remote_key(id, payload));
|
|
79
92
|
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Object.hasOwn(update, QUERY_OVERRIDE_KEY)) {
|
|
99
|
+
// this is a query override release function, so we need to both request that the query instance
|
|
100
|
+
// be refreshed _and_ stash the release function so we can release the override after the command completes
|
|
101
|
+
// @ts-expect-error
|
|
102
|
+
const key = /** @type {string} */ (update[QUERY_OVERRIDE_KEY]);
|
|
103
|
+
refreshes.add(key);
|
|
104
|
+
|
|
105
|
+
if (override_keys.has(key)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
'Multiple overrides for the same query are not allowed in a single updates() invocation'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override_keys.add(key);
|
|
112
|
+
overrides.push(/** @type {() => void} */ (update));
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
cache_key,
|
|
96
|
-
(entry = {
|
|
97
|
-
count: tracking ? 1 : 0,
|
|
98
|
-
resource
|
|
99
|
-
})
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
resource
|
|
103
|
-
.then(() => {
|
|
104
|
-
void tick().then(() => {
|
|
105
|
-
if (
|
|
106
|
-
!(/** @type {NonNullable<typeof entry>} */ (entry).count) &&
|
|
107
|
-
entry === query_map.get(cache_key)
|
|
108
|
-
) {
|
|
109
|
-
// If no one is tracking this resource anymore, we can delete it from the cache
|
|
110
|
-
query_map.delete(cache_key);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
})
|
|
114
|
-
.catch(() => {
|
|
115
|
-
// error delete the resource from the cache
|
|
116
|
-
// TODO is that correct?
|
|
117
|
-
query_map.delete(cache_key);
|
|
118
|
-
});
|
|
117
|
+
if (
|
|
118
|
+
typeof update === 'object' &&
|
|
119
|
+
update !== null &&
|
|
120
|
+
Object.hasOwn(update, QUERY_RESOURCE_KEY)
|
|
121
|
+
) {
|
|
122
|
+
// this is a query instance, so we just need to request that it be refreshed
|
|
123
|
+
// @ts-expect-error
|
|
124
|
+
refreshes.add(/** @type {string} */ (update[QUERY_RESOURCE_KEY]));
|
|
125
|
+
continue;
|
|
119
126
|
}
|
|
120
127
|
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* @param {Array<Query<any> | RemoteQueryOverride>} updates
|
|
127
|
-
*/
|
|
128
|
-
export function release_overrides(updates) {
|
|
129
|
-
for (const update of updates) {
|
|
130
|
-
if ('release' in update) {
|
|
131
|
-
update.release();
|
|
132
|
-
}
|
|
128
|
+
throw new Error('updates() expects a query function, query resource, or query override');
|
|
133
129
|
}
|
|
130
|
+
|
|
131
|
+
return { overrides, refreshes };
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
/**
|
|
135
|
+
* Apply refresh data from the server to the relevant queries
|
|
136
|
+
*
|
|
137
137
|
* @param {string} stringified_refreshes
|
|
138
|
-
* @param {Array<Query<any> | RemoteQueryOverride>} updates
|
|
139
138
|
*/
|
|
140
|
-
export function
|
|
141
|
-
const refreshes = Object.entries(
|
|
139
|
+
export function apply_refreshes(stringified_refreshes) {
|
|
140
|
+
const refreshes = Object.entries(
|
|
141
|
+
/** @type {RemoteRefreshMap} */ (devalue.parse(stringified_refreshes, app.decoders))
|
|
142
|
+
);
|
|
142
143
|
|
|
143
|
-
// `refreshes` is a superset of `updates`
|
|
144
144
|
for (const [key, value] of refreshes) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
const parts = split_remote_key(key);
|
|
146
|
+
|
|
147
|
+
const entry = query_map.get(parts.id)?.get(parts.payload);
|
|
148
|
+
|
|
149
|
+
if (value.type === 'result') {
|
|
150
|
+
entry?.resource.set(value.data);
|
|
151
|
+
} else {
|
|
152
|
+
entry?.resource.fail(new HttpError(value.status ?? 500, value.error));
|
|
149
153
|
}
|
|
150
|
-
// Update the query with the new value
|
|
151
|
-
const entry = query_map.get(key);
|
|
152
|
-
entry?.resource.set(value);
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -84,10 +84,6 @@ export function serialize_binary_form(data, meta) {
|
|
|
84
84
|
/** @type {Array<[file: File, index: number]>} */
|
|
85
85
|
const files = [];
|
|
86
86
|
|
|
87
|
-
if (!meta.remote_refreshes?.length) {
|
|
88
|
-
delete meta.remote_refreshes;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
87
|
const encoded_header = devalue.stringify([data, meta], {
|
|
92
88
|
File: (file) => {
|
|
93
89
|
if (!(file instanceof File)) return;
|
|
@@ -324,7 +320,7 @@ export async function deserialize_binary_form(request) {
|
|
|
324
320
|
}
|
|
325
321
|
}
|
|
326
322
|
|
|
327
|
-
// Read the request body
|
|
323
|
+
// Read the request body asynchronously so it doesn't stall
|
|
328
324
|
void (async () => {
|
|
329
325
|
let has_more = true;
|
|
330
326
|
while (has_more) {
|
|
@@ -718,7 +714,7 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
|
|
|
718
714
|
value: {
|
|
719
715
|
enumerable: true,
|
|
720
716
|
get() {
|
|
721
|
-
return get_value();
|
|
717
|
+
return input_value !== undefined ? input_value : get_value();
|
|
722
718
|
}
|
|
723
719
|
}
|
|
724
720
|
});
|
|
@@ -804,7 +800,7 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
|
|
|
804
800
|
value: {
|
|
805
801
|
enumerable: true,
|
|
806
802
|
get() {
|
|
807
|
-
const value = get_value();
|
|
803
|
+
const value = input_value !== undefined ? input_value : get_value();
|
|
808
804
|
return value != null ? String(value) : '';
|
|
809
805
|
}
|
|
810
806
|
}
|
|
@@ -42,7 +42,6 @@ export async function render_endpoint(event, event_state, mod, state) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
try {
|
|
45
|
-
event_state.allows_commands = true;
|
|
46
45
|
const response = await with_request_store({ event, state: event_state }, () =>
|
|
47
46
|
handler(/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event))
|
|
48
47
|
);
|
|
@@ -266,10 +266,11 @@ async function call_action(event, event_state, actions) {
|
|
|
266
266
|
},
|
|
267
267
|
fn: async (current) => {
|
|
268
268
|
const traced_event = merge_tracing(event, current);
|
|
269
|
-
|
|
269
|
+
|
|
270
270
|
const result = await with_request_store({ event: traced_event, state: event_state }, () =>
|
|
271
271
|
action(traced_event)
|
|
272
272
|
);
|
|
273
|
+
|
|
273
274
|
if (result instanceof ActionFailure) {
|
|
274
275
|
current.setAttributes({
|
|
275
276
|
'sveltekit.form_action.result.type': 'failure',
|
|
@@ -233,7 +233,9 @@ export async function load_data({
|
|
|
233
233
|
},
|
|
234
234
|
fn: async (current) => {
|
|
235
235
|
const traced_event = merge_tracing(event, current);
|
|
236
|
-
|
|
236
|
+
const child_state = { ...event_state, is_in_universal_load: true };
|
|
237
|
+
|
|
238
|
+
return await with_request_store({ event: traced_event, state: child_state }, () =>
|
|
237
239
|
load.call(null, {
|
|
238
240
|
url: event.url,
|
|
239
241
|
params: event.params,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
2
|
import { readable, writable } from 'svelte/store';
|
|
3
3
|
import { DEV } from 'esm-env';
|
|
4
|
-
import { text } from '@sveltejs/kit';
|
|
4
|
+
import { isRedirect, text } from '@sveltejs/kit';
|
|
5
5
|
import * as paths from '$app/paths/internal/server';
|
|
6
6
|
import { hash } from '../../../utils/hash.js';
|
|
7
7
|
import { serialize_data } from './serialize_data.js';
|
|
@@ -189,6 +189,10 @@ export async function render_response({
|
|
|
189
189
|
csp: csp.script_needs_nonce ? { nonce: csp.nonce } : { hash: csp.script_needs_hash },
|
|
190
190
|
transformError: error_components
|
|
191
191
|
? /** @param {unknown} e */ async (e) => {
|
|
192
|
+
if (isRedirect(e)) {
|
|
193
|
+
throw e;
|
|
194
|
+
}
|
|
195
|
+
|
|
192
196
|
const transformed = await handle_error_and_jsonify(event, event_state, options, e);
|
|
193
197
|
props.page.error = props.error = error = transformed;
|
|
194
198
|
props.page.status = status = get_status(e);
|
|
@@ -218,8 +222,9 @@ export async function render_response({
|
|
|
218
222
|
};
|
|
219
223
|
}
|
|
220
224
|
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
const state = { ...event_state, is_in_render: true };
|
|
226
|
+
|
|
227
|
+
rendered = await with_request_store({ event, state }, async () => {
|
|
223
228
|
// use relative paths during rendering, so that the resulting HTML is as
|
|
224
229
|
// portable as possible, but reset afterwards
|
|
225
230
|
if (paths.relative) paths.override({ base, assets });
|
|
@@ -498,31 +503,41 @@ export async function render_response({
|
|
|
498
503
|
args.push(`{\n${indent}\t${hydrate.join(`,\n${indent}\t`)}\n${indent}}`);
|
|
499
504
|
}
|
|
500
505
|
|
|
501
|
-
const {
|
|
506
|
+
const { remote } = event_state;
|
|
502
507
|
|
|
503
|
-
let
|
|
508
|
+
let serialized_query_data = '';
|
|
509
|
+
let serialized_prerender_data = '';
|
|
504
510
|
|
|
505
|
-
if (
|
|
511
|
+
if (remote.data) {
|
|
506
512
|
/** @type {Record<string, any>} */
|
|
507
|
-
const
|
|
513
|
+
const query = {};
|
|
508
514
|
|
|
509
|
-
|
|
515
|
+
/** @type {Record<string, any>} */
|
|
516
|
+
const prerender = {};
|
|
517
|
+
|
|
518
|
+
for (const [internals, cache] of remote.data) {
|
|
510
519
|
// remote functions without an `id` aren't exported, and thus
|
|
511
520
|
// cannot be called from the client
|
|
512
|
-
if (!
|
|
521
|
+
if (!internals.id) continue;
|
|
513
522
|
|
|
514
523
|
for (const key in cache) {
|
|
515
|
-
const
|
|
524
|
+
const entry = cache[key];
|
|
525
|
+
|
|
526
|
+
if (!entry.serialize) continue;
|
|
516
527
|
|
|
517
|
-
|
|
528
|
+
const remote_key = create_remote_key(internals.id, key);
|
|
529
|
+
|
|
530
|
+
const store = internals.type === 'prerender' ? prerender : query;
|
|
531
|
+
|
|
532
|
+
if (event_state.remote.refreshes?.[remote_key] !== undefined) {
|
|
518
533
|
// This entry was refreshed/set by a command or form action.
|
|
519
534
|
// Always await it so the mutation result is serialized.
|
|
520
|
-
|
|
535
|
+
store[remote_key] = await entry.data;
|
|
521
536
|
} else {
|
|
522
537
|
// Don't block the response on pending remote data - if a query
|
|
523
538
|
// hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary).
|
|
524
539
|
const result = await Promise.race([
|
|
525
|
-
Promise.resolve(
|
|
540
|
+
Promise.resolve(entry.data).then(
|
|
526
541
|
(v) => /** @type {const} */ ({ settled: true, value: v }),
|
|
527
542
|
(e) => /** @type {const} */ ({ settled: true, error: e })
|
|
528
543
|
),
|
|
@@ -533,7 +548,7 @@ export async function render_response({
|
|
|
533
548
|
|
|
534
549
|
if (result.settled) {
|
|
535
550
|
if ('error' in result) throw result.error;
|
|
536
|
-
|
|
551
|
+
store[remote_key] = result.value;
|
|
537
552
|
}
|
|
538
553
|
}
|
|
539
554
|
}
|
|
@@ -549,9 +564,17 @@ export async function render_response({
|
|
|
549
564
|
}
|
|
550
565
|
};
|
|
551
566
|
|
|
552
|
-
|
|
567
|
+
if (Object.keys(query).length > 0) {
|
|
568
|
+
serialized_query_data = `${global}.query = ${devalue.uneval(query, replacer)};\n\n\t\t\t\t\t\t`;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (Object.keys(prerender).length > 0) {
|
|
572
|
+
serialized_prerender_data = `${global}.prerender = ${devalue.uneval(prerender, replacer)};\n\n\t\t\t\t\t\t`;
|
|
573
|
+
}
|
|
553
574
|
}
|
|
554
575
|
|
|
576
|
+
const serialized_remote_data = `${serialized_query_data}${serialized_prerender_data}`;
|
|
577
|
+
|
|
555
578
|
// `client.app` is a proxy for `bundleStrategy === 'split'`
|
|
556
579
|
const boot = client.inline
|
|
557
580
|
? `${client.inline.script}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** @import { ActionResult, RemoteForm, RequestEvent, SSRManifest } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { RemoteFunctionResponse,
|
|
2
|
+
/** @import { RemoteFormInternals, RemoteFunctionResponse, RemoteInternals, RequestState, SSROptions } from 'types' */
|
|
3
3
|
|
|
4
4
|
import { json, error } from '@sveltejs/kit';
|
|
5
5
|
import { HttpError, Redirect, SvelteKitError } from '@sveltejs/kit/internal';
|
|
6
6
|
import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
|
|
7
7
|
import { app_dir, base } from '$app/paths/internal/server';
|
|
8
8
|
import { is_form_content_type } from '../../utils/http.js';
|
|
9
|
-
import { parse_remote_arg, stringify } from '../shared.js';
|
|
9
|
+
import { parse_remote_arg, split_remote_key, stringify } from '../shared.js';
|
|
10
10
|
import { handle_error_and_jsonify } from './utils.js';
|
|
11
11
|
import { normalize_error } from '../../utils/error.js';
|
|
12
12
|
import { check_incorrect_fail_use } from './page/actions.js';
|
|
@@ -48,20 +48,17 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
48
48
|
|
|
49
49
|
if (!fn) error(404);
|
|
50
50
|
|
|
51
|
-
/** @type {
|
|
52
|
-
const
|
|
51
|
+
/** @type {RemoteInternals} */
|
|
52
|
+
const internals = fn.__;
|
|
53
53
|
const transport = options.hooks.transport;
|
|
54
54
|
|
|
55
55
|
event.tracing.current.setAttributes({
|
|
56
|
-
'sveltekit.remote.call.type':
|
|
57
|
-
'sveltekit.remote.call.name':
|
|
56
|
+
'sveltekit.remote.call.type': internals.type,
|
|
57
|
+
'sveltekit.remote.call.name': internals.name
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
/** @type {string[] | undefined} */
|
|
61
|
-
let form_client_refreshes;
|
|
62
|
-
|
|
63
60
|
try {
|
|
64
|
-
if (
|
|
61
|
+
if (internals.type === 'query_batch') {
|
|
65
62
|
if (event.request.method !== 'POST') {
|
|
66
63
|
throw new SvelteKitError(
|
|
67
64
|
405,
|
|
@@ -77,7 +74,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
77
74
|
payloads.map((payload) => parse_remote_arg(payload, transport))
|
|
78
75
|
);
|
|
79
76
|
|
|
80
|
-
const results = await with_request_store({ event, state }, () =>
|
|
77
|
+
const results = await with_request_store({ event, state }, () =>
|
|
78
|
+
internals.run(args, options)
|
|
79
|
+
);
|
|
81
80
|
|
|
82
81
|
return json(
|
|
83
82
|
/** @type {RemoteFunctionResponse} */ ({
|
|
@@ -87,7 +86,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
87
86
|
);
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
if (
|
|
89
|
+
if (internals.type === 'form') {
|
|
91
90
|
if (event.request.method !== 'POST') {
|
|
92
91
|
throw new SvelteKitError(
|
|
93
92
|
405,
|
|
@@ -107,8 +106,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
const { data, meta, form_data } = await deserialize_binary_form(event.request);
|
|
110
|
-
|
|
111
|
-
form_client_refreshes = meta.remote_refreshes;
|
|
109
|
+
state.remote.requested = create_requested_map(meta.remote_refreshes);
|
|
112
110
|
|
|
113
111
|
// If this is a keyed form instance (created via form.for(key)), add the key to the form data (unless already set)
|
|
114
112
|
// Note that additional_args will only be set if the form is not enhanced, as enhanced forms transfer the key inside `data`.
|
|
@@ -116,21 +114,22 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
116
114
|
data.id = JSON.parse(decodeURIComponent(additional_args));
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
const fn =
|
|
117
|
+
const fn = internals.fn;
|
|
120
118
|
const result = await with_request_store({ event, state }, () => fn(data, meta, form_data));
|
|
121
119
|
|
|
122
120
|
return json(
|
|
123
121
|
/** @type {RemoteFunctionResponse} */ ({
|
|
124
122
|
type: 'result',
|
|
125
123
|
result: stringify(result, transport),
|
|
126
|
-
refreshes: result.issues ? undefined : await serialize_refreshes(
|
|
124
|
+
refreshes: result.issues ? undefined : await serialize_refreshes()
|
|
127
125
|
})
|
|
128
126
|
);
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
if (
|
|
132
|
-
/** @type {{ payload: string, refreshes
|
|
129
|
+
if (internals.type === 'command') {
|
|
130
|
+
/** @type {{ payload: string, refreshes?: string[] }} */
|
|
133
131
|
const { payload, refreshes } = await event.request.json();
|
|
132
|
+
state.remote.requested = create_requested_map(refreshes);
|
|
134
133
|
const arg = parse_remote_arg(payload, transport);
|
|
135
134
|
const data = await with_request_store({ event, state }, () => fn(arg));
|
|
136
135
|
|
|
@@ -138,13 +137,13 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
138
137
|
/** @type {RemoteFunctionResponse} */ ({
|
|
139
138
|
type: 'result',
|
|
140
139
|
result: stringify(data, transport),
|
|
141
|
-
refreshes: await serialize_refreshes(
|
|
140
|
+
refreshes: await serialize_refreshes()
|
|
142
141
|
})
|
|
143
142
|
);
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
const payload =
|
|
147
|
-
|
|
146
|
+
internals.type === 'prerender'
|
|
148
147
|
? additional_args
|
|
149
148
|
: /** @type {string} */ (
|
|
150
149
|
// new URL(...) necessary because we're hiding the URL from the user in the event object
|
|
@@ -167,7 +166,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
167
166
|
/** @type {RemoteFunctionResponse} */ ({
|
|
168
167
|
type: 'redirect',
|
|
169
168
|
location: error.location,
|
|
170
|
-
refreshes: await serialize_refreshes(
|
|
169
|
+
refreshes: await serialize_refreshes()
|
|
171
170
|
})
|
|
172
171
|
);
|
|
173
172
|
}
|
|
@@ -192,42 +191,58 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
192
191
|
);
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
async function serialize_refreshes() {
|
|
195
|
+
const refreshes = state.remote.refreshes ?? {};
|
|
196
|
+
|
|
197
|
+
const entries = Object.entries(refreshes);
|
|
198
|
+
if (entries.length === 0) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
200
201
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
const results = await Promise.all(
|
|
203
|
+
entries.map(async ([key, promise]) => {
|
|
204
|
+
try {
|
|
205
|
+
return [key, { type: 'result', data: await promise }];
|
|
206
|
+
} catch (error) {
|
|
207
|
+
const status =
|
|
208
|
+
error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
key,
|
|
212
|
+
{
|
|
213
|
+
type: 'error',
|
|
214
|
+
status,
|
|
215
|
+
error: await handle_error_and_jsonify(event, state, options, error)
|
|
216
|
+
}
|
|
217
|
+
];
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
);
|
|
204
221
|
|
|
205
|
-
|
|
222
|
+
return stringify(Object.fromEntries(results), transport);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
206
225
|
|
|
207
|
-
|
|
208
|
-
|
|
226
|
+
/**
|
|
227
|
+
* @param {string[] | undefined} refreshes
|
|
228
|
+
*/
|
|
229
|
+
function create_requested_map(refreshes) {
|
|
230
|
+
/** @type {Map<string, string[]>} */
|
|
231
|
+
const requested = new Map();
|
|
209
232
|
|
|
210
|
-
|
|
233
|
+
for (const key of refreshes ?? []) {
|
|
234
|
+
const parts = split_remote_key(key);
|
|
211
235
|
|
|
212
|
-
|
|
213
|
-
fn(parse_remote_arg(payload, transport))
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
236
|
+
const existing = requested.get(parts.id);
|
|
217
237
|
|
|
218
|
-
if (
|
|
219
|
-
|
|
238
|
+
if (existing) {
|
|
239
|
+
existing.push(parts.payload);
|
|
240
|
+
} else {
|
|
241
|
+
requested.set(parts.id, [parts.payload]);
|
|
220
242
|
}
|
|
221
|
-
|
|
222
|
-
return stringify(
|
|
223
|
-
Object.fromEntries(
|
|
224
|
-
await Promise.all(
|
|
225
|
-
Object.entries(refreshes).map(async ([key, promise]) => [key, await promise])
|
|
226
|
-
)
|
|
227
|
-
),
|
|
228
|
-
transport
|
|
229
|
-
);
|
|
230
243
|
}
|
|
244
|
+
|
|
245
|
+
return requested;
|
|
231
246
|
}
|
|
232
247
|
|
|
233
248
|
/** @type {typeof handle_remote_form_post_internal} */
|
|
@@ -282,7 +297,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
282
297
|
}
|
|
283
298
|
|
|
284
299
|
try {
|
|
285
|
-
const fn = /** @type {
|
|
300
|
+
const fn = /** @type {RemoteFormInternals} */ (/** @type {any} */ (form).__).fn;
|
|
286
301
|
|
|
287
302
|
const { data, meta, form_data } = await deserialize_binary_form(event.request);
|
|
288
303
|
|
|
@@ -39,7 +39,6 @@ import { server_data_serializer } from './page/data_serializer.js';
|
|
|
39
39
|
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
|
-
import { MUTATIVE_METHODS } from '../../constants.js';
|
|
43
42
|
|
|
44
43
|
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
45
44
|
|
|
@@ -150,7 +149,22 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
150
149
|
tracing: {
|
|
151
150
|
record_span
|
|
152
151
|
},
|
|
153
|
-
|
|
152
|
+
remote: {
|
|
153
|
+
data: null,
|
|
154
|
+
forms: null,
|
|
155
|
+
/** A map of remote function key to corresponding single-flight-mutation promise */
|
|
156
|
+
refreshes: null,
|
|
157
|
+
/** A map of remote function ID to payloads requested for refreshing by the client */
|
|
158
|
+
requested: null,
|
|
159
|
+
/**
|
|
160
|
+
* A map of remote function ID to objects that have passed validation;
|
|
161
|
+
* used to prevent revalidating parameters returned from `requested`
|
|
162
|
+
*/
|
|
163
|
+
validated: null
|
|
164
|
+
},
|
|
165
|
+
is_in_remote_function: false,
|
|
166
|
+
is_in_render: false,
|
|
167
|
+
is_in_universal_load: false
|
|
154
168
|
};
|
|
155
169
|
|
|
156
170
|
/** @type {import('@sveltejs/kit').RequestEvent} */
|
|
@@ -420,7 +434,7 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
420
434
|
current: root_span
|
|
421
435
|
}
|
|
422
436
|
};
|
|
423
|
-
|
|
437
|
+
|
|
424
438
|
return await with_request_store({ event: traced_event, state: event_state }, () =>
|
|
425
439
|
options.hooks.handle({
|
|
426
440
|
event: traced_event,
|