@sveltejs/kit 2.41.0 → 2.42.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 +1 -1
- package/src/exports/public.d.ts +103 -10
- package/src/runtime/app/server/remote/form.js +146 -15
- package/src/runtime/client/remote-functions/form.svelte.js +321 -79
- package/src/runtime/server/remote.js +4 -3
- package/src/runtime/utils.js +122 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +121 -12
- package/types/index.d.ts.map +8 -4
package/package.json
CHANGED
package/src/exports/public.d.ts
CHANGED
|
@@ -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<
|
|
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:
|
|
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
|
-
|
|
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<
|
|
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():
|
|
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:
|
|
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';
|
|
@@ -1,34 +1,77 @@
|
|
|
1
|
-
/** @import { RemoteForm } from '@sveltejs/kit' */
|
|
2
|
-
/** @import {
|
|
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
|
|
12
|
-
* @
|
|
13
|
-
* @
|
|
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(
|
|
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<
|
|
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
|
|
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
|
-
|
|
152
|
+
if (is_array) key = key.slice(0, -2);
|
|
60
153
|
|
|
61
|
-
|
|
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] =
|
|
169
|
+
(state.remote_data ??= {})[__.id] = output;
|
|
67
170
|
}
|
|
68
171
|
|
|
69
|
-
return
|
|
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 {
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
240
|
+
const form_data = new FormData(form);
|
|
198
241
|
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
350
|
+
const form_data = new FormData(form);
|
|
229
351
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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 }) =>
|
|
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
|
-
|
|
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<
|
|
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({
|
package/src/runtime/utils.js
CHANGED
|
@@ -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
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<
|
|
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:
|
|
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
|
-
|
|
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<
|
|
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():
|
|
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:
|
|
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<
|
|
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
|
*
|
package/types/index.d.ts.map
CHANGED
|
@@ -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": "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
|
|
200
|
+
"mappings": "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",
|
|
197
201
|
"ignoreList": []
|
|
198
202
|
}
|