@pyreon/query 0.11.5 → 0.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -56
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +7 -7
- package/package.json +16 -16
- package/src/index.ts +21 -33
- package/src/query-client.ts +5 -5
- package/src/tests/query-additional.test.tsx +59 -59
- package/src/tests/query.test.tsx +243 -243
- package/src/tests/sse.test.tsx +131 -131
- package/src/tests/subscription.test.tsx +97 -97
- package/src/use-infinite-query.ts +7 -7
- package/src/use-is-fetching.ts +5 -5
- package/src/use-mutation.ts +8 -8
- package/src/use-queries.ts +6 -6
- package/src/use-query-error-reset-boundary.ts +6 -6
- package/src/use-query.ts +8 -8
- package/src/use-sse.ts +19 -19
- package/src/use-subscription.ts +18 -18
- package/src/use-suspense-query.ts +12 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { signal } from
|
|
2
|
-
import { mount } from
|
|
3
|
-
import { QueryClient } from
|
|
4
|
-
import { QueryClientProvider, type UseSubscriptionResult, useSubscription } from
|
|
1
|
+
import { signal } from '@pyreon/reactivity'
|
|
2
|
+
import { mount } from '@pyreon/runtime-dom'
|
|
3
|
+
import { QueryClient } from '@tanstack/query-core'
|
|
4
|
+
import { QueryClientProvider, type UseSubscriptionResult, useSubscription } from '../index'
|
|
5
5
|
|
|
6
6
|
// ─── Mock WebSocket ──────────────────────────────────────────────────────────
|
|
7
7
|
|
|
@@ -54,20 +54,20 @@ class MockWebSocketClass {
|
|
|
54
54
|
|
|
55
55
|
_simulateOpen() {
|
|
56
56
|
this.readyState = MockWebSocketClass.OPEN
|
|
57
|
-
this.onopen?.({ type:
|
|
57
|
+
this.onopen?.({ type: 'open' })
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
_simulateMessage(data: string) {
|
|
61
|
-
this.onmessage?.({ type:
|
|
61
|
+
this.onmessage?.({ type: 'message', data } as unknown as MessageEvent)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
_simulateClose(code = 1000, reason =
|
|
64
|
+
_simulateClose(code = 1000, reason = '') {
|
|
65
65
|
this.readyState = MockWebSocketClass.CLOSED
|
|
66
|
-
this.onclose?.({ type:
|
|
66
|
+
this.onclose?.({ type: 'close', code, reason } as unknown as CloseEvent)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
_simulateError() {
|
|
70
|
-
this.onerror?.({ type:
|
|
70
|
+
this.onerror?.({ type: 'error' })
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -92,7 +92,7 @@ function makeClient() {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function withProvider(client: QueryClient, component: () => void): () => void {
|
|
95
|
-
const el = document.createElement(
|
|
95
|
+
const el = document.createElement('div')
|
|
96
96
|
document.body.appendChild(el)
|
|
97
97
|
const unmount = mount(
|
|
98
98
|
<QueryClientProvider client={client}>
|
|
@@ -115,53 +115,53 @@ function lastMockWS(): MockWebSocket {
|
|
|
115
115
|
|
|
116
116
|
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
117
117
|
|
|
118
|
-
describe(
|
|
118
|
+
describe('useSubscription', () => {
|
|
119
119
|
beforeEach(() => {
|
|
120
120
|
mockInstances = []
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
-
it(
|
|
123
|
+
it('connects to the WebSocket URL', () => {
|
|
124
124
|
const client = makeClient()
|
|
125
125
|
let sub: UseSubscriptionResult | null = null
|
|
126
126
|
|
|
127
127
|
const unmount = withProvider(client, () => {
|
|
128
128
|
sub = useSubscription({
|
|
129
|
-
url:
|
|
129
|
+
url: 'wss://example.com/ws',
|
|
130
130
|
onMessage: noop,
|
|
131
131
|
})
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
expect(mockInstances).toHaveLength(1)
|
|
135
|
-
expect(lastMockWS().url).toBe(
|
|
136
|
-
expect(sub!.status()).toBe(
|
|
135
|
+
expect(lastMockWS().url).toBe('wss://example.com/ws')
|
|
136
|
+
expect(sub!.status()).toBe('connecting')
|
|
137
137
|
|
|
138
138
|
unmount()
|
|
139
139
|
})
|
|
140
140
|
|
|
141
|
-
it(
|
|
141
|
+
it('status transitions to connected on open', () => {
|
|
142
142
|
const client = makeClient()
|
|
143
143
|
let sub: UseSubscriptionResult | null = null
|
|
144
144
|
|
|
145
145
|
const unmount = withProvider(client, () => {
|
|
146
146
|
sub = useSubscription({
|
|
147
|
-
url:
|
|
147
|
+
url: 'wss://example.com/ws',
|
|
148
148
|
onMessage: noop,
|
|
149
149
|
})
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
lastMockWS()._simulateOpen()
|
|
153
|
-
expect(sub!.status()).toBe(
|
|
153
|
+
expect(sub!.status()).toBe('connected')
|
|
154
154
|
|
|
155
155
|
unmount()
|
|
156
156
|
})
|
|
157
157
|
|
|
158
|
-
it(
|
|
158
|
+
it('calls onMessage with event and queryClient', () => {
|
|
159
159
|
const client = makeClient()
|
|
160
160
|
const messages: string[] = []
|
|
161
161
|
|
|
162
162
|
const unmount = withProvider(client, () => {
|
|
163
163
|
useSubscription({
|
|
164
|
-
url:
|
|
164
|
+
url: 'wss://example.com/ws',
|
|
165
165
|
onMessage: (event, qc) => {
|
|
166
166
|
messages.push(event.data as string)
|
|
167
167
|
expect(qc).toBe(client)
|
|
@@ -170,75 +170,75 @@ describe("useSubscription", () => {
|
|
|
170
170
|
})
|
|
171
171
|
|
|
172
172
|
lastMockWS()._simulateOpen()
|
|
173
|
-
lastMockWS()._simulateMessage(
|
|
174
|
-
lastMockWS()._simulateMessage(
|
|
173
|
+
lastMockWS()._simulateMessage('hello')
|
|
174
|
+
lastMockWS()._simulateMessage('world')
|
|
175
175
|
|
|
176
|
-
expect(messages).toEqual([
|
|
176
|
+
expect(messages).toEqual(['hello', 'world'])
|
|
177
177
|
unmount()
|
|
178
178
|
})
|
|
179
179
|
|
|
180
|
-
it(
|
|
180
|
+
it('invalidates queries on message', () => {
|
|
181
181
|
const client = makeClient()
|
|
182
|
-
const invalidateSpy = vi.spyOn(client,
|
|
182
|
+
const invalidateSpy = vi.spyOn(client, 'invalidateQueries')
|
|
183
183
|
|
|
184
184
|
const unmount = withProvider(client, () => {
|
|
185
185
|
useSubscription({
|
|
186
|
-
url:
|
|
186
|
+
url: 'wss://example.com/ws',
|
|
187
187
|
onMessage: (_event, qc) => {
|
|
188
|
-
qc.invalidateQueries({ queryKey: [
|
|
188
|
+
qc.invalidateQueries({ queryKey: ['orders'] })
|
|
189
189
|
},
|
|
190
190
|
})
|
|
191
191
|
})
|
|
192
192
|
|
|
193
193
|
lastMockWS()._simulateOpen()
|
|
194
|
-
lastMockWS()._simulateMessage(
|
|
194
|
+
lastMockWS()._simulateMessage('order-updated')
|
|
195
195
|
|
|
196
|
-
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: [
|
|
196
|
+
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['orders'] })
|
|
197
197
|
unmount()
|
|
198
198
|
})
|
|
199
199
|
|
|
200
|
-
it(
|
|
200
|
+
it('send() sends data through WebSocket', () => {
|
|
201
201
|
const client = makeClient()
|
|
202
202
|
let sub: UseSubscriptionResult | null = null
|
|
203
203
|
|
|
204
204
|
const unmount = withProvider(client, () => {
|
|
205
205
|
sub = useSubscription({
|
|
206
|
-
url:
|
|
206
|
+
url: 'wss://example.com/ws',
|
|
207
207
|
onMessage: noop,
|
|
208
208
|
})
|
|
209
209
|
})
|
|
210
210
|
|
|
211
211
|
lastMockWS()._simulateOpen()
|
|
212
|
-
sub!.send(
|
|
212
|
+
sub!.send('test-message')
|
|
213
213
|
|
|
214
|
-
expect(lastMockWS().send).toHaveBeenCalledWith(
|
|
214
|
+
expect(lastMockWS().send).toHaveBeenCalledWith('test-message')
|
|
215
215
|
unmount()
|
|
216
216
|
})
|
|
217
217
|
|
|
218
|
-
it(
|
|
218
|
+
it('send() is a no-op when not connected', () => {
|
|
219
219
|
const client = makeClient()
|
|
220
220
|
let sub: UseSubscriptionResult | null = null
|
|
221
221
|
|
|
222
222
|
const unmount = withProvider(client, () => {
|
|
223
223
|
sub = useSubscription({
|
|
224
|
-
url:
|
|
224
|
+
url: 'wss://example.com/ws',
|
|
225
225
|
onMessage: noop,
|
|
226
226
|
})
|
|
227
227
|
})
|
|
228
228
|
|
|
229
229
|
// Still connecting — send should not throw
|
|
230
|
-
sub!.send(
|
|
230
|
+
sub!.send('ignored')
|
|
231
231
|
expect(lastMockWS().send).not.toHaveBeenCalled()
|
|
232
232
|
unmount()
|
|
233
233
|
})
|
|
234
234
|
|
|
235
|
-
it(
|
|
235
|
+
it('close() disconnects and sets status', () => {
|
|
236
236
|
const client = makeClient()
|
|
237
237
|
let sub: UseSubscriptionResult | null = null
|
|
238
238
|
|
|
239
239
|
const unmount = withProvider(client, () => {
|
|
240
240
|
sub = useSubscription({
|
|
241
|
-
url:
|
|
241
|
+
url: 'wss://example.com/ws',
|
|
242
242
|
onMessage: noop,
|
|
243
243
|
})
|
|
244
244
|
})
|
|
@@ -246,18 +246,18 @@ describe("useSubscription", () => {
|
|
|
246
246
|
lastMockWS()._simulateOpen()
|
|
247
247
|
sub!.close()
|
|
248
248
|
|
|
249
|
-
expect(sub!.status()).toBe(
|
|
249
|
+
expect(sub!.status()).toBe('disconnected')
|
|
250
250
|
expect(lastMockWS().close).toHaveBeenCalled()
|
|
251
251
|
unmount()
|
|
252
252
|
})
|
|
253
253
|
|
|
254
|
-
it(
|
|
254
|
+
it('status transitions to disconnected on close', () => {
|
|
255
255
|
const client = makeClient()
|
|
256
256
|
let sub: UseSubscriptionResult | null = null
|
|
257
257
|
|
|
258
258
|
const unmount = withProvider(client, () => {
|
|
259
259
|
sub = useSubscription({
|
|
260
|
-
url:
|
|
260
|
+
url: 'wss://example.com/ws',
|
|
261
261
|
onMessage: noop,
|
|
262
262
|
reconnect: false,
|
|
263
263
|
})
|
|
@@ -266,18 +266,18 @@ describe("useSubscription", () => {
|
|
|
266
266
|
lastMockWS()._simulateOpen()
|
|
267
267
|
lastMockWS()._simulateClose()
|
|
268
268
|
|
|
269
|
-
expect(sub!.status()).toBe(
|
|
269
|
+
expect(sub!.status()).toBe('disconnected')
|
|
270
270
|
unmount()
|
|
271
271
|
})
|
|
272
272
|
|
|
273
|
-
it(
|
|
273
|
+
it('status transitions to error on error', () => {
|
|
274
274
|
const client = makeClient()
|
|
275
275
|
let sub: UseSubscriptionResult | null = null
|
|
276
276
|
const errors: Event[] = []
|
|
277
277
|
|
|
278
278
|
const unmount = withProvider(client, () => {
|
|
279
279
|
sub = useSubscription({
|
|
280
|
-
url:
|
|
280
|
+
url: 'wss://example.com/ws',
|
|
281
281
|
onMessage: noop,
|
|
282
282
|
reconnect: false,
|
|
283
283
|
onError: (e) => errors.push(e as Event),
|
|
@@ -285,18 +285,18 @@ describe("useSubscription", () => {
|
|
|
285
285
|
})
|
|
286
286
|
|
|
287
287
|
lastMockWS()._simulateError()
|
|
288
|
-
expect(sub!.status()).toBe(
|
|
288
|
+
expect(sub!.status()).toBe('error')
|
|
289
289
|
expect(errors).toHaveLength(1)
|
|
290
290
|
unmount()
|
|
291
291
|
})
|
|
292
292
|
|
|
293
|
-
it(
|
|
293
|
+
it('calls onOpen callback', () => {
|
|
294
294
|
const client = makeClient()
|
|
295
295
|
let opened = false
|
|
296
296
|
|
|
297
297
|
const unmount = withProvider(client, () => {
|
|
298
298
|
useSubscription({
|
|
299
|
-
url:
|
|
299
|
+
url: 'wss://example.com/ws',
|
|
300
300
|
onMessage: noop,
|
|
301
301
|
onOpen: () => {
|
|
302
302
|
opened = true
|
|
@@ -309,13 +309,13 @@ describe("useSubscription", () => {
|
|
|
309
309
|
unmount()
|
|
310
310
|
})
|
|
311
311
|
|
|
312
|
-
it(
|
|
312
|
+
it('calls onClose callback', () => {
|
|
313
313
|
const client = makeClient()
|
|
314
314
|
let closed = false
|
|
315
315
|
|
|
316
316
|
const unmount = withProvider(client, () => {
|
|
317
317
|
useSubscription({
|
|
318
|
-
url:
|
|
318
|
+
url: 'wss://example.com/ws',
|
|
319
319
|
onMessage: noop,
|
|
320
320
|
reconnect: false,
|
|
321
321
|
onClose: () => {
|
|
@@ -330,12 +330,12 @@ describe("useSubscription", () => {
|
|
|
330
330
|
unmount()
|
|
331
331
|
})
|
|
332
332
|
|
|
333
|
-
it(
|
|
333
|
+
it('auto-reconnects on unexpected close', async () => {
|
|
334
334
|
const client = makeClient()
|
|
335
335
|
|
|
336
336
|
const unmount = withProvider(client, () => {
|
|
337
337
|
useSubscription({
|
|
338
|
-
url:
|
|
338
|
+
url: 'wss://example.com/ws',
|
|
339
339
|
onMessage: noop,
|
|
340
340
|
reconnect: true,
|
|
341
341
|
reconnectDelay: 50,
|
|
@@ -353,12 +353,12 @@ describe("useSubscription", () => {
|
|
|
353
353
|
unmount()
|
|
354
354
|
})
|
|
355
355
|
|
|
356
|
-
it(
|
|
356
|
+
it('does not reconnect when reconnect is false', async () => {
|
|
357
357
|
const client = makeClient()
|
|
358
358
|
|
|
359
359
|
const unmount = withProvider(client, () => {
|
|
360
360
|
useSubscription({
|
|
361
|
-
url:
|
|
361
|
+
url: 'wss://example.com/ws',
|
|
362
362
|
onMessage: noop,
|
|
363
363
|
reconnect: false,
|
|
364
364
|
})
|
|
@@ -372,13 +372,13 @@ describe("useSubscription", () => {
|
|
|
372
372
|
unmount()
|
|
373
373
|
})
|
|
374
374
|
|
|
375
|
-
it(
|
|
375
|
+
it('does not reconnect after intentional close()', async () => {
|
|
376
376
|
const client = makeClient()
|
|
377
377
|
let sub: UseSubscriptionResult | null = null
|
|
378
378
|
|
|
379
379
|
const unmount = withProvider(client, () => {
|
|
380
380
|
sub = useSubscription({
|
|
381
|
-
url:
|
|
381
|
+
url: 'wss://example.com/ws',
|
|
382
382
|
onMessage: noop,
|
|
383
383
|
reconnect: true,
|
|
384
384
|
reconnectDelay: 50,
|
|
@@ -393,12 +393,12 @@ describe("useSubscription", () => {
|
|
|
393
393
|
unmount()
|
|
394
394
|
})
|
|
395
395
|
|
|
396
|
-
it(
|
|
396
|
+
it('respects maxReconnectAttempts', async () => {
|
|
397
397
|
const client = makeClient()
|
|
398
398
|
|
|
399
399
|
const unmount = withProvider(client, () => {
|
|
400
400
|
useSubscription({
|
|
401
|
-
url:
|
|
401
|
+
url: 'wss://example.com/ws',
|
|
402
402
|
onMessage: noop,
|
|
403
403
|
reconnect: true,
|
|
404
404
|
reconnectDelay: 10,
|
|
@@ -427,13 +427,13 @@ describe("useSubscription", () => {
|
|
|
427
427
|
unmount()
|
|
428
428
|
})
|
|
429
429
|
|
|
430
|
-
it(
|
|
430
|
+
it('reconnect() resets attempts and reconnects', async () => {
|
|
431
431
|
const client = makeClient()
|
|
432
432
|
let sub: UseSubscriptionResult | null = null
|
|
433
433
|
|
|
434
434
|
const unmount = withProvider(client, () => {
|
|
435
435
|
sub = useSubscription({
|
|
436
|
-
url:
|
|
436
|
+
url: 'wss://example.com/ws',
|
|
437
437
|
onMessage: noop,
|
|
438
438
|
reconnect: false,
|
|
439
439
|
})
|
|
@@ -446,35 +446,35 @@ describe("useSubscription", () => {
|
|
|
446
446
|
|
|
447
447
|
sub!.reconnect()
|
|
448
448
|
expect(mockInstances).toHaveLength(2)
|
|
449
|
-
expect(sub!.status()).toBe(
|
|
449
|
+
expect(sub!.status()).toBe('connecting')
|
|
450
450
|
|
|
451
451
|
unmount()
|
|
452
452
|
})
|
|
453
453
|
|
|
454
|
-
it(
|
|
454
|
+
it('enabled: false prevents connection', () => {
|
|
455
455
|
const client = makeClient()
|
|
456
456
|
let sub: UseSubscriptionResult | null = null
|
|
457
457
|
|
|
458
458
|
const unmount = withProvider(client, () => {
|
|
459
459
|
sub = useSubscription({
|
|
460
|
-
url:
|
|
460
|
+
url: 'wss://example.com/ws',
|
|
461
461
|
onMessage: noop,
|
|
462
462
|
enabled: false,
|
|
463
463
|
})
|
|
464
464
|
})
|
|
465
465
|
|
|
466
466
|
expect(mockInstances).toHaveLength(0)
|
|
467
|
-
expect(sub!.status()).toBe(
|
|
467
|
+
expect(sub!.status()).toBe('disconnected')
|
|
468
468
|
unmount()
|
|
469
469
|
})
|
|
470
470
|
|
|
471
|
-
it(
|
|
471
|
+
it('reactive enabled signal controls connection', async () => {
|
|
472
472
|
const client = makeClient()
|
|
473
473
|
const enabled = signal(false)
|
|
474
474
|
|
|
475
475
|
const unmount = withProvider(client, () => {
|
|
476
476
|
useSubscription({
|
|
477
|
-
url:
|
|
477
|
+
url: 'wss://example.com/ws',
|
|
478
478
|
onMessage: noop,
|
|
479
479
|
enabled: () => enabled(),
|
|
480
480
|
})
|
|
@@ -489,9 +489,9 @@ describe("useSubscription", () => {
|
|
|
489
489
|
unmount()
|
|
490
490
|
})
|
|
491
491
|
|
|
492
|
-
it(
|
|
492
|
+
it('reactive URL reconnects when URL changes', () => {
|
|
493
493
|
const client = makeClient()
|
|
494
|
-
const url = signal(
|
|
494
|
+
const url = signal('wss://example.com/ws1')
|
|
495
495
|
|
|
496
496
|
const unmount = withProvider(client, () => {
|
|
497
497
|
useSubscription({
|
|
@@ -501,37 +501,37 @@ describe("useSubscription", () => {
|
|
|
501
501
|
})
|
|
502
502
|
|
|
503
503
|
expect(mockInstances).toHaveLength(1)
|
|
504
|
-
expect(lastMockWS().url).toBe(
|
|
504
|
+
expect(lastMockWS().url).toBe('wss://example.com/ws1')
|
|
505
505
|
|
|
506
|
-
url.set(
|
|
506
|
+
url.set('wss://example.com/ws2')
|
|
507
507
|
|
|
508
508
|
expect(mockInstances).toHaveLength(2)
|
|
509
|
-
expect(lastMockWS().url).toBe(
|
|
509
|
+
expect(lastMockWS().url).toBe('wss://example.com/ws2')
|
|
510
510
|
|
|
511
511
|
unmount()
|
|
512
512
|
})
|
|
513
513
|
|
|
514
|
-
it(
|
|
514
|
+
it('supports WebSocket protocols', () => {
|
|
515
515
|
const client = makeClient()
|
|
516
516
|
|
|
517
517
|
const unmount = withProvider(client, () => {
|
|
518
518
|
useSubscription({
|
|
519
|
-
url:
|
|
520
|
-
protocols: [
|
|
519
|
+
url: 'wss://example.com/ws',
|
|
520
|
+
protocols: ['graphql-ws'],
|
|
521
521
|
onMessage: noop,
|
|
522
522
|
})
|
|
523
523
|
})
|
|
524
524
|
|
|
525
|
-
expect(lastMockWS().protocols).toEqual([
|
|
525
|
+
expect(lastMockWS().protocols).toEqual(['graphql-ws'])
|
|
526
526
|
unmount()
|
|
527
527
|
})
|
|
528
528
|
|
|
529
|
-
it(
|
|
529
|
+
it('cleans up on unmount', () => {
|
|
530
530
|
const client = makeClient()
|
|
531
531
|
|
|
532
532
|
const unmount = withProvider(client, () => {
|
|
533
533
|
useSubscription({
|
|
534
|
-
url:
|
|
534
|
+
url: 'wss://example.com/ws',
|
|
535
535
|
onMessage: noop,
|
|
536
536
|
})
|
|
537
537
|
})
|
|
@@ -545,13 +545,13 @@ describe("useSubscription", () => {
|
|
|
545
545
|
|
|
546
546
|
// ─── Error handling & cleanup ──────────────────────────────────────────────
|
|
547
547
|
|
|
548
|
-
it(
|
|
548
|
+
it('reconnects after connection drop (simulated close)', async () => {
|
|
549
549
|
const client = makeClient()
|
|
550
550
|
let sub: UseSubscriptionResult | null = null
|
|
551
551
|
|
|
552
552
|
const unmount = withProvider(client, () => {
|
|
553
553
|
sub = useSubscription({
|
|
554
|
-
url:
|
|
554
|
+
url: 'wss://example.com/ws',
|
|
555
555
|
onMessage: noop,
|
|
556
556
|
reconnect: true,
|
|
557
557
|
reconnectDelay: 20,
|
|
@@ -560,30 +560,30 @@ describe("useSubscription", () => {
|
|
|
560
560
|
|
|
561
561
|
// Establish connection then drop it
|
|
562
562
|
lastMockWS()._simulateOpen()
|
|
563
|
-
expect(sub!.status()).toBe(
|
|
563
|
+
expect(sub!.status()).toBe('connected')
|
|
564
564
|
|
|
565
565
|
// Simulate unexpected connection drop
|
|
566
|
-
lastMockWS()._simulateClose(1006,
|
|
566
|
+
lastMockWS()._simulateClose(1006, 'abnormal closure')
|
|
567
567
|
|
|
568
568
|
// Wait for reconnect attempt
|
|
569
569
|
await new Promise((r) => setTimeout(r, 50))
|
|
570
570
|
|
|
571
571
|
expect(mockInstances).toHaveLength(2)
|
|
572
|
-
expect(lastMockWS().url).toBe(
|
|
573
|
-
expect(sub!.status()).toBe(
|
|
572
|
+
expect(lastMockWS().url).toBe('wss://example.com/ws')
|
|
573
|
+
expect(sub!.status()).toBe('connecting')
|
|
574
574
|
|
|
575
575
|
unmount()
|
|
576
576
|
})
|
|
577
577
|
|
|
578
|
-
it(
|
|
578
|
+
it('message handler that throws does not crash the subscription', () => {
|
|
579
579
|
const client = makeClient()
|
|
580
580
|
let sub: UseSubscriptionResult | null = null
|
|
581
581
|
|
|
582
582
|
const unmount = withProvider(client, () => {
|
|
583
583
|
sub = useSubscription({
|
|
584
|
-
url:
|
|
584
|
+
url: 'wss://example.com/ws',
|
|
585
585
|
onMessage: () => {
|
|
586
|
-
throw new Error(
|
|
586
|
+
throw new Error('handler boom')
|
|
587
587
|
},
|
|
588
588
|
})
|
|
589
589
|
})
|
|
@@ -591,20 +591,20 @@ describe("useSubscription", () => {
|
|
|
591
591
|
lastMockWS()._simulateOpen()
|
|
592
592
|
|
|
593
593
|
// Should not throw — the error is caught internally
|
|
594
|
-
expect(() => lastMockWS()._simulateMessage(
|
|
594
|
+
expect(() => lastMockWS()._simulateMessage('test')).not.toThrow()
|
|
595
595
|
|
|
596
596
|
// Subscription should still be connected
|
|
597
|
-
expect(sub!.status()).toBe(
|
|
597
|
+
expect(sub!.status()).toBe('connected')
|
|
598
598
|
|
|
599
599
|
unmount()
|
|
600
600
|
})
|
|
601
601
|
|
|
602
|
-
it(
|
|
602
|
+
it('WebSocket is closed on unmount (cleanup)', () => {
|
|
603
603
|
const client = makeClient()
|
|
604
604
|
|
|
605
605
|
const unmount = withProvider(client, () => {
|
|
606
606
|
useSubscription({
|
|
607
|
-
url:
|
|
607
|
+
url: 'wss://example.com/ws',
|
|
608
608
|
onMessage: noop,
|
|
609
609
|
})
|
|
610
610
|
})
|
|
@@ -620,12 +620,12 @@ describe("useSubscription", () => {
|
|
|
620
620
|
expect(ws.readyState).toBe(MockWebSocketClass.CLOSED)
|
|
621
621
|
})
|
|
622
622
|
|
|
623
|
-
it(
|
|
623
|
+
it('no reconnect attempts after unmount', async () => {
|
|
624
624
|
const client = makeClient()
|
|
625
625
|
|
|
626
626
|
const unmount = withProvider(client, () => {
|
|
627
627
|
useSubscription({
|
|
628
|
-
url:
|
|
628
|
+
url: 'wss://example.com/ws',
|
|
629
629
|
onMessage: noop,
|
|
630
630
|
reconnect: true,
|
|
631
631
|
reconnectDelay: 20,
|
|
@@ -640,13 +640,13 @@ describe("useSubscription", () => {
|
|
|
640
640
|
expect(mockInstances).toHaveLength(1)
|
|
641
641
|
})
|
|
642
642
|
|
|
643
|
-
it(
|
|
643
|
+
it('send when disconnected does not crash', () => {
|
|
644
644
|
const client = makeClient()
|
|
645
645
|
let sub: UseSubscriptionResult | null = null
|
|
646
646
|
|
|
647
647
|
const unmount = withProvider(client, () => {
|
|
648
648
|
sub = useSubscription({
|
|
649
|
-
url:
|
|
649
|
+
url: 'wss://example.com/ws',
|
|
650
650
|
onMessage: noop,
|
|
651
651
|
reconnect: false,
|
|
652
652
|
})
|
|
@@ -655,22 +655,22 @@ describe("useSubscription", () => {
|
|
|
655
655
|
lastMockWS()._simulateOpen()
|
|
656
656
|
lastMockWS()._simulateClose()
|
|
657
657
|
|
|
658
|
-
expect(sub!.status()).toBe(
|
|
658
|
+
expect(sub!.status()).toBe('disconnected')
|
|
659
659
|
|
|
660
660
|
// Should not throw
|
|
661
|
-
expect(() => sub!.send(
|
|
661
|
+
expect(() => sub!.send('test')).not.toThrow()
|
|
662
662
|
// The underlying WS send should not have been called (only the one from close)
|
|
663
663
|
expect(lastMockWS().send).not.toHaveBeenCalled()
|
|
664
664
|
|
|
665
665
|
unmount()
|
|
666
666
|
})
|
|
667
667
|
|
|
668
|
-
it(
|
|
668
|
+
it('resets reconnect count on successful connection', async () => {
|
|
669
669
|
const client = makeClient()
|
|
670
670
|
|
|
671
671
|
const unmount = withProvider(client, () => {
|
|
672
672
|
useSubscription({
|
|
673
|
-
url:
|
|
673
|
+
url: 'wss://example.com/ws',
|
|
674
674
|
onMessage: noop,
|
|
675
675
|
reconnect: true,
|
|
676
676
|
reconnectDelay: 10,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { onUnmount } from
|
|
2
|
-
import type { Signal } from
|
|
3
|
-
import { batch, effect, signal } from
|
|
1
|
+
import { onUnmount } from '@pyreon/core'
|
|
2
|
+
import type { Signal } from '@pyreon/reactivity'
|
|
3
|
+
import { batch, effect, signal } from '@pyreon/reactivity'
|
|
4
4
|
import type {
|
|
5
5
|
DefaultError,
|
|
6
6
|
InfiniteData,
|
|
@@ -8,16 +8,16 @@ import type {
|
|
|
8
8
|
InfiniteQueryObserverResult,
|
|
9
9
|
QueryKey,
|
|
10
10
|
QueryObserverResult,
|
|
11
|
-
} from
|
|
12
|
-
import { InfiniteQueryObserver } from
|
|
13
|
-
import { useQueryClient } from
|
|
11
|
+
} from '@tanstack/query-core'
|
|
12
|
+
import { InfiniteQueryObserver } from '@tanstack/query-core'
|
|
13
|
+
import { useQueryClient } from './query-client'
|
|
14
14
|
|
|
15
15
|
export interface UseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {
|
|
16
16
|
/** Raw signal — full observer result. */
|
|
17
17
|
result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>
|
|
18
18
|
data: Signal<InfiniteData<TQueryFnData> | undefined>
|
|
19
19
|
error: Signal<TError | null>
|
|
20
|
-
status: Signal<
|
|
20
|
+
status: Signal<'pending' | 'error' | 'success'>
|
|
21
21
|
isPending: Signal<boolean>
|
|
22
22
|
isLoading: Signal<boolean>
|
|
23
23
|
isFetching: Signal<boolean>
|
package/src/use-is-fetching.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { onUnmount } from
|
|
2
|
-
import type { Signal } from
|
|
3
|
-
import { signal } from
|
|
4
|
-
import type { MutationFilters, QueryFilters } from
|
|
5
|
-
import { useQueryClient } from
|
|
1
|
+
import { onUnmount } from '@pyreon/core'
|
|
2
|
+
import type { Signal } from '@pyreon/reactivity'
|
|
3
|
+
import { signal } from '@pyreon/reactivity'
|
|
4
|
+
import type { MutationFilters, QueryFilters } from '@tanstack/query-core'
|
|
5
|
+
import { useQueryClient } from './query-client'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns a signal that tracks how many queries are currently in-flight.
|