Files
thrilltrack-explorer/src/lib/edgeFunctionTracking.ts
gpt-engineer-app[bot] 6af981a6e4 Fix imports and test flow
2025-11-03 22:03:08 +00:00

136 lines
3.7 KiB
TypeScript

/**
* Edge Function Request Tracking Wrapper
*
* Wraps Supabase function invocations with request tracking for debugging and monitoring.
* Provides correlation IDs for tracing requests across the system.
*/
import { supabase } from '@/lib/supabaseClient';
import { trackRequest } from './requestTracking';
import { getErrorMessage } from './errorHandler';
/**
* Invoke a Supabase edge function with request tracking
*
* @param functionName - Name of the edge function to invoke
* @param payload - Request payload
* @param userId - User ID for tracking (optional)
* @param parentRequestId - Parent request ID for chaining (optional)
* @param traceId - Trace ID for distributed tracing (optional)
* @returns Response data with requestId
*/
export async function invokeWithTracking<T = any>(
functionName: string,
payload: any = {},
userId?: string,
parentRequestId?: string,
traceId?: string,
timeout: number = 30000 // Default 30s timeout
): Promise<{ data: T | null; error: any; requestId: string; duration: number }> {
// Create AbortController for timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const { result, requestId, duration } = await trackRequest(
{
endpoint: `/functions/${functionName}`,
method: 'POST',
userId,
parentRequestId,
traceId,
},
async (context) => {
// Include client request ID in payload for correlation
const { data, error } = await supabase.functions.invoke<T>(functionName, {
body: { ...payload, clientRequestId: context.requestId },
signal: controller.signal, // Add abort signal for timeout
});
if (error) throw error;
return data;
}
);
clearTimeout(timeoutId);
return { data: result, error: null, requestId, duration };
} catch (error: unknown) {
clearTimeout(timeoutId);
// Handle AbortError specifically
if (error instanceof Error && error.name === 'AbortError') {
return {
data: null,
error: {
message: `Request timeout: ${functionName} took longer than ${timeout}ms to respond`,
code: 'TIMEOUT',
},
requestId: 'timeout',
duration: timeout,
};
}
const errorMessage = getErrorMessage(error);
return {
data: null,
error: { message: errorMessage },
requestId: 'unknown',
duration: 0,
};
}
}
/**
* Invoke multiple edge functions in parallel with batch tracking
*
* Uses a shared trace ID to correlate all operations.
*
* @param operations - Array of function invocation configurations
* @param userId - User ID for tracking
* @returns Array of results with their request IDs
*/
export async function invokeBatchWithTracking<T = any>(
operations: Array<{
functionName: string;
payload: any;
}>,
userId?: string
): Promise<
Array<{
functionName: string;
data: T | null;
error: any;
requestId: string;
duration: number;
}>
> {
const traceId = crypto.randomUUID();
const results = await Promise.allSettled(
operations.map(async (op) => {
const result = await invokeWithTracking<T>(
op.functionName,
op.payload,
userId,
undefined,
traceId
);
return { functionName: op.functionName, ...result };
})
);
return results.map((result, index) => {
if (result.status === 'fulfilled') {
return result.value;
} else {
return {
functionName: operations[index].functionName,
data: null,
error: { message: result.reason?.message || 'Unknown error' },
requestId: 'unknown',
duration: 0,
};
}
});
}