From 45e6ef9032442839858589d3241dc438a46904f8 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 29 Aug 2025 00:10:09 +0200 Subject: [PATCH 1/4] fix: allow commands from within endpoints fixes #14325 --- .changeset/sour-vans-fix.md | 5 ++++ .../src/runtime/app/server/remote/command.js | 2 +- packages/kit/src/runtime/server/endpoint.js | 2 ++ packages/kit/src/types/internal.d.ts | 1 + .../remote/server-endpoint/+page.svelte | 23 +++++++++++++++++++ .../remote/server-endpoint/api/+server.ts | 12 ++++++++++ .../remote/server-endpoint/internal.remote.ts | 9 ++++++++ .../kit/test/apps/basics/test/client.test.js | 10 ++++++++ 8 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-vans-fix.md create mode 100644 packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts create mode 100644 packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts diff --git a/.changeset/sour-vans-fix.md b/.changeset/sour-vans-fix.md new file mode 100644 index 000000000000..ff5f688901f6 --- /dev/null +++ b/.changeset/sour-vans-fix.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: allow commands from within endpoints diff --git a/packages/kit/src/runtime/app/server/remote/command.js b/packages/kit/src/runtime/app/server/remote/command.js index 117b5b6eca19..f6fd0a7cc8a7 100644 --- a/packages/kit/src/runtime/app/server/remote/command.js +++ b/packages/kit/src/runtime/app/server/remote/command.js @@ -64,7 +64,7 @@ export function command(validate_or_fn, maybe_fn) { const wrapper = (arg) => { const { event, state } = get_request_store(); - if (!event.isRemoteRequest) { + if (!event.isRemoteRequest && !state.is_endpoint_request) { throw new Error( `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) during server-side rendering` ); diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index e79b5d083925..a8fe8719ca34 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -41,6 +41,8 @@ export async function render_endpoint(event, event_state, mod, state) { } } + event_state.is_endpoint_request = true; + try { const response = await with_request_store({ event, state: event_state }, () => handler(/** @type {import('@sveltejs/kit').RequestEvent>} */ (event)) diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index d5aea28c39f1..14f026d110ab 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -588,6 +588,7 @@ export interface RequestState { form_instances?: Map; remote_data?: Record>; refreshes?: Record>; + is_endpoint_request?: boolean; } export interface RequestStore { diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte new file mode 100644 index 000000000000..c7b770912bf3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte @@ -0,0 +1,23 @@ + + +

{result}

+ + + + diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts new file mode 100644 index 000000000000..dfc4e271b451 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts @@ -0,0 +1,12 @@ +import { json } from '@sveltejs/kit'; +import { add, get } from '../internal.remote'; + +export async function GET() { + const result = await get(); + return json({ result }); +} + +export async function POST() { + const result = await add(1); + return json({ result }); +} diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts new file mode 100644 index 000000000000..25e83a63bee9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts @@ -0,0 +1,9 @@ +import { command, query } from '$app/server'; + +export const get = query(() => { + return 'get'; +}); + +export const add = command('unchecked', () => { + return 'post'; +}); diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 44d6a794f171..0f3f5d1cc792 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -1754,6 +1754,16 @@ test.describe('remote functions', () => { expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response }); + test('query/command inside endpoint works', async ({ page }) => { + await page.goto('/remote/server-endpoint'); + + await page.getByRole('button', { name: 'get' }).click(); + await expect(page.locator('p')).toHaveText('get'); + + await page.getByRole('button', { name: 'post' }).click(); + await expect(page.locator('p')).toHaveText('post'); + }); + test('form.enhance works', async ({ page }) => { await page.goto('/remote/form'); await page.fill('#input-task-enhance', 'abort'); From 72d1e558a3e0c0a1c010a54c87f8be134fd6a4b6 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 3 Sep 2025 00:17:28 +0200 Subject: [PATCH 2/4] limit commands to post/put/patch/delete --- packages/kit/src/runtime/app/server/remote/command.js | 9 ++++++++- packages/kit/src/runtime/server/endpoint.js | 2 +- packages/kit/src/types/internal.d.ts | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/runtime/app/server/remote/command.js b/packages/kit/src/runtime/app/server/remote/command.js index f6fd0a7cc8a7..614d46427118 100644 --- a/packages/kit/src/runtime/app/server/remote/command.js +++ b/packages/kit/src/runtime/app/server/remote/command.js @@ -64,7 +64,14 @@ export function command(validate_or_fn, maybe_fn) { const wrapper = (arg) => { const { event, state } = get_request_store(); - if (!event.isRemoteRequest && !state.is_endpoint_request) { + if ( + state.endpoint_request && + !['POST', 'PUT', 'PATCH', 'DELETE'].includes(state.endpoint_request) + ) { + throw new Error( + `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${state.endpoint_request} endpoint` + ); + } else if (!event.isRemoteRequest) { throw new Error( `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) during server-side rendering` ); diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index a8fe8719ca34..3e5308a12502 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -41,7 +41,7 @@ export async function render_endpoint(event, event_state, mod, state) { } } - event_state.is_endpoint_request = true; + event_state.endpoint_request = method; try { const response = await with_request_store({ event, state: event_state }, () => diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 14f026d110ab..d8ceb9f558b1 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -588,7 +588,7 @@ export interface RequestState { form_instances?: Map; remote_data?: Record>; refreshes?: Record>; - is_endpoint_request?: boolean; + endpoint_request?: HttpMethod; } export interface RequestStore { From 0e3056d11e49445099f5b1ed65629aa4b330a775 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 10 Sep 2025 13:05:21 +0200 Subject: [PATCH 3/4] fix --- .../kit/src/runtime/app/server/remote/command.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/kit/src/runtime/app/server/remote/command.js b/packages/kit/src/runtime/app/server/remote/command.js index 614d46427118..7f972b1bcc67 100644 --- a/packages/kit/src/runtime/app/server/remote/command.js +++ b/packages/kit/src/runtime/app/server/remote/command.js @@ -64,13 +64,12 @@ export function command(validate_or_fn, maybe_fn) { const wrapper = (arg) => { const { event, state } = get_request_store(); - if ( - state.endpoint_request && - !['POST', 'PUT', 'PATCH', 'DELETE'].includes(state.endpoint_request) - ) { - throw new Error( - `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${state.endpoint_request} endpoint` - ); + if (state.endpoint_request) { + if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(state.endpoint_request)) { + throw new Error( + `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${state.endpoint_request} endpoint` + ); + } } else if (!event.isRemoteRequest) { throw new Error( `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) during server-side rendering` From 4b4de69352a725164bdfa5af167bd6c6575fefab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 10 Sep 2025 21:20:38 -0400 Subject: [PATCH 4/4] tweak --- packages/kit/src/runtime/app/server/remote/command.js | 6 +++--- packages/kit/src/runtime/server/endpoint.js | 2 +- packages/kit/src/types/internal.d.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/runtime/app/server/remote/command.js b/packages/kit/src/runtime/app/server/remote/command.js index 7f972b1bcc67..2ea88bfb02f0 100644 --- a/packages/kit/src/runtime/app/server/remote/command.js +++ b/packages/kit/src/runtime/app/server/remote/command.js @@ -64,10 +64,10 @@ export function command(validate_or_fn, maybe_fn) { const wrapper = (arg) => { const { event, state } = get_request_store(); - if (state.endpoint_request) { - if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(state.endpoint_request)) { + if (state.is_endpoint_request) { + if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(event.request.method)) { throw new Error( - `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${state.endpoint_request} endpoint` + `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${event.request.method} handler` ); } } else if (!event.isRemoteRequest) { diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 3e5308a12502..a8fe8719ca34 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -41,7 +41,7 @@ export async function render_endpoint(event, event_state, mod, state) { } } - event_state.endpoint_request = method; + event_state.is_endpoint_request = true; try { const response = await with_request_store({ event, state: event_state }, () => diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index d8ceb9f558b1..14f026d110ab 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -588,7 +588,7 @@ export interface RequestState { form_instances?: Map; remote_data?: Record>; refreshes?: Record>; - endpoint_request?: HttpMethod; + is_endpoint_request?: boolean; } export interface RequestStore {