@sveltejs/kit 2.43.8 → 2.45.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.
@@ -0,0 +1,439 @@
1
+ /** @import { RemoteForm } from '@sveltejs/kit' */
2
+ /** @import { InternalRemoteFormIssue } from 'types' */
3
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
+
5
+ import { DEV } from 'esm-env';
6
+
7
+ /**
8
+ * Sets a value in a nested object using a path string, not mutating the original object but returning a new object
9
+ * @param {Record<string, any>} object
10
+ * @param {string} path_string
11
+ * @param {any} value
12
+ */
13
+ export function set_nested_value(object, path_string, value) {
14
+ if (path_string.startsWith('n:')) {
15
+ path_string = path_string.slice(2);
16
+ value = value === '' ? undefined : parseFloat(value);
17
+ } else if (path_string.startsWith('b:')) {
18
+ path_string = path_string.slice(2);
19
+ value = value === 'on';
20
+ }
21
+
22
+ return deep_set(object, split_path(path_string), value);
23
+ }
24
+
25
+ /**
26
+ * Convert `FormData` into a POJO
27
+ * @param {FormData} data
28
+ */
29
+ export function convert_formdata(data) {
30
+ /** @type {Record<string, any>} */
31
+ let result = Object.create(null); // guard against prototype pollution
32
+
33
+ for (let key of data.keys()) {
34
+ if (key.startsWith('sveltekit:')) {
35
+ continue;
36
+ }
37
+
38
+ const is_array = key.endsWith('[]');
39
+ /** @type {any[]} */
40
+ let values = data.getAll(key);
41
+
42
+ if (is_array) key = key.slice(0, -2);
43
+
44
+ if (values.length > 1 && !is_array) {
45
+ throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
46
+ }
47
+
48
+ // an empty `<input type="file">` will submit a non-existent file, bizarrely
49
+ values = values.filter(
50
+ (entry) => typeof entry === 'string' || entry.name !== '' || entry.size > 0
51
+ );
52
+
53
+ if (key.startsWith('n:')) {
54
+ key = key.slice(2);
55
+ values = values.map((v) => (v === '' ? undefined : parseFloat(/** @type {string} */ (v))));
56
+ } else if (key.startsWith('b:')) {
57
+ key = key.slice(2);
58
+ values = values.map((v) => v === 'on');
59
+ }
60
+
61
+ result = set_nested_value(result, key, is_array ? values : values[0]);
62
+ }
63
+
64
+ return result;
65
+ }
66
+
67
+ const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/;
68
+
69
+ /**
70
+ * @param {string} path
71
+ */
72
+ export function split_path(path) {
73
+ if (!path_regex.test(path)) {
74
+ throw new Error(`Invalid path ${path}`);
75
+ }
76
+
77
+ return path.split(/\.|\[|\]/).filter(Boolean);
78
+ }
79
+
80
+ /**
81
+ * Sets a value in a nested object using an array of keys.
82
+ * Does not mutate the original object; returns a new object.
83
+ * @param {Record<string, any>} object
84
+ * @param {string[]} keys
85
+ * @param {any} value
86
+ */
87
+ export function deep_set(object, keys, value) {
88
+ const result = Object.assign(Object.create(null), object); // guard against prototype pollution
89
+ let current = result;
90
+
91
+ for (let i = 0; i < keys.length - 1; i += 1) {
92
+ const key = keys[i];
93
+ const is_array = /^\d+$/.test(keys[i + 1]);
94
+ const exists = key in current;
95
+ const inner = current[key];
96
+
97
+ if (exists && is_array !== Array.isArray(inner)) {
98
+ throw new Error(`Invalid array key ${keys[i + 1]}`);
99
+ }
100
+
101
+ current[key] = is_array
102
+ ? exists
103
+ ? [...inner]
104
+ : []
105
+ : // guard against prototype pollution
106
+ Object.assign(Object.create(null), inner);
107
+
108
+ current = current[key];
109
+ }
110
+
111
+ current[keys[keys.length - 1]] = value;
112
+ return result;
113
+ }
114
+
115
+ /**
116
+ * @param {readonly StandardSchemaV1.Issue[]} issues
117
+ * @param {boolean} [server=false] - Whether these issues come from server validation
118
+ */
119
+ export function flatten_issues(issues, server = false) {
120
+ /** @type {Record<string, InternalRemoteFormIssue[]>} */
121
+ const result = {};
122
+
123
+ for (const issue of issues) {
124
+ /** @type {InternalRemoteFormIssue} */
125
+ const normalized = { name: '', path: [], message: issue.message, server };
126
+
127
+ (result.$ ??= []).push(normalized);
128
+
129
+ let name = '';
130
+
131
+ if (issue.path !== undefined) {
132
+ for (const segment of issue.path) {
133
+ const key = /** @type {string | number} */ (
134
+ typeof segment === 'object' ? segment.key : segment
135
+ );
136
+
137
+ normalized.path.push(key);
138
+
139
+ if (typeof key === 'number') {
140
+ name += `[${key}]`;
141
+ } else if (typeof key === 'string') {
142
+ name += name === '' ? key : '.' + key;
143
+ }
144
+
145
+ (result[name] ??= []).push(normalized);
146
+ }
147
+
148
+ normalized.name = name;
149
+ }
150
+ }
151
+
152
+ return result;
153
+ }
154
+
155
+ /**
156
+ * Gets a nested value from an object using a path array
157
+ * @param {Record<string, any>} object
158
+ * @param {(string | number)[]} path
159
+ * @returns {any}
160
+ */
161
+
162
+ export function deep_get(object, path) {
163
+ let current = object;
164
+ for (const key of path) {
165
+ if (current == null || typeof current !== 'object') {
166
+ return current;
167
+ }
168
+ current = current[key];
169
+ }
170
+ return current;
171
+ }
172
+
173
+ /**
174
+ * Creates a proxy-based field accessor for form data
175
+ * @param {any} target - Function or empty POJO
176
+ * @param {() => Record<string, any>} get_input - Function to get current input data
177
+ * @param {(path: (string | number)[], value: any) => void} set_input - Function to set input data
178
+ * @param {() => Record<string, InternalRemoteFormIssue[]>} get_issues - Function to get current issues
179
+ * @param {(string | number)[]} path - Current access path
180
+ * @returns {any} Proxy object with name(), value(), and issues() methods
181
+ */
182
+ export function create_field_proxy(target, get_input, set_input, get_issues, path = []) {
183
+ return new Proxy(target, {
184
+ get(target, prop) {
185
+ if (typeof prop === 'symbol') return target[prop];
186
+
187
+ // Handle array access like jobs[0]
188
+ if (/^\d+$/.test(prop)) {
189
+ return create_field_proxy({}, get_input, set_input, get_issues, [
190
+ ...path,
191
+ parseInt(prop, 10)
192
+ ]);
193
+ }
194
+
195
+ const key = build_path_string(path);
196
+
197
+ if (prop === 'set') {
198
+ const set_func = function (/** @type {any} */ newValue) {
199
+ set_input(path, newValue);
200
+ return newValue;
201
+ };
202
+ return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]);
203
+ }
204
+
205
+ if (prop === 'value') {
206
+ const value_func = function () {
207
+ // TODO Ideally we'd create a $derived just above and use it here but we can't because of push_reaction which prevents
208
+ // changes to deriveds created within an effect to rerun the effect - an argument for
209
+ // reverting that change in async mode?
210
+ // TODO we did that in Svelte now; bump Svelte version and use $derived here
211
+ return deep_get(get_input(), path);
212
+ };
213
+ return create_field_proxy(value_func, get_input, set_input, get_issues, [...path, prop]);
214
+ }
215
+
216
+ if (prop === 'issues' || prop === 'allIssues') {
217
+ const issues_func = () => {
218
+ const all_issues = get_issues()[key === '' ? '$' : key];
219
+
220
+ if (prop === 'allIssues') {
221
+ return all_issues?.map((issue) => ({
222
+ message: issue.message
223
+ }));
224
+ }
225
+
226
+ return all_issues
227
+ ?.filter((issue) => issue.name === key)
228
+ ?.map((issue) => ({
229
+ message: issue.message
230
+ }));
231
+ };
232
+
233
+ return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]);
234
+ }
235
+
236
+ if (prop === 'as') {
237
+ /**
238
+ * @param {string} type
239
+ * @param {string} [input_value]
240
+ */
241
+ const as_func = (type, input_value) => {
242
+ const is_array =
243
+ type === 'file multiple' ||
244
+ type === 'select multiple' ||
245
+ (type === 'checkbox' && typeof input_value === 'string');
246
+
247
+ const prefix =
248
+ type === 'number' || type === 'range'
249
+ ? 'n:'
250
+ : type === 'checkbox' && !is_array
251
+ ? 'b:'
252
+ : '';
253
+
254
+ // Base properties for all input types
255
+ /** @type {Record<string, any>} */
256
+ const base_props = {
257
+ name: prefix + key + (is_array ? '[]' : ''),
258
+ get 'aria-invalid'() {
259
+ const issues = get_issues();
260
+ return key in issues ? 'true' : undefined;
261
+ }
262
+ };
263
+
264
+ // Add type attribute only for non-text inputs and non-select elements
265
+ if (type !== 'text' && type !== 'select' && type !== 'select multiple') {
266
+ base_props.type = type === 'file multiple' ? 'file' : type;
267
+ }
268
+
269
+ // Handle select inputs
270
+ if (type === 'select' || type === 'select multiple') {
271
+ return Object.defineProperties(base_props, {
272
+ multiple: { value: is_array, enumerable: true },
273
+ value: {
274
+ enumerable: true,
275
+ get() {
276
+ const input = get_input();
277
+ return deep_get(input, path);
278
+ }
279
+ }
280
+ });
281
+ }
282
+
283
+ // Handle checkbox inputs
284
+ if (type === 'checkbox' || type === 'radio') {
285
+ if (DEV) {
286
+ if (type === 'radio' && !input_value) {
287
+ throw new Error('Radio inputs must have a value');
288
+ }
289
+
290
+ if (type === 'checkbox' && is_array && !input_value) {
291
+ throw new Error('Checkbox array inputs must have a value');
292
+ }
293
+ }
294
+
295
+ return Object.defineProperties(base_props, {
296
+ value: { value: input_value ?? 'on', enumerable: true },
297
+ checked: {
298
+ enumerable: true,
299
+ get() {
300
+ const input = get_input();
301
+ const value = deep_get(input, path);
302
+
303
+ if (type === 'radio') {
304
+ return value === input_value;
305
+ }
306
+
307
+ if (is_array) {
308
+ return (value ?? []).includes(input_value);
309
+ }
310
+
311
+ return value;
312
+ }
313
+ }
314
+ });
315
+ }
316
+
317
+ // Handle file inputs
318
+ if (type === 'file' || type === 'file multiple') {
319
+ return Object.defineProperties(base_props, {
320
+ multiple: { value: is_array, enumerable: true },
321
+ files: {
322
+ enumerable: true,
323
+ get() {
324
+ const input = get_input();
325
+ const value = deep_get(input, path);
326
+
327
+ // Convert File/File[] to FileList-like object
328
+ if (value instanceof File) {
329
+ // In browsers, we can create a proper FileList using DataTransfer
330
+ if (typeof DataTransfer !== 'undefined') {
331
+ const fileList = new DataTransfer();
332
+ fileList.items.add(value);
333
+ return fileList.files;
334
+ }
335
+ // Fallback for environments without DataTransfer
336
+ return { 0: value, length: 1 };
337
+ }
338
+
339
+ if (Array.isArray(value) && value.every((f) => f instanceof File)) {
340
+ if (typeof DataTransfer !== 'undefined') {
341
+ const fileList = new DataTransfer();
342
+ value.forEach((file) => fileList.items.add(file));
343
+ return fileList.files;
344
+ }
345
+ // Fallback for environments without DataTransfer
346
+ /** @type {any} */
347
+ const fileListLike = { length: value.length };
348
+ value.forEach((file, index) => {
349
+ fileListLike[index] = file;
350
+ });
351
+ return fileListLike;
352
+ }
353
+
354
+ return null;
355
+ }
356
+ }
357
+ });
358
+ }
359
+
360
+ // Handle all other input types (text, number, etc.)
361
+ return Object.defineProperties(base_props, {
362
+ value: {
363
+ enumerable: true,
364
+ get() {
365
+ const input = get_input();
366
+ const value = deep_get(input, path);
367
+
368
+ return value != null ? String(value) : '';
369
+ }
370
+ }
371
+ });
372
+ };
373
+
374
+ return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, 'as']);
375
+ }
376
+
377
+ // Handle property access (nested fields)
378
+ return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]);
379
+ }
380
+ });
381
+ }
382
+
383
+ /**
384
+ * Builds a path string from an array of path segments
385
+ * @param {(string | number)[]} path
386
+ * @returns {string}
387
+ */
388
+ function build_path_string(path) {
389
+ let result = '';
390
+
391
+ for (const segment of path) {
392
+ if (typeof segment === 'number') {
393
+ result += `[${segment}]`;
394
+ } else {
395
+ result += result === '' ? segment : '.' + segment;
396
+ }
397
+ }
398
+
399
+ return result;
400
+ }
401
+
402
+ /**
403
+ * @param {RemoteForm<any, any>} instance
404
+ * @deprecated remove in 3.0
405
+ */
406
+ export function throw_on_old_property_access(instance) {
407
+ Object.defineProperty(instance, 'field', {
408
+ value: (/** @type {string} */ name) => {
409
+ const new_name = name.endsWith('[]') ? name.slice(0, -2) : name;
410
+ throw new Error(
411
+ `\`form.field\` has been removed: Instead of \`<input name={form.field('${name}')} />\` do \`<input {...form.fields.${new_name}.as(type)} />\``
412
+ );
413
+ }
414
+ });
415
+
416
+ for (const property of ['input', 'issues']) {
417
+ Object.defineProperty(instance, property, {
418
+ get() {
419
+ const new_name = property === 'issues' ? 'issues' : 'value';
420
+ return new Proxy(
421
+ {},
422
+ {
423
+ get(_, prop) {
424
+ const prop_string = typeof prop === 'string' ? prop : String(prop);
425
+ const old =
426
+ prop_string.includes('[') || prop_string.includes('.')
427
+ ? `['${prop_string}']`
428
+ : `.${prop_string}`;
429
+ const replacement = `.${prop_string}.${new_name}()`;
430
+ throw new Error(
431
+ `\`form.${property}\` has been removed: Instead of \`form.${property}${old}\` write \`form.fields${replacement}\``
432
+ );
433
+ }
434
+ }
435
+ );
436
+ }
437
+ });
438
+ }
439
+ }
@@ -13,7 +13,7 @@ import { SVELTE_KIT_ASSETS } from '../../../constants.js';
13
13
  import { SCHEME } from '../../../utils/url.js';
