@sveltejs/kit 2.50.1 → 2.51.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.50.1",
3
+ "version": "2.51.0",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -29,19 +29,19 @@
29
29
  "magic-string": "^0.30.5",
30
30
  "mrmime": "^2.0.0",
31
31
  "sade": "^1.8.1",
32
- "set-cookie-parser": "^2.6.0",
32
+ "set-cookie-parser": "^3.0.0",
33
33
  "sirv": "^3.0.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@opentelemetry/api": "^1.0.0",
37
- "@playwright/test": "1.56.0",
37
+ "@playwright/test": "1.58.2",
38
38
  "@sveltejs/vite-plugin-svelte": "^6.0.0-next.3",
39
39
  "@types/connect": "^3.4.38",
40
40
  "@types/node": "^18.19.119",
41
41
  "@types/set-cookie-parser": "^2.4.7",
42
42
  "dts-buddy": "^0.6.2",
43
43
  "rollup": "^4.14.2",
44
- "svelte": "^5.46.4",
44
+ "svelte": "^5.48.4",
45
45
  "svelte-preprocess": "^6.0.0",
46
46
  "typescript": "^5.3.3",
47
47
  "vite": "^6.3.5",
@@ -1,4 +1,4 @@
1
- import { resolve } from '../../utils/url.js';
1
+ import { resolve, decode_uri } from '../../utils/url.js';
2
2
  import { decode } from './entities.js';
3
3
 
4
4
  const DOCTYPE = 'DOCTYPE';
@@ -193,11 +193,11 @@ export function crawl(html, base) {
193
193
  }
194
194
 
195
195
  if (id) {
196
- ids.push(id);
196
+ ids.push(decode_uri(id));
197
197
  }
198
198
 
199
199
  if (name && tag === 'A') {
200
- ids.push(name);
200
+ ids.push(decode_uri(name));
201
201
  }
202
202
 
203
203
  if (src) {
@@ -62,10 +62,8 @@ export function get_tsconfig(kit) {
62
62
  config_relative('vite.config.js'),
63
63
  config_relative('vite.config.ts')
64
64
  ]);
