UNPKG

longurl-js

Version:

LongURL - Programmable URL management framework with entity-driven design and production-ready infrastructure

211 lines (208 loc) 8.64 kB
"use strict"; /** * Supabase Error Handler * * Provides detailed, actionable error messages for common Supabase/PostgreSQL errors. * Helps developers debug schema issues, connection problems, and data constraints. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SupabaseAdapterError = void 0; exports.parseSupabaseError = parseSupabaseError; exports.logSupabaseError = logSupabaseError; exports.isTemporaryError = isTemporaryError; exports.getSchemaHelp = getSchemaHelp; /** * Enhanced error class with detailed context */ class SupabaseAdapterError extends Error { constructor(details, originalError) { super(details.message); this.name = 'SupabaseAdapterError'; this.code = details.code; this.suggestion = details.suggestion; this.sqlHint = details.sqlHint; this.docsUrl = details.docsUrl; this.originalError = originalError; } } exports.SupabaseAdapterError = SupabaseAdapterError; /** * Parse and enhance Supabase errors with actionable context */ function parseSupabaseError(error, operation, tableName = 'short_urls') { var _a; const code = (error === null || error === void 0 ? void 0 : error.code) || 'UNKNOWN'; const message = (error === null || error === void 0 ? void 0 : error.message) || 'Unknown error'; // Common PostgreSQL/Supabase error codes switch (code) { case 'PGRST116': return new SupabaseAdapterError({ code, message: `Table '${tableName}' not found`, suggestion: `Create the table '${tableName}' in your Supabase database. You can use any schema structure as long as it has the required columns.`, sqlHint: ` CREATE TABLE ${tableName} ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, url_id TEXT UNIQUE NOT NULL, entity_type TEXT, entity_id TEXT, original_url TEXT NOT NULL, click_count INTEGER DEFAULT 0, metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() );`, docsUrl: 'https://supabase.com/docs/guides/database/tables' }, error); case 'PGRST301': return new SupabaseAdapterError({ code, message: `Connection timeout during ${operation}`, suggestion: 'Check your network connection and Supabase service status. Consider increasing timeout or retry settings.', docsUrl: 'https://supabase.com/docs/guides/platform/performance' }, error); case 'PGRST302': return new SupabaseAdapterError({ code, message: `Connection failed during ${operation}`, suggestion: 'Verify your Supabase URL and API key. Check if your project is paused or has connection limits.', docsUrl: 'https://supabase.com/docs/guides/platform/logs' }, error); case '23505': const constraint = ((_a = error === null || error === void 0 ? void 0 : error.details) === null || _a === void 0 ? void 0 : _a.includes('url_id')) ? 'url_id' : 'unknown'; return new SupabaseAdapterError({ code, message: `Duplicate ${constraint} constraint violation`, suggestion: `A URL with this ID already exists. This usually indicates a collision in URL generation. Check your collision detection logic.`, sqlHint: `SELECT * FROM ${tableName} WHERE url_id = 'your_url_id';` }, error); case '23502': const column = extractColumnFromError(message); return new SupabaseAdapterError({ code, message: `Required column '${column}' is missing`, suggestion: `Ensure your table schema includes the '${column}' column with NOT NULL constraint, or modify your data to include this field.`, sqlHint: `ALTER TABLE ${tableName} ADD COLUMN ${column} TEXT NOT NULL;` }, error); case '42703': const missingColumn = extractColumnFromError(message); return new SupabaseAdapterError({ code, message: `Column '${missingColumn}' does not exist`, suggestion: `Add the missing column to your table schema, or check if you're using the correct column names.`, sqlHint: `ALTER TABLE ${tableName} ADD COLUMN ${missingColumn} TEXT;` }, error); case '42P01': return new SupabaseAdapterError({ code, message: `Table '${tableName}' does not exist`, suggestion: `Create the table in your Supabase database. You can customize the schema to fit your needs.`, sqlHint: `CREATE TABLE ${tableName} (...);`, docsUrl: 'https://supabase.com/docs/guides/database/tables' }, error); case 'PGRST204': return new SupabaseAdapterError({ code, message: `No rows found during ${operation}`, suggestion: 'This might be expected behavior (URL not found), or indicate a data consistency issue.', }, error); case '42501': return new SupabaseAdapterError({ code, message: `Permission denied for ${operation}`, suggestion: 'Check your RLS (Row Level Security) policies and ensure your API key has the required permissions.', docsUrl: 'https://supabase.com/docs/guides/auth/row-level-security' }, error); case 'PGRST103': return new SupabaseAdapterError({ code, message: `Invalid API key or authentication failed`, suggestion: 'Verify your Supabase API key is correct and has not expired. Use the service role key for server-side operations.', docsUrl: 'https://supabase.com/docs/guides/api/api-keys' }, error); default: return new SupabaseAdapterError({ code: code || 'UNKNOWN', message: `${operation} failed: ${message}`, suggestion: 'Check the error details and Supabase logs for more information. This might be a network issue or unexpected database constraint.', docsUrl: 'https://supabase.com/docs/guides/platform/logs' }, error); } } /** * Extract column name from PostgreSQL error messages */ function extractColumnFromError(message) { // Try to extract column name from common error patterns const patterns = [ /column "([^"]+)"/i, /null value in column "([^"]+)"/i, /"([^"]+)" violates/i, ]; for (const pattern of patterns) { const match = message.match(pattern); if (match) return match[1]; } return 'unknown_column'; } /** * Log detailed error information for debugging */ function logSupabaseError(error, context) { console.error('🔥 Supabase Adapter Error:', { code: error.code, message: error.message, suggestion: error.suggestion, sqlHint: error.sqlHint, docsUrl: error.docsUrl, context, originalError: error.originalError }); } /** * Check if error indicates a temporary issue (for user's retry logic) */ function isTemporaryError(error) { const temporaryCodes = new Set([ 'PGRST301', // Connection timeout 'PGRST302', // Connection failed 'ECONNRESET', 'ENOTFOUND', 'ETIMEDOUT' ]); return temporaryCodes.has(error === null || error === void 0 ? void 0 : error.code); } /** * Generate helpful schema suggestions based on operation */ function getSchemaHelp(tableName = 'short_urls') { return ` -- Minimal required schema for LongURL: CREATE TABLE ${tableName} ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, url_id TEXT UNIQUE NOT NULL, original_url TEXT NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Recommended full schema: CREATE TABLE ${tableName} ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, url_id TEXT UNIQUE NOT NULL, entity_type TEXT, entity_id TEXT, original_url TEXT NOT NULL, click_count INTEGER DEFAULT 0, metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Optional analytics table: CREATE TABLE url_analytics ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, url_id TEXT REFERENCES ${tableName}(url_id), timestamp TIMESTAMPTZ DEFAULT NOW(), metadata JSONB DEFAULT '{}' ); `; }