import { expectTypeOf, test } from 'vitest' import { createServerFn } from '../createServerFn' import { createMiddleware } from '../createMiddleware' import type { Constrain, Validator } from '@tanstack/router-core' test('createServerFn method with autocomplete', () => { createServerFn().handler((options) => { expectTypeOf(options.method).toEqualTypeOf<'GET' | 'POST'>() }) }) test('createServerFn without middleware', () => { expectTypeOf(createServerFn()).toHaveProperty('handler') expectTypeOf(createServerFn()).toHaveProperty('middleware') expectTypeOf(createServerFn()).toHaveProperty('validator') createServerFn({ method: 'GET' }).handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: undefined signal: AbortSignal }>() }) }) test('createServerFn with validator', () => { const fnAfterValidator = createServerFn({ method: 'GET' }).validator( (input: { input: string }) => ({ a: input.input, }), ) expectTypeOf(fnAfterValidator).toHaveProperty('handler') expectTypeOf(fnAfterValidator).toHaveProperty('middleware') expectTypeOf(fnAfterValidator).not.toHaveProperty('validator') const fn = fnAfterValidator.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: { a: string } signal: AbortSignal }>() }) expectTypeOf(fn).parameter(0).toEqualTypeOf<{ data: { input: string } headers?: HeadersInit type?: 'static' | 'dynamic' fullResponse?: boolean signal?: AbortSignal }>() expectTypeOf(fn).returns.resolves.toEqualTypeOf() }) test('createServerFn with middleware and context', () => { const middleware1 = createMiddleware().server(({ next }) => { return next({ context: { a: 'a' } as const }) }) const middleware2 = createMiddleware().server(({ next }) => { return next({ context: { b: 'b' } as const }) }) const middleware3 = createMiddleware() .middleware([middleware1, middleware2]) .client(({ next }) => { return next({ context: { c: 'c' } as const }) }) const middleware4 = createMiddleware() .middleware([middleware3]) .client(({ context, next }) => { return next({ sendContext: context }) }) .server(({ context, next }) => { expectTypeOf(context).toEqualTypeOf<{ readonly a: 'a' readonly b: 'b' readonly c: 'c' }>() return next({ context: { d: 'd' } as const }) }) const fnWithMiddleware = createServerFn({ method: 'GET' }).middleware([ middleware4, ]) expectTypeOf(fnWithMiddleware).toHaveProperty('handler') expectTypeOf(fnWithMiddleware).toHaveProperty('validator') expectTypeOf(fnWithMiddleware).not.toHaveProperty('middleware') fnWithMiddleware.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: { readonly a: 'a' readonly b: 'b' readonly c: 'c' readonly d: 'd' } data: undefined signal: AbortSignal }>() }) }) test('createServerFn with middleware and validator', () => { const middleware1 = createMiddleware().validator( (input: { readonly inputA: 'inputA' }) => ({ outputA: 'outputA', }) as const, ) const middleware2 = createMiddleware().validator( (input: { readonly inputB: 'inputB' }) => ({ outputB: 'outputB', }) as const, ) const middleware3 = createMiddleware().middleware([middleware1, middleware2]) const fn = createServerFn({ method: 'GET' }) .middleware([middleware3]) .validator( (input: { readonly inputC: 'inputC' }) => ({ outputC: 'outputC', }) as const, ) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: { readonly outputA: 'outputA' readonly outputB: 'outputB' readonly outputC: 'outputC' } signal: AbortSignal }>() return 'data' as const }) expectTypeOf(fn).parameter(0).toEqualTypeOf<{ data: { readonly inputA: 'inputA' readonly inputB: 'inputB' readonly inputC: 'inputC' } headers?: HeadersInit type?: 'static' | 'dynamic' fullResponse?: boolean signal?: AbortSignal }>() expectTypeOf(fn).returns.resolves.toEqualTypeOf<'data'>() expectTypeOf(() => fn({ fullResponse: false, data: { inputA: 'inputA', inputB: 'inputB', inputC: 'inputC' }, }), ).returns.resolves.toEqualTypeOf<'data'>() expectTypeOf(() => fn({ fullResponse: true, data: { inputA: 'inputA', inputB: 'inputB', inputC: 'inputC' }, }), ).returns.resolves.toEqualTypeOf<{ result: 'data' context: undefined error: unknown }>() }) test('createServerFn overrides properties', () => { const middleware1 = createMiddleware() .validator( () => ({ input: 'a' as 'a' | 'b' | 'c', }) as const, ) .client(({ context, next }) => { expectTypeOf(context).toEqualTypeOf() const newContext = { context: 'a' } as const return next({ sendContext: newContext, context: newContext }) }) .server(({ data, context, next }) => { expectTypeOf(data).toEqualTypeOf<{ readonly input: 'a' | 'b' | 'c' }>() expectTypeOf(context).toEqualTypeOf<{ readonly context: 'a' }>() const newContext = { context: 'b' } as const return next({ sendContext: newContext, context: newContext }) }) const middleware2 = createMiddleware() .middleware([middleware1]) .validator( () => ({ input: 'b' as 'b' | 'c', }) as const, ) .client(({ context, next }) => { expectTypeOf(context).toEqualTypeOf<{ readonly context: 'a' }>() const newContext = { context: 'aa' } as const return next({ sendContext: newContext, context: newContext }) }) .server(({ context, next }) => { expectTypeOf(context).toEqualTypeOf<{ readonly context: 'aa' }>() const newContext = { context: 'bb' } as const return next({ sendContext: newContext, context: newContext }) }) createServerFn() .middleware([middleware2]) .validator( () => ({ input: 'c', }) as const, ) .handler(({ data, context }) => { expectTypeOf(data).toEqualTypeOf<{ readonly input: 'c' }>() expectTypeOf(context).toEqualTypeOf<{ readonly context: 'bb' }>() }) }) test('createServerFn where validator is a primitive', () => { createServerFn({ method: 'GET' }) .validator(() => 'c' as const) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: 'c' signal: AbortSignal }>() }) }) test('createServerFn where validator is optional if object is optional', () => { const fn = createServerFn({ method: 'GET' }) .validator((input: 'c' | undefined) => input) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: 'c' | undefined signal: AbortSignal }>() }) expectTypeOf(fn).parameter(0).toEqualTypeOf< | { data?: 'c' | undefined headers?: HeadersInit type?: 'static' | 'dynamic' fullResponse?: boolean signal?: AbortSignal } | undefined >() expectTypeOf(fn).returns.resolves.toEqualTypeOf() }) test('createServerFn where data is optional if there is no validator', () => { const fn = createServerFn({ method: 'GET' }).handler((options) => { expectTypeOf(options).toEqualTypeOf<{ method: 'GET' context: undefined data: undefined signal: AbortSignal }>() }) expectTypeOf(fn).parameter(0).toEqualTypeOf< | { data?: undefined headers?: HeadersInit type?: 'static' | 'dynamic' fullResponse?: boolean signal?: AbortSignal } | undefined >() expectTypeOf(fn).returns.resolves.toEqualTypeOf() }) test('createServerFn returns Date', () => { const fn = createServerFn().handler(() => ({ dates: [new Date(), new Date()] as const, })) expectTypeOf(fn()).toEqualTypeOf>() }) test('createServerFn returns RSC', () => { const fn = createServerFn().handler(() => ({ rscs: [
I'm an RSC
,
I'm an RSC
, ] as const, })) expectTypeOf(fn()).toEqualTypeOf< Promise<{ rscs: readonly [ReadableStream, ReadableStream] }> >() }) test('createServerFn returns undefined', () => { const fn = createServerFn().handler(() => ({ nothing: undefined, })) expectTypeOf(fn()).toEqualTypeOf>() }) test('createServerFn cannot return function', () => { expectTypeOf(createServerFn().handler<{ func: () => 'func' }>) .parameter(0) .returns.toEqualTypeOf< | { func: 'Function is not serializable' } | Promise<{ func: 'Function is not serializable' }> >() }) test('createServerFn cannot validate function', () => { const validator = createServerFn().validator< (input: { func: () => 'string' }) => { output: 'string' } > expectTypeOf(validator) .parameter(0) .toEqualTypeOf< Constrain< (input: { func: () => 'string' }) => { output: 'string' }, Validator<{ func: 'Function is not serializable' }, any> > >() }) test('createServerFn can validate Date', () => { const validator = createServerFn().validator< (input: Date) => { output: 'string' } > expectTypeOf(validator) .parameter(0) .toEqualTypeOf< Constrain<(input: Date) => { output: 'string' }, Validator> >() }) test('createServerFn can validate FormData', () => { const validator = createServerFn().validator< (input: FormData) => { output: 'string' } > expectTypeOf(validator) .parameter(0) .toEqualTypeOf< Constrain< (input: FormData) => { output: 'string' }, Validator > >() })