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..c363fdad786 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -102,13 +102,14 @@ 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) => { - if (TSS_SERVER_FUNCTION_FACTORY in m) { - if (m.options.middleware) { - newMiddleware.push(...m.options.middleware) + // 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) } } else { - newMiddleware.push(m) + newMiddleware.push(item) } }) 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..5958720a7b5 --- /dev/null +++ b/packages/start-client-core/tests/createServerFn.test.ts @@ -0,0 +1,54 @@ +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) +}) + +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) +})