@sveltejs/kit 2.41.0 → 2.42.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.41.0",
3
+ "version": "2.42.1",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -1810,28 +1810,105 @@ export interface Snapshot<T = any> {
1810
1810
  restore: (snapshot: T) => void;
1811
1811
  }
1812
1812
 
1813
+ // If T is unknown or RemoteFormInput, the types below will recurse indefinitely and create giant unions that TS can't handle
1814
+ type WillRecurseIndefinitely<T> = unknown extends T
1815
+ ? true
1816
+ : RemoteFormInput extends T
1817
+ ? true
1818
+ : false;
1819
+
1820
+ // Helper type to convert union to intersection
1821
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
1822
+ ? I
1823
+ : never;
1824
+
1825
+ type FlattenInput<T, Prefix extends string> =
1826
+ WillRecurseIndefinitely<T> extends true
1827
+ ? { [key: string]: string }
1828
+ : T extends Array<infer U>
1829
+ ? U extends string | File
1830
+ ? { [P in Prefix]: string[] }
1831
+ : FlattenInput<U, `${Prefix}[${number}]`>
1832
+ : T extends File
1833
+ ? { [P in Prefix]: string }
1834
+ : T extends object
1835
+ ? {
1836
+ [K in keyof T]: FlattenInput<
1837
+ T[K],
1838
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1839
+ >;
1840
+ }[keyof T]
1841
+ : { [P in Prefix]: string };
1842
+
1843
+ type FlattenIssues<T, Prefix extends string> =
1844
+ WillRecurseIndefinitely<T> extends true
1845
+ ? { [key: string]: RemoteFormIssue[] }
1846
+ : T extends Array<infer U>
1847
+ ? { [P in Prefix | `${Prefix}[${number}]`]: RemoteFormIssue[] } & FlattenIssues<
1848
+ U,
1849
+ `${Prefix}[${number}]`
1850
+ >
1851
+ : T extends File
1852
+ ? { [P in Prefix]: RemoteFormIssue[] }
1853
+ : T extends object
1854
+ ? {
1855
+ [K in keyof T]: FlattenIssues<
1856
+ T[K],
1857
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1858
+ >;
1859
+ }[keyof T]
1860
+ : { [P in Prefix]: RemoteFormIssue[] };
1861
+
1862
+ type FlattenKeys<T, Prefix extends string> =
1863
+ WillRecurseIndefinitely<T> extends true
1864
+ ? { [key: string]: string }
1865
+ : T extends Array<infer U>
1866
+ ? U extends string | File
1867
+ ? { [P in `${Prefix}[]`]: string[] }
1868
+ : FlattenKeys<U, `${Prefix}[${number}]`>
1869
+ : T extends File
1870
+ ? { [P in Prefix]: string }
1871
+ : T extends object
1872
+ ? {
1873
+ [K in keyof T]: FlattenKeys<
1874
+ T[K],
1875
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1876
+ >;
1877
+ }[keyof T]
1878
+ : { [P in Prefix]: string };
1879
+
1880
+ export interface RemoteFormInput {
1881
+ [key: string]: FormDataEntryValue | FormDataEntryValue[] | RemoteFormInput | RemoteFormInput[];
1882
+ }
1883
+
1884
+ export interface RemoteFormIssue {
1885
+ name: string;
1886
+ path: Array<string | number>;
1887
+ message: string;
1888
+ }
1889
+
1813
1890
  /**
1814
1891
  * The return value of a remote `form` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
1815
1892
  */
1816
- export type RemoteForm<Result> = {
1893
+ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
1894
+ /** Attachment that sets up an event handler that intercepts the form submission on the client to prevent a full page reload */
1895
+ [attachment: symbol]: (node: HTMLFormElement) => void;
1817
1896
  method: 'POST';
1818
1897
  /** The URL to send the form to. */
1819
1898
  action: string;
1820
- /** Event handler that intercepts the form submission on the client to prevent a full page reload */
1821
- onsubmit: (event: SubmitEvent) => void;
1822
1899
  /** Use the `enhance` method to influence what happens when the form is submitted. */
1823
1900
  enhance(
1824
1901
  callback: (opts: {
1825
1902
  form: HTMLFormElement;
1826
- data: FormData;
1903
+ data: Input;
1827
1904
  submit: () => Promise<void> & {
1828
1905
  updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => Promise<void>;
1829
1906
  };
1830
- }) => void
1907
+ }) => void | Promise<void>
1831
1908
  ): {
1832
1909
  method: 'POST';
1833
1910
  action: string;
1834
- onsubmit: (event: SubmitEvent) => void;
1911
+ [attachment: symbol]: (node: HTMLFormElement) => void;
1835
1912
  };
1836
1913
  /**
1837
1914
  * Create an instance of the form for the given key.
@@ -1847,11 +1924,27 @@ export type RemoteForm<Result> = {
1847
1924
  * {/each}
1848
1925
  * ```
1849
1926
  */
1850
- for(key: string | number | boolean): Omit<RemoteForm<Result>, 'for'>;
1927
+ for(key: string | number | boolean): Omit<RemoteForm<Input, Output>, 'for'>;
1928
+ /**
1929
+ * This method exists to allow you to typecheck `name` attributes. It returns its argument
1930
+ * @example
1931
+ * ```svelte
1932
+ * <input name={login.field('username')} />
1933
+ * ```
1934
+ **/
1935
+ field<Name extends keyof UnionToIntersection<FlattenKeys<Input, ''>>>(string: Name): Name;
1936
+ /** Preflight checks */
1937
+ preflight(schema: StandardSchemaV1<Input, any>): RemoteForm<Input, Output>;
1938
+ /** Validate the form contents programmatically */
1939
+ validate(options?: { includeUntouched?: boolean }): Promise<void>;
1851
1940
  /** The result of the form submission */
1852
- get result(): Result | undefined;
1941
+ get result(): Output | undefined;
1853
1942
  /** The number of pending submissions */
1854
1943
  get pending(): number;
1944
+ /** The submitted values */
1945
+ input: null | UnionToIntersection<FlattenInput<Input, ''>>;
1946
+ /** Validation issues */
1947
+ issues: null | UnionToIntersection<FlattenIssues<Input, ''>>;
1855
1948
  /** Spread this onto a `<button>` or `<input type="submit">` */
