From 2deab69ebef5d80e5af07a61817225f88cb28d78 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:37:30 +0000 Subject: [PATCH] Fix Supabase client proxy --- src/hooks/useUnitPreferences.ts | 2 + src/lib/supabaseClient.ts | 88 ++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/hooks/useUnitPreferences.ts b/src/hooks/useUnitPreferences.ts index 6a18b9fe..3e1b60ce 100644 --- a/src/hooks/useUnitPreferences.ts +++ b/src/hooks/useUnitPreferences.ts @@ -101,6 +101,8 @@ export function useUnitPreferences() { user_id: user.id, unit_preferences: newPreferences as unknown as Json, updated_at: new Date().toISOString() + }, { + onConflict: 'user_id' }); if (error) { diff --git a/src/lib/supabaseClient.ts b/src/lib/supabaseClient.ts index 2fe418b4..0b01771d 100644 --- a/src/lib/supabaseClient.ts +++ b/src/lib/supabaseClient.ts @@ -10,6 +10,62 @@ import { breadcrumb } from './errorBreadcrumbs'; type SupabaseClient = typeof baseClient; +/** + * Create a recursive proxy for query builders that tracks terminal method calls + */ +function createQueryProxy(queryBuilder: any, endpoint: string, operations: string[] = []): any { + return new Proxy(queryBuilder, { + get(target, prop: string | symbol) { + const value = target[prop]; + + if (typeof value !== 'function') { + return value; + } + + // Terminal methods that execute queries and return promises + const terminalMethods = ['then', 'single', 'maybeSingle']; + + if (terminalMethods.includes(String(prop))) { + // Wrap terminal method to log breadcrumb when promise resolves + return function(...args: any[]) { + const result = value.apply(target, args); + const fullOperation = operations.join('.'); + + // Intercept promise resolution to log breadcrumb + if (result && typeof result.then === 'function') { + return result.then( + (response: any) => { + // Log successful API call + breadcrumb.apiCall( + endpoint, + fullOperation || 'query', + response?.error ? 400 : 200 + ); + return response; + }, + (error: any) => { + // Log failed API call + breadcrumb.apiCall(endpoint, fullOperation || 'query', 500); + throw error; + } + ); + } + + return result; + }; + } + + // Builder methods - pass through synchronously and continue proxying + return function(...args: any[]) { + const result = value.apply(target, args); + + // Continue proxying the returned builder with accumulated operations + return createQueryProxy(result, endpoint, [...operations, String(prop)]); + }; + } + }); +} + /** * Wrap Supabase client to automatically track API calls as breadcrumbs */ @@ -24,36 +80,8 @@ function wrapSupabaseClient(client: SupabaseClient): SupabaseClient { const result = (value as any).apply(target, args); const endpoint = prop === 'from' ? `/table/${args[0]}` : `/rpc/${args[0]}`; - // Return a proxy for chained query methods - return new Proxy(result, { - get(queryTarget: any, queryProp: string | symbol) { - const queryValue = queryTarget[queryProp]; - - // If it's a function, wrap it to track the call - if (typeof queryValue === 'function') { - return async (...queryArgs: any[]) => { - try { - const response = await queryValue.apply(queryTarget, queryArgs); - - // Log breadcrumb after response - breadcrumb.apiCall( - endpoint, - String(queryProp).toUpperCase(), - response.error ? 400 : 200 - ); - - return response; - } catch (error) { - // Log breadcrumb for exceptions - breadcrumb.apiCall(endpoint, String(queryProp).toUpperCase(), 500); - throw error; - } - }; - } - - return queryValue; - } - }); + // Return a recursive proxy that tracks the query chain + return createQueryProxy(result, endpoint, []); }; }