14
14
  import { create_server_routing_response, generate_route_object } from './server_routing.js';
15
15
  import { add_resolution_suffix } from '../../pathname.js';
16
- import { with_request_store } from '@sveltejs/kit/internal/server';
16
+ import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
17
17
  import { text_encoder } from '../../utils.js';
18
18
  import { get_global_name } from '../utils.js';
19
19
  import { create_remote_cache_key } from '../../shared.js';
@@ -190,7 +190,7 @@ export async function render_response({
190
190
  throw new Error(
191
191
  `Cannot call \`fetch\` eagerly during server-side rendering with relative URL (${info}) — put your \`fetch\` calls inside \`onMount\` or a \`load\` function instead`
192
192
  );
193
- } else if (!warned) {
193
+ } else if (!warned && !try_get_request_store()?.state.is_in_remote_function) {
194
194
  console.warn(
195
195
  'Avoid calling `fetch` eagerly during server-side rendering — put your `fetch` calls inside `onMount` or a `load` function instead'
196
196
  );
@@ -12,7 +12,6 @@ import { normalize_error } from '../../utils/error.js';
12
12
  import { check_incorrect_fail_use } from './page/actions.js';
13
13
  import { DEV } from 'esm-env';
14
14
  import { record_span } from '../telemetry/record_span.js';
15
- import { file_transport } from '../utils.js';
16
15
 
17
16
  /** @type {typeof handle_remote_call_internal} */
18
17
  export async function handle_remote_call(event, state, options, manifest, id) {
@@ -38,7 +37,7 @@ export async function handle_remote_call(event, state, options, manifest, id) {
38
37
  * @param {string} id
39
38
  */
40
39
  async function handle_remote_call_internal(event, state, options, manifest, id) {
41
- const [hash, name, prerender_args] = id.split('/');
40
+ const [hash, name, additional_args] = id.split('/');
42
41
  const remotes = manifest._.remotes;
43
42
 
44
43
  if (!remotes[hash]) error(404);
@@ -123,13 +122,18 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
123
122
  );
124
123
  form_data.delete('sveltekit:remote_refreshes');
125
124
 
125
+ // If this is a keyed form instance (created via form.for(key)), add the key to the form data (unless already set)
126
+ if (additional_args) {
127
+ form_data.set('sveltekit:id', decodeURIComponent(additional_args));
128
+ }
129
+
126
130
  const fn = info.fn;
127
131
  const data = await with_request_store({ event, state }, () => fn(form_data));
128
132
 
129
133
  return json(
130
134
  /** @type {RemoteFunctionResponse} */ ({
131
135
  type: 'result',
132
- result: stringify(data, { ...transport, File: file_transport }),
136
+ result: stringify(data, transport),
133
137
  refreshes: data.issues ? {} : await serialize_refreshes(form_client_refreshes)
134
138
  })
135
139
  );
@@ -152,7 +156,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
152
156
 
153
157
  const payload =
154
158
  info.type === 'prerender'
155
- ? prerender_args
159
+ ? additional_args
156
160
  : /** @type {string} */ (
157
161
  // new URL(...) necessary because we're hiding the URL from the user in the event object
158
162
  new URL(event.request.url).searchParams.get('payload')
@@ -290,6 +294,12 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
290
294
  const form_data = await event.request.formData();
291
295
  const fn = /** @type {RemoteInfo & { type: 'form' }} */ (/** @type {any} */ (form).__).fn;
292
296
 
297
+ // If this is a keyed form instance (created via form.for(key)), add the key to the form data (unless already set)
298
+ if (action_id && !form_data.has('id')) {
299
+ // The action_id is URL-encoded JSON, decode and parse it
300
+ form_data.set('sveltekit:id', decodeURIComponent(action_id));
301
+ }
302
+
293
303
  await with_request_store({ event, state }, () => fn(form_data));
294
304
 
295
305
  // We don't want the data to appear on `let { form } = $props()`, which is why we're not returning it.
@@ -129,8 +129,8 @@ export async function internal_respond(request, options, manifest, state) {
129
129
  .map((node) => node === '1');
130
130
  url.searchParams.delete(INVALIDATED_PARAM);
131
131
  } else if (remote_id) {
132
- url.pathname = base;
133
- url.search = '';
132
+ url.pathname = request.headers.get('x-sveltekit-pathname') ?? base;
133
+ url.search = request.headers.get('x-sveltekit-search') ?? '';
134
134
  }
135
135
 
136
136
  /** @type {Record<string, string>} */
@@ -148,7 +148,8 @@ export async function internal_respond(request, options, manifest, state) {
148
148
  handleValidationError: options.hooks.handleValidationError,
149
149
  tracing: {
150
150
  record_span
151
- }
151
+ },
152
+ is_in_remote_function: false
152
153
  };
153
154
 
154
155
  /** @type {import('@sveltejs/kit').RequestEvent} */
@@ -294,7 +295,7 @@ export async function internal_respond(request, options, manifest, state) {
294
295
  return text('Not found', { status: 404, headers });
295
296
  }
296
297
 
297
- if (!state.prerendering?.fallback && !remote_id) {
298
+ if (!state.prerendering?.fallback) {
298
299
  // TODO this could theoretically break — should probably be inside a try-catch
299
300
  const matchers = await manifest._.matchers();
300
301
 
@@ -329,7 +330,7 @@ export async function internal_respond(request, options, manifest, state) {
329
330
  : undefined;
330
331
 
331
332
  // determine whether we need to redirect to add/remove a trailing slash
332
- if (route) {
333
+ if (route && !remote_id) {
333
334
  // if `paths.base === '/a/b/c`, then the root route is `/a/b/c/`,
334
335
  // regardless of the `trailingSlash` route option
335
336
  if (url.pathname === base || url.pathname === base + '/') {
@@ -1,5 +1,3 @@
1
- /** @import { RemoteFormIssue } from '@sveltejs/kit' */
2
- /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
3
1
  import { BROWSER } from 'esm-env';
4
2
 
5
3
  export const text_encoder = new TextEncoder();
@@ -65,124 +63,3 @@ export function base64_decode(encoded) {
65
63
 
66
64
  return bytes;
67
65
  }
68
-
69
- /**
70
- * Convert `FormData` into a POJO
71
- * @param {FormData} data
72
- */
73
- export function convert_formdata(data) {
74
- /** @type {Record<string, any>} */
75
- const result = Object.create(null); // guard against prototype pollution
76
-
77
- for (let key of data.keys()) {
78
- const is_array = key.endsWith('[]');
79
- let values = data.getAll(key);
80
-
81
- if (is_array) key = key.slice(0, -2);
82
-
83
- if (values.length > 1 && !is_array) {
84
- throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
85
- }
86
-
87
- // an empty `<input type="file">` will submit a non-existent file, bizarrely
88
- values = values.filter(
89
- (entry) => typeof entry === 'string' || entry.name !== '' || entry.size > 0
90
- );
91
-
92
- deep_set(result, split_path(key), is_array ? values : values[0]);
93
- }
94
-
95
- return result;
96
- }
97
-
98
- const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/;
99
-
100
- /**
101
- * @param {string} path
102
- */
103
- export function split_path(path) {
104
- if (!path_regex.test(path)) {
105
- throw new Error(`Invalid path ${path}`);
106
- }
107
-
108
- return path.split(/\.|\[|\]/).filter(Boolean);
109
- }
110
-
111
- /**
112
- * @param {Record<string, any>} object
113
- * @param {string[]} keys
114
- * @param {any} value
115
- */
116
- export function deep_set(object, keys, value) {
117
- for (let i = 0; i < keys.length - 1; i += 1) {
118
- const key = keys[i];
119
- const is_array = /^\d+$/.test(keys[i + 1]);
120
-
121
- if (object[key]) {
122
- if (is_array !== Array.isArray(object[key])) {
123
- throw new Error(`Invalid array key ${keys[i + 1]}`);
124
- }
125
- } else {
126
- object[key] ??= is_array ? [] : Object.create(null); // guard against prototype pollution
127
- }
128
- object = object[key];
129
- }
130
-
131
- object[keys[keys.length - 1]] = value;
132
- }
133
-
134
- /**
135
- * @param {readonly StandardSchemaV1.Issue[]} issues
136
- */
137
- export function flatten_issues(issues) {
138
- /** @type {Record<string, RemoteFormIssue[]>} */
139
- const result = {};
140
-
141
- for (const issue of issues) {
142
- /** @type {RemoteFormIssue} */
143
- const normalized = { name: '', path: [], message: issue.message };
144
-
145
- (result.$ ??= []).push(normalized);
146
-
147
- let name = '';
148
-
149
- if (issue.path !== undefined) {
150
- for (const segment of issue.path) {
151
- const key = /** @type {string | number} */ (
152
- typeof segment === 'object' ? segment.key : segment
153
- );
154
-
155
- normalized.path.push(key);
156
-
157
- if (typeof key === 'number') {
158
- name += `[${key}]`;
159
- } else if (typeof key === 'string') {
160
- name += name === '' ? key : '.' + key;
161
- }
162
-
163
- (result[name] ??= []).push(normalized);
164
- }
165
-
166
- normalized.name = name;
167
- }
168
- }
169
-
170
- return result;
171
- }
172
-
173
- /**
174
- * We need to encode `File` objects when returning `issues` from a `form` submission,
175
- * because some validators include the original value in the issue. It doesn't
176
- * need to deserialize to a `File` object
177
- * @type {import('@sveltejs/kit').Transporter}
178
- */
179
- export const file_transport = {
180
- encode: (file) =>
181
- file instanceof File && {
182
- size: file.size,
183
- type: file.type,
184
- name: file.name,
185
- lastModified: file.lastModified
186
- },
187
- decode: (data) => data
188
- };