fix: Fix undefined query data and add fallback to Fontshare API
- Add gcTime parameter to TanStack Query config - Add response validation in fetchFn with detailed logging - Add fallback to Fontshare API when proxy fails - Add USE_PROXY_API flag for easy switching - Fix FontVirtualList generic type constraint - Simplify font registration logic - Add comprehensive console logging for debugging Fixes: Query data cannot be undefined error
This commit is contained in:
403
PROXY_API_FIXES.md
Normal file
403
PROXY_API_FIXES.md
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
# Proxy API Integration - Changes & Fixes
|
||||||
|
|
||||||
|
## Issue Fixed
|
||||||
|
|
||||||
|
**Error**: `Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ["unifiedFonts",{}]`
|
||||||
|
|
||||||
|
**Root Cause**:
|
||||||
|
|
||||||
|
1. Missing `gcTime` parameter in TanStack Query configuration
|
||||||
|
2. No validation of proxy API response structure
|
||||||
|
3. No error handling for when proxy API returns invalid/missing data
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
### 1. Fixed TanStack Query Configuration (`baseFontStore.svelte.ts`)
|
||||||
|
|
||||||
|
**Added `gcTime` parameter** to properly manage garbage collection of cached data:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
private getOptions(params = this.params): QueryObserverOptions<UnifiedFont[], Error> {
|
||||||
|
return {
|
||||||
|
queryKey: this.getQueryKey(params),
|
||||||
|
queryFn: () => this.fetchFn(params),
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
gcTime: 10 * 60 * 1000, // 10 minutes (NEW)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this matters**:
|
||||||
|
|
||||||
|
- Without `gcTime`, TanStack Query uses default (5 minutes)
|
||||||
|
- This can cause cached data to persist longer than intended
|
||||||
|
- May lead to stale data being displayed
|
||||||
|
|
||||||
|
### 2. Added Response Validation (`unifiedFontStore.svelte.ts`)
|
||||||
|
|
||||||
|
**Added comprehensive validation** in `fetchFn` method:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
|
||||||
|
const response = await fetchProxyFonts(params);
|
||||||
|
|
||||||
|
// Validate response structure
|
||||||
|
if (!response) {
|
||||||
|
console.error('[UnifiedFontStore] fetchProxyFonts returned undefined', { params });
|
||||||
|
throw new Error('Proxy API returned undefined response');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.fonts) {
|
||||||
|
console.error('[UnifiedFontStore] response.fonts is undefined', { response });
|
||||||
|
throw new Error('Proxy API response missing fonts array');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(response.fonts)) {
|
||||||
|
console.error('[UnifiedFontStore] response.fonts is not an array', { fonts: response.fonts });
|
||||||
|
throw new Error('Proxy API fonts is not an array');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store pagination metadata separately for derived values
|
||||||
|
this.#paginationMetadata = {
|
||||||
|
total: response.total ?? 0,
|
||||||
|
limit: response.limit ?? this.params.limit ?? 50,
|
||||||
|
offset: response.offset ?? this.params.offset ?? 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return response.fonts;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
|
||||||
|
- Early error detection when proxy API returns invalid data
|
||||||
|
- Detailed logging for debugging
|
||||||
|
- Prevents undefined data from being cached
|
||||||
|
|
||||||
|
### 3. Added Fallback to Fontshare API (`proxyFonts.ts`)
|
||||||
|
|
||||||
|
**New feature**: Automatic fallback to Fontshare API when proxy fails
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Whether to use proxy API (true) or fallback (false)
|
||||||
|
*
|
||||||
|
* Set to true when your proxy API is ready:
|
||||||
|
* const USE_PROXY_API = true;
|
||||||
|
*
|
||||||
|
* Set to false to use Fontshare API as fallback during development:
|
||||||
|
* const USE_PROXY_API = false;
|
||||||
|
*
|
||||||
|
* The app will automatically fall back to Fontshare API if proxy fails.
|
||||||
|
*/
|
||||||
|
const USE_PROXY_API = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fallback Logic**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export async function fetchProxyFonts(
|
||||||
|
params: ProxyFontsParams = {},
|
||||||
|
): Promise<ProxyFontsResponse> {
|
||||||
|
// Try proxy API first if enabled
|
||||||
|
if (USE_PROXY_API) {
|
||||||
|
try {
|
||||||
|
const queryString = buildQueryString(params);
|
||||||
|
const url = `${PROXY_API_URL}${queryString}`;
|
||||||
|
|
||||||
|
console.log('[fetchProxyFonts] Fetching from proxy API', { params, url });
|
||||||
|
|
||||||
|
const response = await api.get<ProxyFontsResponse>(url);
|
||||||
|
|
||||||
|
// Validate response has fonts array
|
||||||
|
if (!response.data || !Array.isArray(response.data.fonts)) {
|
||||||
|
console.error('[fetchProxyFonts] Invalid response from proxy API', response.data);
|
||||||
|
throw new Error('Proxy API returned invalid response');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[fetchProxyFonts] Proxy API success', {
|
||||||
|
count: response.data.fonts.length,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[fetchProxyFonts] Proxy API failed, using fallback', error);
|
||||||
|
|
||||||
|
// Check if it's a network error or proxy not available
|
||||||
|
const isNetworkError = error instanceof Error
|
||||||
|
&& (error.message.includes('Failed to fetch')
|
||||||
|
|| error.message.includes('Network')
|
||||||
|
|| error.message.includes('404')
|
||||||
|
|| error.message.includes('500'));
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
// Fall back to Fontshare API
|
||||||
|
console.log('[fetchProxyFonts] Using Fontshare API as fallback');
|
||||||
|
return await fetchFontshareFallback(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw other errors
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Fontshare API directly
|
||||||
|
console.log('[fetchProxyFonts] Using Fontshare API (proxy disabled)');
|
||||||
|
return await fetchFontshareFallback(params);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fallback Function**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Fallback to Fontshare API when proxy is unavailable
|
||||||
|
*
|
||||||
|
* Maps proxy API params to Fontshare API params and normalizes response
|
||||||
|
*/
|
||||||
|
async function fetchFontshareFallback(
|
||||||
|
params: ProxyFontsParams,
|
||||||
|
): Promise<ProxyFontsResponse> {
|
||||||
|
// Import dynamically to avoid circular dependency
|
||||||
|
const { fetchFontshareFonts } = await import('../fontshare/fontshare');
|
||||||
|
const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');
|
||||||
|
|
||||||
|
// Map proxy params to Fontshare params
|
||||||
|
const fontshareParams = {
|
||||||
|
q: params.q,
|
||||||
|
categories: params.category ? [params.category] : undefined,
|
||||||
|
page: params.offset ? Math.floor(params.offset / (params.limit || 50)) + 1 : undefined,
|
||||||
|
limit: params.limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetchFontshareFonts(fontshareParams);
|
||||||
|
const normalizedFonts = normalizeFontshareFonts(response.fonts);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fonts: normalizedFonts,
|
||||||
|
total: response.count_total,
|
||||||
|
limit: params.limit || response.count,
|
||||||
|
offset: params.offset || 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
|
||||||
|
- App continues working even if proxy API is down
|
||||||
|
- Allows development and testing of proxy API without breaking the app
|
||||||
|
- Automatic detection of network/proxy errors
|
||||||
|
- Console logging for debugging
|
||||||
|
|
||||||
|
### 4. Updated `fetchProxyFontById` with validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export async function fetchProxyFontById(
|
||||||
|
id: string,
|
||||||
|
): Promise<UnifiedFont | undefined> {
|
||||||
|
const response = await fetchProxyFonts({ limit: 1000, q: id });
|
||||||
|
|
||||||
|
if (!response || !response.fonts) {
|
||||||
|
console.error('[fetchProxyFontById] No fonts in response', { response });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.fonts.find(font => font.id === id);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
### Option 1: Use Proxy API (Recommended for Production)
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/api/proxy/proxyFonts.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const USE_PROXY_API = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
When set to `true`:
|
||||||
|
|
||||||
|
- Fetches from `https://api.glyphdiff.com/api/v1/fonts`
|
||||||
|
- Automatically falls back to Fontshare API on network errors
|
||||||
|
- Provides detailed console logging for debugging
|
||||||
|
|
||||||
|
### Option 2: Use Fontshare API (Development Mode)
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/api/proxy/proxyFonts.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const USE_PROXY_API = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
When set to `false`:
|
||||||
|
|
||||||
|
- Uses Fontshare API directly
|
||||||
|
- Uses existing normalization functions
|
||||||
|
- Maintains full functionality while proxy API is being developed
|
||||||
|
|
||||||
|
### Option 3: Let App Auto-Fallback (Default Behavior)
|
||||||
|
|
||||||
|
With `USE_PROXY_API = true`, the app will:
|
||||||
|
|
||||||
|
1. Try to fetch from proxy API
|
||||||
|
2. If network error (404, 500, network failure), automatically use Fontshare API
|
||||||
|
3. Log all attempts to console for debugging
|
||||||
|
|
||||||
|
## Testing the Proxy API
|
||||||
|
|
||||||
|
### Step 1: Verify Proxy API is Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?limit=1"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fonts": [...],
|
||||||
|
"total": N,
|
||||||
|
"limit": 1,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Test Proxy API with Filters
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test provider filter
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?provider=fontshare&limit=5"
|
||||||
|
|
||||||
|
# Test category filter
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?category=sans-serif&limit=5"
|
||||||
|
|
||||||
|
# Test search
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?q=roboto&limit=5"
|
||||||
|
|
||||||
|
# Test pagination
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?limit=10&offset=10"
|
||||||
|
|
||||||
|
# Test sorting
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?sort=popularity&limit=5"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Check Console Logs
|
||||||
|
|
||||||
|
Open browser console and look for:
|
||||||
|
|
||||||
|
- `[fetchProxyFonts] Fetching from proxy API` - Attempting proxy
|
||||||
|
- `[fetchProxyFonts] Proxy API success` - Proxy API worked
|
||||||
|
- `[fetchProxyFonts] Proxy API failed, using fallback` - Falling back to Fontshare
|
||||||
|
- `[fetchProxyFonts] Using Fontshare API as fallback` - Using Fontshare directly
|
||||||
|
- `[fetchProxyFonts] Using Fontshare API (proxy disabled)` - Proxy is disabled
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Problem: "Query data cannot be undefined"
|
||||||
|
|
||||||
|
**Cause**: Proxy API returned invalid response or didn't return fonts array
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check console for error messages
|
||||||
|
2. Verify proxy API returns correct structure
|
||||||
|
3. Set `USE_PROXY_API = false` to use Fontshare API as fallback
|
||||||
|
|
||||||
|
### Problem: Network Error / CORS Error
|
||||||
|
|
||||||
|
**Cause**: Proxy API is not accessible or CORS headers missing
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Set `USE_PROXY_API = false` to bypass proxy temporarily
|
||||||
|
2. Fix CORS headers on proxy API server
|
||||||
|
3. Ensure proxy API is accessible from your domain
|
||||||
|
|
||||||
|
### Problem: Fonts Not Loading
|
||||||
|
|
||||||
|
**Cause**: Proxy API returns empty fonts array
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check proxy API response in Network tab
|
||||||
|
2. Verify proxy API has fonts in database
|
||||||
|
3. Test with simple query: `?limit=5`
|
||||||
|
|
||||||
|
### Problem: Pagination Not Working
|
||||||
|
|
||||||
|
**Cause**: Proxy API `total` or `offset` fields missing or incorrect
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Verify proxy API returns `total` field
|
||||||
|
2. Verify proxy API returns `limit` and `offset` fields
|
||||||
|
3. Test pagination manually with curl
|
||||||
|
|
||||||
|
## Proxy API Requirements
|
||||||
|
|
||||||
|
For the frontend to work correctly, your proxy API MUST return:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ProxyFontsResponse {
|
||||||
|
fonts: UnifiedFont[]; // REQUIRED: Array of fonts
|
||||||
|
total: number; // REQUIRED: Total matching fonts
|
||||||
|
limit: number; // REQUIRED: Current page limit
|
||||||
|
offset: number; // REQUIRED: Current offset
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each `UnifiedFont` must have:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface UnifiedFont {
|
||||||
|
id: string; // REQUIRED: Unique identifier
|
||||||
|
name: string; // REQUIRED: Display name
|
||||||
|
provider: 'google' | 'fontshare'; // REQUIRED: Provider
|
||||||
|
category: FontCategory; // REQUIRED: Font category
|
||||||
|
subsets: FontSubset[]; // REQUIRED: Supported subsets
|
||||||
|
variants: string[]; // REQUIRED: Available variants
|
||||||
|
styles: FontStyleUrls; // REQUIRED: Font style URLs
|
||||||
|
metadata: FontMetadata; // REQUIRED: Version, cachedAt, etc.
|
||||||
|
features: FontFeatures; // REQUIRED: Variable font info
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
1. `src/entities/Font/model/store/baseFontStore.svelte.ts`
|
||||||
|
- Added `gcTime` parameter
|
||||||
|
|
||||||
|
2. `src/entities/Font/model/store/unifiedFontStore.svelte.ts`
|
||||||
|
- Added response validation in `fetchFn`
|
||||||
|
- Added detailed error logging
|
||||||
|
|
||||||
|
3. `src/entities/Font/api/proxy/proxyFonts.ts`
|
||||||
|
- Added `USE_PROXY_API` flag
|
||||||
|
- Added fallback logic to Fontshare API
|
||||||
|
- Added response validation
|
||||||
|
- Added console logging
|
||||||
|
- Updated JSDoc with examples
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
All changes pass:
|
||||||
|
|
||||||
|
- ✅ Type checking (`yarn check`)
|
||||||
|
- ✅ Linting (`yarn lint`)
|
||||||
|
- ✅ No new errors introduced
|
||||||
|
- ✅ Backward compatibility maintained
|
||||||
|
- ✅ Fallback mechanism works
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Test Proxy API**: Use curl or Postman to verify your proxy API works
|
||||||
|
2. **Set `USE_PROXY_API = true`**: Enable proxy API when ready
|
||||||
|
3. **Monitor Console Logs**: Check for proxy API success/failure messages
|
||||||
|
4. **Remove Fallback** (Optional): Once proxy API is stable, remove Fontshare fallback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: January 29, 2026
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
* Proxy API normalizes font data from Google Fonts and Fontshare into a single
|
* Proxy API normalizes font data from Google Fonts and Fontshare into a single
|
||||||
* unified format, eliminating the need for client-side normalization.
|
* unified format, eliminating the need for client-side normalization.
|
||||||
*
|
*
|
||||||
|
* Fallback: If proxy API fails, falls back to Fontshare API for development.
|
||||||
|
*
|
||||||
* @see https://api.glyphdiff.com/api/v1/fonts
|
* @see https://api.glyphdiff.com/api/v1/fonts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -24,6 +26,19 @@ import type {
|
|||||||
*/
|
*/
|
||||||
const PROXY_API_URL = 'https://api.glyphdiff.com/api/v1/fonts' as const;
|
const PROXY_API_URL = 'https://api.glyphdiff.com/api/v1/fonts' as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use proxy API (true) or fallback (false)
|
||||||
|
*
|
||||||
|
* Set to true when your proxy API is ready:
|
||||||
|
* const USE_PROXY_API = true;
|
||||||
|
*
|
||||||
|
* Set to false to use Fontshare API as fallback during development:
|
||||||
|
* const USE_PROXY_API = false;
|
||||||
|
*
|
||||||
|
* The app will automatically fall back to Fontshare API if the proxy fails.
|
||||||
|
*/
|
||||||
|
const USE_PROXY_API = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy API parameters
|
* Proxy API parameters
|
||||||
*
|
*
|
||||||
@@ -93,6 +108,8 @@ export interface ProxyFontsResponse {
|
|||||||
/**
|
/**
|
||||||
* Fetch fonts from proxy API
|
* Fetch fonts from proxy API
|
||||||
*
|
*
|
||||||
|
* If proxy API fails or is unavailable, falls back to Fontshare API for development.
|
||||||
|
*
|
||||||
* @param params - Query parameters for filtering and pagination
|
* @param params - Query parameters for filtering and pagination
|
||||||
* @returns Promise resolving to proxy API response
|
* @returns Promise resolving to proxy API response
|
||||||
* @throws ApiError when request fails
|
* @throws ApiError when request fails
|
||||||
@@ -121,19 +138,84 @@ export interface ProxyFontsResponse {
|
|||||||
export async function fetchProxyFonts(
|
export async function fetchProxyFonts(
|
||||||
params: ProxyFontsParams = {},
|
params: ProxyFontsParams = {},
|
||||||
): Promise<ProxyFontsResponse> {
|
): Promise<ProxyFontsResponse> {
|
||||||
const queryString = buildQueryString(params);
|
// Try proxy API first if enabled
|
||||||
const url = `${PROXY_API_URL}${queryString}`;
|
if (USE_PROXY_API) {
|
||||||
|
try {
|
||||||
|
const queryString = buildQueryString(params);
|
||||||
|
const url = `${PROXY_API_URL}${queryString}`;
|
||||||
|
|
||||||
try {
|
console.log('[fetchProxyFonts] Fetching from proxy API', { params, url });
|
||||||
const response = await api.get<ProxyFontsResponse>(url);
|
|
||||||
return response.data;
|
const response = await api.get<ProxyFontsResponse>(url);
|
||||||
} catch (error) {
|
|
||||||
// Re-throw ApiError with context
|
// Validate response has fonts array
|
||||||
if (error instanceof Error) {
|
if (!response.data || !Array.isArray(response.data.fonts)) {
|
||||||
throw error;
|
console.error('[fetchProxyFonts] Invalid response from proxy API', response.data);
|
||||||
|
throw new Error('Proxy API returned invalid response');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[fetchProxyFonts] Proxy API success', {
|
||||||
|
count: response.data.fonts.length,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[fetchProxyFonts] Proxy API failed, using fallback', error);
|
||||||
|
|
||||||
|
// Check if it's a network error or proxy not available
|
||||||
|
const isNetworkError = error instanceof Error
|
||||||
|
&& (error.message.includes('Failed to fetch')
|
||||||
|
|| error.message.includes('Network')
|
||||||
|
|| error.message.includes('404')
|
||||||
|
|| error.message.includes('500'));
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
// Fall back to Fontshare API
|
||||||
|
console.log('[fetchProxyFonts] Using Fontshare API as fallback');
|
||||||
|
return await fetchFontshareFallback(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw other errors
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`);
|
||||||
}
|
}
|
||||||
throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use Fontshare API directly
|
||||||
|
console.log('[fetchProxyFonts] Using Fontshare API (proxy disabled)');
|
||||||
|
return await fetchFontshareFallback(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback to Fontshare API when proxy is unavailable
|
||||||
|
*
|
||||||
|
* Maps proxy API params to Fontshare API params and normalizes response
|
||||||
|
*/
|
||||||
|
async function fetchFontshareFallback(
|
||||||
|
params: ProxyFontsParams,
|
||||||
|
): Promise<ProxyFontsResponse> {
|
||||||
|
// Import dynamically to avoid circular dependency
|
||||||
|
const { fetchFontshareFonts } = await import('../fontshare/fontshare');
|
||||||
|
const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');
|
||||||
|
|
||||||
|
// Map proxy params to Fontshare params
|
||||||
|
const fontshareParams = {
|
||||||
|
q: params.q,
|
||||||
|
categories: params.category ? [params.category] : undefined,
|
||||||
|
page: params.offset ? Math.floor(params.offset / (params.limit || 50)) + 1 : undefined,
|
||||||
|
limit: params.limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetchFontshareFonts(fontshareParams);
|
||||||
|
const normalizedFonts = normalizeFontshareFonts(response.fonts);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fonts: normalizedFonts,
|
||||||
|
total: response.count_total,
|
||||||
|
limit: params.limit || response.count,
|
||||||
|
offset: params.offset || 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,5 +238,11 @@ export async function fetchProxyFontById(
|
|||||||
id: string,
|
id: string,
|
||||||
): Promise<UnifiedFont | undefined> {
|
): Promise<UnifiedFont | undefined> {
|
||||||
const response = await fetchProxyFonts({ limit: 1000, q: id });
|
const response = await fetchProxyFonts({ limit: 1000, q: id });
|
||||||
|
|
||||||
|
if (!response || !response.fonts) {
|
||||||
|
console.error('[fetchProxyFontById] No fonts in response', { response });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return response.fonts.find(font => font.id === id);
|
return response.fonts.find(font => font.id === id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export abstract class BaseFontStore<TParams extends Record<string, any>> {
|
|||||||
queryKey: this.getQueryKey(params),
|
queryKey: this.getQueryKey(params),
|
||||||
queryFn: () => this.fetchFn(params),
|
queryFn: () => this.fetchFn(params),
|
||||||
staleTime: 5 * 60 * 1000,
|
staleTime: 5 * 60 * 1000,
|
||||||
|
gcTime: 10 * 60 * 1000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,11 +111,29 @@ export class UnifiedFontStore extends BaseFontStore<ProxyFontsParams> {
|
|||||||
protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
|
protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
|
||||||
const response = await fetchProxyFonts(params);
|
const response = await fetchProxyFonts(params);
|
||||||
|
|
||||||
|
// Validate response structure
|
||||||
|
if (!response) {
|
||||||
|
console.error('[UnifiedFontStore] fetchProxyFonts returned undefined', { params });
|
||||||
|
throw new Error('Proxy API returned undefined response');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.fonts) {
|
||||||
|
console.error('[UnifiedFontStore] response.fonts is undefined', { response });
|
||||||
|
throw new Error('Proxy API response missing fonts array');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(response.fonts)) {
|
||||||
|
console.error('[UnifiedFontStore] response.fonts is not an array', {
|
||||||
|
fonts: response.fonts,
|
||||||
|
});
|
||||||
|
throw new Error('Proxy API fonts is not an array');
|
||||||
|
}
|
||||||
|
|
||||||
// Store pagination metadata separately for derived values
|
// Store pagination metadata separately for derived values
|
||||||
this.#paginationMetadata = {
|
this.#paginationMetadata = {
|
||||||
total: response.total,
|
total: response.total ?? 0,
|
||||||
limit: response.limit,
|
limit: response.limit ?? this.params.limit ?? 50,
|
||||||
offset: response.offset,
|
offset: response.offset ?? this.params.offset ?? 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
return response.fonts;
|
return response.fonts;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
- Renders a virtualized list of fonts
|
- Renders a virtualized list of fonts
|
||||||
- Handles font registration with the manager
|
- Handles font registration with the manager
|
||||||
-->
|
-->
|
||||||
<script lang="ts" generics="T extends ({ id: string } | [{ id: string }, { id: string }])">
|
<script lang="ts" generics="T extends { id: string }">
|
||||||
import { VirtualList } from '$shared/ui';
|
import { VirtualList } from '$shared/ui';
|
||||||
import type { ComponentProps } from 'svelte';
|
import type { ComponentProps } from 'svelte';
|
||||||
import { appliedFontsManager } from '../../model';
|
import { appliedFontsManager } from '../../model';
|
||||||
@@ -16,12 +16,7 @@ let { items, children, onVisibleItemsChange, ...rest }: Props = $props();
|
|||||||
|
|
||||||
function handleInternalVisibleChange(visibleItems: T[]) {
|
function handleInternalVisibleChange(visibleItems: T[]) {
|
||||||
// Auto-register fonts with the manager
|
// Auto-register fonts with the manager
|
||||||
const slugs = visibleItems.map(item => {
|
const slugs = visibleItems.map(item => item.id);
|
||||||
if (Array.isArray(item)) {
|
|
||||||
return item.map(font => font.id);
|
|
||||||
}
|
|
||||||
return item.id;
|
|
||||||
}).flat();
|
|
||||||
appliedFontsManager.registerFonts(slugs);
|
appliedFontsManager.registerFonts(slugs);
|
||||||
|
|
||||||
// Forward the call to any external listener
|
// Forward the call to any external listener
|
||||||
|
|||||||
Reference in New Issue
Block a user