@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.3
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 +2 -2
- package/src/core/postbuild/analyse.js +0 -8
- package/src/core/postbuild/prerender.js +2 -0
- package/src/exports/public.d.ts +1 -1
- package/src/exports/vite/build/build_server.js +43 -58
- package/src/exports/vite/build/remote.js +18 -11
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/index.js +220 -216
- package/src/runtime/app/server/remote/command.js +0 -3
- package/src/runtime/app/server/remote/form.js +18 -13
- package/src/runtime/app/server/remote/prerender.js +28 -34
- package/src/runtime/app/server/remote/query.js +105 -94
- package/src/runtime/app/server/remote/requested.js +14 -10
- package/src/runtime/app/server/remote/shared.js +25 -19
- package/src/runtime/client/client.js +19 -13
- package/src/runtime/client/remote-functions/command.svelte.js +5 -30
- package/src/runtime/client/remote-functions/form.svelte.js +62 -82
- package/src/runtime/client/remote-functions/prerender.svelte.js +14 -6
- package/src/runtime/client/remote-functions/query/index.js +6 -14
- package/src/runtime/client/remote-functions/query/instance.svelte.js +20 -0
- package/src/runtime/client/remote-functions/query/proxy.js +3 -3
- package/src/runtime/client/remote-functions/query-batch.svelte.js +59 -68
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +21 -6
- package/src/runtime/client/remote-functions/shared.svelte.js +76 -59
- package/src/runtime/server/page/render.js +20 -80
- package/src/runtime/server/page/server_routing.js +20 -15
- package/src/runtime/server/remote.js +296 -204
- package/src/runtime/server/respond.js +4 -2
- package/src/types/global-private.d.ts +3 -3
- package/src/types/internal.d.ts +53 -34
- package/src/version.js +1 -1
- package/types/index.d.ts +6 -5
- package/types/index.d.ts.map +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** @import { ActionResult, RemoteForm, RequestEvent, SSRManifest } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { RemoteFormInternals, RemoteFunctionResponse, RemoteInternals, RequestState, SSROptions } from 'types' */
|
|
2
|
+
/** @import { RemoteFormInternals, RemoteFunctionData, 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, split_remote_key, stringify } from '../shared.js';
|
|
9
|
+
import { create_remote_key, 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';
|
|
@@ -58,225 +58,233 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
/** @type {RemoteFunctionData} */
|
|
62
|
+
const data = {};
|
|
63
|
+
|
|
64
|
+
switch (internals.type) {
|
|
65
|
+
case 'query_live': {
|
|
66
|
+
if (event.request.method !== 'GET') {
|
|
67
|
+
throw new SvelteKitError(
|
|
68
|
+
405,
|
|
69
|
+
'Method Not Allowed',
|
|
70
|
+
`\`query.live\` functions must be invoked via GET request, not ${event.request.method}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const payload = /** @type {string} */ (
|
|
75
|
+
new URL(event.request.url).searchParams.get('payload')
|
|
67
76
|
);
|
|
68
|
-
}
|
|
69
77
|
|
|
70
|
-
|
|
71
|
-
const { payloads } = await event.request.json();
|
|
78
|
+
const generator = internals.run(event, state, parse_remote_arg(payload, transport));
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
payloads.map((payload) => parse_remote_arg(payload, transport))
|
|
75
|
-
);
|
|
80
|
+
const encoder = new TextEncoder();
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/**
|
|
83
|
+
* @param {ReadableStreamDefaultController} controller
|
|
84
|
+
* @param {any} payload
|
|
85
|
+
*/
|
|
86
|
+
function send(controller, payload) {
|
|
87
|
+
controller.enqueue(encoder.encode('data: ' + JSON.stringify(payload) + '\n\n'));
|
|
88
|
+
}
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
/** @type {RemoteFunctionResponse} */ ({
|
|
83
|
-
type: 'result',
|
|
84
|
-
result: stringify(results, transport)
|
|
85
|
-
})
|
|
86
|
-
);
|
|
87
|
-
}
|
|
90
|
+
let closed = false;
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
throw new SvelteKitError(
|
|
92
|
-
405,
|
|
93
|
-
'Method Not Allowed',
|
|
94
|
-
`\`form\` functions must be invoked via POST request, not ${event.request.method}`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
92
|
+
/** @type {string | undefined} */
|
|
93
|
+
let result = undefined;
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
'content-type'
|
|
104
|
-
)}`
|
|
105
|
-
);
|
|
106
|
-
}
|
|
95
|
+
async function cancel() {
|
|
96
|
+
if (closed) return;
|
|
97
|
+
closed = true;
|
|
98
|
+
await generator.return(undefined);
|
|
99
|
+
}
|
|
107
100
|
|
|
108
|
-
|
|
109
|
-
state.remote.requested = create_requested_map(meta.remote_refreshes);
|
|
101
|
+
event.request.signal.addEventListener('abort', cancel, { once: true });
|
|
110
102
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
return new Response(
|
|
104
|
+
new ReadableStream({
|
|
105
|
+
async pull(controller) {
|
|
106
|
+
if (event.request.signal.aborted) {
|
|
107
|
+
await cancel();
|
|
108
|
+
controller.close();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
116
111
|
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
try {
|
|
113
|
+
while (true) {
|
|
114
|
+
const { value, done } = await generator.next();
|
|
115
|
+
|
|
116
|
+
if (done) {
|
|
117
|
+
await cancel();
|
|
118
|
+
controller.close();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// only send changed data
|
|
123
|
+
if (result !== (result = stringify(value, transport))) {
|
|
124
|
+
send(controller, {
|
|
125
|
+
type: 'result',
|
|
126
|
+
result
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (!event.request.signal.aborted) {
|
|
134
|
+
if (error instanceof Redirect) {
|
|
135
|
+
send(controller, {
|
|
136
|
+
type: 'redirect',
|
|
137
|
+
location: error.location
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
const status =
|
|
141
|
+
error instanceof HttpError || error instanceof SvelteKitError
|
|
142
|
+
? error.status
|
|
143
|
+
: 500;
|
|
144
|
+
|
|
145
|
+
send(controller, {
|
|
146
|
+
type: 'error',
|
|
147
|
+
error: await handle_error_and_jsonify(event, state, options, error),
|
|
148
|
+
status
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
119
152
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
153
|
+
await cancel();
|
|
154
|
+
controller.close();
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
cancel
|
|
158
|
+
}),
|
|
159
|
+
{
|
|
160
|
+
headers: {
|
|
161
|
+
'cache-control': 'private, no-store',
|
|
162
|
+
'content-type': 'text/event-stream'
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
133
167
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
168
|
+
case 'query_batch': {
|
|
169
|
+
if (event.request.method !== 'POST') {
|
|
170
|
+
throw new SvelteKitError(
|
|
171
|
+
405,
|
|
172
|
+
'Method Not Allowed',
|
|
173
|
+
`\`query.batch\` functions must be invoked via POST request, not ${event.request.method}`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
140
176
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
type: 'result',
|
|
144
|
-
result: stringify(data, transport),
|
|
145
|
-
refreshes: await serialize_singleflight(state.remote.refreshes),
|
|
146
|
-
reconnects: await serialize_singleflight(state.remote.reconnects)
|
|
147
|
-
})
|
|
148
|
-
);
|
|
149
|
-
}
|
|
177
|
+
/** @type {{ payloads: string[] }} */
|
|
178
|
+
const { payloads } = await event.request.json();
|
|
150
179
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
throw new SvelteKitError(
|
|
154
|
-
405,
|
|
155
|
-
'Method Not Allowed',
|
|
156
|
-
`\`query.live\` functions must be invoked via GET request, not ${event.request.method}`
|
|
180
|
+
const args = await Promise.all(
|
|
181
|
+
payloads.map((payload) => parse_remote_arg(payload, transport))
|
|
157
182
|
);
|
|
183
|
+
|
|
184
|
+
data._ = await with_request_store({ event, state }, () => internals.run(args, options));
|
|
185
|
+
|
|
186
|
+
break;
|
|
158
187
|
}
|
|
159
188
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
case 'form': {
|
|
190
|
+
if (event.request.method !== 'POST') {
|
|
191
|
+
throw new SvelteKitError(
|
|
192
|
+
405,
|
|
193
|
+
'Method Not Allowed',
|
|
194
|
+
`\`form\` functions must be invoked via POST request, not ${event.request.method}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
163
197
|
|
|
164
|
-
|
|
198
|
+
if (!is_form_content_type(event.request)) {
|
|
199
|
+
throw new SvelteKitError(
|
|
200
|
+
415,
|
|
201
|
+
'Unsupported Media Type',
|
|
202
|
+
`\`form\` functions expect form-encoded data — received ${event.request.headers.get(
|
|
203
|
+
'content-type'
|
|
204
|
+
)}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
165
207
|
|
|
166
|
-
|
|
208
|
+
const { data: input, meta, form_data } = await deserialize_binary_form(event.request);
|
|
209
|
+
state.remote.requested = create_requested_map(meta.remote_refreshes);
|
|
167
210
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
controller.enqueue(encoder.encode('data: ' + JSON.stringify(payload) + '\n\n'));
|
|
174
|
-
}
|
|
211
|
+
// If this is a keyed form instance (created via form.for(key)), add the key to the form data (unless already set)
|
|
212
|
+
// Note that additional_args will only be set if the form is not enhanced, as enhanced forms transfer the key inside `data`.
|
|
213
|
+
if (additional_args && !('id' in input)) {
|
|
214
|
+
input.id = JSON.parse(decodeURIComponent(additional_args));
|
|
215
|
+
}
|
|
175
216
|
|
|
176
|
-
|
|
217
|
+
const fn = internals.fn;
|
|
218
|
+
data._ = await with_request_store(
|
|
219
|
+
{ event, state: { ...state, is_in_remote_form_or_command: true } },
|
|
220
|
+
() => fn(input, meta, form_data)
|
|
221
|
+
);
|
|
177
222
|
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
if (data._.issues) {
|
|
224
|
+
// special case — don't serialize refreshes/reconnects
|
|
225
|
+
return json(
|
|
226
|
+
/** @type {RemoteFunctionResponse} */ ({
|
|
227
|
+
type: 'result',
|
|
228
|
+
data: stringify(data, transport)
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
}
|
|
180
232
|
|
|
181
|
-
|
|
182
|
-
if (closed) return;
|
|
183
|
-
closed = true;
|
|
184
|
-
await generator.return(undefined);
|
|
233
|
+
break;
|
|
185
234
|
}
|
|
186
235
|
|
|
187
|
-
|
|
236
|
+
case 'command': {
|
|
237
|
+
/** @type {{ payload: string, refreshes?: string[] }} */
|
|
238
|
+
const { payload, refreshes } = await event.request.json();
|
|
239
|
+
state.remote.requested = create_requested_map(refreshes);
|
|
240
|
+
const arg = parse_remote_arg(payload, transport);
|
|
188
241
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
await cancel();
|
|
194
|
-
controller.close();
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
242
|
+
data._ = await with_request_store(
|
|
243
|
+
{ event, state: { ...state, is_in_remote_form_or_command: true } },
|
|
244
|
+
() => fn(arg)
|
|
245
|
+
);
|
|
197
246
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const { value, done } = await generator.next();
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
201
249
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
250
|
+
case 'prerender': {
|
|
251
|
+
data._ = await with_request_store({ event, state }, () =>
|
|
252
|
+
fn(parse_remote_arg(additional_args, transport))
|
|
253
|
+
);
|
|
207
254
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
send(controller, {
|
|
211
|
-
type: 'result',
|
|
212
|
-
result
|
|
213
|
-
});
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
214
257
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (error instanceof Redirect) {
|
|
221
|
-
send(controller, {
|
|
222
|
-
type: 'redirect',
|
|
223
|
-
location: error.location
|
|
224
|
-
});
|
|
225
|
-
} else {
|
|
226
|
-
const status =
|
|
227
|
-
error instanceof HttpError || error instanceof SvelteKitError
|
|
228
|
-
? error.status
|
|
229
|
-
: 500;
|
|
230
|
-
|
|
231
|
-
send(controller, {
|
|
232
|
-
type: 'error',
|
|
233
|
-
error: await handle_error_and_jsonify(event, state, options, error),
|
|
234
|
-
status
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
258
|
+
case 'query': {
|
|
259
|
+
const payload = /** @type {string} */ (
|
|
260
|
+
// new URL(...) necessary because we're hiding the URL from the user in the event object
|
|
261
|
+
new URL(event.request.url).searchParams.get('payload')
|
|
262
|
+
);
|
|
238
263
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{
|
|
246
|
-
headers: {
|
|
247
|
-
'cache-control': 'private, no-store',
|
|
248
|
-
'content-type': 'text/event-stream'
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
);
|
|
264
|
+
data._ = await with_request_store({ event, state }, () =>
|
|
265
|
+
fn(parse_remote_arg(payload, transport))
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
252
270
|
}
|
|
253
271
|
|
|
254
|
-
|
|
255
|
-
internals.type === 'prerender'
|
|
256
|
-
? additional_args
|
|
257
|
-
: /** @type {string} */ (
|
|
258
|
-
// new URL(...) necessary because we're hiding the URL from the user in the event object
|
|
259
|
-
new URL(event.request.url).searchParams.get('payload')
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const data = await with_request_store({ event, state }, () =>
|
|
263
|
-
fn(parse_remote_arg(payload, transport))
|
|
264
|
-
);
|
|
272
|
+
await collect_remote_data(data, event, state, options);
|
|
265
273
|
|
|
266
274
|
return json(
|
|
267
275
|
/** @type {RemoteFunctionResponse} */ ({
|
|
268
276
|
type: 'result',
|
|
269
|
-
|
|
277
|
+
data: stringify(data, transport)
|
|
270
278
|
})
|
|
271
279
|
);
|
|
272
280
|
} catch (error) {
|
|
273
281
|
if (error instanceof Redirect) {
|
|
282
|
+
const data = await collect_remote_data({ redirect: error.location }, event, state, options);
|
|
283
|
+
|
|
274
284
|
return json(
|
|
275
285
|
/** @type {RemoteFunctionResponse} */ ({
|
|
276
|
-
type: '
|
|
277
|
-
|
|
278
|
-
refreshes: await serialize_singleflight(state.remote.refreshes),
|
|
279
|
-
reconnects: await serialize_singleflight(state.remote.reconnects)
|
|
286
|
+
type: 'result',
|
|
287
|
+
data: stringify(data, transport)
|
|
280
288
|
})
|
|
281
289
|
);
|
|
282
290
|
}
|
|
@@ -300,35 +308,113 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
300
308
|
}
|
|
301
309
|
);
|
|
302
310
|
}
|
|
311
|
+
}
|
|
303
312
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Collects all the query/prerender data that was retrieved
|
|
315
|
+
* during the request and adds it to `data`
|
|
316
|
+
* @param {RemoteFunctionData} data
|
|
317
|
+
* @param {RequestEvent} event
|
|
318
|
+
* @param {RequestState} state
|
|
319
|
+
* @param {SSROptions} options
|
|
320
|
+
*/
|
|
321
|
+
export async function collect_remote_data(data, event, state, options) {
|
|
322
|
+
/**
|
|
323
|
+
*
|
|
324
|
+
* @param {unknown} error
|
|
325
|
+
* @returns {Promise<[status: number, error: App.Error]>}
|
|
326
|
+
*/
|
|
327
|
+
async function convert_error(error) {
|
|
328
|
+
const status =
|
|
329
|
+
error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
|
|
309
330
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
331
|
+
return [status, await handle_error_and_jsonify(event, state, options, error)];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** @type {Promise<any>[]} */
|
|
335
|
+
const promises = [];
|
|
336
|
+
|
|
337
|
+
if (state.remote.explicit) {
|
|
338
|
+
for (const [remote_key, { internals, promise }] of state.remote.explicit) {
|
|
339
|
+
// there were explicit refreshes/reconnects (via `refresh()`/`set()`/`reconnect()`),
|
|
340
|
+
// so the client should apply these single-flight updates instead of calling `invalidateAll()`
|
|
341
|
+
data.r = true;
|
|
342
|
+
|
|
343
|
+
const type = /** @type {'p' | 'q' | 'l'} */ (
|
|
344
|
+
internals.type === 'query_live' ? 'l' : internals.type[0]
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
await promise.then(
|
|
348
|
+
(v) => {
|
|
349
|
+
((data[type] ??= {})[remote_key] ??= {}).v = v;
|
|
350
|
+
},
|
|
351
|
+
async (e) => {
|
|
352
|
+
if (e instanceof Redirect) {
|
|
353
|
+
// already handled elsewhere
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
((data[type] ??= {})[remote_key] ??= {}).e = await convert_error(e);
|
|
326
358
|
}
|
|
327
|
-
|
|
328
|
-
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
await Promise.all(promises);
|
|
364
|
+
|
|
365
|
+
if (state.remote.implicit) {
|
|
366
|
+
for (const [internals, record] of state.remote.implicit) {
|
|
367
|
+
// Private (non-exported) remote functions have no `id` and must never be
|
|
368
|
+
// serialized into the response — otherwise their (potentially private) result
|
|
369
|
+
// would be shipped to the client under a malformed `undefined/...` key.
|
|
370
|
+
if (!internals.id) continue;
|
|
371
|
+
|
|
372
|
+
for (const key in record) {
|
|
373
|
+
// form outputs are registered under the client-side action id directly
|
|
374
|
+
const remote_key = internals.type === 'form' ? key : create_remote_key(internals.id, key);
|
|
329
375
|
|
|
330
|
-
|
|
376
|
+
const type = /** @type {'p' | 'q' | 'l' | 'f'} */ (
|
|
377
|
+
internals.type === 'query_live' ? 'l' : internals.type[0]
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const promise = state.remote.data?.get(internals)?.[key] ?? record[key]();
|
|
381
|
+
|
|
382
|
+
// If the promise is still pending (e.g. the query was rendered in its loading
|
|
383
|
+
// state during SSR), omit it from the payload entirely so that the client
|
|
384
|
+
// fetches it itself — an entry without `v`/`e` would hydrate as `undefined`.
|
|
385
|
+
let resolved = true;
|
|
386
|
+
|
|
387
|
+
await Promise.race([
|
|
388
|
+
Promise.resolve(promise).then(
|
|
389
|
+
(v) => {
|
|
390
|
+
if (resolved) {
|
|
391
|
+
((data[type] ??= {})[remote_key] ??= {}).v = v;
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
(e) => {
|
|
395
|
+
if (e instanceof Redirect) {
|
|
396
|
+
// already handled elsewhere
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (resolved) {
|
|
401
|
+
promises.push(
|
|
402
|
+
convert_error(e).then((e) => {
|
|
403
|
+
((data[type] ??= {})[remote_key] ??= {}).e = e;
|
|
404
|
+
})
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
),
|
|
409
|
+
Promise.resolve().then(() => (resolved = false))
|
|
410
|
+
]);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
331
413
|
}
|
|
414
|
+
|
|
415
|
+
await Promise.all(promises);
|
|
416
|
+
|
|
417
|
+
return data;
|
|
332
418
|
}
|
|
333
419
|
|
|
334
420
|
/**
|
|
@@ -377,7 +463,10 @@ export async function handle_remote_form_post(event, state, manifest, id) {
|
|
|
377
463
|
* @returns {Promise<ActionResult>}
|
|
378
464
|
*/
|
|
379
465
|
async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
380
|
-
|
|
466
|
+
// `hash` and `name` can never contain a `/`, but the JSON-stringified key of a
|
|
467
|
+
// keyed (`form.for(key)`) instance can — rejoin the remaining segments
|
|
468
|
+
const [hash, name, ...rest] = id.split('/');
|
|
469
|
+
const action_id = rest.join('/');
|
|
381
470
|
const remotes = manifest._.remotes;
|
|
382
471
|
const module = await remotes[hash]?.();
|
|
383
472
|
|
|
@@ -413,7 +502,10 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
413
502
|
data.id = JSON.parse(decodeURIComponent(action_id));
|
|
414
503
|
}
|
|
415
504
|
|
|
416
|
-
await with_request_store(
|
|
505
|
+
await with_request_store(
|
|
506
|
+
{ event, state: { ...state, is_in_remote_form_or_command: true } },
|
|
507
|
+
() => fn(data, meta, form_data)
|
|
508
|
+
);
|
|
417
509
|
|
|
418
510
|
// We don't want the data to appear on `let { form } = $props()`, which is why we're not returning it.
|
|
419
511
|
// It is instead available on `myForm.result`, setting of which happens within the remote `form` function.
|
|
@@ -147,14 +147,16 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
147
147
|
},
|
|
148
148
|
remote: {
|
|
149
149
|
data: null,
|
|
150
|
+
explicit: null,
|
|
151
|
+
implicit: null,
|
|
150
152
|
forms: null,
|
|
151
|
-
refreshes: null,
|
|
152
153
|
requested: null,
|
|
153
|
-
reconnects: null,
|
|
154
154
|
batches: null,
|
|
155
155
|
live_iterators: null
|
|
156
156
|
},
|
|
157
157
|
is_in_remote_function: false,
|
|
158
|
+
is_in_remote_form_or_command: false,
|
|
159
|
+
is_in_remote_query: false,
|
|
158
160
|
is_in_render: false,
|
|
159
161
|
is_in_universal_load: false
|
|
160
162
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RemoteFunctionData } from 'types';
|
|
2
|
+
|
|
1
3
|
declare global {
|
|
2
4
|
const __SVELTEKIT_ADAPTER_NAME__: string;
|
|
3
5
|
const __SVELTEKIT_APP_DIR__: string;
|
|
@@ -40,9 +42,7 @@ declare global {
|
|
|
40
42
|
/** Public environment variables */
|
|
41
43
|
env?: Record<string, string>;
|
|
42
44
|
/** Serialized data from query/form/command functions */
|
|
43
|
-
|
|
44
|
-
/** Serialized data from prerender functions */
|
|
45
|
-
prerender?: Record<string, any>;
|
|
45
|
+
data?: RemoteFunctionData;
|
|
46
46
|
/** Create a placeholder promise */
|
|
47
47
|
defer?: (id: number) => Promise<any>;
|
|
48
48
|
/** Resolve a placeholder promise */
|