65
- // TODO(v2): find a better way to include all src files. We can't just use routes/lib only because
66
- // people might have other folders/files in src that they want included.
67
- const src_includes = [kit.files.routes, kit.files.lib, path.resolve('src')].filter((dir) => {
68
- const relative = path.relative(path.resolve('src'), dir);
65
+ const src_includes = [kit.files.routes, kit.files.lib, kit.files.src].filter((dir) => {
66
+ const relative = path.relative(kit.files.src, dir);
69
67
  return !relative || relative.startsWith('..');
70
68
  });
71
69
  for (const dir of src_includes) {
@@ -76,10 +74,14 @@ export function get_tsconfig(kit) {
76
74
 
77
75
  // Test folder is a special case - we advocate putting tests in a top-level test folder
78
76
  // and it's not configurable (should we make it?)
79
- const test_folder = project_relative('tests');
77
+ const test_folder = project_relative('test');
80
78
  include.add(config_relative(`${test_folder}/**/*.js`));
81
79
  include.add(config_relative(`${test_folder}/**/*.ts`));
82
80
  include.add(config_relative(`${test_folder}/**/*.svelte`));
81
+ const tests_folder = project_relative('tests');
82
+ include.add(config_relative(`${tests_folder}/**/*.js`));
83
+ include.add(config_relative(`${tests_folder}/**/*.ts`));
84
+ include.add(config_relative(`${tests_folder}/**/*.svelte`));
83
85
 
84
86
  const exclude = [config_relative('node_modules/**')];
85
87
  // Add service worker to exclude list so that worker types references in it don't spill over into the rest of the app
@@ -1200,6 +1200,19 @@ export interface NavigationTarget<
1200
1200
  * The URL that is navigated to
1201
1201
  */
1202
1202
  url: URL;
1203
+ /**
1204
+ * The scroll position associated with this navigation.
1205
+ *
1206
+ * For the `from` target, this is the scroll position at the moment of navigation.
1207
+ *
1208
+ * For the `to` target, this represents the scroll position that will be or was restored:
1209
+ * - In `beforeNavigate` and `onNavigate`, this is only available for `popstate` navigations (back/forward button)
1210
+ * and will be `null` for other navigation types, since the final scroll position isn't known
1211
+ * ahead of time.
1212
+ * - In `afterNavigate`, this is always the scroll position that was applied after the navigation
1213
+ * completed.
1214
+ */
1215
+ scroll: { x: number; y: number } | null;
1203
1216
  }
1204
1217
 
1205
1218
  /**
@@ -1249,7 +1262,7 @@ export interface NavigationEnter extends NavigationBase {
1249
1262
  delta?: undefined;
1250
1263
 
1251
1264
  /**
1252
- * Dispatched `Event` object when navigation occured by `popstate` or `link`.
1265
+ * Dispatched `Event` object when navigation occurred by `popstate` or `link`.
1253
1266
  */
1254
1267
  event?: undefined;
1255
1268
  }
@@ -5,14 +5,43 @@ import { s } from '../../../utils/misc.js';
5
5
  import { normalizePath } from 'vite';
6
6
  import { basename, join } from 'node:path';
7
7
  import { create_node_analyser } from '../static_analysis/index.js';
8
+ import { fix_css_urls } from '../../../utils/css.js';
8
9
 
10
+ /**
11
+ * Regenerate server nodes after acquiring client manifest
12
+ * @overload
13
+ * @param {string} out
14
+ * @param {import('types').ValidatedKitConfig} kit
15
+ * @param {import('types').ManifestData} manifest_data
16
+ * @param {import('vite').Manifest} server_manifest
17
+ * @param {import('vite').Manifest} client_manifest
18
+ * @param {string} assets_path
19
+ * @param {import('vite').Rollup.RollupOutput['output']} client_chunks
20
+ * @param {import('types').RecursiveRequired<import('types').ValidatedConfig['kit']['output']>} output_config
21
+ * @param {Map<string, { page_options: Record<string, any> | null, children: string[] }>} static_exports
22
+ * @returns {Promise<void>}
23
+ */
24
+ /**
25
+ * Build server nodes without client manifest for analysis phase
26
+ * @overload
27
+ * @param {string} out
28
+ * @param {import('types').ValidatedKitConfig} kit
29
+ * @param {import('types').ManifestData} manifest_data
30
+ * @param {import('vite').Manifest} server_manifest
31
+ * @param {null} client_manifest
32
+ * @param {null} assets_path
33
+ * @param {null} client_chunks
34
+ * @param {import('types').RecursiveRequired<import('types').ValidatedConfig['kit']['output']>} output_config
35
+ * @param {Map<string, { page_options: Record<string, any> | null, children: string[] }>} static_exports
36
+ * @returns {Promise<void>}
37
+ */
9
38
  /**
10
39
  * @param {string} out
11
40
  * @param {import('types').ValidatedKitConfig} kit
12
41
  * @param {import('types').ManifestData} manifest_data
13
42
  * @param {import('vite').Manifest} server_manifest
14
43
  * @param {import('vite').Manifest | null} client_manifest
15
- * @param {import('vite').Rollup.OutputBundle | null} server_bundle
44
+ * @param {string | null} assets_path
16
45
  * @param {import('vite').Rollup.RollupOutput['output'] | null} client_chunks
17
46
  * @param {import('types').RecursiveRequired<import('types').ValidatedConfig['kit']['output']>} output_config
18
47
  * @param {Map<string, { page_options: Record<string, any> | null, children: string[] }>} static_exports
@@ -23,7 +52,7 @@ export async function build_server_nodes(
23
52
  manifest_data,
24
53
  server_manifest,
25
54
  client_manifest,
26
- server_bundle,
55
+ assets_path,
27
56
  client_chunks,
28
57
  output_config,
29
58
  static_exports
@@ -31,38 +60,59 @@ export async function build_server_nodes(
31
60
  mkdirp(`${out}/server/nodes`);
32
61
  mkdirp(`${out}/server/stylesheets`);
33
62
 
34
- /** @type {Map<string, string>} */
63
+ /**
64
+ * Stylesheet names and their contents which are below the inline threshold
65
+ * @type {Map<string, string>}
66
+ */
35
67
  const stylesheets_to_inline = new Map();
36
68
 
37
- if (server_bundle && client_chunks && kit.inlineStyleThreshold > 0) {
38
- const client = get_stylesheets(client_chunks);
39
- const server = get_stylesheets(Object.values(server_bundle));
69
+ /**
70
+ * For CSS inlining, we either store a string or a function that returns the
71
+ * styles with the correct relative URLs
72
+ * @type {(css: string, eager_assets: Set<string>) => string}
73
+ */
74
+ let prepare_css_for_inlining = (css) => s(css);
40
75
 
41
- // map server stylesheet name to the client stylesheet name
42
- for (const [id, client_stylesheet] of client.stylesheets_used) {
43
- const server_stylesheet = server.stylesheets_used.get(id);
44
- if (!server_stylesheet) {
76
+ if (client_chunks && kit.inlineStyleThreshold > 0 && output_config.bundleStrategy === 'split') {
77
+ for (const chunk of client_chunks) {
78
+ if (chunk.type !== 'asset' || !chunk.fileName.endsWith('.css')) {
45
79
  continue;
46
80
  }
47
- client_stylesheet.forEach((file, i) => {
48
- stylesheets_to_inline.set(file, server_stylesheet[i]);
49
- });
50
- }
51
81
 
52
- // filter out stylesheets that should not be inlined
53
- for (const [fileName, content] of client.stylesheet_content) {
54
- if (content.length >= kit.inlineStyleThreshold) {
55
- stylesheets_to_inline.delete(fileName);
82
+ const source = chunk.source.toString();
83
+ if (source.length < kit.inlineStyleThreshold) {
84
+ stylesheets_to_inline.set(chunk.fileName, source);
56
85
  }
57
86
  }
58
87
 
59
- // map server stylesheet source to the client stylesheet name
60
- for (const [client_file, server_file] of stylesheets_to_inline) {
61
- const source = server.stylesheet_content.get(server_file);
62
- if (!source) {
63
- throw new Error(`Server stylesheet source not found for client stylesheet ${client_file}`);
64
- }
65
- stylesheets_to_inline.set(client_file, source);
88
+ // If the client CSS has URL references to assets, we need to adjust the
89
+ // relative path so that they are correct when inlined into the document.
90
+ // Although `paths.assets` is static, we need to pass in a fake path
91
+ // `/_svelte_kit_assets` at runtime when running `vite preview`
92
+ if (kit.paths.assets || kit.paths.relative) {
93
+ const static_assets = new Set(
94
+ manifest_data.assets.map((asset) => decodeURIComponent(asset.file))
95
+ );
96
+
97
+ const segments = /** @type {string} */ (assets_path).split('/');
98
+ const static_asset_prefix = segments.map(() => '..').join('/') + '/';
99
+
100
+ prepare_css_for_inlining = (css, eager_assets) => {
101
+ const transformed_css = fix_css_urls({
102
+ css,
103
+ vite_assets: eager_assets,
104
+ static_assets,
105
+ paths_assets: '${assets}',
106
+ base: '${base}',
107
+ static_asset_prefix
108
+ });
109
+
110
+ // only convert to a function if we have adjusted any URLs
111
+ if (css !== transformed_css) {
112
+ return `function css(assets, base) { return \`${s(transformed_css).slice(1, -1)}\`; }`;
113
+ }
114
+ return s(css);
115
+ };
66
116
  }
67
117
  }
68
118
 
@@ -96,6 +146,9 @@ export async function build_server_nodes(
96
146
  /** @type {string[]} */
97
147
  let fonts = [];
98
148
 
149
+ /** @type {Set<string>} */
150
+ let eager_assets = new Set();
151
+
99
152
  if (node.component && client_manifest) {
100
153
  exports.push(
101
154
  'let component_cache;',
@@ -135,7 +188,7 @@ export async function build_server_nodes(
135
188
  const entry_path = `${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`;
136
189
  const entry = find_deps(client_manifest, entry_path, true);
137
190
 
138
- // eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
191
+ // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
139
192
  // However, if it is not used during SSR (not present in the server manifest),
140
193
  // then it can be lazily loaded in the browser.
141
194
 
@@ -153,8 +206,6 @@ export async function build_server_nodes(
153
206
 
154
207
  /** @type {Set<string>} */
155
208
  const eager_css = new Set();
156
- /** @type {Set<string>} */
157
- const eager_assets = new Set();
158
209
 
159
210
  entry.stylesheet_map.forEach((value, filepath) => {
160
211
  // pages and layouts are renamed to node indexes when optimised for the client
@@ -180,27 +231,46 @@ export async function build_server_nodes(
180
231
  `export const fonts = ${s(fonts)};`
181
232
  );
182
233
 
183
- /** @type {string[]} */
184
- const inline_styles = [];
185
-
186
- stylesheets.forEach((file, i) => {
187
- if (stylesheets_to_inline.has(file)) {
188
- const filename = basename(file);
189
- const dest = `${out}/server/stylesheets/${filename}.js`;
190
- const source = stylesheets_to_inline.get(file);
191
- if (!source) {
192
- throw new Error(`Server stylesheet source not found for client stylesheet ${file}`);
234
+ /**
235
+ * Assets that have been processed by Vite (decoded and with the asset path stripped)
236
+ * @type {Set<string>}
237
+ */
238
+ let vite_assets = new Set();
239
+
240
+ // Keep track of Vite asset filenames so that we avoid touching unrelated ones
241
+ // when adjusting the inlined CSS
242
+ if (stylesheets_to_inline.size && assets_path && eager_assets.size) {
243
+ vite_assets = new Set(
244
+ Array.from(eager_assets).map((asset) => {
245
+ return decodeURIComponent(asset.replace(`${assets_path}/`, ''));
246
+ })
247
+ );
248
+ }
249
+
250
+ if (stylesheets_to_inline.size) {
251
+ /** @type {string[]} */
252
+ const inline_styles = [];
253
+
254
+ stylesheets.forEach((file, i) => {
255
+ if (stylesheets_to_inline.has(file)) {
256
+ const filename = basename(file);
257
+ const dest = `${out}/server/stylesheets/${filename}.js`;
258
+
259
+ let css = /** @type {string} */ (stylesheets_to_inline.get(file));
260
+
261
+ fs.writeFileSync(
262
+ dest,
263
+ `// ${filename}\nexport default ${prepare_css_for_inlining(css, vite_assets)};`
264
+ );
265
+ const name = `stylesheet_${i}`;
266
+ imports.push(`import ${name} from '../stylesheets/${filename}.js';`);
267
+ inline_styles.push(`\t${s(file)}: ${name}`);
193
268
  }
194
- fs.writeFileSync(dest, `// ${filename}\nexport default ${s(source)};`);
269
+ });
195
270
 
196
- const name = `stylesheet_${i}`;
197
- imports.push(`import ${name} from '../stylesheets/${filename}.js';`);
198
- inline_styles.push(`\t${s(file)}: ${name}`);
271
+ if (inline_styles.length > 0) {
272
+ exports.push(`export const inline_styles = () => ({\n${inline_styles.join(',\n')}\n});`);
199
273
  }
200
- });
201
-
202
- if (inline_styles.length > 0) {
203
- exports.push(`export const inline_styles = () => ({\n${inline_styles.join(',\n')}\n});`);
204
274
  }
205
275
 
206
276
  fs.writeFileSync(
@@ -209,37 +279,3 @@ export async function build_server_nodes(
209
279
  );
210
280
  }
211
281
  }
212
-
213
- /**
214
- * @param {(import('vite').Rollup.OutputAsset | import('vite').Rollup.OutputChunk)[]} chunks
215
- */
216
- function get_stylesheets(chunks) {
217
- /**
218
- * A map of module IDs and the stylesheets they use.
219
- * @type {Map<string, string[]>}
220
- */
221
- const stylesheets_used = new Map();
222
-
223
- /**
224
- * A map of stylesheet names and their content.
225
- * @type {Map<string, string>}
226
- */
227
- const stylesheet_content = new Map();
228
-
229
- for (const chunk of chunks) {
230
- if (chunk.type === 'asset') {
231
- if (chunk.fileName.endsWith('.css')) {
232
- stylesheet_content.set(chunk.fileName, chunk.source.toString());
233
- }
234
- continue;
235
- }
236
-
237
- if (chunk.viteMetadata?.importedCss.size) {
238
- const css = Array.from(chunk.viteMetadata.importedCss);
239
- for (const id of chunk.moduleIds) {
240
- stylesheets_used.set(id, css);
241
- }
242
- }
243
- }
244
- return { stylesheets_used, stylesheet_content };
245
- }
@@ -40,7 +40,7 @@ import {
40
40
  } from './module_ids.js';
41
41
  import { import_peer } from '../../utils/import.js';
42
42
  import { compact } from '../../utils/array.js';
43
- import { should_ignore } from './static_analysis/utils.js';
43
+ import { should_ignore, has_children } from './static_analysis/utils.js';
44
44
 
45
45
  const cwd = posixify(process.cwd());
46
46
 
@@ -112,10 +112,8 @@ const warning_preprocessor = {
112
112
  if (!filename) return;
113
113
 
114
114
  const basename = path.basename(filename);
115
- const has_children =
116
- content.includes('<slot') || (isSvelte5Plus() && content.includes('{@render'));
117
115
 
118
- if (basename.startsWith('+layout.') && !has_children) {
116
+ if (basename.startsWith('+layout.') && !has_children(content, isSvelte5Plus())) {
119
117
  const message =
120
118
  `\n${colors.bold().red(path.relative('.', filename))}\n` +
121
119
  `\`<slot />\`${isSvelte5Plus() ? ' or `{@render ...}` tag' : ''}` +
@@ -1031,7 +1029,7 @@ async function kit({ svelte_config }) {
1031
1029
  */
1032
1030
  writeBundle: {
1033
1031
  sequential: true,
1034
- async handler(_options, bundle) {
1032
+ async handler(_options) {
1035
1033
  if (secondary_build_started) return; // only run this once
1036
1034
 
1037
1035
  const verbose = vite_config.logLevel === 'info';
@@ -1271,7 +1269,7 @@ async function kit({ svelte_config }) {
1271
1269
  manifest_data,
1272
1270
  server_manifest,
1273
1271
  client_manifest,
1274
- bundle,
1272
+ assets_path,
1275
1273
  client_chunks,
1276
1274
  svelte_config.kit.output,
1277
1275
  static_exports
@@ -1,3 +1,20 @@
1
+ /**
2
+ * Check if content has children rendering (slot, @render, or children prop forwarding)
3
+ * @param {string} content - The markup content
4
+ * @param {boolean} is_svelte_5_plus - Whether the project uses Svelte 5+
5
+ * @returns {boolean}
6
+ */
7
+ export function has_children(content, is_svelte_5_plus) {
8
+ return (
9
+ content.includes('<slot') ||
10
+ (is_svelte_5_plus &&
11
+ (content.includes('{@render') ||
12
+ // children may be forwarded to a child component as a prop
13
+ content.includes('{children}') ||
14
+ content.includes('children={')))
15
+ );
16
+ }
17
+
1
18
  /**
2
19
  * Check if a match position is within a comment or a string
3
20
  * @param {string} content - The full content
@@ -1,6 +1,2 @@
1
- import { BROWSER, DEV } from 'esm-env';
1
+ export { BROWSER as browser, DEV as dev } from 'esm-env';
2
2
  export { building, version } from '__sveltekit/environment';
3
-
4
- export const browser = BROWSER;
5
-
6
- export const dev = DEV;
@@ -78,7 +78,9 @@ export function command(validate_or_fn, maybe_fn) {
78
78
 
79
79
  state.refreshes ??= {};
80
80
 
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
+ );
82
84
 
83
85
  // @ts-expect-error
84
86
  promise.updates = () => {
@@ -145,8 +145,7 @@ export function form(validate_or_fn, maybe_fn) {
145
145
  event,
146
146
  state,
147
147
  true,
148
- data,
149
- (d) => d,
148
+ () => data,
150
149
  (data) => (!maybe_fn ? fn() : fn(data, issue))
151
150
  );
152
151
  } catch (e) {
@@ -131,7 +131,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
131
131
  }
132
132
 
133
133
  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)
135
135
  );
136
136
 
137
137
  if (state.prerendering) {
@@ -5,6 +5,8 @@ import { get_request_store } from '@sveltejs/kit/internal/server';
5
5
  import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
6
6
  import { prerendering } from '__sveltekit/environment';
7
7
  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';
8
10
 
9
11
  /**
10
12
  * 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) {
73
75
  const { event, state } = get_request_store();
74
76
 
75
77
  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);
77
79
 
78
80
  /** @type {Promise<any> & Partial<RemoteQuery<any>>} */
79
81
  const promise = get_response(__, arg, state, get_remote_function_result);
@@ -137,7 +139,7 @@ export function query(validate_or_fn, maybe_fn) {
137
139
  */
138
140
  /*@__NO_SIDE_EFFECTS__*/
139
141
  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>} */
141
143
  const fn = maybe_fn ?? validate_or_fn;
142
144
 
143
145
  /** @type {(arg?: any) => MaybePromise<Input>} */
@@ -148,16 +150,34 @@ function batch(validate_or_fn, maybe_fn) {
148
150
  type: 'query_batch',
149
151
  id: '',
150
152
  name: '',
151
- run: (args) => {
153
+ run: async (args, options) => {
152
154
  const { event, state } = get_request_store();
153
155
 
154
156
  return run_remote_function(
155
157
  event,
156
158
  state,
157
159
  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
+ }
161
181
  );
162
182
  }
163
183
  };
@@ -190,22 +210,23 @@ function batch(validate_or_fn, maybe_fn) {
190
210
  batching = { args: [], resolvers: [] };
191
211
 
192
212
  try {
193
- const get_result = await run_remote_function(
213
+ return await run_remote_function(
194
214
  event,
195
215
  state,
196
216
  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
+ }
207
228
  }
208
- }
229
+ );
209
230
  } catch (error) {
210
231
  for (const resolver of batched.resolvers) {
211
232
  resolver.reject(error);
@@ -97,11 +97,10 @@ export function parse_remote_response(data, transport) {
97
97
  * @param {RequestEvent} event
98
98
  * @param {RequestState} state
99
99
  * @param {boolean} allow_cookies
100
- * @param {any} arg
101
- * @param {(arg: any) => any} validate
100
+ * @param {() => any} get_input
102
101
  * @param {(arg?: any) => T} fn
103
102
  */
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) {
105
104
  /** @type {RequestStore} */
106
105
  const store = {
107
106
  event: {
@@ -142,8 +141,8 @@ export async function run_remote_function(event, state, allow_cookies, arg, vali
142
141
  };
143
142
 
144
143
  // 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));
147
146
  }
148
147
 
149
148
  /**