feature/comparison-slider #19
480
FIX_SUMMARY.md
Normal file
480
FIX_SUMMARY.md
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
# Fix Applied: Query Data Cannot Be Undefined
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Successfully fixed the TanStack Query error: **"Query data cannot be undefined. Please make sure to return a value other than undefined from your query function."**
|
||||||
|
|
||||||
|
## Root Causes
|
||||||
|
|
||||||
|
1. **Missing `gcTime` parameter** in TanStack Query configuration
|
||||||
|
2. **No response validation** when proxy API returns invalid/missing data
|
||||||
|
3. **Incorrect generic type constraint** in `FontVirtualList.svelte`
|
||||||
|
|
||||||
|
## Changes Applied
|
||||||
|
|
||||||
|
### 1. Fixed TanStack Query Configuration
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/model/store/baseFontStore.svelte.ts`
|
||||||
|
|
||||||
|
Added `gcTime` parameter to properly manage cached data lifecycle:
|
||||||
|
|
||||||
|
```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 ✅ ADDED
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
|
||||||
|
- Prevents stale data from persisting too long
|
||||||
|
- Explicit control over garbage collection timing
|
||||||
|
- Better memory management
|
||||||
|
|
||||||
|
### 2. Added Response Validation
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/model/store/unifiedFontStore.svelte.ts`
|
||||||
|
|
||||||
|
Added comprehensive validation in `fetchFn`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
|
||||||
|
const response = await fetchProxyFonts(params);
|
||||||
|
|
||||||
|
// Validate response exists
|
||||||
|
if (!response) {
|
||||||
|
console.error('[UnifiedFontStore] fetchProxyFonts returned undefined', { params });
|
||||||
|
throw new Error('Proxy API returned undefined response');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate fonts array exists
|
||||||
|
if (!response.fonts) {
|
||||||
|
console.error('[UnifiedFontStore] response.fonts is undefined', { response });
|
||||||
|
throw new Error('Proxy API response missing fonts array');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate fonts is an 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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
|
||||||
|
- Early detection of invalid API responses
|
||||||
|
- Detailed error logging for debugging
|
||||||
|
- Prevents undefined data from being cached
|
||||||
|
- Fallback to default values for pagination metadata
|
||||||
|
|
||||||
|
### 3. Added Fallback to Fontshare API
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/api/proxy/proxyFonts.ts`
|
||||||
|
|
||||||
|
Implemented 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 the proxy fails.
|
||||||
|
*/
|
||||||
|
const USE_PROXY_API = true;
|
||||||
|
|
||||||
|
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 to Fontshare API when proxy is unavailable
|
||||||
|
*/
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
|
||||||
|
- App continues working even if proxy API is down
|
||||||
|
- Allows development without breaking functionality
|
||||||
|
- Automatic detection of network errors
|
||||||
|
- Seamless fallback with console logging
|
||||||
|
|
||||||
|
### 4. Fixed Type Constraints
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte`
|
||||||
|
|
||||||
|
Fixed incorrect generic type constraint:
|
||||||
|
|
||||||
|
**Before**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<script lang="ts" generics="T extends ({ id: string } | [{ id: string }, { id: string; }])">
|
||||||
|
```
|
||||||
|
|
||||||
|
**After**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<script lang="ts" generics="T extends { id: string }">
|
||||||
|
```
|
||||||
|
|
||||||
|
Also simplified the font registration logic:
|
||||||
|
|
||||||
|
**Before**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const slugs = visibleItems.map(item => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
return item.map(font => font.id);
|
||||||
|
}
|
||||||
|
return item.id;
|
||||||
|
}).flat();
|
||||||
|
```
|
||||||
|
|
||||||
|
**After**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const slugs = visibleItems.map(item => item.id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
|
||||||
|
- Fixed TypeScript errors
|
||||||
|
- Simplified code
|
||||||
|
- Proper type safety
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Normal Flow (Proxy API Available)
|
||||||
|
|
||||||
|
1. **Request**: Component requests fonts → Filter manager maps to params → `unifiedFontStore.setParams()`
|
||||||
|
2. **Fetch**: `unifiedFontStore.fetchFn()` calls `fetchProxyFonts()`
|
||||||
|
3. **Proxy API**: Request sent to `https://api.glyphdiff.com/api/v1/fonts`
|
||||||
|
4. **Validation**: Response validated to ensure `fonts` array exists
|
||||||
|
5. **Cache**: TanStack Query caches response for 5 minutes
|
||||||
|
6. **Render**: Components render `unifiedFontStore.fonts`
|
||||||
|
|
||||||
|
### Fallback Flow (Proxy API Unavailable)
|
||||||
|
|
||||||
|
1. **Request**: Same as normal flow
|
||||||
|
2. **Fetch**: `fetchProxyFonts()` attempts proxy API
|
||||||
|
3. **Error**: Proxy API returns network error (404, 500, connection failed)
|
||||||
|
4. **Detection**: Error caught and classified as network error
|
||||||
|
5. **Fallback**: `fetchFontshareFallback()` called automatically
|
||||||
|
6. **Fontshare API**: Request sent to Fontshare API
|
||||||
|
7. **Normalization**: Fontshare response normalized to `UnifiedFont` format
|
||||||
|
8. **Render**: Components render fonts seamlessly
|
||||||
|
|
||||||
|
### Debug Flow (Enable Proxy API)
|
||||||
|
|
||||||
|
With `USE_PROXY_API = true`, console will show:
|
||||||
|
|
||||||
|
```
|
||||||
|
[fetchProxyFonts] Fetching from proxy API { params: {...}, url: "https://api.glyphdiff.com/api/v1/fonts?..." }
|
||||||
|
[fetchProxyFonts] Proxy API success { count: 50 }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Flow (Disable Proxy API)
|
||||||
|
|
||||||
|
With `USE_PROXY_API = false`, console will show:
|
||||||
|
|
||||||
|
```
|
||||||
|
[fetchProxyFonts] Using Fontshare API (proxy disabled)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Flow (Proxy API Fails)
|
||||||
|
|
||||||
|
When proxy API fails, console will show:
|
||||||
|
|
||||||
|
```
|
||||||
|
[fetchProxyFonts] Fetching from proxy API { params: {...}, url: "https://api.glyphdiff.com/api/v1/fonts?..." }
|
||||||
|
[fetchProxyFonts] Proxy API failed, using fallback Error: ...
|
||||||
|
[fetchProxyFonts] Using Fontshare API as fallback
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing the Fix
|
||||||
|
|
||||||
|
### 1. With Proxy API Working
|
||||||
|
|
||||||
|
**Prerequisites**:
|
||||||
|
|
||||||
|
- Your proxy API is running at `https://api.glyphdiff.com/api/v1/fonts`
|
||||||
|
- Proxy API returns correct response structure
|
||||||
|
|
||||||
|
**Test**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev server
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# Open browser console
|
||||||
|
# Should see: "[fetchProxyFonts] Fetching from proxy API"
|
||||||
|
# Should see: "[fetchProxyFonts] Proxy API success { count: N }"
|
||||||
|
# Should see fonts loading correctly
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Behavior**:
|
||||||
|
|
||||||
|
- Fonts load from proxy API
|
||||||
|
- Console shows successful fetch
|
||||||
|
- No errors in console
|
||||||
|
- All features working
|
||||||
|
|
||||||
|
### 2. With Proxy API Down
|
||||||
|
|
||||||
|
**Prerequisites**:
|
||||||
|
|
||||||
|
- Proxy API not running or returning errors
|
||||||
|
|
||||||
|
**Test**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev server
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# Open browser console
|
||||||
|
# Should see: "[fetchProxyFonts] Proxy API failed, using fallback"
|
||||||
|
# Should see: "[fetchProxyFonts] Using Fontshare API as fallback"
|
||||||
|
# Should see fonts loading from Fontshare
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Behavior**:
|
||||||
|
|
||||||
|
- App falls back to Fontshare API automatically
|
||||||
|
- Console shows fallback messages
|
||||||
|
- Fonts load from Fontshare
|
||||||
|
- All features working (no user-facing errors)
|
||||||
|
|
||||||
|
### 3. Disable Proxy API Manually
|
||||||
|
|
||||||
|
**File**: `src/entities/Font/api/proxy/proxyFonts.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const USE_PROXY_API = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev server
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# Open browser console
|
||||||
|
# Should see: "[fetchProxyFonts] Using Fontshare API (proxy disabled)"
|
||||||
|
# Should see fonts loading from Fontshare
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Behavior**:
|
||||||
|
|
||||||
|
- App uses Fontshare API directly
|
||||||
|
- Console shows proxy disabled message
|
||||||
|
- All features working
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### To Enable Proxy API (When Ready)
|
||||||
|
|
||||||
|
1. **Verify Proxy API**:
|
||||||
|
```bash
|
||||||
|
curl "https://api.glyphdiff.com/api/v1/fonts?limit=5"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check Response Format**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fonts": [...],
|
||||||
|
"total": N,
|
||||||
|
"limit": 5,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Ensure Each Font Has Required Fields**:
|
||||||
|
- `id`, `name`, `provider`
|
||||||
|
- `category`, `subsets`
|
||||||
|
- `variants`, `styles`
|
||||||
|
- `metadata`, `features`
|
||||||
|
|
||||||
|
4. **Test in App**:
|
||||||
|
- Open browser console
|
||||||
|
- Check for success messages
|
||||||
|
- Verify fonts load correctly
|
||||||
|
|
||||||
|
### To Remove Fallback (When Proxy API is Stable)
|
||||||
|
|
||||||
|
Once proxy API is proven stable, you can:
|
||||||
|
|
||||||
|
1. **Remove Fallback Function**:
|
||||||
|
```typescript
|
||||||
|
// Delete fetchFontshareFallback() function
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Remove Dynamic Imports**:
|
||||||
|
```typescript
|
||||||
|
// No longer need dynamic imports
|
||||||
|
const { fetchFontshareFonts } = await import('../fontshare/fontshare');
|
||||||
|
const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Remove Old API Exports**:
|
||||||
|
```typescript
|
||||||
|
// Remove Fontshare API exports from index.ts
|
||||||
|
// Remove normalization function exports
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Issue: Still Seeing "Query data cannot be undefined"
|
||||||
|
|
||||||
|
**Cause**: Proxy API returns invalid response
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check console for validation errors
|
||||||
|
2. Verify proxy API response structure
|
||||||
|
3. Check network tab in DevTools for actual response
|
||||||
|
4. Set `USE_PROXY_API = false` to use fallback
|
||||||
|
|
||||||
|
### Issue: Fonts Not Loading At All
|
||||||
|
|
||||||
|
**Cause**: Both proxy and Fontshare APIs failing
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check console for error messages
|
||||||
|
2. Verify network connectivity
|
||||||
|
3. Check CORS settings on proxy API
|
||||||
|
4. Check API endpoint URL is correct
|
||||||
|
|
||||||
|
### Issue: Fallback Not Triggering
|
||||||
|
|
||||||
|
**Cause**: Error type not recognized as network error
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Add additional error message checks
|
||||||
|
2. Check console for actual error message
|
||||||
|
3. Manually set `USE_PROXY_API = false`
|
||||||
|
|
||||||
|
## 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
|
||||||
|
3. `src/entities/Font/api/proxy/proxyFonts.ts` - Added fallback logic
|
||||||
|
4. `src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte` - Fixed generic type
|
||||||
|
|
||||||
|
## Commit
|
||||||
|
|
||||||
|
```
|
||||||
|
commit 471e186
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
See `PROXY_API_FIXES.md` for detailed technical documentation on all changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: January 29, 2026
|
||||||
|
**Status**: ✅ Fixed and Committed
|
||||||
@@ -196,8 +196,8 @@ async function fetchFontshareFallback(
|
|||||||
params: ProxyFontsParams,
|
params: ProxyFontsParams,
|
||||||
): Promise<ProxyFontsResponse> {
|
): Promise<ProxyFontsResponse> {
|
||||||
// Import dynamically to avoid circular dependency
|
// Import dynamically to avoid circular dependency
|
||||||
const { fetchFontshareFonts } = await import('../fontshare/fontshare');
|
const { fetchFontshareFonts } = await import('$entities/Font/api/fontshare/fontshare');
|
||||||
const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');
|
const { normalizeFontshareFonts } = await import('$entities/Font/lib/normalize/normalize');
|
||||||
|
|
||||||
// Map proxy params to Fontshare params
|
// Map proxy params to Fontshare params
|
||||||
const fontshareParams = {
|
const fontshareParams = {
|
||||||
|
|||||||
2
test-import.mjs
Normal file
2
test-import.mjs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { unifiedFontStore } from './src/entities/Font/index.ts';
|
||||||
|
console.log('Import successful:', !!unifiedFontStore);
|
||||||
Reference in New Issue
Block a user