From d72a761944bdf9df9d870bc3842fcf4bca08b539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20C=2E=20For=C3=A9s?= Date: Sun, 12 Apr 2026 14:48:57 +0200 Subject: [PATCH] chore: fix low-severity audit issues across codebase - Correct BroadcastPayload.event type to template literal matching actual emitted values - Fix misleading JSDoc on keys(), HydrateOptions, Query.forget, defaultFetcher, and emit() - Fix spelling errors: immediatly, responsability, controll, dont, sale - Add missing readonly modifiers to QueryTransitionProps, HydrateOptions, MutateOptions - Align NextFunction type signature with implementation (add optional signal param) - Remove redundant Partial wrapper from ConfigureFunction type - Fix QueryPrefetchTags leaking query prop onto elements - Fix duplicate/tautological assertion in once events test - Replace inert eslint-disable-next-line with oxlint-disable-next-line in 4 test files - Remove unnecessary 'as Error' casts in trigger error handling - Remove redundant skipDefaultLibCheck from tsconfig.base.json --- src/query/options.ts | 22 ++++++++++---------- src/query/query.test.ts | 2 +- src/query/query.ts | 23 +++++++++++---------- src/react/components/QueryPrefetch.test.tsx | 2 +- src/react/components/QueryPrefetchTags.tsx | 6 +++--- src/react/components/QueryProvider.test.tsx | 2 +- src/react/components/QueryTransition.tsx | 6 +++--- src/react/hooks/useQuery.test.tsx | 2 +- src/react/hooks/useQueryPrefetch.test.tsx | 2 +- tsconfig.base.json | 1 - 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/query/options.ts b/src/query/options.ts index b4a50ae..c95708d 100644 --- a/src/query/options.ts +++ b/src/query/options.ts @@ -32,7 +32,7 @@ export interface Configuration extends Options { * * By default it does not use any broadcast channel. * If a broadcast channel is provided, query - * won't close automatically, therefore, the responsability + * won't close automatically, therefore, the responsibility * of closing the broadcast channel is up to the user. */ readonly broadcast?: BroadcastChannel @@ -107,7 +107,7 @@ export interface BroadcastPayload { /** * The event name. */ - readonly event: QueryEvent + readonly event: `${QueryEvent}:${string}` /** * The event detail. @@ -150,14 +150,14 @@ export type FetcherFunction = { } /** - * The mutate function options. + * The hydrate function options. */ export interface HydrateOptions { /** * Custom expiration function for the hydrated item, overriding * the default expiration configuration. */ - expiration?: ExpirationOptionFunction + readonly expiration?: ExpirationOptionFunction } /** @@ -168,7 +168,7 @@ export interface MutateOptions { * Custom expiration function for the mutated item, overriding * the default expiration configuration. */ - expiration?: ExpirationOptionFunction + readonly expiration?: ExpirationOptionFunction } /** @@ -211,7 +211,7 @@ export type SequenceFunction = { * for one or more keys after a refetching event occurs. */ export type NextFunction = { - (keys: string | { [K in keyof T]: string }): Promise + (keys: string | { [K in keyof T]: string }, signal?: AbortSignal): Promise } /** @@ -227,7 +227,7 @@ export type StreamFunction = { * Allows partial updates to the configuration options. */ export type ConfigureFunction = { - (options?: Partial): void + (options?: Configuration): void } /** @@ -289,7 +289,7 @@ export interface Query { /** * Emit is able to send events to active subscribers * with the given payload. It is a low level API - * and should be used with case. + * and should be used with care. */ readonly emit: EmitFunction @@ -311,7 +311,7 @@ export interface Query { /** * Mutates the key with a given optimistic value. * The mutated value is considered expired and will be - * replaced immediatly if a refetch happens. + * replaced immediately if a refetch happens. */ readonly mutate: MutateFunction @@ -324,8 +324,8 @@ export interface Query { readonly abort: AbortFunction /** - * Forgets the given keys from the cache. - * Removes items from both, the cache and resolvers. + * Forgets the given keys from the items cache. + * Does not remove any resolvers. */ readonly forget: ForgetFunction diff --git a/src/query/query.test.ts b/src/query/query.test.ts index 95a8bb7..9ed3180 100644 --- a/src/query/query.test.ts +++ b/src/query/query.test.ts @@ -741,7 +741,7 @@ describe.concurrent('query', function () { const event = await promise expect(event).toBeDefined() - expect(event).toBeDefined() + expect(event.detail).toBe('works') }) it('uses the same promises for the same result', ({ expect, signal }) => { diff --git a/src/query/query.ts b/src/query/query.ts index b7aa993..349bf51 100644 --- a/src/query/query.ts +++ b/src/query/query.ts @@ -22,7 +22,8 @@ import { } from 'query:options' /** - * Stores the default fetcher function. + * Creates a default fetcher function that performs JSON requests + * using the provided fetch implementation. */ export function defaultFetcher( fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise @@ -70,7 +71,7 @@ export function createQuery(instanceOptions?: Configuration): Query { * * By default it does not use any broadcast channel. * If a broadcast channel is provided, query - * won't close automatically, therefore, the responsability + * won't close automatically, therefore, the responsibility * of closing the broadcast channel is up to the user. */ let broadcast = instanceOptions?.broadcast @@ -153,7 +154,7 @@ export function createQuery(instanceOptions?: Configuration): Query { * does have a payload parameter that will contain relevant * information depending on the event type. * If there's a pending resolver for that key, the `refetching` - * event is fired immediatly. + * event is fired immediately. */ function subscribe( key: string, @@ -163,7 +164,7 @@ export function createQuery(instanceOptions?: Configuration): Query { events.addEventListener(`${event}:${key}`, listener) const value = resolversCache.get(key) - // For the refetching event, we want to immediatly return if there's + // For the refetching event, we want to immediately return if there's // a pending resolver. if (event === 'refetching' && value !== undefined) { emit(key, event, value.item) @@ -177,7 +178,7 @@ export function createQuery(instanceOptions?: Configuration): Query { /** * Mutates the key with a given optimistic value. * The mutated value is considered expired and will be - * replaced immediatly if a refetch happens when expired + * replaced immediately if a refetch happens when expired * is true. If expired is false, the value expiration time * is added as if it was a valid data refetched. Alternatively * you can provide a Date to decide when the expiration happens. @@ -224,7 +225,7 @@ export function createQuery(instanceOptions?: Configuration): Query { } /** - * Determines if the given key is currently resolving. + * Returns all keys currently stored in the specified cache. */ function keys(type: CacheType = 'items'): readonly string[] { return Array.from(type === 'items' ? itemsCache.keys() : resolversCache.keys()) @@ -335,7 +336,7 @@ export function createQuery(instanceOptions?: Configuration): Query { const fetcher = (options?.fetcher ?? instanceFetcher) as FetcherFunction /** - * Determines if we can return a sale item + * Determines if we can return a stale item. * If true, it will return the previous stale item * stored in the cache if it has expired. It will attempt * to revalidate it in the background. If false, the returned @@ -384,7 +385,7 @@ export function createQuery(instanceOptions?: Configuration): Query { // before we write to the cache, bail out to avoid writing // stale data that contradicts the abort. if (controller.signal.aborted) { - reject(controller.signal.reason as Error) + reject(controller.signal.reason) return } @@ -418,7 +419,7 @@ export function createQuery(instanceOptions?: Configuration): Query { emit(key, 'error', error) // Throw back the error. - reject(error as Error) + reject(error) } } }) @@ -461,13 +462,13 @@ export function createQuery(instanceOptions?: Configuration): Query { // in the background. if (hasExpired && stale) { // We have to silence the error to avoid unhandled promises. - // Refer to the error event if you need full controll of errors. + // Refer to the error event if you need full control of errors. refetch(key).catch(() => {}) return cached.item as Promise } - // The item has expired but we dont allow stale + // The item has expired but we don't allow stale // responses so we need to wait for the revalidation. if (hasExpired) { return refetch(key) diff --git a/src/react/components/QueryPrefetch.test.tsx b/src/react/components/QueryPrefetch.test.tsx index 0beb1a2..d2a7512 100644 --- a/src/react/components/QueryPrefetch.test.tsx +++ b/src/react/components/QueryPrefetch.test.tsx @@ -28,7 +28,7 @@ describe.concurrent('QueryPrefetch', function () { const query = createQuery({ fetcher }) const promise = query.once('/user', 'refetching') - // eslint-disable-next-line + // oxlint-disable-next-line await act(async function () { createRoot(container).render( diff --git a/src/react/components/QueryPrefetchTags.tsx b/src/react/components/QueryPrefetchTags.tsx index c2aebee..bb3b905 100644 --- a/src/react/components/QueryPrefetchTags.tsx +++ b/src/react/components/QueryPrefetchTags.tsx @@ -43,11 +43,11 @@ export interface QueryPrefetchTagsProps extends Additional { * } * ``` */ -export function QueryPrefetchTags({ keys, children, ...options }: QueryPrefetchTagsProps) { - useQueryPrefetch(keys, options) +export function QueryPrefetchTags({ keys, children, query, ...linkProps }: QueryPrefetchTagsProps) { + useQueryPrefetch(keys, { query }) const tags = keys.map((key) => ( - + )) return ( diff --git a/src/react/components/QueryProvider.test.tsx b/src/react/components/QueryProvider.test.tsx index 4334934..d190b55 100644 --- a/src/react/components/QueryProvider.test.tsx +++ b/src/react/components/QueryProvider.test.tsx @@ -27,7 +27,7 @@ describe.concurrent('QueryProvider', function () { const query = createQuery({ fetcher }) const promise = query.once('/user', 'refetching') - // eslint-disable-next-line + // oxlint-disable-next-line await act(async function () { createRoot(container).render( diff --git a/src/react/components/QueryTransition.tsx b/src/react/components/QueryTransition.tsx index ed81775..e377760 100644 --- a/src/react/components/QueryTransition.tsx +++ b/src/react/components/QueryTransition.tsx @@ -9,17 +9,17 @@ export interface QueryTransitionProps { /** * Indicates whether a transition is currently pending. */ - isPending: boolean + readonly isPending: boolean /** * The function to start a transition, typically from useTransition. */ - startTransition: TransitionStartFunction + readonly startTransition: TransitionStartFunction /** * The child elements that will have access to the transition context. */ - children?: ReactNode + readonly children?: ReactNode } /** diff --git a/src/react/hooks/useQuery.test.tsx b/src/react/hooks/useQuery.test.tsx index c4603f8..d8adbad 100644 --- a/src/react/hooks/useQuery.test.tsx +++ b/src/react/hooks/useQuery.test.tsx @@ -22,7 +22,7 @@ describe.concurrent('useQuery', function () { const container = document.createElement('div') const promise = query.next('/user') - // eslint-disable-next-line + // oxlint-disable-next-line await act(async function () { createRoot(container).render( diff --git a/src/react/hooks/useQueryPrefetch.test.tsx b/src/react/hooks/useQueryPrefetch.test.tsx index 58cbb37..c35c5b3 100644 --- a/src/react/hooks/useQueryPrefetch.test.tsx +++ b/src/react/hooks/useQueryPrefetch.test.tsx @@ -23,7 +23,7 @@ describe.concurrent('useQueryPrefetch', function () { const promise = query.next<[string, string]>(['/user', '/config']) - // eslint-disable-next-line + // oxlint-disable-next-line await act(async function () { createRoot(container).render( diff --git a/tsconfig.base.json b/tsconfig.base.json index 353414d..bacd69b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -13,7 +13,6 @@ "esModuleInterop": true, "declaration": true, "emitDeclarationOnly": true, - "skipDefaultLibCheck": true, "skipLibCheck": true, "noUnusedLocals": true, "noUnusedParameters": true,