UNPKG

@smartsamurai/krapi-sdk

Version:

KRAPI TypeScript SDK - Easy-to-use client SDK for connecting to self-hosted KRAPI servers (like Appwrite SDK)

204 lines (191 loc) 6.46 kB
/** * Endpoint Utilities for KRAPI SDK * * Provides endpoint validation, normalization, and path handling utilities. * * @module utils/endpoint-utils */ /** * Normalize and validate endpoint URL * * - Detects if endpoint points to backend port (3470) and warns external clients * - Does not warn for localhost connections (frontend server making internal calls) * - Automatically appends appropriate path based on URL type: * - Backend URLs (port 3470): /krapi/k1 (NO /api prefix) * - Frontend URLs (port 3498): /api/krapi/k1 * - Handles both frontend URLs (with /api) and direct backend URLs * * @param {string} endpoint - Raw endpoint URL * @param {Object} options - Normalization options * @param {boolean} [options.warnOnBackendPort] - Whether to warn if connecting to backend port (default: true) * Note: Warnings are only shown for external clients, not for localhost connections * @param {boolean} [options.autoAppendPath] - Whether to automatically append path (default: true) * @returns {string} Normalized endpoint URL * * @example * normalizeEndpoint('http://localhost:3470') // Returns: 'http://localhost:3470/krapi/k1' (no warning for localhost) * normalizeEndpoint('http://example.com:3470') // Returns: 'http://example.com:3470/krapi/k1' (with warning) * normalizeEndpoint('http://localhost:3498') // Returns: 'http://localhost:3498/api/krapi/k1' * normalizeEndpoint('http://localhost:3498/api/krapi/k1') // Returns: 'http://localhost:3498/api/krapi/k1' */ export function normalizeEndpoint( endpoint: string, options: { warnOnBackendPort?: boolean; autoAppendPath?: boolean; logger?: Console; } = {} ): string { const { warnOnBackendPort = true, autoAppendPath = true, logger = console, } = options; // Remove trailing slashes let normalized = endpoint.replace(/\/+$/, ""); // Parse URL to check port try { const url = new URL(normalized); const port = url.port ? parseInt(url.port, 10) : url.protocol === "https:" ? 443 : 80; // Warn if connecting to backend port (3470) from external clients // Don't warn if connecting from localhost - this is likely the frontend server making internal calls if (warnOnBackendPort && port === 3470) { const hostname = url.hostname.toLowerCase(); const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === ""; // Only warn for external clients, not for localhost connections (frontend server) if (!isLocalhost) { const frontendPort = 3498; const suggestedUrl = normalized.replace(`:${port}`, `:${frontendPort}`); logger.warn( `⚠️ Warning: You've connected to the backend port (3470).\n` + ` External clients should connect to the frontend port (${frontendPort}).\n` + ` Did you mean: ${suggestedUrl}?\n` + ` Note: Backend port is typically for internal use only.` ); } } } catch { // Invalid URL, but we'll continue with normalization // The actual HTTP client will catch this later } // Auto-append appropriate path if not present and autoAppendPath is true // Backend URLs (port 3470) use /krapi/k1 (NO /api prefix) // Frontend URLs (port 3498) use /api/krapi/k1 if ( autoAppendPath && !normalized.includes("/api/krapi/k1") && !normalized.includes("/krapi/k1") ) { try { const url = new URL(normalized); const port = url.port ? parseInt(url.port, 10) : url.protocol === "https:" ? 443 : 80; if (port === 3470) { normalized = `${normalized}/krapi/k1`; // Backend: NO /api prefix } else { normalized = `${normalized}/api/krapi/k1`; // Frontend: WITH /api prefix } } catch { // Invalid URL, default to frontend path normalized = `${normalized}/api/krapi/k1`; } } return normalized; } /** * Validate endpoint URL format * * @param {string} endpoint - Endpoint URL to validate * @returns {{ valid: boolean; error?: string }} Validation result * * @example * validateEndpoint('http://localhost:3498') // Returns: { valid: true } * validateEndpoint('invalid-url') // Returns: { valid: false, error: 'Invalid URL format' } */ export function validateEndpoint(endpoint: string): { valid: boolean; error?: string; } { if (!endpoint || typeof endpoint !== "string") { return { valid: false, error: "Endpoint must be a non-empty string" }; } try { const url = new URL(endpoint); // Check for valid protocol if (!["http:", "https:"].includes(url.protocol)) { return { valid: false, error: "Endpoint must use http:// or https:// protocol", }; } return { valid: true }; } catch (error) { return { valid: false, error: `Invalid URL format: ${ error instanceof Error ? error.message : "Unknown error" }`, }; } } /** * Extract port number from endpoint URL * * @param {string} endpoint - Endpoint URL * @returns {number | null} Port number or null if not found * * @example * extractPort('http://localhost:3498') // Returns: 3498 * extractPort('http://localhost') // Returns: null (default port) */ export function extractPort(endpoint: string): number | null { try { const url = new URL(endpoint); if (url.port) { return parseInt(url.port, 10); } // Return default port based on protocol return url.protocol === "https:" ? 443 : 80; } catch { return null; } } /** * Check if endpoint is a backend URL (port 3470) * * @param {string} endpoint - Endpoint URL * @returns {boolean} True if endpoint uses backend port * * @example * isBackendUrl('http://localhost:3470') // Returns: true * isBackendUrl('http://localhost:3498') // Returns: false */ export function isBackendUrl(endpoint: string): boolean { const port = extractPort(endpoint); return port === 3470; } /** * Check if endpoint is a frontend URL (port 3498) * * @param {string} endpoint - Endpoint URL * @returns {boolean} True if endpoint uses frontend port * * @example * isFrontendUrl('http://localhost:3498') // Returns: true * isFrontendUrl('http://localhost:3470') // Returns: false */ export function isFrontendUrl(endpoint: string): boolean { const port = extractPort(endpoint); return port === 3498; }