From 240d7b748476231d4b34272223b65c7c295c7cea Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Thu, 10 Sep 2026 20:57:34 -0700 Subject: [PATCH 1/3] perf(start-client-core): avoid unused middleware array --- .changeset/start-middleware-registration.md | 5 +++ .../start-client-core/src/createServerFn.ts | 2 +- .../tests/createServerFn.test.ts | 37 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/start-middleware-registration.md create mode 100644 packages/start-client-core/tests/createServerFn.test.ts diff --git a/.changeset/start-middleware-registration.md b/.changeset/start-middleware-registration.md new file mode 100644 index 00000000000..a8a8e30b273 --- /dev/null +++ b/.changeset/start-middleware-registration.md @@ -0,0 +1,5 @@ +--- +'@tanstack/start-client-core': patch +--- + +Avoid an unused array when registering server-function middleware. diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index bfa48039cf2..0b80ddb97e9 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -102,7 +102,7 @@ export const createServerFn: CreateServerFn = (options, __opts) => { // this is primarily useful for letting users create their own abstractions on top of `createServerFn` const newMiddleware = [...(resolvedOptions.middleware || [])] - middleware.map((m) => { + middleware.forEach((m) => { if (TSS_SERVER_FUNCTION_FACTORY in m) { if (m.options.middleware) { newMiddleware.push(...m.options.middleware) diff --git a/packages/start-client-core/tests/createServerFn.test.ts b/packages/start-client-core/tests/createServerFn.test.ts new file mode 100644 index 00000000000..93d65f6f47d --- /dev/null +++ b/packages/start-client-core/tests/createServerFn.test.ts @@ -0,0 +1,37 @@ +import { expect, test } from 'vitest' +import { createMiddleware } from '../src/createMiddleware' +import { createServerFn } from '../src/createServerFn' + +test('appends factory middleware in order without changing source builders', () => { + const first = createMiddleware({ type: 'function' }) + const second = createMiddleware({ type: 'function' }) + const third = createMiddleware({ type: 'function' }) + const factory = createServerFn().middleware([second]) + const base = createServerFn({ method: 'POST' }).middleware([first]) + const middlewares = Object.freeze([factory, third, second]) + + const combined = base.middleware(middlewares) + + expect(combined.options.middleware).toEqual([first, second, third, second]) + expect(combined.options.method).toBe('POST') + expect(base.options.middleware).toEqual([first]) + expect(factory.options.middleware).toEqual([second]) + expect(middlewares).toEqual([factory, third, second]) + expect(combined.middleware([]).options.middleware).toEqual([ + first, + second, + third, + second, + ]) +}) + +test('ignores empty slots in a middleware array', () => { + const middleware = createMiddleware({ type: 'function' }) + const middlewares: Array = [] + middlewares[1] = middleware + + expect(createServerFn().middleware(middlewares).options.middleware).toEqual([ + middleware, + ]) + expect(0 in middlewares).toBe(false) +}) From ec6bd7660001ff380da7bcbc7c09a56e584ec17b Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Fri, 11 Sep 2026 08:10:43 -0700 Subject: [PATCH 2/3] perf(start-client-core): use indexed middleware iteration --- .../start-client-core/src/createServerFn.ts | 16 ++++++++++------ .../tests/createServerFn.test.ts | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index 0b80ddb97e9..826052c6fd2 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -102,15 +102,19 @@ export const createServerFn: CreateServerFn = (options, __opts) => { // this is primarily useful for letting users create their own abstractions on top of `createServerFn` const newMiddleware = [...(resolvedOptions.middleware || [])] - middleware.forEach((m) => { - if (TSS_SERVER_FUNCTION_FACTORY in m) { - if (m.options.middleware) { - newMiddleware.push(...m.options.middleware) + for (let index = 0, length = middleware.length; index < length; index++) { + if (!(index in middleware)) { + continue + } + const item = middleware[index]! + if (TSS_SERVER_FUNCTION_FACTORY in item) { + if (item.options.middleware) { + newMiddleware.push(...item.options.middleware) } } else { - newMiddleware.push(m) + newMiddleware.push(item) } - }) + } const newOptions = { ...resolvedOptions, diff --git a/packages/start-client-core/tests/createServerFn.test.ts b/packages/start-client-core/tests/createServerFn.test.ts index 93d65f6f47d..5958720a7b5 100644 --- a/packages/start-client-core/tests/createServerFn.test.ts +++ b/packages/start-client-core/tests/createServerFn.test.ts @@ -35,3 +35,20 @@ test('ignores empty slots in a middleware array', () => { ]) expect(0 in middlewares).toBe(false) }) + +test('does not register middleware appended while reading the input', () => { + const first = createMiddleware({ type: 'function' }) + const second = createMiddleware({ type: 'function' }) + const middlewares = [first] + Object.defineProperty(middlewares, 0, { + get() { + middlewares.push(second) + return first + }, + }) + + expect(createServerFn().middleware(middlewares).options.middleware).toEqual([ + first, + ]) + expect(middlewares).toHaveLength(2) +}) From 81414d840daea434c1cb4e5f08e9fd515ecf099b Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 11 Sep 2026 17:49:04 -0700 Subject: [PATCH 3/3] perf(start-client-core): iterate middleware with forEach forEach keeps the hole-skipping and length-snapshot semantics the tests cover without the extra `in` check and length hoist, and it avoids the result array that map allocated. Co-Authored-By: Claude Fable 5.1 --- packages/start-client-core/src/createServerFn.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index 826052c6fd2..c363fdad786 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -102,11 +102,8 @@ export const createServerFn: CreateServerFn = (options, __opts) => { // this is primarily useful for letting users create their own abstractions on top of `createServerFn` const newMiddleware = [...(resolvedOptions.middleware || [])] - for (let index = 0, length = middleware.length; index < length; index++) { - if (!(index in middleware)) { - continue - } - const item = middleware[index]! + // forEach skips holes and ignores items appended during iteration + middleware.forEach((item) => { if (TSS_SERVER_FUNCTION_FACTORY in item) { if (item.options.middleware) { newMiddleware.push(...item.options.middleware) @@ -114,7 +111,7 @@ export const createServerFn: CreateServerFn = (options, __opts) => { } else { newMiddleware.push(item) } - } + }) const newOptions = { ...resolvedOptions,