← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
src/client-cache.ts
114 lines · 3.3 KB · TypeScript/**
* TanStack Query + IndexedDB persistence helpers for vanilla package apps.
* Prefer `@tanstack/query-core` (not React Query). React adapters can come later.
*
* These helpers are meant to be imported from a browser/client bundle (starter
* may ship a dedicated client module). They are not Worker-safe (IndexedDB).
*/
import { QueryClient } from '@tanstack/query-core'
import { persistQueryClient } from '@tanstack/query-persist-client-core'
import { get, set, del } from 'idb-keyval'
export type IdBPersister = {
persistClient: (client: unknown) => Promise<void>
restoreClient: () => Promise<unknown | undefined>
removeClient: () => Promise<void>
}
/**
* Create an IndexedDB persister backed by `idb-keyval`.
* Use with `createPersistedQueryClient` for offline-friendly package app clients.
*
* @param input.idbKey - IndexedDB key for the dehydrated client
* @returns Persister compatible with `persistQueryClient`
*
* @example
* import { createIdbPersister } from 'kody:@kentcdodds/package-app-kit/client-cache'
* const persister = createIdbPersister({ idbKey: 'my-app-query' })
*/
export function createIdbPersister(input?: { idbKey?: string }): IdBPersister {
const idbKey = input?.idbKey || 'pak-query-cache'
return {
persistClient: async (client) => {
await set(idbKey, client)
},
restoreClient: async () => {
return await get(idbKey)
},
removeClient: async () => {
await del(idbKey)
},
}
}
/**
* Build a QueryClient with sensible PWA defaults (long gcTime for persistence).
*
* @example
* import { createPackageQueryClient } from 'kody:@kentcdodds/package-app-kit/client-cache'
* const client = createPackageQueryClient()
*/
export function createPackageQueryClient(input?: {
staleTimeMs?: number
gcTimeMs?: number
}): QueryClient {
const staleTime = input?.staleTimeMs ?? 60_000
const gcTime = input?.gcTimeMs ?? 1000 * 60 * 60 * 24
return new QueryClient({
defaultOptions: {
queries: {
staleTime,
gcTime,
retry: 1,
refetchOnWindowFocus: true,
},
},
})
}
/**
* Create a QueryClient and attach IndexedDB persistence.
* Call once at client boot; await restore before mounting data-dependent UI when possible.
*
* @param input.buster - Cache buster (bump on breaking schema / major publish)
* @param input.maxAgeMs - Max age for restored cache
* @returns `{ queryClient, persister, restorePromise }`
*
* @example
* import { createPersistedQueryClient } from 'kody:@kentcdodds/package-app-kit/client-cache'
* const { queryClient, restorePromise } = createPersistedQueryClient({ buster: 'v1' })
* await restorePromise
*/
export function createPersistedQueryClient(input?: {
idbKey?: string
buster?: string
maxAgeMs?: number
staleTimeMs?: number
gcTimeMs?: number
}): {
queryClient: QueryClient
persister: IdBPersister
restorePromise: Promise<unknown>
} {
const queryClient = createPackageQueryClient({
staleTimeMs: input?.staleTimeMs,
gcTimeMs: input?.gcTimeMs,
})
const persister = createIdbPersister({ idbKey: input?.idbKey })
const [, restorePromise] = persistQueryClient({
queryClient,
persister: persister as never,
buster: input?.buster ?? '',
maxAge: input?.maxAgeMs ?? 1000 * 60 * 60 * 24 * 7,
})
return { queryClient, persister, restorePromise }
}
/**
* Client-cache helpers overview.
*
*/
/** Primary callable export for this subpath. */
export default createPersistedQueryClient