From d6a5f78f348cad1e0b92104caf74797d4d82eb37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Thu, 7 Nov 2024 19:18:45 +0300 Subject: [PATCH 01/13] feat(plugins): add auto-refetch plugin --- plugins/auto-refetch/README.md | 44 ++++++ plugins/auto-refetch/package.json | 71 ++++++++++ plugins/auto-refetch/src/auto-refetch.spec.ts | 129 ++++++++++++++++++ plugins/auto-refetch/src/index.ts | 78 +++++++++++ plugins/auto-refetch/tsup.config.ts | 19 +++ pnpm-lock.yaml | 6 + 6 files changed, 347 insertions(+) create mode 100644 plugins/auto-refetch/README.md create mode 100644 plugins/auto-refetch/package.json create mode 100644 plugins/auto-refetch/src/auto-refetch.spec.ts create mode 100644 plugins/auto-refetch/src/index.ts create mode 100644 plugins/auto-refetch/tsup.config.ts diff --git a/plugins/auto-refetch/README.md b/plugins/auto-refetch/README.md new file mode 100644 index 0000000..37cf003 --- /dev/null +++ b/plugins/auto-refetch/README.md @@ -0,0 +1,44 @@ +

+ Pinia Colada logo + Pinia Colada Auto Refetch +

+ + + npm package + + +Automatically refetch queries when they become stale in Pinia Colada. + +## Installation + +```sh +npm install @pinia/colada-plugin-auto-refetch +``` + +## Usage + +```js +import { PiniaColadaAutoRefetch } from '@pinia/colada-plugin-auto-refetch' + +// Pass the plugin to Pinia Colada options +app.use(PiniaColada, { + // ... + plugins: [ + PiniaColadaAutoRefetch(), + ], +}) +``` + +You can customize the refetch behavior individually for each query with the `autoRefetch` option: + +```ts +useQuery({ + key: ['todos'], + query: getTodos, + autoRefetch: false, // disable auto refetch +}) +``` + +## License + +[MIT](http://opensource.org/licenses/MIT) diff --git a/plugins/auto-refetch/package.json b/plugins/auto-refetch/package.json new file mode 100644 index 0000000..d194d9b --- /dev/null +++ b/plugins/auto-refetch/package.json @@ -0,0 +1,71 @@ +{ + "name": "@pinia/colada-plugin-auto-refetch", + "type": "module", + "publishConfig": { + "access": "public" + }, + "version": "0.0.1", + "description": "Automatically refetch queries when they become stale in Pinia Colada", + "author": { + "name": "Yusuf Mansur Ozer", + "email": "ymansurozer@gmail.com" + }, + "license": "MIT", + "homepage": "https://github.com/posva/pinia-colada/plugins/auto-refetch#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/posva/pinia-colada.git" + }, + "bugs": { + "url": "https://github.com/posva/pinia-colada/issues" + }, + "keywords": [ + "pinia", + "plugin", + "data", + "fetching", + "query", + "mutation", + "cache", + "layer", + "refetch" + ], + "sideEffects": false, + "exports": { + ".": { + "types": { + "import": "./dist/index.d.ts", + "require": "./dist/index.d.cts" + }, + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "./dist/*", + "./*" + ] + } + }, + "files": [ + "LICENSE", + "README.md", + "dist" + ], + "scripts": { + "build": "tsup", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/colada-plugin-auto-refetch -r 1", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "peerDependencies": { + "@pinia/colada": "workspace:^" + }, + "devDependencies": { + "@pinia/colada": "workspace:^" + } +} diff --git a/plugins/auto-refetch/src/auto-refetch.spec.ts b/plugins/auto-refetch/src/auto-refetch.spec.ts new file mode 100644 index 0000000..1306dd4 --- /dev/null +++ b/plugins/auto-refetch/src/auto-refetch.spec.ts @@ -0,0 +1,129 @@ +/** + * @vitest-environment happy-dom + */ +import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { defineComponent } from 'vue' +import { createPinia } from 'pinia' +import { useQuery, PiniaColada } from '@pinia/colada' +import type { UseQueryOptions } from '@pinia/colada' +import type { PiniaColadaAutoRefetchOptions } from '.' +import { PiniaColadaAutoRefetch } from '.' + +describe('Auto Refetch plugin', () => { + beforeEach(() => { + vi.clearAllTimers() + vi.useFakeTimers() + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + enableAutoUnmount(afterEach) + + function mountQuery( + queryOptions?: Partial, + pluginOptions?: PiniaColadaAutoRefetchOptions, + ) { + const query = vi.fn(async () => 'result') + const wrapper = mount( + defineComponent({ + template: '
', + setup() { + return useQuery({ + query, + key: ['test'], + ...queryOptions, + }) + }, + }), + { + global: { + plugins: [ + createPinia(), + [PiniaColada, { + plugins: [PiniaColadaAutoRefetch(pluginOptions)], + ...pluginOptions, + }], + ], + }, + }, + ) + + return { wrapper, query } + } + + it('automatically refetches when stale time is reached', async () => { + const { query } = mountQuery({ + staleTime: 1000, + }) + + // Wait for initial query + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + // Advance time past stale time in one go + vi.advanceTimersByTime(1000) + await flushPromises() + + expect(query).toHaveBeenCalledTimes(2) + }) + + it('respects disabled option globally', async () => { + const { query } = mountQuery( + { + staleTime: 1000, + }, + { + enabled: false, + }, + ) + + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(2000) + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + }) + + it('respects disabled option per query', async () => { + const { query } = mountQuery({ + staleTime: 1000, + autoRefetch: false, + }) + + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(2000) + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + }) + + it('cleans up timeouts when query is unmounted', async () => { + const { wrapper, query } = mountQuery({ + staleTime: 1000, + }) + + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + wrapper.unmount() + vi.advanceTimersByTime(2000) + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + }) + + it('does not refetch when staleTime is not set', async () => { + const { query } = mountQuery({}) + + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + vi.advanceTimersByTime(2000) + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + }) +}) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts new file mode 100644 index 0000000..80b7ab5 --- /dev/null +++ b/plugins/auto-refetch/src/index.ts @@ -0,0 +1,78 @@ +import type { PiniaColadaPlugin } from '@pinia/colada' + +export interface PiniaColadaAutoRefetchOptions { + /** + * Whether to enable auto refresh by default. + * @default true + */ + enabled?: boolean +} + +/** + * Plugin that automatically refreshes queries when they become stale + */ +export function PiniaColadaAutoRefetch( + options: PiniaColadaAutoRefetchOptions = {}, +): PiniaColadaPlugin { + const { enabled = true } = options + + return ({ queryCache }) => { + // Keep track of active entries and their timeouts + const refetchTimeouts = new Map() + + queryCache.$onAction(({ name, args, after }) => { + // We want refetch to happen only on the client + if (!import.meta.client) return + + // Handle fetch to set up auto-refetch + if (name === 'refresh') { + const [entry] = args + const key = entry.key.join('/') + + after(async () => { + // Skip if auto-refetch is disabled or if the query has no stale time + const queryEnabled = entry.options?.autoRefetch ?? enabled + const staleTime = entry.options?.staleTime + if (!queryEnabled || !staleTime || !entry.active) return + + // Clear any existing timeout for this key + const existingTimeout = refetchTimeouts.get(key) + if (existingTimeout) { + clearTimeout(existingTimeout) + } + + // Schedule next refetch + const timeout = setTimeout(() => { + if (entry.active && entry.options) { + queryCache.refresh(entry).catch(console.error) + refetchTimeouts.delete(key) + } + }, staleTime) + + refetchTimeouts.set(key, timeout) + }) + } + + // Clean up timeouts when entry is removed + if (name === 'remove') { + const [entry] = args + const key = entry.key.join('/') + const timeout = refetchTimeouts.get(key) + if (timeout) { + clearTimeout(timeout) + refetchTimeouts.delete(key) + } + } + }) + } +} + +// Add types for the new option +declare module '@pinia/colada' { + interface UseQueryOptions { + /** + * Whether to automatically refresh this query when it becomes stale. + */ + autoRefetch?: boolean + } +} diff --git a/plugins/auto-refetch/tsup.config.ts b/plugins/auto-refetch/tsup.config.ts new file mode 100644 index 0000000..49d270e --- /dev/null +++ b/plugins/auto-refetch/tsup.config.ts @@ -0,0 +1,19 @@ +import { type Options, defineConfig } from 'tsup' + +const commonOptions = { + // splitting: false, + sourcemap: true, + format: ['cjs', 'esm'], + external: ['vue', 'pinia', '@pinia/colada'], + dts: true, + target: 'esnext', +} satisfies Options + +export default defineConfig([ + { + ...commonOptions, + clean: true, + entry: ['src/index.ts'], + globalName: 'PiniaColadaAutoRefetch', + }, +]) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4d5403..80c520d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,6 +230,12 @@ importers: specifier: workspace:^ version: link:../.. + plugins/auto-refetch: + devDependencies: + '@pinia/colada': + specifier: workspace:^ + version: link:../.. + plugins/cache-persister: devDependencies: '@pinia/colada': From 76996722965bf381993433d5a6972810c394ae9a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:42:47 +0000 Subject: [PATCH 02/13] [autofix.ci] apply automated fixes --- scripts/release.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/release.mjs b/scripts/release.mjs index 55a4115..5472100 100644 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import fs from 'node:fs/promises' import { existsSync } from 'node:fs' import { dirname, join } from 'node:path' From a7899d3fe4b4a06318736934d42d1c1a1cb7734d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Thu, 7 Nov 2024 21:20:57 +0300 Subject: [PATCH 03/13] Update plugins/auto-refetch/package.json Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/package.json b/plugins/auto-refetch/package.json index d194d9b..a9674ef 100644 --- a/plugins/auto-refetch/package.json +++ b/plugins/auto-refetch/package.json @@ -60,7 +60,7 @@ "scripts": { "build": "tsup", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/colada-plugin-auto-refetch -r 1", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "vitest --ui" }, "peerDependencies": { "@pinia/colada": "workspace:^" From db1c48be629e95a695d13ca2f6be39cf18562ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Thu, 7 Nov 2024 21:48:18 +0300 Subject: [PATCH 04/13] refactor: default to false, use ensure + fetch hooks, use maybeRef for plugin options --- plugins/auto-refetch/src/index.ts | 76 +++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index 80b7ab5..514c0ef 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -1,9 +1,11 @@ -import type { PiniaColadaPlugin } from '@pinia/colada' +import type { PiniaColadaPlugin, UseQueryOptions } from '@pinia/colada' +import type { MaybeRefOrGetter } from 'vue' +import { toValue } from 'vue' export interface PiniaColadaAutoRefetchOptions { /** * Whether to enable auto refresh by default. - * @default true + * @default false */ enabled?: boolean } @@ -14,7 +16,7 @@ export interface PiniaColadaAutoRefetchOptions { export function PiniaColadaAutoRefetch( options: PiniaColadaAutoRefetchOptions = {}, ): PiniaColadaPlugin { - const { enabled = true } = options + const { enabled = false } = options return ({ queryCache }) => { // Keep track of active entries and their timeouts @@ -24,32 +26,56 @@ export function PiniaColadaAutoRefetch( // We want refetch to happen only on the client if (!import.meta.client) return - // Handle fetch to set up auto-refetch - if (name === 'refresh') { - const [entry] = args - const key = entry.key.join('/') + const scheduleRefetch = (options: UseQueryOptions) => { + const key = toValue(options.key).join('/') - after(async () => { - // Skip if auto-refetch is disabled or if the query has no stale time - const queryEnabled = entry.options?.autoRefetch ?? enabled - const staleTime = entry.options?.staleTime - if (!queryEnabled || !staleTime || !entry.active) return - - // Clear any existing timeout for this key - const existingTimeout = refetchTimeouts.get(key) - if (existingTimeout) { - clearTimeout(existingTimeout) - } + // Clear any existing timeout for this key + const existingTimeout = refetchTimeouts.get(key) + if (existingTimeout) { + clearTimeout(existingTimeout) + } - // Schedule next refetch - const timeout = setTimeout(() => { - if (entry.active && entry.options) { + // Schedule next refetch + const timeout = setTimeout(() => { + if (options) { + const entry = queryCache.getEntries({ key: toValue(options.key) })?.[0] + if (entry) { queryCache.refresh(entry).catch(console.error) - refetchTimeouts.delete(key) } - }, staleTime) + refetchTimeouts.delete(key) + } + }, options.staleTime) + + refetchTimeouts.set(key, timeout) + } + + /** + * Whether to schedule a refetch for the given entry + */ + const shouldScheduleRefetch = (options: UseQueryOptions) => { + const queryEnabled = options.autoRefetch ?? enabled + const staleTime = options.staleTime + return queryEnabled && staleTime + } + + // Trigger a fetch on creation to enable auto-refetch on initial load + if (name === 'ensure') { + const [entry] = args + + if (shouldScheduleRefetch(entry)) { + scheduleRefetch(entry) + } + } + + // Set up auto-refetch on every fetch + if (name === 'fetch') { + const [entry] = args + + after(async () => { + if (!entry.options) return - refetchTimeouts.set(key, timeout) + if (!shouldScheduleRefetch(entry.options)) return + scheduleRefetch(entry.options) }) } @@ -73,6 +99,6 @@ declare module '@pinia/colada' { /** * Whether to automatically refresh this query when it becomes stale. */ - autoRefetch?: boolean + autoRefetch?: MaybeRefOrGetter } } From 133685db50bd0d94736d5d1cd3d2324fb5071950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Thu, 7 Nov 2024 21:54:31 +0300 Subject: [PATCH 05/13] refactor: use coherent if inversion and extract map key creation to function --- plugins/auto-refetch/src/index.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index 514c0ef..1c5db6b 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -26,8 +26,10 @@ export function PiniaColadaAutoRefetch( // We want refetch to happen only on the client if (!import.meta.client) return + const createMapKey = (options: UseQueryOptions) => toValue(options.key).join('/') + const scheduleRefetch = (options: UseQueryOptions) => { - const key = toValue(options.key).join('/') + const key = createMapKey(options) // Clear any existing timeout for this key const existingTimeout = refetchTimeouts.get(key) @@ -61,10 +63,9 @@ export function PiniaColadaAutoRefetch( // Trigger a fetch on creation to enable auto-refetch on initial load if (name === 'ensure') { const [entry] = args + if (!shouldScheduleRefetch(entry)) return - if (shouldScheduleRefetch(entry)) { - scheduleRefetch(entry) - } + scheduleRefetch(entry) } // Set up auto-refetch on every fetch @@ -73,8 +74,8 @@ export function PiniaColadaAutoRefetch( after(async () => { if (!entry.options) return - if (!shouldScheduleRefetch(entry.options)) return + scheduleRefetch(entry.options) }) } @@ -82,7 +83,9 @@ export function PiniaColadaAutoRefetch( // Clean up timeouts when entry is removed if (name === 'remove') { const [entry] = args - const key = entry.key.join('/') + if (!entry.options) return + + const key = createMapKey(entry.options) const timeout = refetchTimeouts.get(key) if (timeout) { clearTimeout(timeout) From ca98e04410ad02d286517d4fb954e507c4155e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Fri, 8 Nov 2024 20:14:36 +0300 Subject: [PATCH 06/13] Update plugins/auto-refetch/src/auto-refetch.spec.ts Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/src/auto-refetch.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/src/auto-refetch.spec.ts b/plugins/auto-refetch/src/auto-refetch.spec.ts index 1306dd4..57deb05 100644 --- a/plugins/auto-refetch/src/auto-refetch.spec.ts +++ b/plugins/auto-refetch/src/auto-refetch.spec.ts @@ -70,7 +70,7 @@ describe('Auto Refetch plugin', () => { expect(query).toHaveBeenCalledTimes(2) }) - it('respects disabled option globally', async () => { + it('respects enabled option globally', async () => { const { query } = mountQuery( { staleTime: 1000, From 548598d95fa3b0d3e0a8c739d7c236956a4fad73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Fri, 8 Nov 2024 20:14:43 +0300 Subject: [PATCH 07/13] Update plugins/auto-refetch/src/auto-refetch.spec.ts Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/src/auto-refetch.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/src/auto-refetch.spec.ts b/plugins/auto-refetch/src/auto-refetch.spec.ts index 57deb05..6579117 100644 --- a/plugins/auto-refetch/src/auto-refetch.spec.ts +++ b/plugins/auto-refetch/src/auto-refetch.spec.ts @@ -102,7 +102,7 @@ describe('Auto Refetch plugin', () => { expect(query).toHaveBeenCalledTimes(1) }) - it('cleans up timeouts when query is unmounted', async () => { + it('avoids refetching an unactive query', async () => { const { wrapper, query } = mountQuery({ staleTime: 1000, }) From 8a05e15eb78d926768054783edc6b70b7e68ed96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Fri, 8 Nov 2024 20:14:50 +0300 Subject: [PATCH 08/13] Update plugins/auto-refetch/src/index.ts Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index 1c5db6b..a56e324 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -7,7 +7,7 @@ export interface PiniaColadaAutoRefetchOptions { * Whether to enable auto refresh by default. * @default false */ - enabled?: boolean + autoRefetch?: boolean } /** From e38224b8b03b5d018543a24727bb97cb6ce0f64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Fri, 8 Nov 2024 20:40:01 +0300 Subject: [PATCH 09/13] test: add timer test and organize plugin utilities --- plugins/auto-refetch/src/auto-refetch.spec.ts | 35 +++++++++++++++++-- plugins/auto-refetch/src/index.ts | 7 ++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/plugins/auto-refetch/src/auto-refetch.spec.ts b/plugins/auto-refetch/src/auto-refetch.spec.ts index 6579117..92a3399 100644 --- a/plugins/auto-refetch/src/auto-refetch.spec.ts +++ b/plugins/auto-refetch/src/auto-refetch.spec.ts @@ -43,7 +43,7 @@ describe('Auto Refetch plugin', () => { plugins: [ createPinia(), [PiniaColada, { - plugins: [PiniaColadaAutoRefetch(pluginOptions)], + plugins: [PiniaColadaAutoRefetch({ autoRefetch: true, ...pluginOptions })], ...pluginOptions, }], ], @@ -76,7 +76,7 @@ describe('Auto Refetch plugin', () => { staleTime: 1000, }, { - enabled: false, + autoRefetch: false, }, ) @@ -126,4 +126,35 @@ describe('Auto Refetch plugin', () => { await flushPromises() expect(query).toHaveBeenCalledTimes(1) }) + + it('resets the stale timer when a new request occurs', async () => { + const { query } = mountQuery({ + staleTime: 1000, + }) + + // Wait for initial query + await flushPromises() + expect(query).toHaveBeenCalledTimes(1) + + // Advance time partially (500ms) + vi.advanceTimersByTime(500) + + // Manually trigger a new request + query.mockImplementationOnce(async () => 'new result') + await query() + await flushPromises() + expect(query).toHaveBeenCalledTimes(2) + + // Advance time to what would have been the original stale time (500ms more) + vi.advanceTimersByTime(500) + await flushPromises() + // Should not have triggered another request yet + expect(query).toHaveBeenCalledTimes(2) + + // Advance to the new stale time (500ms more to reach full 1000ms from last request) + vi.advanceTimersByTime(500) + await flushPromises() + // Now it should have triggered another request + expect(query).toHaveBeenCalledTimes(3) + }) }) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index a56e324..5115946 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -16,7 +16,7 @@ export interface PiniaColadaAutoRefetchOptions { export function PiniaColadaAutoRefetch( options: PiniaColadaAutoRefetchOptions = {}, ): PiniaColadaPlugin { - const { enabled = false } = options + const { autoRefetch = false } = options return ({ queryCache }) => { // Keep track of active entries and their timeouts @@ -55,9 +55,10 @@ export function PiniaColadaAutoRefetch( * Whether to schedule a refetch for the given entry */ const shouldScheduleRefetch = (options: UseQueryOptions) => { - const queryEnabled = options.autoRefetch ?? enabled + if (!options) return false + const queryEnabled = options.autoRefetch ?? autoRefetch const staleTime = options.staleTime - return queryEnabled && staleTime + return Boolean(queryEnabled && staleTime) } // Trigger a fetch on creation to enable auto-refetch on initial load From 9182290eab8f0f0121ebb30d287d3f475bb3e1a5 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Sun, 10 Nov 2024 13:43:29 +0100 Subject: [PATCH 10/13] refactor: renames and functions --- plugins/auto-refetch/src/index.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index 5115946..a7fd987 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -1,4 +1,4 @@ -import type { PiniaColadaPlugin, UseQueryOptions } from '@pinia/colada' +import type { PiniaColadaPlugin, UseQueryEntry, UseQueryOptions } from '@pinia/colada' import type { MaybeRefOrGetter } from 'vue' import { toValue } from 'vue' @@ -10,6 +10,8 @@ export interface PiniaColadaAutoRefetchOptions { autoRefetch?: boolean } +const createMapKey = (options: UseQueryOptions) => toValue(options.key).join('/') + /** * Plugin that automatically refreshes queries when they become stale */ @@ -24,11 +26,9 @@ export function PiniaColadaAutoRefetch( queryCache.$onAction(({ name, args, after }) => { // We want refetch to happen only on the client - if (!import.meta.client) return - - const createMapKey = (options: UseQueryOptions) => toValue(options.key).join('/') + if (typeof document === 'undefined') return - const scheduleRefetch = (options: UseQueryOptions) => { + function scheduleRefetch(options: UseQueryOptions) { const key = createMapKey(options) // Clear any existing timeout for this key @@ -40,7 +40,9 @@ export function PiniaColadaAutoRefetch( // Schedule next refetch const timeout = setTimeout(() => { if (options) { - const entry = queryCache.getEntries({ key: toValue(options.key) })?.[0] + const entry: UseQueryEntry | undefined = queryCache.getEntries({ + key: toValue(options.key), + })?.[0] if (entry) { queryCache.refresh(entry).catch(console.error) } @@ -54,8 +56,7 @@ export function PiniaColadaAutoRefetch( /** * Whether to schedule a refetch for the given entry */ - const shouldScheduleRefetch = (options: UseQueryOptions) => { - if (!options) return false + function shouldScheduleRefetch(options: UseQueryOptions) { const queryEnabled = options.autoRefetch ?? autoRefetch const staleTime = options.staleTime return Boolean(queryEnabled && staleTime) @@ -63,10 +64,10 @@ export function PiniaColadaAutoRefetch( // Trigger a fetch on creation to enable auto-refetch on initial load if (name === 'ensure') { - const [entry] = args - if (!shouldScheduleRefetch(entry)) return + const [options] = args + if (!shouldScheduleRefetch(options)) return - scheduleRefetch(entry) + scheduleRefetch(options) } // Set up auto-refetch on every fetch From 9eb4e39926bf7e6a81ffb6a20b6dcba329ced98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Mon, 11 Nov 2024 16:40:26 +0300 Subject: [PATCH 11/13] Update plugins/auto-refetch/README.md Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/README.md b/plugins/auto-refetch/README.md index 37cf003..a8c4952 100644 --- a/plugins/auto-refetch/README.md +++ b/plugins/auto-refetch/README.md @@ -35,7 +35,7 @@ You can customize the refetch behavior individually for each query with the `aut useQuery({ key: ['todos'], query: getTodos, - autoRefetch: false, // disable auto refetch + autoRefetch: true, // override local autoRefetch }) ``` From d446701851afe00cd4bad1d3bb6f68a192da59a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Mon, 11 Nov 2024 16:40:43 +0300 Subject: [PATCH 12/13] Update plugins/auto-refetch/README.md Co-authored-by: Eduardo San Martin Morote --- plugins/auto-refetch/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-refetch/README.md b/plugins/auto-refetch/README.md index a8c4952..e713600 100644 --- a/plugins/auto-refetch/README.md +++ b/plugins/auto-refetch/README.md @@ -24,7 +24,7 @@ import { PiniaColadaAutoRefetch } from '@pinia/colada-plugin-auto-refetch' app.use(PiniaColada, { // ... plugins: [ - PiniaColadaAutoRefetch(), + PiniaColadaAutoRefetch({ autoRefetch: true }), // enable globally ], }) ``` From 8224f86de133dc5facc666605ab609e298a48f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Mansur=20=C3=96zer?= Date: Tue, 12 Nov 2024 07:23:28 +0300 Subject: [PATCH 13/13] refactor: unref query option, clear timeouts consistently, move on server check to parent scope --- plugins/auto-refetch/src/index.ts | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/plugins/auto-refetch/src/index.ts b/plugins/auto-refetch/src/index.ts index a7fd987..aec7f6d 100644 --- a/plugins/auto-refetch/src/index.ts +++ b/plugins/auto-refetch/src/index.ts @@ -21,21 +21,18 @@ export function PiniaColadaAutoRefetch( const { autoRefetch = false } = options return ({ queryCache }) => { + // Skip setting auto-refetch on the server + if (typeof document === 'undefined') return + // Keep track of active entries and their timeouts const refetchTimeouts = new Map() queryCache.$onAction(({ name, args, after }) => { - // We want refetch to happen only on the client - if (typeof document === 'undefined') return - function scheduleRefetch(options: UseQueryOptions) { const key = createMapKey(options) - // Clear any existing timeout for this key - const existingTimeout = refetchTimeouts.get(key) - if (existingTimeout) { - clearTimeout(existingTimeout) - } + // Always clear existing timeout first + clearExistingTimeout(key) // Schedule next refetch const timeout = setTimeout(() => { @@ -43,7 +40,7 @@ export function PiniaColadaAutoRefetch( const entry: UseQueryEntry | undefined = queryCache.getEntries({ key: toValue(options.key), })?.[0] - if (entry) { + if (entry && entry.active) { queryCache.refresh(entry).catch(console.error) } refetchTimeouts.delete(key) @@ -53,11 +50,19 @@ export function PiniaColadaAutoRefetch( refetchTimeouts.set(key, timeout) } + function clearExistingTimeout(key: string) { + const existingTimeout = refetchTimeouts.get(key) + if (existingTimeout) { + clearTimeout(existingTimeout) + refetchTimeouts.delete(key) + } + } + /** * Whether to schedule a refetch for the given entry */ function shouldScheduleRefetch(options: UseQueryOptions) { - const queryEnabled = options.autoRefetch ?? autoRefetch + const queryEnabled = toValue(options.autoRefetch) ?? autoRefetch const staleTime = options.staleTime return Boolean(queryEnabled && staleTime) } @@ -74,11 +79,20 @@ export function PiniaColadaAutoRefetch( if (name === 'fetch') { const [entry] = args + // Clear any existing timeout before scheduling a new one + if (entry.options) { + const key = createMapKey(entry.options) + clearExistingTimeout(key) + } + after(async () => { if (!entry.options) return if (!shouldScheduleRefetch(entry.options)) return - scheduleRefetch(entry.options) + // Schedule new refetch only if the entry is still active + if (entry.active) { + scheduleRefetch(entry.options) + } }) }