Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,26 @@ describe('createPersister', () => {
expect(query.fetch).toHaveBeenCalledTimes(0)
})

it('should restore persisted undefined data from storage', async () => {
const storage = getFreshStorage()
const { context, persister, query, queryFn } = setupPersister(['foo'], {
storage,
refetchOnRestore: false,
})

query.setData(undefined)
await persister.persistQuery(query)

query.fetch = vi.fn()
await persister.persisterFn(queryFn, context, query)

await vi.advanceTimersByTimeAsync(0)

expect(queryFn).toHaveBeenCalledTimes(0)
expect(query.fetch).toHaveBeenCalledTimes(0)
expect(query.state.data).toBe(undefined)
})

it('should store item after successful fetch', async () => {
const storage = getFreshStorage()
const {
Expand Down
12 changes: 8 additions & 4 deletions packages/query-persist-client-core/src/createPersister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import type {
QueryState,
} from '@tanstack/query-core'

type PersistedResult<T> = {
data: T
}

export interface PersistedQuery {
buster: string
queryHash: string
Expand Down Expand Up @@ -125,7 +129,7 @@ export function experimental_createQueryPersister<TStorageValue = string>({
async function retrieveQuery<T>(
queryHash: string,
afterRestoreMacroTask?: (persistedQuery: PersistedQuery) => void,
) {
): Promise<PersistedResult<T> | undefined> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for direct usages of retrieveQuery outside this file to assess impact

# Search for retrieveQuery calls that may be affected by the return type change
rg -nP --type=ts --type=tsx -C3 'retrieveQuery\s*\(' \
  -g '!**/createPersister.ts' \
  -g '!**/__tests__/**' \
  -g '!**/node_modules/**'

Repository: TanStack/query

Length of output: 86


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect createPersister.ts around retrieveQuery export + its return type
FILE="packages/query-persist-client-core/src/createPersister.ts"
if [ -f "$FILE" ]; then
  echo "== retrieveQuery export context =="
  rg -n "export\s+(async\s+)?function\s+retrieveQuery|export\s*\{\s*retrieveQuery|retrieveQuery\s*=" "$FILE"
  echo
  # Print a small window around the first match
  LINE=$(rg -n "export\s+(async\s+)?function\s+retrieveQuery|export\s*\{\s*retrieveQuery|retrieveQuery\s*=" "$FILE" | head -n1 | cut -d: -f1)
  START=$((LINE-20))
  END=$((LINE+60))
  sed -n "${START},${END}p" "$FILE" | nl -ba
fi

# 2) Search for direct usages of retrieveQuery outside createPersister.ts
echo
echo "== retrieveQuery call sites outside createPersister.ts =="
rg -n "retrieveQuery\s*\(" \
  --glob "!**/createPersister.ts" \
  --glob "!**/__tests__/**" \
  --glob "!**/node_modules/**" \
  --glob "**/*.ts" \
  --glob "**/*.tsx" \
  -S

Repository: TanStack/query

Length of output: 93


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate retrieveQuery in the target file
FILE="packages/query-persist-client-core/src/createPersister.ts"
echo "== locate retrieveQuery in $FILE =="
if [ -f "$FILE" ]; then
  rg -n "retrieveQuery" "$FILE" || true
  echo
  # show around the most likely export area (lines near 371 as mentioned in review comment)
  sed -n '340,420p' "$FILE" | nl -ba | sed -n '1,120p'
  echo
  # show around line 120-160 for the type snippet mentioned
  sed -n '100,170p' "$FILE" | nl -ba
else
  echo "File not found: $FILE"
fi

echo
echo "== locate retrieveQuery across repository (excluding node_modules) =="
rg -n "retrieveQuery" \
  --glob "!**/node_modules/**" \
  --glob "!**/dist/**" \
  --glob "!**/build/**" \
  -S

Repository: TanStack/query

Length of output: 326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/query-persist-client-core/src/createPersister.ts"
echo "== show retrieveQuery definition around line 129 =="
sed -n '110,170p' "$FILE" | sed -n '1,120p'

echo
echo "== show internal call around line 216 (restoredData) =="
sed -n '190,260p' "$FILE" | sed -n '1,120p'

echo
echo "== show export block around line 371 =="
sed -n '350,410p' "$FILE" | sed -n '1,120p'

echo
echo "== locate retrieveQuery references across repository (excluding node_modules/dist/build) =="
rg -n "retrieveQuery\s*<|retrieveQuery\s*\(" \
  --glob "!**/node_modules/**" \
  --glob "!**/dist/**" \
  --glob "!**/build/**" \
  -S

Repository: TanStack/query

Length of output: 5586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "retrieveQuery<T>\(queryHash" docs packages -S || true

echo
echo "== docs/framework/react/plugins/createPersister.md excerpt =="
sed -n '80,130p' docs/framework/react/plugins/createPersister.md | nl -ba | head -n 80 || true

echo
echo "== docs/framework/vue/plugins/createPersister.md excerpt =="
sed -n '70,120p' docs/framework/vue/plugins/createPersister.md | nl -ba | head -n 70 || true

echo
echo "== search for PersistedResult in docs/framework/** createPersister =="
rg -n "PersistedResult" docs/framework/react/plugins/createPersister.md docs/framework/vue/plugins/createPersister.md -S || true

echo
echo "== search for retrieveQuery return type hints in typescript files =="
rg -n "retrieveQuery.*Promise<" packages docs -S || true

Repository: TanStack/query

Length of output: 890


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/query-persist-client-core/src/createPersister.ts"
echo "== find PersistedResult definition in createPersister.ts =="
rg -n "type\s+PersistedResult|interface\s+PersistedResult|export\s+type\s+PersistedResult|PersistedResult\s*<" "$FILE" -S || true

echo
echo "== find retrieveQuery signature types near its declaration =="
sed -n '120,155p' "$FILE" | sed -n '1,120p'

echo
echo "== find where createPersister returns exported API shape (persister object) =="
rg -n "return\s*\{\s*persisterFn|return\s*\{" "$FILE" -n -S || true

echo
echo "== look for exported type(s) for the persister returned by createPersister =="
rg -n "export (type|interface) .*Persister|createPersister.*export|PersisterOptions" packages/query-persist-client-core/src -S || true

Repository: TanStack/query

Length of output: 2447


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/query-persist-client-core/src/createPersister.ts"
echo "== show top of createPersister.ts (types + exports) =="
sed -n '1,120p' "$FILE"

echo
echo "== show createPersister function signature and return statement =="
sed -n '120,220p' "$FILE"
sed -n '320,410p' "$FILE"

echo
echo "== check for public return type/interface mentioning retrieveQuery =="
rg -n "createPersister|retrieveQuery|Persister" "$FILE"

echo
echo "== inspect docs for retrieveQuery return type formatting around those lines =="
sed -n '85,120p' docs/framework/react/plugins/createPersister.md
sed -n '80,115p' docs/framework/vue/plugins/createPersister.md

Repository: TanStack/query

Length of output: 11743


Fix/communicate the public retrieveQuery return type change

retrieveQuery is exposed on the persister object returned by experimental_createQueryPersister, and it now returns Promise<PersistedResult<T> | undefined> (i.e., { data: T }) instead of Promise<T | undefined>. Consumers must unwrap result.data.

Docs are still written for the old signature in docs/framework/react/plugins/createPersister.md and docs/framework/vue/plugins/createPersister.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/query-persist-client-core/src/createPersister.ts` at line 132, The
public API for retrieveQuery on the object returned by
experimental_createQueryPersister now returns Promise<PersistedResult<T> |
undefined> (an object with a data property) instead of Promise<T | undefined>;
update all consumer-facing docs (docs/framework/react/plugins/createPersister.md
and docs/framework/vue/plugins/createPersister.md) to reflect the new signature
and show unwrapping via result?.data, and either provide a note/example in the
docs demonstrating how to migrate callers of retrieveQuery to use result.data or
restore a thin helper on the persister (e.g., retrieveQueryValue) that returns T
| undefined for backward compatibility while keeping retrieveQuery typed as
PersistedResult<T> | undefined.

if (storage != null) {
const storageKey = `${prefix}-${queryHash}`
try {
Expand All @@ -149,7 +153,7 @@ export function experimental_createQueryPersister<TStorageValue = string>({
)
}
// We must resolve the promise here, as otherwise we will have `loading` state in the app until `queryFn` resolves
return persistedQuery.state.data as T
return { data: persistedQuery.state.data as T }
}
}
} catch (err) {
Expand Down Expand Up @@ -209,7 +213,7 @@ export function experimental_createQueryPersister<TStorageValue = string>({

// Try to restore only if we do not have any data in the cache and we have persister defined
if (matchesFilter && query.state.data === undefined && storage != null) {
const restoredData = await retrieveQuery(
const restoredData = await retrieveQuery<T>(
query.queryHash,
(persistedQuery: PersistedQuery) => {
// Set proper updatedAt, since resolving in the first pass overrides those values
Expand All @@ -228,7 +232,7 @@ export function experimental_createQueryPersister<TStorageValue = string>({
)

if (restoredData !== undefined) {
return Promise.resolve(restoredData as T)
return Promise.resolve(restoredData.data)
}
}

Expand Down
14 changes: 14 additions & 0 deletions packages/vue-query/src/__tests__/vueQueryPlugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { isVue2, isVue3, ref } from 'vue-demi'
import { environmentManager } from '@tanstack/query-core'
import { queryKey } from '@tanstack/query-test-utils'
import { QueryClient } from '../queryClient'
import { VueQueryPlugin } from '../vueQueryPlugin'
Expand Down Expand Up @@ -218,6 +219,19 @@ describe('VueQueryPlugin', () => {
customClient,
)
})

it('should not mount client when in a server environment', () => {
environmentManager.setIsServer(() => true)
try {
const appMock = getAppMock()
const customClient = { mount: vi.fn() } as unknown as QueryClient
VueQueryPlugin.install(appMock, { queryClient: customClient })

expect(customClient.mount).not.toHaveBeenCalled()
} finally {
environmentManager.setIsServer(() => false)
}
})
})

describe('when called with custom client config', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-query/src/vueQueryPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isVue2 } from 'vue-demi'
import { isServer } from '@tanstack/query-core'
import { environmentManager } from '@tanstack/query-core'

import { QueryClient } from './queryClient'
import { getClientKey } from './utils'
Expand Down Expand Up @@ -38,7 +38,7 @@ export const VueQueryPlugin = {
client = new QueryClient(clientConfig)
}

if (!isServer) {
if (!environmentManager.isServer()) {
client.mount()
}

Expand Down