Skip to content

Commit 781fa12

Browse files
Simplify polling, add manual refresh with sync context check
Co-authored-by: sam.willis <[email protected]>
1 parent 32b5a1c commit 781fa12

File tree

3 files changed

+62
-89
lines changed

3 files changed

+62
-89
lines changed

packages/rss-db-collection/README.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ const blogCollection = createCollection({
279279
})
280280
```
281281

282-
### Manual Polling Control
282+
### Manual Refresh
283283

284284
```typescript
285285
const collection = createCollection({
@@ -290,12 +290,9 @@ const collection = createCollection({
290290
})
291291
})
292292

293-
// Control polling manually
294-
collection.utils.startPolling()
295-
console.log(`Polling: ${collection.utils.isPolling()}`) // true
296-
297-
collection.utils.stopPolling()
298-
console.log(`Polling: ${collection.utils.isPolling()}`) // false
293+
// Manually refresh the feed
294+
await collection.utils.refresh()
295+
console.log('Feed refreshed!')
299296

300297
// Get status
301298
console.log(`Seen items: ${collection.utils.getSeenItemsCount()}`)
@@ -381,10 +378,7 @@ Common error scenarios handled:
381378
```typescript
382379
// Available on collection.utils for both RSS and Atom collections
383380
interface FeedCollectionUtils {
384-
refresh(): Promise<void> // Manual refresh (limited outside sync context)
385-
startPolling(): void // Start polling
386-
stopPolling(): void // Stop polling
387-
isPolling(): boolean // Check polling status
381+
refresh(): Promise<void> // Manual feed refresh
388382
clearSeenItems(): void // Clear deduplication cache
389383
getSeenItemsCount(): number // Get number of tracked items
390384
}

packages/rss-db-collection/src/rss.ts

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -220,21 +220,6 @@ export interface FeedCollectionUtils extends UtilsRecord {
220220
*/
221221
refresh: () => Promise<void>
222222

223-
/**
224-
* Start polling if it was stopped
225-
*/
226-
startPolling: () => void
227-
228-
/**
229-
* Stop polling
230-
*/
231-
stopPolling: () => void
232-
233-
/**
234-
* Get the current polling status
235-
*/
236-
isPolling: () => boolean
237-
238223
/**
239224
* Clear the seen items cache
240225
*/
@@ -475,8 +460,12 @@ function createFeedCollectionOptions<
475460

476461
// State management
477462
let seenItems = new Map<string, { id: string; lastSeen: number }>()
478-
let isPolling = false
479463
let pollingTimeoutId: NodeJS.Timeout | null = null
464+
let syncParams:
465+
| Parameters<
466+
SyncConfig<ResolveType<TExplicit, TSchema, TFallback>, TKey>[`sync`]
467+
>[0]
468+
| null = null
480469

481470
/**
482471
* Clean up old seen items to prevent memory leaks
@@ -515,7 +504,7 @@ function createFeedCollectionOptions<
515504
/**
516505
* Refresh feed data
517506
*/
518-
const refreshFeed = async (syncParams: {
507+
const refreshFeed = async (params: {
519508
begin: () => void
520509
write: (message: {
521510
type: `insert` | `update` | `delete`
@@ -546,7 +535,7 @@ function createFeedCollectionOptions<
546535
throw new UnsupportedFeedFormatError(feedUrl)
547536
}
548537

549-
const { begin, write, commit } = syncParams
538+
const { begin, write, commit } = params
550539
begin()
551540

552541
let newItemsCount = 0
@@ -608,42 +597,10 @@ function createFeedCollectionOptions<
608597
}
609598
}
610599

611-
/**
612-
* Start polling
613-
*/
614-
const startPollingFn = (syncParams?: any) => {
615-
if (isPolling) {
616-
return // Already polling
617-
}
618-
619-
isPolling = true
620-
621-
const poll = async () => {
622-
if (!isPolling) {
623-
return // Polling was stopped
624-
}
625-
626-
try {
627-
if (syncParams) {
628-
await refreshFeed(syncParams)
629-
}
630-
} catch (error) {
631-
debug(`Polling error: ${error}`)
632-
// Continue polling despite errors
633-
}
634-
635-
// Schedule next poll
636-
pollingTimeoutId = setTimeout(poll, pollingInterval)
637-
}
638-
639-
poll()
640-
}
641-
642600
/**
643601
* Stop polling
644602
*/
645-
const stopPollingFn = () => {
646-
isPolling = false
603+
const stopPolling = () => {
647604
if (pollingTimeoutId) {
648605
clearTimeout(pollingTimeoutId)
649606
pollingTimeoutId = null
@@ -657,14 +614,32 @@ function createFeedCollectionOptions<
657614
sync: (params) => {
658615
const { markReady } = params
659616

617+
// Store sync params for manual refresh
618+
syncParams = params
619+
620+
// Polling function
621+
const poll = async () => {
622+
try {
623+
await refreshFeed(syncParams!)
624+
} catch (error) {
625+
debug(`Polling error: ${error}`)
626+
// Continue polling despite errors
627+
}
628+
629+
// Schedule next poll if polling is enabled
630+
if (startPolling) {
631+
pollingTimeoutId = setTimeout(poll, pollingInterval)
632+
}
633+
}
634+
660635
// Initial feed fetch
661636
refreshFeed(params)
662637
.then(() => {
663638
markReady()
664639

665640
// Start polling if configured to do so
666641
if (startPolling) {
667-
startPollingFn(params)
642+
pollingTimeoutId = setTimeout(poll, pollingInterval)
668643
}
669644
})
670645
.catch((error) => {
@@ -673,29 +648,26 @@ function createFeedCollectionOptions<
673648

674649
// Still start polling for retry attempts
675650
if (startPolling) {
676-
startPollingFn(params)
651+
pollingTimeoutId = setTimeout(poll, pollingInterval)
677652
}
678653
})
679654

680655
// Return cleanup function
681656
return () => {
682-
stopPollingFn()
657+
stopPolling()
658+
syncParams = null
683659
}
684660
},
685661
}
686662

687663
// Utils
688664
const utils: FeedCollectionUtils = {
689-
refresh: () => {
690-
// For manual refresh, we need access to sync params
691-
// This is a limitation - manual refresh without sync params
692-
return Promise.reject(
693-
new Error(`Manual refresh not supported outside of sync context`)
694-
)
665+
refresh: async () => {
666+
if (!syncParams) {
667+
throw new Error(`Collection not synced yet - cannot refresh`)
668+
}
669+
await refreshFeed(syncParams)
695670
},
696-
startPolling: () => startPollingFn(),
697-
stopPolling: stopPollingFn,
698-
isPolling: () => isPolling,
699671
clearSeenItems: () => {
700672
seenItems = new Map()
701673
},

packages/rss-db-collection/tests/rss.test.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ describe(`RSS Collection`, () => {
261261
expect(fetchMock).toHaveBeenCalledTimes(3)
262262
})
263263

264-
it(`should allow manual polling control`, async () => {
264+
it(`should allow manual refresh`, async () => {
265265
const fetchMock = vi.fn().mockResolvedValue({
266266
ok: true,
267267
text: () => Promise.resolve(sampleRSSFeed),
@@ -284,25 +284,32 @@ describe(`RSS Collection`, () => {
284284

285285
expect(fetchMock).toHaveBeenCalledTimes(1) // Initial fetch only
286286

287-
// Start polling manually
288-
collection.utils.startPolling()
289-
expect(collection.utils.isPolling()).toBe(true)
290-
291-
// Advance time
292-
vi.advanceTimersByTime(10000)
293-
await flushPromises()
287+
// Manually refresh the feed
288+
await collection.utils.refresh()
294289

295290
expect(fetchMock).toHaveBeenCalledTimes(2)
296291

297-
// Stop polling
298-
collection.utils.stopPolling()
299-
expect(collection.utils.isPolling()).toBe(false)
292+
// Refresh again
293+
await collection.utils.refresh()
300294

301-
// Advance time - should not fetch again
302-
vi.advanceTimersByTime(10000)
303-
await flushPromises()
295+
expect(fetchMock).toHaveBeenCalledTimes(3)
296+
})
297+
298+
it(`should throw error when refresh is called before sync`, async () => {
299+
const config: RSSCollectionConfig = {
300+
feedUrl: `https://example.com/rss.xml`,
301+
pollingInterval: 10000,
302+
getKey: (item: any) => item.guid || item.link,
303+
startPolling: false,
304+
}
304305

305-
expect(fetchMock).toHaveBeenCalledTimes(2) // No additional fetch
306+
const options = rssCollectionOptions(config)
307+
const collection = createCollection(options)
308+
309+
// Try to refresh before collection has synced
310+
await expect(collection.utils.refresh()).rejects.toThrow(
311+
`Collection not synced yet - cannot refresh`
312+
)
306313
})
307314
})
308315

0 commit comments

Comments
 (0)