From c8faabc391d6c32768808836de83259125054260 Mon Sep 17 00:00:00 2001 From: Ottomated Date: Sat, 6 Jun 2026 15:07:47 -0700 Subject: [PATCH 1/3] feat: return `data` to form enhance callback function --- .changeset/violet-buckets-leave.md | 5 ++++ .../client/remote-functions/form.svelte.js | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 .changeset/violet-buckets-leave.md diff --git a/.changeset/violet-buckets-leave.md b/.changeset/violet-buckets-leave.md new file mode 100644 index 000000000000..76d77ecc6609 --- /dev/null +++ b/.changeset/violet-buckets-leave.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: return `data` to form enhance callback function diff --git a/packages/kit/src/runtime/client/remote-functions/form.svelte.js b/packages/kit/src/runtime/client/remote-functions/form.svelte.js index 5061951710f4..d662c87a874e 100644 --- a/packages/kit/src/runtime/client/remote-functions/form.svelte.js +++ b/packages/kit/src/runtime/client/remote-functions/form.svelte.js @@ -142,10 +142,11 @@ export function form(id) { /** * @param {FormData} form_data + * @param {Record} data * @param {boolean} should_preflight * @returns {Promise & { updates: (...args: any[]) => Promise }} */ - function submit(form_data, should_preflight) { + function submit(form_data, data, should_preflight) { // Store a reference to the current instance and increment the usage count for the duration // of the request. This ensures that the instance is not deleted in case of an optimistic update // (e.g. when deleting an item in a list) that fails and wants to surface an error to the user afterwards. @@ -174,11 +175,11 @@ export function form(id) { } if (should_preflight) { - const valid = await preflight(form_data); + const valid = await preflight(form_data, data); if (!valid) return false; } - const { blob } = serialize_binary_form(convert(form_data), { + const { blob } = serialize_binary_form(data, { remote_refreshes: Array.from(refreshes ?? []) }); @@ -293,9 +294,10 @@ export function form(id) { /** * @param {HTMLFormElement} form * @param {FormData} form_data + * @param {Record} data * @returns {Omit, 'enhance' | 'element'> & { readonly element: HTMLFormElement }} */ - function create_enhance_callback_instance(form, form_data) { + function create_enhance_callback_instance(form, form_data, data) { const { enhance: _enhance, ...descriptors } = Object.getOwnPropertyDescriptors(instance); void _enhance; @@ -306,10 +308,7 @@ export function form(id) { ...descriptors, data: { get() { - // TODO 3.0 remove - throw new Error( - `The \`data\` property has been removed from the \`enhance\` callback argument. Use \`instance.fields.value()\` instead.` - ); + return data; } }, form: { @@ -324,7 +323,7 @@ export function form(id) { value: form }, submit: { - value: () => submit(form_data, false) + value: () => submit(form_data, data, false) } } ) @@ -333,9 +332,9 @@ export function form(id) { /** * @param {FormData} form_data + * @param {Record} data */ - async function preflight(form_data) { - const data = convert(form_data); + async function preflight(form_data, data) { const validated = await preflight_schema?.['~standard'].validate(data); if (validated?.issues) { @@ -417,6 +416,8 @@ export function form(id) { validate_form_data(form_data, clone(form).enctype); } + const data = convert(form_data); + submitted = true; try { @@ -424,10 +425,10 @@ export function form(id) { // the in-progress state during async preflight validation pending_count++; - const valid = await preflight(form_data); + const valid = await preflight(form_data, data); if (!valid) return; - await enhance_callback(create_enhance_callback_instance(form, form_data)); + await enhance_callback(create_enhance_callback_instance(form, form_data, data)); } catch (e) { const error = e instanceof HttpError ? e.body : { message: /** @type {any} */ (e).message }; @@ -580,7 +581,7 @@ export function form(id) { submitted = true; pending_count++; - const submission = submit(form_data, true); + const submission = submit(form_data, convert(form_data), true); void submission.finally(() => { pending_count--; From ef57af12ddb80f7ceb6b97f8a2ab6d440f48b5f4 Mon Sep 17 00:00:00 2001 From: Ottomated Date: Sat, 6 Jun 2026 15:15:15 -0700 Subject: [PATCH 2/3] fix types --- packages/kit/src/exports/public.d.ts | 3 ++- .../client/remote-functions/form.svelte.js | 24 ++++++++++--------- packages/kit/types/index.d.ts | 3 ++- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 745cf149317a..0910f4be1e3b 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -2161,8 +2161,9 @@ export type RemoteForm = { /** Use the `enhance` method to influence what happens when the form is submitted. */ enhance( callback: ( - form: Omit, 'enhance' | 'element'> & { + form: Omit, 'enhance' | 'element' | 'data'> & { readonly element: HTMLFormElement; + readonly data: Input; } ) => MaybePromise ): { diff --git a/packages/kit/src/runtime/client/remote-functions/form.svelte.js b/packages/kit/src/runtime/client/remote-functions/form.svelte.js index d662c87a874e..6398edfbaa7c 100644 --- a/packages/kit/src/runtime/client/remote-functions/form.svelte.js +++ b/packages/kit/src/runtime/client/remote-functions/form.svelte.js @@ -45,15 +45,17 @@ function merge_with_server_issues(form_data, current_issues, client_issues) { /** * Client-version of the `form` function from `$app/server`. - * @template {RemoteFormInput} T - * @template U + * @template {RemoteFormInput} TInput + * @template TOutput * @param {string} id - * @returns {RemoteForm} + * @returns {RemoteForm} */ export function form(id) { - /** @type {Map }>} */ + /** @type {Map }>} */ const instances = new Map(); + /** @typedef {Omit, 'enhance' | 'element' | 'data'> & { readonly element: HTMLFormElement, readonly data: TInput }} EnhanceCallbackInstance */ + /** @param {string | number | boolean} [key] */ function create_instance(key) { const action_id_without_key = id; @@ -80,7 +82,7 @@ export function form(id) { let preflight_schema = undefined; /** - * @param {Omit, 'enhance' | 'element'> & { readonly element: HTMLFormElement }} instance + * @param {} instance */ let enhance_callback = async (instance) => { if (await instance.submit()) { @@ -295,13 +297,13 @@ export function form(id) { * @param {HTMLFormElement} form * @param {FormData} form_data * @param {Record} data - * @returns {Omit, 'enhance' | 'element'> & { readonly element: HTMLFormElement }} + * @returns {EnhanceCallbackInstance} */ function create_enhance_callback_instance(form, form_data, data) { const { enhance: _enhance, ...descriptors } = Object.getOwnPropertyDescriptors(instance); void _enhance; - return /** @type {Omit, 'enhance' | 'element'> & { readonly element: HTMLFormElement }} */ ( + return /** @type {EnhanceCallbackInstance} */ ( Object.defineProperties( {}, { @@ -359,7 +361,7 @@ export function form(id) { return true; } - /** @type {RemoteForm} */ + /** @type {RemoteForm} */ const instance = {}; instance.method = 'POST'; @@ -626,7 +628,7 @@ export function form(id) { get: () => pending_count }, preflight: { - /** @type {RemoteForm['preflight']} */ + /** @type {RemoteForm['preflight']} */ value: (schema) => { preflight_schema = schema; return instance; @@ -701,7 +703,7 @@ export function form(id) { }, enhance: { /** - * @param {(instance: Omit, 'enhance' | 'element'> & { readonly element: HTMLFormElement }) => any} callback + * @param {(instance: EnhanceCallbackInstance) => any} callback */ value: (callback) => { enhance_callback = callback; @@ -716,7 +718,7 @@ export function form(id) { const instance = create_instance(); Object.defineProperty(instance, 'for', { - /** @type {RemoteForm['for']} */ + /** @type {RemoteForm['for']} */ value: (key) => { const entry = instances.get(key) ?? { count: 0, instance: create_instance(key) }; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index fa15b700a746..72bb7899d70e 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -2135,8 +2135,9 @@ declare module '@sveltejs/kit' { /** Use the `enhance` method to influence what happens when the form is submitted. */ enhance( callback: ( - form: Omit, 'enhance' | 'element'> & { + form: Omit, 'enhance' | 'element' | 'data'> & { readonly element: HTMLFormElement; + readonly data: Input; } ) => MaybePromise ): { From 73a1eb56cbd99b8a91049da516d36534c61e7f57 Mon Sep 17 00:00:00 2001 From: Ottomated Date: Sat, 6 Jun 2026 15:28:30 -0700 Subject: [PATCH 3/3] add type comments --- packages/kit/src/exports/public.d.ts | 2 ++ packages/kit/src/runtime/client/remote-functions/form.svelte.js | 2 +- packages/kit/types/index.d.ts | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 0910f4be1e3b..5aec9fb832a0 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -2162,7 +2162,9 @@ export type RemoteForm = { enhance( callback: ( form: Omit, 'enhance' | 'element' | 'data'> & { + /** The
element */ readonly element: HTMLFormElement; + /** The data being submitted */ readonly data: Input; } ) => MaybePromise diff --git a/packages/kit/src/runtime/client/remote-functions/form.svelte.js b/packages/kit/src/runtime/client/remote-functions/form.svelte.js index 6398edfbaa7c..6ff8740887b9 100644 --- a/packages/kit/src/runtime/client/remote-functions/form.svelte.js +++ b/packages/kit/src/runtime/client/remote-functions/form.svelte.js @@ -82,7 +82,7 @@ export function form(id) { let preflight_schema = undefined; /** - * @param {} instance + * @param {EnhanceCallbackInstance} instance */ let enhance_callback = async (instance) => { if (await instance.submit()) { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 72bb7899d70e..e0ffc5de64ce 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -2136,7 +2136,9 @@ declare module '@sveltejs/kit' { enhance( callback: ( form: Omit, 'enhance' | 'element' | 'data'> & { + /** The element */ readonly element: HTMLFormElement; + /** The data being submitted */ readonly data: Input; } ) => MaybePromise