1856
1949
  buttonProps: {
1857
1950
  type: 'submit';
@@ -1862,11 +1955,11 @@ export type RemoteForm<Result> = {
1862
1955
  enhance(
1863
1956
  callback: (opts: {
1864
1957
  form: HTMLFormElement;
1865
- data: FormData;
1958
+ data: Input;
1866
1959
  submit: () => Promise<void> & {
1867
1960
  updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => Promise<void>;
1868
1961
  };
1869
- }) => void
1962
+ }) => void | Promise<void>
1870
1963
  ): {
1871
1964
  type: 'submit';
1872
1965
  formmethod: 'POST';
@@ -682,9 +682,13 @@ async function kit({ svelte_config }) {
682
682
  config.build.rollupOptions.output = {
683
683
  ...config.build.rollupOptions.output,
684
684
  manualChunks(id, meta) {
685
+ // Prevent core runtime and env from ending up in a remote chunk, which could break because of initialization order
685
686
  if (id === `${runtime_directory}/app/server/index.js`) {
686
687
  return 'app-server';
687
688
  }
689
+ if (id === `${runtime_directory}/shared-server.js`) {
690
+ return 'app-shared-server';
691
+ }
688
692
 
689
693
  // Check if this is a *.remote.ts file
690
694
  if (svelte_config.kit.moduleExtensions.some((ext) => id.endsWith(`.remote${ext}`))) {
@@ -1,34 +1,77 @@
1
- /** @import { RemoteForm } from '@sveltejs/kit' */
2
- /** @import { RemoteInfo, MaybePromise } from 'types' */
1
+ /** @import { RemoteFormInput, RemoteForm } from '@sveltejs/kit' */
2
+ /** @import { MaybePromise, RemoteInfo } from 'types' */
3
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
3
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
5
+ import { DEV } from 'esm-env';
4
6
  import { run_remote_function } from './shared.js';
7
+ import { convert_formdata, flatten_issues } from '../../../utils.js';
5
8
 
6
9
  /**
7
10
  * Creates a form object that can be spread onto a `<form>` element.
8
11
  *
9
12
  * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
10
13
  *
11
- * @template T
12
- * @param {(data: FormData) => MaybePromise<T>} fn
13
- * @returns {RemoteForm<T>}
14
+ * @template Output
15
+ * @overload
16
+ * @param {() => Output} fn
17
+ * @returns {RemoteForm<void, Output>}
18
+ * @since 2.27
19
+ */
20
+ /**
21
+ * Creates a form object that can be spread onto a `<form>` element.
22
+ *
23
+ * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
24
+ *
25
+ * @template {RemoteFormInput} Input
26
+ * @template Output
27
+ * @overload
28
+ * @param {'unchecked'} validate
29
+ * @param {(data: Input) => MaybePromise<Output>} fn
30
+ * @returns {RemoteForm<Input, Output>}
31
+ * @since 2.27
32
+ */
33
+ /**
34
+ * Creates a form object that can be spread onto a `<form>` element.
35
+ *
36
+ * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
37
+ *
38
+ * @template {StandardSchemaV1<RemoteFormInput, Record<string, any>>} Schema
39
+ * @template Output
40
+ * @overload
41
+ * @param {Schema} validate
42
+ * @param {(data: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>} fn
43
+ * @returns {RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>}
44
+ * @since 2.27
45
+ */
46
+ /**
47
+ * @template {RemoteFormInput} Input
48
+ * @template Output
49
+ * @param {any} validate_or_fn
50
+ * @param {(data?: Input) => MaybePromise<Output>} [maybe_fn]
51
+ * @returns {RemoteForm<Input, Output>}
14
52
  * @since 2.27
15
53
  */
16
54
  /*@__NO_SIDE_EFFECTS__*/
17
55
  // @ts-ignore we don't want to prefix `fn` with an underscore, as that will be user-visible
18
- export function form(fn) {
56
+ export function form(validate_or_fn, maybe_fn) {
57
+ /** @type {(data?: Input) => Output} */
58
+ const fn = maybe_fn ?? validate_or_fn;
59
+
60
+ /** @type {StandardSchemaV1 | null} */
61
+ const schema = !maybe_fn || validate_or_fn === 'unchecked' ? null : validate_or_fn;
62
+
19
63
  /**
20
64
  * @param {string | number | boolean} [key]
21
65
  */
22
66
  function create_instance(key) {
23
- /** @type {RemoteForm<T>} */
67
+ /** @type {RemoteForm<Input, Output>} */
24
68
  const instance = {};
25
69
 
26
70
  instance.method = 'POST';
27
- instance.onsubmit = () => {};
28
71
 
29
72
  Object.defineProperty(instance, 'enhance', {
30
73
  value: () => {
31
- return { action: instance.action, method: instance.method, onsubmit: instance.onsubmit };
74
+ return { action: instance.action, method: instance.method };
32
75
  }
33
76
  });
34
77
 
@@ -54,19 +97,79 @@ export function form(fn) {
54
97
  id: '',
55
98
  /** @param {FormData} form_data */
56
99
  fn: async (form_data) => {
100
+ const validate_only = form_data.get('sveltekit:validate_only') === 'true';
101
+ form_data.delete('sveltekit:validate_only');
102
+
103
+ let data = maybe_fn ? convert_formdata(form_data) : undefined;
104
+
105
+ // TODO 3.0 remove this warning
106
+ if (DEV && !data) {
107
+ const error = () => {
108
+ throw new Error(
109
+ 'Remote form functions no longer get passed a FormData object. ' +
110
+ "`form` now has the same signature as `query` or `command`, i.e. it expects to be invoked like `form(schema, callback)` or `form('unchecked', callback)`. " +
111
+ 'The payload of the callback function is now a POJO instead of a FormData object. See https://kit.svelte.dev/docs/remote-functions#form for details.'
112
+ );
113
+ };
114
+ data = {};
115
+ for (const key of [
116
+ 'append',
117
+ 'delete',
118
+ 'entries',
119
+ 'forEach',
120
+ 'get',
121
+ 'getAll',
122
+ 'has',
123
+ 'keys',
124
+ 'set',
125
+ 'values'
126
+ ]) {
127
+ Object.defineProperty(data, key, { get: error });
128
+ }
129
+ }
130
+
131
+ /** @type {{ input?: Record<string, string | string[]>, issues?: Record<string, StandardSchemaV1.Issue[]>, result: Output }} */
132
+ const output = {};
133
+
57
134
  const { event, state } = get_request_store();
135
+ const validated = await schema?.['~standard'].validate(data);
136
+
137
+ if (validate_only) {
138
+ return validated?.issues ?? [];
139
+ }
140
+
141
+ if (validated?.issues !== undefined) {
142
+ output.issues = flatten_issues(validated.issues);
143
+ output.input = {};
144
+
145
+ for (let key of form_data.keys()) {
146
+ // redact sensitive fields
147
+ if (/^[.\]]?_/.test(key)) continue;
148
+
149
+ const is_array = key.endsWith('[]');
150
+ const values = form_data.getAll(key).filter((value) => typeof value === 'string');
58
151
 
59
- state.refreshes ??= {};
152
+ if (is_array) key = key.slice(0, -2);
60
153
 
61
- const result = await run_remote_function(event, state, true, form_data, (d) => d, fn);
154
+ output.input[key] = is_array ? values : values[0];
155
+ }
156
+ } else {
157
+ if (validated !== undefined) {
158
+ data = validated.value;
159
+ }
160
+
161
+ state.refreshes ??= {};
162
+
163
+ output.result = await run_remote_function(event, state, true, data, (d) => d, fn);
164
+ }
62
165
 
63
166
  // We don't need to care about args or deduplicating calls, because uneval results are only relevant in full page reloads
64
167
  // where only one form submission is active at the same time
65
168
  if (!event.isRemoteRequest) {
66
- (state.remote_data ??= {})[__.id] = result;
169
+ (state.remote_data ??= {})[__.id] = output;
67
170
  }
68
171
 
69
- return result;
172
+ return output;
70
173
  }
71
174
  };
72
175
 
@@ -82,11 +185,24 @@ export function form(fn) {
82
185
  enumerable: true
83
186
  });
84
187
 
188
+ for (const property of ['input', 'issues']) {
189
+ Object.defineProperty(instance, property, {
190
+ get() {
191
+ try {
192
+ const { remote_data } = get_request_store().state;
193
+ return remote_data?.[__.id]?.[property] ?? {};
194
+ } catch {
195
+ return undefined;
196
+ }
197
+ }
198
+ });
199
+ }
200
+
85
201
  Object.defineProperty(instance, 'result', {
86
202
  get() {
87
203
  try {
88
204
  const { remote_data } = get_request_store().state;
89
- return remote_data?.[__.id];
205
+ return remote_data?.[__.id]?.result;
90
206
  } catch {
91
207
  return undefined;
92
208
  }
@@ -103,9 +219,24 @@ export function form(fn) {
103
219
  get: () => 0
104
220
  });
105
221
 
222
+ Object.defineProperty(instance, 'field', {
223
+ value: (/** @type {string} */ name) => name
224
+ });
225
+
226
+ Object.defineProperty(instance, 'preflight', {
227
+ // preflight is a noop on the server
228
+ value: () => instance
229
+ });
230
+
231
+ Object.defineProperty(instance, 'validate', {
232
+ value: () => {
233
+ throw new Error('Cannot call validate() on the server');
234
+ }
235
+ });
236
+
106
237
  if (key == undefined) {
107
238
  Object.defineProperty(instance, 'for', {
108
- /** @type {RemoteForm<any>['for']} */
239
+ /** @type {RemoteForm<any, any>['for']} */
109
240
  value: (key) => {
110
241
  const { state } = get_request_store();
111
242
  const cache_key = __.id + '|' + JSON.stringify(key);
@@ -1,4 +1,5 @@
1
- /** @import { RemoteForm, RemoteQueryOverride } from '@sveltejs/kit' */
1
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
+ /** @import { RemoteFormInput, RemoteForm, RemoteQueryOverride } from '@sveltejs/kit' */
2
3
  /** @import { RemoteFunctionResponse } from 'types' */
3
4
  /** @import { Query } from './query.svelte.js' */
4
5
  import { app_dir, base } from '__sveltekit/paths';
@@ -15,15 +16,18 @@ import {
15
16
  } from '../client.js';
16
17
  import { tick } from 'svelte';
17
18
  import { refresh_queries, release_overrides } from './shared.svelte.js';
19
+ import { createAttachmentKey } from 'svelte/attachments';
20
+ import { convert_formdata, file_transport, flatten_issues } from '../../utils.js';
18
21
 
19
22
  /**
20
23
  * Client-version of the `form` function from `$app/server`.
21
- * @template T
24
+ * @template {RemoteFormInput} T
25
+ * @template U
22
26
  * @param {string} id
23
- * @returns {RemoteForm<T>}
27
+ * @returns {RemoteForm<T, U>}
24
28
  */
25
29
  export function form(id) {
26
- /** @type {Map<any, { count: number, instance: RemoteForm<T> }>} */
30
+ /** @type {Map<any, { count: number, instance: RemoteForm<T, U> }>} */
27
31
  const instances = new Map();
28
32
 
29
33
  /** @param {string | number | boolean} [key] */
@@ -31,12 +35,80 @@ export function form(id) {
31
35
  const action_id = id + (key != undefined ? `/${JSON.stringify(key)}` : '');
32
36
  const action = '?/remote=' + encodeURIComponent(action_id);
33
37
 
38
+ /** @type {Record<string, string | string[] | File | File[]>} */
39
+ let input = $state({});
40
+
41
+ /** @type {Record<string, StandardSchemaV1.Issue[]>} */
42
+ let issues = $state.raw({});
43
+
34
44
  /** @type {any} */
35
45
  let result = $state.raw(started ? undefined : remote_responses[action_id]);
36
46
 
37
47
  /** @type {number} */
38
48
  let pending_count = $state(0);
39
49
 
50
+ /** @type {StandardSchemaV1 | undefined} */
51
+ let preflight_schema = undefined;
52
+
53
+ /** @type {HTMLFormElement | null} */
54
+ let element = null;
55
+
56
+ /** @type {Record<string, boolean>} */
57
+ let touched = {};
58
+
59
+ /**
60
+ * @param {HTMLFormElement} form
61
+ * @param {FormData} form_data
62
+ * @param {Parameters<RemoteForm<any, any>['enhance']>[0]} callback
63
+ */
64
+ async function handle_submit(form, form_data, callback) {
65
+ const data = convert_formdata(form_data);
66
+
67
+ const validated = await preflight_schema?.['~standard'].validate(data);
68
+
69
+ if (validated?.issues) {
70
+ issues = flatten_issues(validated.issues);
71
+ return;
72
+ }
73
+
74
+ // TODO 3.0 remove this warning
75
+ if (DEV) {
76
+ const error = () => {
77
+ throw new Error(
78
+ 'Remote form functions no longer get passed a FormData object. The payload is now a POJO. See https://kit.svelte.dev/docs/remote-functions#form for details.'
79
+ );
80
+ };
81
+ for (const key of [
82
+ 'append',
83
+ 'delete',
84
+ 'entries',
85
+ 'forEach',
86
+ 'get',
87
+ 'getAll',
88
+ 'has',
89
+ 'keys',
90
+ 'set',
91
+ 'values'
92
+ ]) {
93
+ if (!(key in data)) {
94
+ Object.defineProperty(data, key, { get: error });
95
+ }
96
+ }
97
+ }
98
+
99
+ try {
100
+ await callback({
101
+ form,
102
+ data,
103
+ submit: () => submit(form_data)
104
+ });
105
+ } catch (e) {
106
+ const error = e instanceof HttpError ? e.body : { message: /** @type {any} */ (e).message };
107
+ const status = e instanceof HttpError ? e.status : 500;
108
+ void set_nearest_error_page(error, status);
109
+ }
110
+ }
111
+
40
112
  /**
41
113
  * @param {FormData} data
42
114
  * @returns {Promise<any> & { updates: (...args: any[]) => any }}
@@ -64,13 +136,6 @@ export function form(id) {
64
136
  await Promise.resolve();
65
137
 
66
138
  if (updates.length > 0) {
67
- if (DEV) {
68
- if (data.get('sveltekit:remote_refreshes')) {
69
- throw new Error(
70
- 'The FormData key `sveltekit:remote_refreshes` is reserved for internal use and should not be set manually'
71
- );
72
- }
73
- }
74
139
  data.set('sveltekit:remote_refreshes', JSON.stringify(updates.map((u) => u._key)));
75
140
  }
76
141
 
@@ -88,9 +153,18 @@ export function form(id) {
88
153
  const form_result = /** @type { RemoteFunctionResponse} */ (await response.json());
89
154
 
90
155
  if (form_result.type === 'result') {
91
- result = devalue.parse(form_result.result, app.decoders);
92
-
93
- if (form_result.refreshes) {
156
+ ({
157
+ input = {},
158
+ issues = {},
159
+ result
160
+ } = devalue.parse(form_result.result, {
161
+ ...app.decoders,
162
+ File: file_transport.decode
163
+ }));
164
+
165
+ if (issues.$) {
166
+ release_overrides(updates);
167
+ } else if (form_result.refreshes) {
94
168
  refresh_queries(form_result.refreshes, updates);
95
169
  } else {
96
170
  void invalidateAll();
@@ -104,7 +178,6 @@ export function form(id) {
104
178
  // Use internal version to allow redirects to external URLs
105
179
  void _goto(form_result.location, { invalidateAll }, 0);
106
180
  } else {
107
- result = undefined;
108
181
  throw new HttpError(form_result.status ?? 500, form_result.error);
109
182
  }
110
183
  } catch (e) {
@@ -134,43 +207,13 @@ export function form(id) {
134
207
  return promise;
135
208
  }
136
209
 
137
- /** @type {RemoteForm<T>} */
210
+ /** @type {RemoteForm<T, U>} */
138
211
  const instance = {};
139
212
 
140
213
  instance.method = 'POST';
141
214
  instance.action = action;
142
215
 
143
- /**
144
- * @param {HTMLFormElement} form_element
145
- * @param {HTMLElement | null} submitter
146
- */
147
- function create_form_data(form_element, submitter) {
148
- const form_data = new FormData(form_element);
149
-
150
- if (DEV) {
151
- const enctype = submitter?.hasAttribute('formenctype')
152
- ? /** @type {HTMLButtonElement | HTMLInputElement} */ (submitter).formEnctype
153
- : clone(form_element).enctype;
154
- if (enctype !== 'multipart/form-data') {
155
- for (const value of form_data.values()) {
156
- if (value instanceof File) {
157
- throw new Error(
158
- 'Your form contains <input type="file"> fields, but is missing the necessary `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819.'
159
- );
160
- }
161
- }
162
- }
163
- }
164
-
165
- const submitter_name = submitter?.getAttribute('name');
166
- if (submitter_name) {
167
- form_data.append(submitter_name, submitter?.getAttribute('value') ?? '');
168
- }
169
-
170
- return form_data;
171
- }
172
-
173
- /** @param {Parameters<RemoteForm<any>['enhance']>[0]} callback */
216
+ /** @param {Parameters<RemoteForm<any, any>['enhance']>[0]} callback */
174
217
  const form_onsubmit = (callback) => {
175
218
  /** @param {SubmitEvent} event */
176
219
  return async (event) => {
@@ -194,26 +237,105 @@ export function form(id) {
194
237
 
195
238
  event.preventDefault();
196
239
 
197
- const data = create_form_data(form, event.submitter);
240
+ const form_data = new FormData(form);
198
241
 
199
- try {
200
- await callback({
201
- form,
202
- data,
203
- submit: () => submit(data)
204
- });
205
- } catch (e) {
206
- const error =
207
- e instanceof HttpError ? e.body : { message: /** @type {any} */ (e).message };
208
- const status = e instanceof HttpError ? e.status : 500;
209
- void set_nearest_error_page(error, status);
242
+ if (DEV) {
243
+ validate_form_data(form_data, clone(form).enctype);
210
244
  }
245
+
246
+ await handle_submit(form, form_data, callback);
211
247
  };
212
248
  };
213
249
 
214
- instance.onsubmit = form_onsubmit(({ submit, form }) => submit().then(() => form.reset()));
250
+ /** @param {(event: SubmitEvent) => void} onsubmit */
251
+ function create_attachment(onsubmit) {
252
+ return (/** @type {HTMLFormElement} */ form) => {
253
+ if (element) {
254
+ let message = `A form object can only be attached to a single \`<form>\` element`;
255
+ if (DEV && !key) {
256
+ const name = id.split('/').pop();
257
+ message += `. To create multiple instances, use \`${name}.for(key)\``;
258
+ }
259
+
260
+ throw new Error(message);
261
+ }
262
+
263
+ element = form;
264
+
265
+ touched = {};
266
+
267
+ form.addEventListener('submit', onsubmit);
268
+
269
+ form.addEventListener('input', (e) => {
270
+ // strictly speaking it can be an HTMLTextAreaElement or HTMLSelectElement
271
+ // but that makes the types unnecessarily awkward
272
+ const element = /** @type {HTMLInputElement} */ (e.target);
273
+
274
+ let name = element.name;
275
+ if (!name) return;
276
+
277
+ const is_array = name.endsWith('[]');
278
+ if (is_array) name = name.slice(0, -2);
279
+
280
+ const is_file = element.type === 'file';
281
+
282
+ touched[name] = true;
283
+
284
+ if (is_array) {
285
+ const elements = /** @type {HTMLInputElement[]} */ (
286
+ Array.from(form.querySelectorAll(`[name="${name}[]"]`))
287
+ );
288
+
289
+ if (DEV) {
290
+ for (const e of elements) {
291
+ if ((e.type === 'file') !== is_file) {
292
+ throw new Error(
293
+ `Cannot mix and match file and non-file inputs under the same name ("${element.name}")`
294
+ );
295
+ }
296
+ }
297
+ }
298
+
299
+ input[name] = is_file
300
+ ? elements.map((input) => Array.from(input.files ?? [])).flat()
301
+ : elements.map((element) => element.value);
302
+ } else if (is_file) {
303
+ if (DEV && element.multiple) {
304
+ throw new Error(
305
+ `Can only use the \`multiple\` attribute when \`name\` includes a \`[]\` suffix — consider changing "${name}" to "${name}[]"`
306
+ );
307
+ }
308
+
309
+ const file = /** @type {HTMLInputElement & { files: FileList }} */ (element).files[0];
215
310
 
216
- /** @param {Parameters<RemoteForm<any>['buttonProps']['enhance']>[0]} callback */
311
+ if (file) {
312
+ input[name] = file;
313
+ } else {
314
+ delete input[name];
315
+ }
316
+ } else {
317
+ input[name] = element.value;
318
+ }
319
+ });
320
+
321
+ return () => {
322
+ element = null;
323
+ preflight_schema = undefined;
324
+ };
325
+ };
326
+ }
327
+
328
+ instance[createAttachmentKey()] = create_attachment(
329
+ form_onsubmit(({ submit, form }) =>
330
+ submit().then(() => {
331
+ if (!issues.$) {
332
+ form.reset();
333
+ }
334
+ })
335
+ )
336
+ );
337
+
338
+ /** @param {Parameters<RemoteForm<any, any>['buttonProps']['enhance']>[0]} callback */
217
339
  const form_action_onclick = (callback) => {
218
340
  /** @param {Event} event */
219
341
  return async (event) => {
@@ -225,34 +347,41 @@ export function form(id) {
225
347
  event.stopPropagation();
226
348
  event.preventDefault();
227
349
 
228
- const data = create_form_data(form, target);
350
+ const form_data = new FormData(form);
229
351
 
230
- try {
231
- await callback({
232
- form,
233
- data,
234
- submit: () => submit(data)
235
- });
236
- } catch (e) {
237
- const error =
238
- e instanceof HttpError ? e.body : { message: /** @type {any} */ (e).message };
239
- const status = e instanceof HttpError ? e.status : 500;
240
- void set_nearest_error_page(error, status);
352
+ if (DEV) {
353
+ const enctype = target.hasAttribute('formenctype')
354
+ ? target.formEnctype
355
+ : clone(form).enctype;
356
+
357
+ validate_form_data(form_data, enctype);
241
358
  }
359
+
360
+ if (target.name) {
361
+ form_data.append(target.name, target?.getAttribute('value') ?? '');
362
+ }
363
+
364
+ await handle_submit(form, form_data, callback);
242
365
  };
243
366
  };
244
367
 
245
- /** @type {RemoteForm<any>['buttonProps']} */
368
+ /** @type {RemoteForm<any, any>['buttonProps']} */
246
369
  // @ts-expect-error we gotta set enhance as a non-enumerable property
247
370
  const button_props = {
248
371
  type: 'submit',
249
372
  formmethod: 'POST',
250
373
  formaction: action,
251
- onclick: form_action_onclick(({ submit, form }) => submit().then(() => form.reset()))
374
+ onclick: form_action_onclick(({ submit, form }) =>
375
+ submit().then(() => {
376
+ if (!issues.$) {
377
+ form.reset();
378
+ }
379
+ })
380
+ )
252
381
  };
253
382
 
254
383
  Object.defineProperty(button_props, 'enhance', {
255
- /** @type {RemoteForm<any>['buttonProps']['enhance']} */
384
+ /** @type {RemoteForm<any, any>['buttonProps']['enhance']} */
256
385
  value: (callback) => {
257
386
  return {
258
387
  type: 'submit',
@@ -267,23 +396,106 @@ export function form(id) {
267
396
  get: () => pending_count
268
397
  });
269
398
 
399
+ let validate_id = 0;
400
+
270
401
  Object.defineProperties(instance, {
271
402
  buttonProps: {
272
403
  value: button_props
273
404
  },
405
+ input: {
406
+ get: () => input,
407
+ set: (v) => {
408
+ input = v;
409
+ }
410
+ },
411
+ issues: {
412
+ get: () => issues
413
+ },
274
414
  result: {
275
415
  get: () => result
276
416
  },
277
417
  pending: {
278
418
  get: () => pending_count
279
419
  },
420
+ field: {
421
+ value: (/** @type {string} */ name) => name
422
+ },
423
+ preflight: {
424
+ /** @type {RemoteForm<any, any>['preflight']} */
425
+ value: (schema) => {
426
+ preflight_schema = schema;
427
+ return instance;
428
+ }
429
+ },
430
+ validate: {
431
+ /** @type {RemoteForm<any, any>['validate']} */
432
+ value: async ({ includeUntouched = false } = {}) => {
433
+ if (!element) return;
434
+
435
+ const id = ++validate_id;
436
+
437
+ const form_data = new FormData(element);
438
+
439
+ /** @type {readonly StandardSchemaV1.Issue[]} */
440
+ let array = [];
441
+
442
+ const validated = await preflight_schema?.['~standard'].validate(
443
+ convert_formdata(form_data)
444
+ );
445
+
446
+ if (validated?.issues) {
447
+ array = validated.issues;
448
+ } else {
449
+ form_data.set('sveltekit:validate_only', 'true');
450
+
451
+ const response = await fetch(`${base}/${app_dir}/remote/${action_id}`, {
452
+ method: 'POST',
453
+ body: form_data
454
+ });
455
+
456
+ const result = await response.json();
457
+
458
+ if (validate_id !== id) {
459
+ return;
460
+ }
461
+
462
+ if (result.type === 'result') {
463
+ array = /** @type {StandardSchemaV1.Issue[]} */ (
464
+ devalue.parse(result.result, app.decoders)
465
+ );
466
+ }
467
+ }
468
+
469
+ if (!includeUntouched) {
470
+ array = array.filter((issue) => {
471
+ if (issue.path !== undefined) {
472
+ let path = '';
473
+
474
+ for (const segment of issue.path) {
475
+ const key = typeof segment === 'object' ? segment.key : segment;
476
+
477
+ if (typeof key === 'number') {
478
+ path += `[${key}]`;
479
+ } else if (typeof key === 'string') {
480
+ path += path === '' ? key : '.' + key;
481
+ }
482
+ }
483
+
484
+ return touched[path];
485
+ }
486
+ });
487
+ }
488
+
489
+ issues = flatten_issues(array);
490
+ }
491
+ },
280
492
  enhance: {
281
- /** @type {RemoteForm<any>['enhance']} */
493
+ /** @type {RemoteForm<any, any>['enhance']} */
282
494
  value: (callback) => {
283
495
  return {
284
496
  method: 'POST',
285
497
  action,
286
- onsubmit: form_onsubmit(callback)
498
+ [createAttachmentKey()]: create_attachment(form_onsubmit(callback))
287
499
  };
288
500
  }
289
501
  }
@@ -295,7 +507,7 @@ export function form(id) {
295
507
  const instance = create_instance();
296
508
 
297
509
  Object.defineProperty(instance, 'for', {
298
- /** @type {RemoteForm<any>['for']} */
510
+ /** @type {RemoteForm<T, U>['for']} */
299
511
  value: (key) => {
300
512
  const entry = instances.get(key) ?? { count: 0, instance: create_instance(key) };
301
513
 
@@ -335,3 +547,33 @@ export function form(id) {
335
547
  function clone(element) {
336
548
  return /** @type {T} */ (HTMLElement.prototype.cloneNode.call(element));
337
549
  }
550
+
551
+ /**
552
+ * @param {FormData} form_data
553
+ * @param {string} enctype
554
+ */
555
+ function validate_form_data(form_data, enctype) {
556
+ for (const key of form_data.keys()) {
557
+ if (key.startsWith('sveltekit:')) {
558
+ throw new Error(
559
+ 'FormData keys starting with `sveltekit:` are reserved for internal use and should not be set manually'
560
+ );
561
+ }
562
+
563
+ if (/^\$[.[]?/.test(key)) {
564
+ throw new Error(
565
+ '`$` is used to collect all FormData validation issues and cannot be used as the `name` of a form control'
566
+ );
567
+ }
568
+ }
569
+
570
+ if (enctype !== 'multipart/form-data') {
571
+ for (const value of form_data.values()) {
572
+ if (value instanceof File) {
573
+ throw new Error(
574
+ 'Your form contains <input type="file"> fields, but is missing the necessary `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819.'
575
+ );
576
+ }
577
+ }
578
+ }
579
+ }
@@ -12,6 +12,7 @@ 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';
15
16
 
16
17
  /** @type {typeof handle_remote_call_internal} */
17
18
  export async function handle_remote_call(event, state, options, manifest, id) {
@@ -128,8 +129,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
128
129
  return json(
129
130
  /** @type {RemoteFunctionResponse} */ ({
130
131
  type: 'result',
131
- result: stringify(data, transport),
132
- refreshes: await serialize_refreshes(form_client_refreshes)
132
+ result: stringify(data, { ...transport, File: file_transport }),
133
+ refreshes: data.issues ? {} : await serialize_refreshes(form_client_refreshes)
133
134
  })
134
135
  );
135
136
  }
@@ -262,7 +263,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
262
263
  const remotes = manifest._.remotes;
263
264
  const module = await remotes[hash]?.();
264
265
 
265
- let form = /** @type {RemoteForm<any>} */ (module?.default[name]);
266
+ let form = /** @type {RemoteForm<any, any>} */ (module?.default[name]);
266
267
 
267
268
  if (!form) {
268
269
  event.setHeaders({
@@ -1,3 +1,5 @@
1
+ /** @import { RemoteFormIssue } from '@sveltejs/kit' */
2
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
1
3
  import { BROWSER } from 'esm-env';
2
4
 
3
5
  export const text_encoder = new TextEncoder();
@@ -63,3 +65,123 @@ export function base64_decode(encoded) {
63
65
 
64
66
  return bytes;
65
67
  }
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 = object[key] ??= is_array ? [] : Object.create(null); // guard against prototype pollution
127
+ }
128
+ }
129
+
130
+ object[keys[keys.length - 1]] = value;
131
+ }
132
+
133
+ /**
134
+ * @param {readonly StandardSchemaV1.Issue[]} issues
135
+ */
136
+ export function flatten_issues(issues) {
137
+ /** @type {Record<string, RemoteFormIssue[]>} */
138
+ const result = {};
139
+
140
+ for (const issue of issues) {
141
+ /** @type {RemoteFormIssue} */
142
+ const normalized = { name: '', path: [], message: issue.message };
143
+
144
+ (result.$ ??= []).push(normalized);
145
+
146
+ let name = '';
147
+
148
+ if (issue.path !== undefined) {
149
+ for (const segment of issue.path) {
150
+ const key = /** @type {string | number} */ (
151
+ typeof segment === 'object' ? segment.key : segment
152
+ );
153
+
154
+ normalized.path.push(key);
155
+
156
+ if (typeof key === 'number') {
157
+ name += `[${key}]`;
158
+ } else if (typeof key === 'string') {
159
+ name += name === '' ? key : '.' + key;
160
+ }
161
+
162
+ (result[name] ??= []).push(normalized);
163
+ }
164
+
165
+ normalized.name = name;
166
+ }
167
+ }
168
+
169
+ return result;
170
+ }
171
+
172
+ /**
173
+ * We need to encode `File` objects when returning `issues` from a `form` submission,
174
+ * because some validators include the original value in the issue. It doesn't
175
+ * need to deserialize to a `File` object
176
+ * @type {import('@sveltejs/kit').Transporter}
177
+ */
178
+ export const file_transport = {
179
+ encode: (file) =>
180
+ file instanceof File && {
181
+ size: file.size,
182
+ type: file.type,
183
+ name: file.name,
184
+ lastModified: file.lastModified
185
+ },
186
+ decode: (data) => data
187
+ };
package/src/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '2.41.0';
4
+ export const VERSION = '2.42.1';
package/types/index.d.ts CHANGED
@@ -1786,28 +1786,105 @@ declare module '@sveltejs/kit' {
1786
1786
  restore: (snapshot: T) => void;
1787
1787
  }
1788
1788
 
1789
+ // If T is unknown or RemoteFormInput, the types below will recurse indefinitely and create giant unions that TS can't handle
1790
+ type WillRecurseIndefinitely<T> = unknown extends T
1791
+ ? true
1792
+ : RemoteFormInput extends T
1793
+ ? true
1794
+ : false;
1795
+
1796
+ // Helper type to convert union to intersection
1797
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
1798
+ ? I
1799
+ : never;
1800
+
1801
+ type FlattenInput<T, Prefix extends string> =
1802
+ WillRecurseIndefinitely<T> extends true
1803
+ ? { [key: string]: string }
1804
+ : T extends Array<infer U>
1805
+ ? U extends string | File
1806
+ ? { [P in Prefix]: string[] }
1807
+ : FlattenInput<U, `${Prefix}[${number}]`>
1808
+ : T extends File
1809
+ ? { [P in Prefix]: string }
1810
+ : T extends object
1811
+ ? {
1812
+ [K in keyof T]: FlattenInput<
1813
+ T[K],
1814
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1815
+ >;
1816
+ }[keyof T]
1817
+ : { [P in Prefix]: string };
1818
+
1819
+ type FlattenIssues<T, Prefix extends string> =
1820
+ WillRecurseIndefinitely<T> extends true
1821
+ ? { [key: string]: RemoteFormIssue[] }
1822
+ : T extends Array<infer U>
1823
+ ? { [P in Prefix | `${Prefix}[${number}]`]: RemoteFormIssue[] } & FlattenIssues<
1824
+ U,
1825
+ `${Prefix}[${number}]`
1826
+ >
1827
+ : T extends File
1828
+ ? { [P in Prefix]: RemoteFormIssue[] }
1829
+ : T extends object
1830
+ ? {
1831
+ [K in keyof T]: FlattenIssues<
1832
+ T[K],
1833
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1834
+ >;
1835
+ }[keyof T]
1836
+ : { [P in Prefix]: RemoteFormIssue[] };
1837
+
1838
+ type FlattenKeys<T, Prefix extends string> =
1839
+ WillRecurseIndefinitely<T> extends true
1840
+ ? { [key: string]: string }
1841
+ : T extends Array<infer U>
1842
+ ? U extends string | File
1843
+ ? { [P in `${Prefix}[]`]: string[] }
1844
+ : FlattenKeys<U, `${Prefix}[${number}]`>
1845
+ : T extends File
1846
+ ? { [P in Prefix]: string }
1847
+ : T extends object
1848
+ ? {
1849
+ [K in keyof T]: FlattenKeys<
1850
+ T[K],
1851
+ Prefix extends '' ? K & string : `${Prefix}.${K & string}`
1852
+ >;
1853
+ }[keyof T]
1854
+ : { [P in Prefix]: string };
1855
+
1856
+ export interface RemoteFormInput {
1857
+ [key: string]: FormDataEntryValue | FormDataEntryValue[] | RemoteFormInput | RemoteFormInput[];
1858
+ }
1859
+
1860
+ export interface RemoteFormIssue {
1861
+ name: string;
1862
+ path: Array<string | number>;
1863
+ message: string;
1864
+ }
1865
+
1789
1866
  /**
1790
1867
  * The return value of a remote `form` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
1791
1868
  */
1792
- export type RemoteForm<Result> = {
1869
+ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
1870
+ /** Attachment that sets up an event handler that intercepts the form submission on the client to prevent a full page reload */
1871
+ [attachment: symbol]: (node: HTMLFormElement) => void;
1793
1872
  method: 'POST';
1794
1873
  /** The URL to send the form to. */
1795
1874
  action: string;
1796
- /** Event handler that intercepts the form submission on the client to prevent a full page reload */
1797
- onsubmit: (event: SubmitEvent) => void;
1798
1875
  /** Use the `enhance` method to influence what happens when the form is submitted. */
1799
1876
  enhance(
1800
1877
  callback: (opts: {
1801
1878
  form: HTMLFormElement;
1802
- data: FormData;
1879
+ data: Input;
1803
1880
  submit: () => Promise<void> & {
1804
1881
  updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => Promise<void>;
1805
1882
  };
1806
- }) => void
1883
+ }) => void | Promise<void>
1807
1884
  ): {
1808
1885
  method: 'POST';
1809
1886
  action: string;
1810
- onsubmit: (event: SubmitEvent) => void;
1887
+ [attachment: symbol]: (node: HTMLFormElement) => void;
1811
1888
  };
1812
1889
  /**
1813
1890
  * Create an instance of the form for the given key.
@@ -1823,11 +1900,27 @@ declare module '@sveltejs/kit' {
1823
1900
  * {/each}
1824
1901
  * ```
1825
1902
  */
1826
- for(key: string | number | boolean): Omit<RemoteForm<Result>, 'for'>;
1903
+ for(key: string | number | boolean): Omit<RemoteForm<Input, Output>, 'for'>;
1904
+ /**
1905
+ * This method exists to allow you to typecheck `name` attributes. It returns its argument
1906
+ * @example
1907
+ * ```svelte
1908
+ * <input name={login.field('username')} />
1909
+ * ```
1910
+ **/
1911
+ field<Name extends keyof UnionToIntersection<FlattenKeys<Input, ''>>>(string: Name): Name;
1912
+ /** Preflight checks */
1913
+ preflight(schema: StandardSchemaV1<Input, any>): RemoteForm<Input, Output>;
1914
+ /** Validate the form contents programmatically */
1915
+ validate(options?: { includeUntouched?: boolean }): Promise<void>;
1827
1916
  /** The result of the form submission */
1828
- get result(): Result | undefined;
1917
+ get result(): Output | undefined;
1829
1918
  /** The number of pending submissions */
1830
1919
  get pending(): number;
1920
+ /** The submitted values */
1921
+ input: null | UnionToIntersection<FlattenInput<Input, ''>>;
1922
+ /** Validation issues */
1923
+ issues: null | UnionToIntersection<FlattenIssues<Input, ''>>;
1831
1924
  /** Spread this onto a `<button>` or `<input type="submit">` */
1832
1925
  buttonProps: {
1833
1926
  type: 'submit';
@@ -1838,11 +1931,11 @@ declare module '@sveltejs/kit' {
1838
1931
  enhance(
1839
1932
  callback: (opts: {
1840
1933
  form: HTMLFormElement;
1841
- data: FormData;
1934
+ data: Input;
1842
1935
  submit: () => Promise<void> & {
1843
1936
  updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => Promise<void>;
1844
1937
  };
1845
- }) => void
1938
+ }) => void | Promise<void>
1846
1939
  ): {
1847
1940
  type: 'submit';
1848
1941
  formmethod: 'POST';
@@ -2910,7 +3003,7 @@ declare module '$app/paths' {
2910
3003
  }
2911
3004
 
2912
3005
  declare module '$app/server' {
2913
- import type { RequestEvent, RemoteCommand, RemoteForm, RemotePrerenderFunction, RemoteQueryFunction } from '@sveltejs/kit';
3006
+ import type { RequestEvent, RemoteCommand, RemoteForm, RemoteFormInput, RemotePrerenderFunction, RemoteQueryFunction } from '@sveltejs/kit';
2914
3007
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2915
3008
  /**
2916
3009
  * Read the contents of an imported asset from the filesystem
@@ -2964,7 +3057,23 @@ declare module '$app/server' {
2964
3057
  *
2965
3058
  * @since 2.27
2966
3059
  */
2967
- export function form<T>(fn: (data: FormData) => MaybePromise<T>): RemoteForm<T>;
3060
+ export function form<Output>(fn: () => Output): RemoteForm<void, Output>;
3061
+ /**
3062
+ * Creates a form object that can be spread onto a `<form>` element.
3063
+ *
3064
+ * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
3065
+ *
3066
+ * @since 2.27
3067
+ */
3068
+ export function form<Input extends RemoteFormInput, Output>(validate: "unchecked", fn: (data: Input) => MaybePromise<Output>): RemoteForm<Input, Output>;
3069
+ /**
3070
+ * Creates a form object that can be spread onto a `<form>` element.
3071
+ *
3072
+ * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
3073
+ *
3074
+ * @since 2.27
3075
+ */
3076
+ export function form<Schema extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(validate: Schema, fn: (data: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>): RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>;
2968
3077
  /**
2969
3078
  * Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
2970
3079
  *
@@ -57,6 +57,13 @@
57
57
  "Redirect",
58
58
  "SubmitFunction",
59
59
  "Snapshot",
60
+ "WillRecurseIndefinitely",
61
+ "UnionToIntersection",
62
+ "FlattenInput",
63
+ "FlattenIssues",
64
+ "FlattenKeys",
65
+ "RemoteFormInput",
66
+ "RemoteFormIssue",
60
67
  "RemoteForm",
61
68
  "RemoteCommand",
62
69
  "RemoteResource",
@@ -144,7 +151,6 @@
144
151
  "resolveRoute",
145
152
  "read",
146
153
  "getRequestEvent",
147
- "form",
148
154
  "RemotePrerenderInputsGenerator",
149
155
  "page",
150
156
  "navigating",
@@ -168,7 +174,6 @@
168
174
  "../src/runtime/app/paths/types.d.ts",
169
175
  "../src/runtime/app/server/index.js",
170
176
  "../src/exports/internal/event.js",
171
- "../src/runtime/app/server/remote/form.js",
172
177
  "../src/runtime/app/state/index.js",
173
178
  "../src/runtime/app/stores.js"
174
179
  ],
@@ -190,9 +195,8 @@
190
195
  null,
191
196
  null,
192
197
  null,
193
- null,
194
198
  null
195
199
  ],
196
- "mappings": ";;;;;;;;;;;kBAkCiBA,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAiCZC,cAAcA;;;;;;aAMdC,cAAcA;;;;;;;;MAQrBC,aAAaA;;;;;OAKJC,YAAYA;;kBAETC,aAAaA;;;;;;MAMzBC,qBAAqBA;;;;;;;;;;;kBAWTC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA8IPC,MAAMA;;;;;;;;;;;kBAWNC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA4DPC,QAAQA;;;;;;;;kBAQRC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAqkBdC,MAAMA;;;;;;;;;;;aAWNC,iBAAiBA;;;;;;;;;;;;aAYjBC,qBAAqBA;;;;;;;;;aASrBC,iBAAiBA;;;;;;;;;;aAUjBC,WAAWA;;;;;;;;;;aAUXC,UAAUA;;;;;;aAMVC,UAAUA;;;;;;aAMVC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;aA0BPC,SAASA;;;;;kBAKJC,WAAWA;;;;;;;;;;;;aAYhBC,IAAIA;;;;;;;;;;;;kBAYCC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAyHTC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0BfC,gBAAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAgCrBC,cAAcA;;kBAETC,cAAcA;;;;;;;;;;;;;;;;;;;;kBAoBdC,eAAeA;;;;;;;;;;;;;;;;;;;;;;kBAsBfC,kBAAkBA;;;;;;;;;;;;;;;;;;;kBAmBlBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;;;kBAwBpBC,kBAAkBA;;;;;;;;;;;;;;;;;;;;;;kBAsBlBC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;aAwBnBC,UAAUA;;;;;;;;;aASVC,cAAcA;;;;;;;;;;aAUdC,UAAUA;;;;;;;;;;;;;;;;;;aAkBVC,aAAaA;;;;;;;;;;;;;;;;;;;kBAmBRC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA8CTC,YAAYA;;kBAEPC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA+GjBC,cAAcA;;;;;kBAKTC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;kBAuBdC,eAAeA;;;;;;;;;;;;;;;cAenBC,MAAMA;;;;;;kBAMFC,iBAAiBA;;;;;;;kBAOjBC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;aAyBhBC,UAAUA;;;;;;;kBAOLC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAkFpBC,MAAMA;;;;;;;;;;aAUNC,OAAOA;;;;;;;;;;;;;;;;aAgBPC,YAAYA;;;;;;;;;;;;kBCrtDXC,SAASA;;;;;;;;;;kBAqBTC,QAAQA;;;;;;;aD6tDTC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BTC,QAAQA;;;;;;;;aAQbC,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAoEVC,aAAaA;;;;;;;;aAQbC,cAAcA;;;;;;;;;;;;;;;;;;aAkBdC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqCNC,mBAAmBA;;;;;;;;aAQxBC,uBAAuBA;;;;;aAKvBC,mBAAmBA;WEj6DdC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAkDZC,GAAGA;;;;;;;;;;;;;;;;;;;;;WAqBHC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmElBC,UAAUA;;WAELC,MAAMA;;;;;;;;;MASXC,YAAYA;;WAEPC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAmCXC,yBAAyBA;;;;;;;;;;WAUzBC,yBAAyBA;;;;WAIzBC,sCAAsCA;;;;WAItCC,4BAA4BA;;;;MAIjCC,8BAA8BA;MAC9BC,8BAA8BA;MAC9BC,iCAAiCA;;;;;MAKjCC,2CAA2CA;;;;;;aAM3CC,eAAeA;;WAIVC,cAAcA;;;;;WAKdC,YAAYA;;;;;;MAMjBC,aAAaA;WC/LRC,KAAKA;;;;;;WAeLC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuHTC,YAAYA;;;;;;;;;;;;;WAkBZC,QAAQA;;;;;;;;;;;;;;MAgCbC,iBAAiBA;;;;;;;;;WAWZC,UAAUA;;;;;;;;;;;;;WAaVC,SAASA;;;;;;;;;;;;;;;;;;;;;;;WAsHTC,YAAYA;;;;;;;;;;;;;;;;MAgBjBC,kBAAkBA;;WAEbC,aAAaA;;;;;;;;;;WAUbC,UAAUA;;;;;;;;;;;WAWVC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;MAuBZC,aAAaA;;WA6BRC,eAAeA;;;;;;MAMpBC,uBAAuBA;;MAGvBC,WAAWA;;;;;;;;WAQNC,QAAQA;;;;;;;;;WASRC,cAAcA;;;;;;;;;MA+CnBC,eAAeA;;;;;MAKfC,kBAAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC3cdC,WAAWA;;;;;;;;;;;;;;;;;;;iBAsBXC,QAAQA;;;;;iBAiBRC,UAAUA;;;;;;iBASVC,IAAIA;;;;;;iBA4BJC,IAAIA;;;;;;;;;;;;;;;;iBAkDJC,eAAeA;;;;;;;;;;;;;;iBAmBfC,YAAYA;;;;;;;cCrOfC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC4EJC,QAAQA;;;;;;iBC4BFC,UAAUA;;;;;;iBAkCVC,WAAWA;;;;;iBAgFjBC,oBAAoBA;;;;;;;;;;;iBC3MpBC,gBAAgBA;;;;;;;;;iBCsHVC,SAASA;;;;;;;;;cCrIlBC,OAAOA;;;;;cAKPC,GAAGA;;;;;cAKHC,QAAQA;;;;;cAKRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;iBCYJC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;iBAgDXC,OAAOA;;;;;;;iBCupEDC,WAAWA;;;;;;;;;;;iBA9UjBC,aAAaA;;;;;;;;;;;;iBAiBbC,cAAcA;;;;;;;;;;iBAedC,UAAUA;;;;;iBASVC,qBAAqBA;;;;;;;;;;iBA8BrBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCJC,UAAUA;;;;iBA0BVC,aAAaA;;;;;iBAebC,UAAUA;;;;;;;;;;;;;;iBAqBJC,WAAWA;;;;;;;;;;;;;;;;;;iBAoCXC,WAAWA;;;;;iBAsCjBC,SAASA;;;;;iBA+CTC,YAAYA;MVhiEhBlE,YAAYA;;;;;;;;;;;;;;YWlJbmE,IAAIA;;;;;;;;;YASJC,MAAMA;;MAEZC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBAC,OAAOA;;;;;;;;;;;;;;;;;iBAiBPC,KAAKA;;;;;iBAKLC,YAAYA;;;;;;;;;;;;;;;;;;;;;;iBChDZC,IAAIA;;;;;;;;iBCOJC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCTfC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Mb0cRC,8BAA8BA;MDhU9B5E,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ce1GX6E,IAAIA;;;;;cAQJC,UAAUA;;;;;;;;;;;cAMVC,OAAOA;;;;;;;;;iBCrDPC,SAASA;;;;;;;;;;;;;;;cAyBTH,IAAIA;;;;;;;;;;cAiBJC,UAAUA;;;;;;;;cAeVC,OAAOA",
200
+ "mappings": "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",
197
201
  "ignoreList": []
198
202
  }