Skip to content

Commit 16d8ed2

Browse files
committed
fix: improve tanstack query cancellation and type/test safety
1 parent 270e3fd commit 16d8ed2

8 files changed

Lines changed: 46 additions & 64 deletions

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"license": "MIT",
4141
"scripts": {
4242
"build": "tsdown",
43-
"test": "bun test",
43+
"test": "bun test && bun run test:types",
44+
"test:types": "bunx tsc --project tsconfig.test.json",
4445
"prepublishOnly": "bun run build"
4546
},
4647
"peerDependencies": {

src/index.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ async function dataOrThrow<T>(
103103
return result.data as T
104104
}
105105

106+
function withContextSignal(
107+
fetch: RequestInit | undefined,
108+
signal: AbortSignal | undefined
109+
): RequestInit | undefined {
110+
if (!signal || fetch?.signal) return fetch
111+
return {
112+
...(fetch ?? {}),
113+
signal
114+
}
115+
}
116+
106117
interface ProxyContext {
107118
raw: any
108119
prefix: QueryKey
@@ -147,9 +158,14 @@ function createMethodDecorator(
147158
): EdenQueryOptions<TData> => {
148159
return {
149160
queryKey: fn.queryKey(input),
150-
queryFn: () => {
161+
queryFn: (context) => {
151162
const materializedPath = materializePath(pathTemplate, input?.params)
152-
return dataOrThrow(callTreaty(ctx.raw, materializedPath, method, input))
163+
return dataOrThrow(
164+
callTreaty(ctx.raw, materializedPath, method, {
165+
...input,
166+
fetch: withContextSignal(input.fetch, context?.signal)
167+
})
168+
)
153169
},
154170
...overrides
155171
}
@@ -172,7 +188,8 @@ function createMethodDecorator(
172188
}
173189
return dataOrThrow<TData>(callTreaty(ctx.raw, materializedPath, method, {
174190
...input,
175-
query: queryWithCursor
191+
query: queryWithCursor,
192+
fetch: withContextSignal(input.fetch, context.signal)
176193
}))
177194
},
178195
initialPageParam: opts.initialPageParam,

src/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ type ExtractError<Res> = Res extends Record<number, unknown>
5050
}[Exclude<keyof Res, SuccessCodeRange>]
5151
: { status: unknown; value: unknown }
5252

53+
type TreatyResponseMap<Res> = Res extends Record<number, unknown>
54+
? Res
55+
: Record<number, unknown>
56+
5357
interface TQParamBase {
5458
fetch?: RequestInit
5559
}
@@ -93,7 +97,7 @@ type TQMethodParam<
9397

9498
export interface EdenQueryOptions<TData = unknown, TError = unknown> {
9599
queryKey: QueryKey
96-
queryFn: () => Promise<TData>
100+
queryFn: (context?: QueryFunctionContext<QueryKey>) => Promise<TData>
97101
enabled?: boolean
98102
staleTime?: number
99103
gcTime?: number
@@ -160,7 +164,7 @@ export interface EdenTQMethod<
160164
<TQueryFnData = ExtractData<Res>>(
161165
input: TQMethodParam<Body, Headers, Query, Params>,
162166
options?: RequestInit
163-
): Promise<Treaty.TreatyResponse<Res>>
167+
): Promise<Treaty.TreatyResponse<TreatyResponseMap<Res>>>
164168

165169
queryKey(input?: OmitQueryInput<TQMethodParam<Body, Headers, Query, Params>>): QueryKey
166170

test/tanstack-query-advanced.test.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Elysia, t } from 'elysia'
22
import { createEdenTQ, createEdenTQUtils } from '../src'
33
import { QueryClient } from '@tanstack/query-core'
4-
import { describe, expect, it, beforeAll, afterAll, mock, test } from 'bun:test'
4+
import { describe, expect, it, mock, test } from 'bun:test'
55

66
const posts = Array.from({ length: 50 }, (_, i) => ({
77
id: `post-${i + 1}`,
@@ -53,21 +53,11 @@ const app = new Elysia()
5353
query: t.Object({
5454
page: t.Optional(t.String())
5555
})
56-
})
57-
58-
let server: ReturnType<typeof app.listen>
59-
60-
beforeAll(() => {
61-
server = app.listen(3459)
6256
})
6357

64-
afterAll(() => {
65-
server.stop()
66-
})
58+
const eden = createEdenTQ<typeof app>(app)
6759

6860
describe('Infinite Query', () => {
69-
const eden = createEdenTQ<typeof app>('http://localhost:3459')
70-
7161
it('generates infiniteQueryOptions with cursor pagination', async () => {
7262
const options = eden.posts.get.infiniteQueryOptions(
7363
{ query: { limit: '5' } },
@@ -187,8 +177,6 @@ describe('Infinite Query', () => {
187177
})
188178

189179
describe('EdenTQ Utils', () => {
190-
const eden = createEdenTQ<typeof app>('http://localhost:3459')
191-
192180
it('creates utils with bound QueryClient', () => {
193181
const queryClient = new QueryClient()
194182
const utils = createEdenTQUtils(eden, queryClient)
@@ -267,8 +255,6 @@ describe('EdenTQ Utils', () => {
267255
})
268256

269257
describe('Prefetch and Cache helpers on EdenTQ', () => {
270-
const eden = createEdenTQ<typeof app>('http://localhost:3459')
271-
272258
it('prefetch works directly on eden', async () => {
273259
const queryClient = new QueryClient()
274260

@@ -302,8 +288,6 @@ describe('Prefetch and Cache helpers on EdenTQ', () => {
302288
})
303289

304290
describe('Query Options extensions', () => {
305-
const eden = createEdenTQ<typeof app>('http://localhost:3459')
306-
307291
it('queryOptions accepts extended options', () => {
308292
const options = eden.get.queryOptions({}, {
309293
staleTime: 1000,

test/tanstack-query-integration.test.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Elysia, t } from 'elysia'
22
import { createEdenTQ } from '../src'
33
import { QueryClient } from '@tanstack/query-core'
4-
import { describe, expect, it, beforeAll, afterAll, test } from 'bun:test'
4+
import { describe, expect, it, test } from 'bun:test'
55
import { expectTypeOf } from 'expect-type'
66

77
const ShareLinkSchema = t.Object({
@@ -108,19 +108,9 @@ const app = new Elysia()
108108
})
109109
)
110110

111-
let server: ReturnType<typeof app.listen>
112-
113-
beforeAll(() => {
114-
server = app.listen(3458)
115-
})
116-
117-
afterAll(() => {
118-
server.stop()
119-
})
111+
const eden = createEdenTQ<typeof app>(app)
120112

121113
describe('TanStack Query Integration', () => {
122-
const eden = createEdenTQ<typeof app>('http://localhost:3458')
123-
124114
describe('Works with real QueryClient', () => {
125115
it('queryOptions works with QueryClient.fetchQuery', async () => {
126116
const queryClient = new QueryClient()
@@ -289,8 +279,6 @@ describe('TanStack Query Integration', () => {
289279
})
290280

291281
describe('Usage Examples - Before vs After', () => {
292-
const eden = createEdenTQ<typeof app>('http://localhost:3458')
293-
294282
test('Share Links Query - shows reduced boilerplate', async () => {
295283
const caseId = 'demo-case'
296284

test/tanstack-query-real-world.test.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Elysia, t } from 'elysia'
22
import { createEdenTQ } from '../src'
3-
import { describe, expect, it, beforeAll, afterAll, mock, test } from 'bun:test'
3+
import { describe, expect, it, mock, test } from 'bun:test'
44
import { expectTypeOf } from 'expect-type'
55

66
const ShareLinkSchema = t.Object({
@@ -93,19 +93,9 @@ const app = new Elysia()
9393
})
9494
)
9595

96-
let server: ReturnType<typeof app.listen>
97-
98-
beforeAll(() => {
99-
server = app.listen(3457)
100-
})
101-
102-
afterAll(() => {
103-
server.stop()
104-
})
96+
const eden = createEdenTQ<typeof app>(app)
10597

10698
describe('Real-world API patterns', () => {
107-
const eden = createEdenTQ<typeof app>('http://localhost:3457')
108-
10999
describe('Share Links Query', () => {
110100
it('generates correct queryOptions with type-safe response', async () => {
111101
const caseId = 'case-123'
@@ -293,8 +283,6 @@ describe('Real-world API patterns', () => {
293283
})
294284

295285
describe('Comparison: Before vs After', () => {
296-
const eden = createEdenTQ<typeof app>('http://localhost:3457')
297-
298286
it('shows the difference in boilerplate', async () => {
299287
const caseId = 'demo-case'
300288

test/tanstack-query.test.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Elysia, t } from 'elysia'
22
import { createEdenTQ } from '../src'
3-
import { describe, expect, it, beforeAll, afterAll, mock, test } from 'bun:test'
3+
import { describe, expect, it, mock, test } from 'bun:test'
44

55
const app = new Elysia()
66
.get('/', () => 'hello')
@@ -23,18 +23,8 @@ const app = new Elysia()
2323
})
2424
})
2525

26-
let server: ReturnType<typeof app.listen>
27-
28-
beforeAll(() => {
29-
server = app.listen(3456)
30-
})
31-
32-
afterAll(() => {
33-
server.stop()
34-
})
35-
3626
describe('createEdenTQ', () => {
37-
const eden = createEdenTQ<typeof app>('http://localhost:3456')
27+
const eden = createEdenTQ<typeof app>(app)
3828

3929
describe('queryKey', () => {
4030
it('generates query key for simple route', () => {

tsconfig.test.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"noEmit": true,
5+
"rootDir": ".",
6+
"types": ["bun"]
7+
},
8+
"include": ["src/**/*", "test/types/**/*"],
9+
"exclude": ["node_modules", "dist"]
10+
}

0 commit comments

Comments
 (0)