fix(api): mark schema-validation errors as non-retryable
The proxy returned `{fonts: null, total: 0}` for empty results, which
fetchProxyFonts surfaced as a generic Error. fontCatalogStore wrapped it
as FontNetworkError, and TanStack retried 3× with exponential backoff —
pinning the loading skeleton for ~7s before settling on an empty list.
Schema mismatches are deterministic; retrying only delays surfacing the
contract violation.
- shared/api/queryClient: introduce NonRetryableError marker class.
The default retry handler short-circuits when it sees this so any
store using the shared client gets fail-fast behavior for free.
- entities/Font/lib/errors: FontResponseError extends NonRetryableError.
- entities/Font/api/proxy/proxyFonts: throw FontResponseError (was a
bare Error). Document that ProxyFontsResponse.fonts is always an array.
- entities/Font/.../fontCatalogStore.fetchPage: preserve a
FontResponseError raised lower in the stack instead of re-wrapping
it as FontNetworkError.
- features/FilterAndSortFonts/api/filters: throw NonRetryableError on
invalid filters payloads and document the array-never-null contract.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
import { api } from '$shared/api/api';
|
||||
import { API_ENDPOINTS } from '$shared/api/endpoints';
|
||||
import { NonRetryableError } from '$shared/api/queryClient';
|
||||
|
||||
const PROXY_API_URL = API_ENDPOINTS.filters;
|
||||
|
||||
@@ -37,7 +38,8 @@ export interface FilterMetadata {
|
||||
type: 'enum' | 'string' | 'array';
|
||||
|
||||
/**
|
||||
* Available filter options
|
||||
* Available filter options.
|
||||
* Always an array; empty when the group has no options.
|
||||
*/
|
||||
options: FilterOption[];
|
||||
}
|
||||
@@ -68,11 +70,16 @@ export interface FilterOption {
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy filters API response
|
||||
* Proxy filters API response.
|
||||
*
|
||||
* Contract: `filters` (and each nested `options`) is always an array — never
|
||||
* `null` or omitted. Wire-level `null` here is a backend regression and
|
||||
* surfaces as a non-retryable error on the client.
|
||||
*/
|
||||
export interface ProxyFiltersResponse {
|
||||
/**
|
||||
* Array of filter metadata
|
||||
* Array of filter metadata.
|
||||
* Always an array; empty when no filter groups are configured.
|
||||
*/
|
||||
filters: FilterMetadata[];
|
||||
}
|
||||
@@ -99,7 +106,7 @@ export async function fetchProxyFilters(): Promise<FilterMetadata[]> {
|
||||
const response = await api.get<FilterMetadata[]>(PROXY_API_URL);
|
||||
|
||||
if (!response.data || !Array.isArray(response.data)) {
|
||||
throw new Error('Proxy API returned invalid response');
|
||||
throw new NonRetryableError('Proxy API returned invalid filters response');
|
||||
}
|
||||
|
||||
return response.data;
|
||||
|
||||
Reference in New Issue
Block a user