Skip to content

Commit 21495d7

Browse files
Rich-Harriselliott-with-the-longest-name-on-githubRishab49
authored andcommitted
fix: use validated args in batch resolver (sveltejs#15215)
Co-authored-by: Elliott Johnson <hello@ell.iott.dev> Co-authored-by: Rishab49 <25582966+Rishab49@users.noreply.github.com>
1 parent e2c0a6d commit 21495d7

File tree

11 files changed

+92
-44
lines changed

11 files changed

+92
-44
lines changed

.changeset/rude-islands-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: use validated args in batch resolver in both csr and ssr

packages/kit/src/runtime/app/server/remote/command.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ export function command(validate_or_fn, maybe_fn) {
7878

7979
state.refreshes ??= {};
8080

81-
const promise = Promise.resolve(run_remote_function(event, state, true, arg, validate, fn));
81+
const promise = Promise.resolve(
82+
run_remote_function(event, state, true, () => validate(arg), fn)
83+
);
8284

8385
// @ts-expect-error
8486
promise.updates = () => {

packages/kit/src/runtime/app/server/remote/form.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ export function form(validate_or_fn, maybe_fn) {
145145
event,
146146
state,
147147
true,
148-
data,
149-
(d) => d,
148+
() => data,
150149
(data) => (!maybe_fn ? fn() : fn(data, issue))
151150
);
152151
} catch (e) {

packages/kit/src/runtime/app/server/remote/prerender.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
131131
}
132132

133133
const promise = get_response(__, arg, state, () =>
134-
run_remote_function(event, state, false, arg, validate, fn)
134+
run_remote_function(event, state, false, () => validate(arg), fn)
135135
);
136136

137137
if (state.prerendering) {

packages/kit/src/runtime/app/server/remote/query.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { get_request_store } from '@sveltejs/kit/internal/server';
55
import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
66
import { prerendering } from '__sveltekit/environment';
77
import { create_validator, get_cache, get_response, run_remote_function } from './shared.js';
8+
import { handle_error_and_jsonify } from '../../../server/utils.js';
9+
import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
810

911
/**
1012
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -73,7 +75,7 @@ export function query(validate_or_fn, maybe_fn) {
7375
const { event, state } = get_request_store();
7476

7577
const get_remote_function_result = () =>
76-
run_remote_function(event, state, false, arg, validate, fn);
78+
run_remote_function(event, state, false, () => validate(arg), fn);
7779

7880
/** @type {Promise<any> & Partial<RemoteQuery<any>>} */
7981
const promise = get_response(__, arg, state, get_remote_function_result);
@@ -137,7 +139,7 @@ export function query(validate_or_fn, maybe_fn) {
137139
*/
138140
/*@__NO_SIDE_EFFECTS__*/
139141
function batch(validate_or_fn, maybe_fn) {
140-
/** @type {(args?: Input[]) => (arg: Input, idx: number) => Output} */
142+
/** @type {(args?: Input[]) => MaybePromise<(arg: Input, idx: number) => Output>} */
141143
const fn = maybe_fn ?? validate_or_fn;
142144

143145
/** @type {(arg?: any) => MaybePromise<Input>} */
@@ -148,16 +150,34 @@ function batch(validate_or_fn, maybe_fn) {
148150
type: 'query_batch',
149151
id: '',
150152
name: '',
151-
run: (args) => {
153+
run: async (args, options) => {
152154
const { event, state } = get_request_store();
153155

154156
return run_remote_function(
155157
event,
156158
state,
157159
false,
158-
args,
159-
(array) => Promise.all(array.map(validate)),
160-
fn
160+
async () => Promise.all(args.map(validate)),
161+
async (/** @type {any[]} */ input) => {
162+
const get_result = await fn(input);
163+
164+
return Promise.all(
165+
input.map(async (arg, i) => {
166+
try {
167+
return { type: 'result', data: get_result(arg, i) };
168+
} catch (error) {
169+
return {
170+
type: 'error',
171+
error: await handle_error_and_jsonify(event, state, options, error),
172+
status:
173+
error instanceof HttpError || error instanceof SvelteKitError
174+
? error.status
175+
: 500
176+
};
177+
}
178+
})
179+
);
180+
}
161181
);
162182
}
163183
};
@@ -190,22 +210,23 @@ function batch(validate_or_fn, maybe_fn) {
190210
batching = { args: [], resolvers: [] };
191211

192212
try {
193-
const get_result = await run_remote_function(
213+
return await run_remote_function(
194214
event,
195215
state,
196216
false,
197-
batched.args,
198-
(array) => Promise.all(array.map(validate)),
199-
fn
200-
);
201-
202-
for (let i = 0; i < batched.resolvers.length; i++) {
203-
try {
204-
batched.resolvers[i].resolve(get_result(batched.args[i], i));
205-
} catch (error) {
206-
batched.resolvers[i].reject(error);
217+
async () => Promise.all(batched.args.map(validate)),
218+
async (input) => {
219+
const get_result = await fn(input);
220+
221+
for (let i = 0; i < batched.resolvers.length; i++) {
222+
try {
223+
batched.resolvers[i].resolve(get_result(input[i], i));
224+
} catch (error) {
225+
batched.resolvers[i].reject(error);
226+
}
227+
}
207228
}
208-
}
229+
);
209230
} catch (error) {
210231
for (const resolver of batched.resolvers) {
211232
resolver.reject(error);

packages/kit/src/runtime/app/server/remote/shared.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,10 @@ export function parse_remote_response(data, transport) {
9797
* @param {RequestEvent} event
9898
* @param {RequestState} state
9999
* @param {boolean} allow_cookies
100-
* @param {any} arg
101-
* @param {(arg: any) => any} validate
100+
* @param {() => any} get_input
102101
* @param {(arg?: any) => T} fn
103102
*/
104-
export async function run_remote_function(event, state, allow_cookies, arg, validate, fn) {
103+
export async function run_remote_function(event, state, allow_cookies, get_input, fn) {
105104
/** @type {RequestStore} */
106105
const store = {
107106
event: {
@@ -142,8 +141,8 @@ export async function run_remote_function(event, state, allow_cookies, arg, vali
142141
};
143142

144143
// In two parts, each with_event, so that runtimes without async local storage can still get the event at the start of the function
145-
const validated = await with_request_store(store, () => validate(arg));
146-
return with_request_store(store, () => fn(validated));
144+
const input = await with_request_store(store, get_input);
145+
return with_request_store(store, () => fn(input));
147146
}
148147

149148
/**

packages/kit/src/runtime/server/remote.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,12 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
7373
/** @type {{ payloads: string[] }} */
7474
const { payloads } = await event.request.json();
7575

76-
const args = payloads.map((payload) => parse_remote_arg(payload, transport));
77-
const get_result = await with_request_store({ event, state }, () => info.run(args));
78-
const results = await Promise.all(
79-
args.map(async (arg, i) => {
80-
try {
81-
return { type: 'result', data: get_result(arg, i) };
82-
} catch (error) {
83-
return {
84-
type: 'error',
85-
error: await handle_error_and_jsonify(event, state, options, error),
86-
status:
87-
error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500
88-
};
89-
}
90-
})
76+
const args = await Promise.all(
77+
payloads.map((payload) => parse_remote_arg(payload, transport))
9178
);
9279

80+
const results = await with_request_store({ event, state }, () => info.run(args, options));
81+
9382
return json(
9483
/** @type {RemoteFunctionResponse} */ ({
9584
type: 'result',

packages/kit/src/types/internal.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,8 @@ export type RemoteInfo =
572572
type: 'query_batch';
573573
id: string;
574574
name: string;
575-
/** Direct access to the function without batching etc logic, for remote functions called from the client */
576-
run: (args: any[]) => Promise<(arg: any, idx: number) => any>;
575+
/** Direct access to the function, for remote functions called from the client */
576+
run: (args: any[], options: SSROptions) => Promise<any[]>;
577577
}
578578
| {
579579
type: 'form';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts">
2+
import { reverse } from './batch.remote.js';
3+
4+
let phrase = $state('ecrof eht esu');
5+
const words = $derived(phrase.split(' ').reverse());
6+
</script>
7+
8+
<div id="phrase">
9+
{#each words as word, i}
10+
{await reverse(word)}{i === words.length - 1 ? '' : ' '}
11+
{/each}
12+
</div>
13+
<button onclick={() => (phrase = 'rehtaf ruoy ma i')}>get dramatic</button>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { query } from '$app/server';
2+
import * as v from 'valibot';
3+
4+
export const reverse = query.batch(
5+
v.pipe(
6+
v.string(),
7+
v.transform((val) => val.split('').reverse().join(''))
8+
),
9+
() => {
10+
return (x) => x;
11+
}
12+
);

0 commit comments

Comments
 (0)