Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/pinia-colada/src/meta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,36 @@ describe('contractOptionsUtilsPlugin', () => {
expect(client.planet.find).toHaveBeenCalledTimes(1)
})

it('lets object utils options beat defaults of a function meta modifier', () => {
const contract = {
planet: {
find: oc.meta(piniaColada({
queryOptions: (options: any) => ({ staleTime: 1000, gcTime: 1, ...options }),
})),
},
}

const client = { planet: { find: vi.fn(async () => '__found__') } }

const utils = createRouterUtils(client as any, {
scoped: {
planet: {
find: {
queryOptions: { gcTime: 2 },
},
},
},
plugins: [new ContractOptionsUtilsPlugin(contract)],
}) as any

const options = utils.planet.find.queryOptions({ input: { id: 1 } })

expect(options.staleTime).toBe(1000)
expect(options.gcTime).toBe(2)

expect(utils.planet.find.queryOptions({ input: { id: 1 }, gcTime: 5 }).gcTime).toBe(5)
})

it('keeps contract meta interceptors when utils define none', async () => {
const metaInterceptor = vi.fn(({ next }: any) => next())
const contract = { planet: { find: oc.meta(piniaColada({ queryInterceptors: [metaInterceptor] })) } }
Expand Down
10 changes: 10 additions & 0 deletions packages/pinia-colada/src/procedure-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1004,4 +1004,14 @@ describe('mergeProcedureUtilsOptions', () => {

expect((functionFirst.queryOptions as any)({ enabled: true })).toEqual({ enabled: true, staleTime: 1000, gcTime: 2 })
})

it('lets an object override beat defaults of a function base, below per-call options', () => {
const merged = mergeProcedureUtilsOptions<any, any, any, any>(
{ queryOptions: (options: any) => ({ staleTime: 1000, ...options }) },
{ queryOptions: { staleTime: 0 } },
)

expect((merged.queryOptions as any)({})).toEqual({ staleTime: 0 })
expect((merged.queryOptions as any)({ staleTime: 5 })).toEqual({ staleTime: 5 })
})
})
17 changes: 10 additions & 7 deletions packages/pinia-colada/src/procedure-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,22 +215,25 @@ function mergeProcedureUtilsModifier<T extends object>(
base: ProcedureUtilsModifier<T>,
override: ProcedureUtilsModifier<T>,
): ProcedureUtilsModifier<T> {
if (typeof base !== 'function' && typeof override !== 'function') {
return { ...base, ...override } as Partial<T>
if (typeof override !== 'function') {
if (typeof base !== 'function') {
return { ...base, ...override } as Partial<T>
}

return options => base({ ...override, ...options })
}

const applyBase = typeof base === 'function' ? base : (options: T) => ({ ...base, ...options })
const applyOverride = typeof override === 'function' ? override : (options: T) => ({ ...override, ...options })

return options => applyOverride(applyBase(options))
return options => override(applyBase(options))
}

/**
* Merge two procedure utils options where `override` takes priority.
* A key explicitly set to `undefined` in `override` resets the base value.
* When both sides define a key: interceptors are concatenated (base ones run first),
* modifiers are spread-merged when both are plain objects and composed (base applied
* first) otherwise, with plain objects applied as regular spread merges.
* When both sides define a key, interceptors are concatenated (base ones run first) and
* modifiers are combined so that, for object values, per-call options beat `override`,
* which beats `base`. A function modifier receives the merged options and its result wins.
*/
export function mergeProcedureUtilsOptions<TClientContext extends ClientContext, TInput, TOutput, TError>(
base: ProcedureUtilsOptions<TClientContext, TInput, TOutput, TError>,
Expand Down
30 changes: 30 additions & 0 deletions packages/tanstack-query/src/meta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,36 @@ describe('contractMetaPlugin', () => {
expect(client.planet.find).toHaveBeenCalledTimes(1)
})

it('lets object utils options beat defaults of a function meta modifier', () => {
const contract = {
planet: {
find: oc.meta(tanstackQuery({
queryOptions: (options: any) => ({ staleTime: 1000, retry: 1, ...options }),
})),
},
}

const client = { planet: { find: vi.fn(async () => '__found__') } }

const utils = createRouterUtils(client as any, {
scoped: {
planet: {
find: {
queryOptions: { retry: 2 },
},
},
},
plugins: [new ContractOptionsUtilsPlugin(contract)],
}) as any

const options = utils.planet.find.queryOptions({ input: { id: 1 } })

expect(options.staleTime).toBe(1000)
expect(options.retry).toBe(2)

expect(utils.planet.find.queryOptions({ input: { id: 1 }, retry: 5 }).retry).toBe(5)
})

it('keeps contract meta interceptors when utils define none', async () => {
const metaInterceptor = vi.fn(({ next }: any) => next())
const contract = { planet: { find: oc.meta(tanstackQuery({ queryInterceptors: [metaInterceptor] })) } }
Expand Down
10 changes: 10 additions & 0 deletions packages/tanstack-query/src/procedure-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1253,4 +1253,14 @@ describe('mergeProcedureUtilsOptions', () => {

expect((functionFirst.queryOptions as any)({ enabled: true })).toEqual({ enabled: true, staleTime: 1000, retry: 2 })
})

it('lets an object override beat defaults of a function base, below per-call options', () => {
const merged = mergeProcedureUtilsOptions<any, any, any, any>(
{ queryOptions: (options: any) => ({ staleTime: 1000, ...options }) },
{ queryOptions: { staleTime: 0 } },
)

expect((merged.queryOptions as any)({})).toEqual({ staleTime: 0 })
expect((merged.queryOptions as any)({ staleTime: 5 })).toEqual({ staleTime: 5 })
})
})
17 changes: 10 additions & 7 deletions packages/tanstack-query/src/procedure-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,22 +227,25 @@ function mergeProcedureUtilsModifier<T extends object>(
base: ProcedureUtilsModifier<T>,
override: ProcedureUtilsModifier<T>,
): ProcedureUtilsModifier<T> {
if (typeof base !== 'function' && typeof override !== 'function') {
return { ...base, ...override } as Partial<T>
if (typeof override !== 'function') {
if (typeof base !== 'function') {
return { ...base, ...override } as Partial<T>
}

return options => base({ ...override, ...options })
}

const applyBase = typeof base === 'function' ? base : (options: T) => ({ ...base, ...options })
const applyOverride = typeof override === 'function' ? override : (options: T) => ({ ...override, ...options })

return options => applyOverride(applyBase(options))
return options => override(applyBase(options))
}

/**
* Merge two procedure utils options where `override` takes priority.
* A key explicitly set to `undefined` in `override` resets the base value.
* When both sides define a key: interceptors are concatenated (base ones run first),
* modifiers are spread-merged when both are plain objects and composed (base applied
* first) otherwise, with plain objects applied as regular spread merges.
* When both sides define a key, interceptors are concatenated (base ones run first) and
* modifiers are combined so that, for object values, per-call options beat `override`,
* which beats `base`. A function modifier receives the merged options and its result wins.
*/
export function mergeProcedureUtilsOptions<TClientContext extends ClientContext, TInput, TOutput, TError>(
base: ProcedureUtilsOptions<TClientContext, TInput, TOutput, TError>,
Expand Down
Loading