@gzl10/baserow 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +435 -0
- package/README.md +847 -0
- package/dist/index.d.ts +8749 -0
- package/dist/index.js +11167 -0
- package/dist/index.js.map +1 -0
- package/package.json +91 -0
- package/src/BaserowClient.ts +501 -0
- package/src/ClientWithCreds.ts +545 -0
- package/src/ClientWithCredsWs.ts +852 -0
- package/src/ClientWithToken.ts +171 -0
- package/src/contexts/DatabaseClientContext.ts +114 -0
- package/src/contexts/DatabaseContext.ts +870 -0
- package/src/contexts/DatabaseTokenContext.ts +331 -0
- package/src/contexts/FieldContext.ts +399 -0
- package/src/contexts/RowContext.ts +99 -0
- package/src/contexts/TableClientContext.ts +291 -0
- package/src/contexts/TableContext.ts +1247 -0
- package/src/contexts/TableOnlyContext.ts +74 -0
- package/src/contexts/WorkspaceContext.ts +490 -0
- package/src/express/errors.ts +260 -0
- package/src/express/index.ts +69 -0
- package/src/express/middleware.ts +225 -0
- package/src/express/serializers.ts +314 -0
- package/src/index.ts +247 -0
- package/src/presets/performance.ts +262 -0
- package/src/services/AuthService.ts +472 -0
- package/src/services/DatabaseService.ts +246 -0
- package/src/services/DatabaseTokenService.ts +186 -0
- package/src/services/FieldService.ts +1543 -0
- package/src/services/RowService.ts +982 -0
- package/src/services/SchemaControlService.ts +420 -0
- package/src/services/TableService.ts +781 -0
- package/src/services/WorkspaceService.ts +113 -0
- package/src/services/core/BaseAuthClient.ts +111 -0
- package/src/services/core/BaseClient.ts +107 -0
- package/src/services/core/BaseService.ts +71 -0
- package/src/services/core/HttpService.ts +115 -0
- package/src/services/core/ValidationService.ts +149 -0
- package/src/types/auth.ts +177 -0
- package/src/types/core.ts +91 -0
- package/src/types/errors.ts +105 -0
- package/src/types/fields.ts +456 -0
- package/src/types/index.ts +222 -0
- package/src/types/requests.ts +333 -0
- package/src/types/responses.ts +50 -0
- package/src/types/schema.ts +446 -0
- package/src/types/tokens.ts +36 -0
- package/src/types.ts +11 -0
- package/src/utils/auth.ts +174 -0
- package/src/utils/axios.ts +647 -0
- package/src/utils/field-cache.ts +164 -0
- package/src/utils/httpFactory.ts +66 -0
- package/src/utils/jwt-decoder.ts +188 -0
- package/src/utils/jwtTokens.ts +50 -0
- package/src/utils/performance.ts +105 -0
- package/src/utils/prisma-mapper.ts +961 -0
- package/src/utils/validation.ts +463 -0
- package/src/validators/schema.ts +419 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/axios.ts","../src/utils/performance.ts","../src/types/tokens.ts","../src/types/errors.ts","../src/utils/httpFactory.ts","../src/utils/validation.ts","../src/services/core/BaseService.ts","../src/services/core/HttpService.ts","../src/services/core/ValidationService.ts","../src/services/TableService.ts","../src/services/FieldService.ts","../src/services/RowService.ts","../src/utils/prisma-mapper.ts","../src/utils/field-cache.ts","../src/contexts/TableClientContext.ts","../src/contexts/RowContext.ts","../src/contexts/TableOnlyContext.ts","../src/services/core/BaseClient.ts","../src/ClientWithToken.ts","../src/utils/jwt-decoder.ts","../src/utils/jwtTokens.ts","../src/services/AuthService.ts","../src/services/WorkspaceService.ts","../src/services/DatabaseService.ts","../src/services/DatabaseTokenService.ts","../src/contexts/FieldContext.ts","../src/contexts/TableContext.ts","../src/services/SchemaControlService.ts","../src/validators/schema.ts","../src/contexts/DatabaseContext.ts","../src/contexts/DatabaseTokenContext.ts","../src/contexts/WorkspaceContext.ts","../src/services/core/BaseAuthClient.ts","../src/ClientWithCreds.ts","../src/ClientWithCredsWs.ts","../src/utils/auth.ts","../src/BaserowClient.ts","../src/presets/performance.ts","../src/express/index.ts","../src/express/middleware.ts","../src/express/errors.ts","../src/express/serializers.ts"],"sourcesContent":["/**\n * Cliente HTTP simplificado con plugins oficiales de Axios\n *\n * Cliente HTTP optimizado para la API de Baserow con funcionalidades avanzadas:\n * - Retry automático con backoff exponencial\n * - Rate limiting configurable\n * - Manejo robusto de errores con tipos específicos\n * - Logging estructurado de requests y respuestas\n * - Soporte para JWT y Database Tokens\n * - Connection pooling automático (Node.js 20+)\n *\n * **Características principales:**\n * - **Retry Logic**: 3 intentos con backoff exponencial usando axios-retry oficial\n * - **Rate Limiting**: Control de requests/segundo con axios-rate-limit oficial\n * - **Error Handling**: Transformación automática a errores tipados de Baserow\n * - **Auto-detection**: Detecta formato de token (JWT vs Database Token)\n * - **Optimizado**: 36% menos código que versiones anteriores\n * - **Probado**: Plugins mantenidos por millones de desarrolladores\n *\n * **Configuración recomendada:**\n * - Producción: maxRequestsPerSecond = 10 (default)\n * - Testing: maxRequestsPerSecond = 1 (usar HttpClient.forTesting())\n * - Retries: 3 (default) para alta disponibilidad\n * - Timeout: 30s (default) para operaciones complejas\n *\n * @example\n * ```typescript\n * // Cliente estándar\n * const client = new HttpClient({\n * baseURL: 'https://baserow.example.com',\n * token: 'your-token-here',\n * maxRequestsPerSecond: 10,\n * retries: 3,\n * logger: console\n * })\n *\n * // Cliente para testing (más conservador)\n * const testClient = HttpClient.forTesting({\n * baseURL: 'https://test.baserow.com',\n * token: 'test-token'\n * })\n *\n * // Uso básico\n * const data = await client.get('/api/endpoint')\n * const result = await client.post('/api/create', { name: 'Test' })\n * ```\n *\n * @since 1.0.0 - Versión simplificada con plugins oficiales\n * @since 1.1.0 - Migración de agentkeepalive a Node.js 20+ nativo\n */\n\nimport axios, { AxiosInstance, AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios'\nimport axiosRetry from 'axios-retry'\nimport rateLimit from 'axios-rate-limit'\nimport { PERFORMANCE_DEFAULTS } from './performance'\nimport {\n BaserowError,\n BaserowRateLimitError,\n BaserowValidationError,\n BaserowNotFoundError,\n BaserowNetworkError,\n BaserowTimeoutError,\n Logger\n} from '../types'\n\n/**\n * Configuración del cliente HTTP\n *\n * Define todas las opciones disponibles para personalizar el comportamiento\n * del cliente HTTP, incluyendo autenticación, retry logic y rate limiting.\n *\n * @since 1.0.0\n */\nexport interface HttpClientConfig {\n baseURL: string\n token: string\n timeout?: number\n retries?: number\n logger?: Logger\n // Rate limiting options simplificadas\n maxRequestsPerSecond?: number // Máximo requests por segundo (default: 10)\n enableRateLimiting?: boolean // Habilitar rate limiting (default: true)\n}\n\n/**\n * Cliente HTTP avanzado para la API de Baserow\n *\n * Implementa un cliente HTTP robusto y optimizado con características\n * empresariales como retry automático, rate limiting y manejo de errores.\n * Utiliza plugins oficiales de Axios para máxima compatibilidad.\n *\n * **Arquitectura simplificada:**\n * - Axios core para requests HTTP\n * - axios-retry para lógica de reintentos\n * - axios-rate-limit para control de velocidad\n * - Node.js 20+ keepAlive nativo para connection pooling\n *\n * @since 1.0.0\n */\nexport class HttpClient {\n private client: AxiosInstance\n private config: HttpClientConfig\n private logger?: Logger\n\n /**\n * Crea una nueva instancia del cliente HTTP\n *\n * @param config - Configuración del cliente HTTP\n * @param config.baseURL - URL base de la API de Baserow\n * @param config.token - Token de autenticación (JWT o Database Token)\n * @param config.timeout - Timeout en ms (default: 30000)\n * @param config.retries - Número de reintentos (default: 3)\n * @param config.maxRequestsPerSecond - Rate limit (default: 10)\n * @param config.enableRateLimiting - Habilitar rate limiting (default: true)\n * @param config.logger - Logger para debug y monitoreo\n *\n * @example\n * ```typescript\n * const client = new HttpClient({\n * baseURL: 'https://baserow.example.com',\n * token: 'your-database-token-or-jwt',\n * timeout: 30000,\n * retries: 3,\n * maxRequestsPerSecond: 10,\n * logger: console\n * })\n * ```\n *\n * @since 1.0.0\n */\n constructor(config: HttpClientConfig) {\n this.config = {\n ...PERFORMANCE_DEFAULTS,\n ...config\n }\n this.logger = config.logger\n\n // Crear instancia básica de Axios (Node.js 20+ maneja keepAlive automáticamente)\n this.client = axios.create({\n baseURL: config.baseURL,\n timeout: this.config.timeout,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json'\n }\n })\n\n // Configurar token inicial\n this.setAuthToken(config.token)\n\n // Configurar interceptores básicos\n this.setupRequestInterceptor()\n this.setupResponseInterceptor()\n\n // Configurar retry automático con plugin oficial\n this.setupRetryPlugin()\n\n // Configurar rate limiting con plugin oficial\n if (this.config.enableRateLimiting) {\n this.setupRateLimitingPlugin()\n }\n }\n\n private getAuthHeader(token: string): string {\n // Detectar formato del token automáticamente\n const isJWT = token.includes('.') && token.split('.').length === 3\n return isJWT ? `JWT ${token}` : `Token ${token}`\n }\n\n private extractResourceFromUrl(url: string): { resource: string; id: string } {\n // Extraer información del contexto desde la URL para mejores mensajes de error\n const patterns = [\n { pattern: /\\/database\\/tables\\/(\\d+)\\//, resource: 'Table', idIndex: 1 },\n { pattern: /\\/database\\/fields\\/(\\d+)\\//, resource: 'Field', idIndex: 1 },\n { pattern: /\\/database\\/rows\\/(\\d+)\\//, resource: 'Row', idIndex: 1 },\n { pattern: /\\/applications\\/(\\d+)\\//, resource: 'Database', idIndex: 1 },\n { pattern: /\\/workspaces\\/(\\d+)\\/applications\\//, resource: 'Workspace', idIndex: 1 },\n { pattern: /\\/workspaces\\/(\\d+)\\//, resource: 'Workspace', idIndex: 1 }\n ]\n\n for (const { pattern, resource, idIndex } of patterns) {\n const match = url.match(pattern)\n if (match) {\n return { resource, id: match[idIndex] }\n }\n }\n\n // Si no encontramos patrón específico, intentar extraer último número como ID\n const genericMatch = url.match(/\\/(\\d+)\\/?$/)\n if (genericMatch) {\n return { resource: 'Resource', id: genericMatch[1] }\n }\n\n return { resource: 'Resource', id: 'unknown' }\n }\n\n private setupRequestInterceptor(): void {\n this.client.interceptors.request.use(config => {\n // Agregar timestamp para calcular duración\n ;(config as any).startTime = Date.now()\n\n // Log de requests salientes (solo en debug)\n if (this.logger?.debug) {\n this.logger.debug('HTTP Request starting', {\n method: config.method?.toUpperCase(),\n url: config.url,\n baseURL: config.baseURL\n })\n }\n\n return config\n })\n }\n\n private setupResponseInterceptor(): void {\n this.client.interceptors.response.use(\n (response: AxiosResponse) => {\n // Log de respuestas exitosas (solo en modo debug si es necesario)\n if (this.logger?.debug) {\n this.logger.debug('HTTP Request successful', {\n method: response.config.method?.toUpperCase(),\n url: response.config.url,\n status: response.status,\n duration: Date.now() - (response.config as any).startTime\n })\n }\n return response\n },\n async (error: AxiosError) => {\n const requestInfo = {\n method: error.config?.method?.toUpperCase(),\n url: error.config?.url,\n startTime: (error.config as any)?.startTime\n }\n\n // Transformar errores de Axios a errores de Baserow\n if (error.response) {\n const { status, data } = error.response\n const errorData = data as any\n const message = errorData?.detail || errorData?.message || `HTTP ${status}: ${error.message}`\n\n // Log estructurado del error\n this.logger?.error('HTTP Request failed', {\n ...requestInfo,\n status,\n message,\n errorData: errorData ? JSON.stringify(errorData, null, 2) : null,\n fullErrorData: errorData,\n duration: requestInfo.startTime ? Date.now() - requestInfo.startTime : null\n })\n\n switch (status) {\n case 400:\n if (errorData?.error && typeof errorData.error === 'object') {\n throw new BaserowValidationError(message, errorData.error)\n }\n // Mejorar mensaje de error para debugging\n const detailedMessage = `${message} | Full error: ${JSON.stringify(errorData, null, 2)}`\n throw new BaserowError(detailedMessage, 400, 'BAD_REQUEST', errorData)\n\n case 404:\n // Intentar extraer contexto del URL para mejor mensaje de error\n const resourceContext = this.extractResourceFromUrl(requestInfo.url || '')\n throw new BaserowNotFoundError(resourceContext.resource, resourceContext.id)\n\n case 429:\n const retryAfter = error.response.headers['retry-after']\n throw new BaserowRateLimitError(retryAfter ? parseInt(retryAfter) : undefined)\n\n case 401:\n throw new BaserowError('Unauthorized - check your token', 401, 'UNAUTHORIZED', errorData, error.config)\n\n case 403:\n throw new BaserowError('Forbidden - insufficient permissions', 403, 'FORBIDDEN', errorData)\n\n case 408:\n throw new BaserowTimeoutError(this.config.timeout || 30000)\n\n case 500:\n case 502:\n case 503:\n case 504:\n // Errores de servidor - potencialmente transitorios\n throw new BaserowError(\n `Servidor Baserow no disponible (HTTP ${status}). El servidor puede estar sobrecargado o en mantenimiento. Intenta de nuevo más tarde.`,\n status,\n 'SERVER_ERROR',\n errorData\n )\n\n default:\n throw new BaserowError(message, status, 'HTTP_ERROR', errorData)\n }\n }\n\n // Error de red, timeout o conexión\n if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {\n this.logger?.error('Request timeout', {\n ...requestInfo,\n timeout: this.config.timeout,\n duration: requestInfo.startTime ? Date.now() - requestInfo.startTime : null\n })\n throw new BaserowTimeoutError(this.config.timeout || 30000)\n }\n\n // Error de red\n this.logger?.error('Network error', {\n ...requestInfo,\n errorCode: error.code,\n errorMessage: error.message,\n duration: requestInfo.startTime ? Date.now() - requestInfo.startTime : null\n })\n throw new BaserowNetworkError(error.message, error)\n }\n )\n }\n\n private setupRetryPlugin(): void {\n axiosRetry(this.client, {\n retries: this.config.retries!,\n retryDelay: axiosRetry.exponentialDelay,\n retryCondition: error => {\n // Retry en errores de red o métodos idempotentes con 5xx\n if (axiosRetry.isNetworkOrIdempotentRequestError(error)) {\n return true\n }\n\n // También retry en 429 (rate limiting)\n if (error.response?.status === 429) {\n return true\n }\n\n return false\n },\n onRetry: (retryCount, error, requestConfig) => {\n this.logger?.warn('Retrying request', {\n method: requestConfig.method?.toUpperCase(),\n url: requestConfig.url,\n attempt: retryCount,\n maxAttempts: this.config.retries,\n errorType: error.name,\n errorMessage: error.message\n })\n }\n })\n }\n\n private setupRateLimitingPlugin(): void {\n const maxRequests = this.config.maxRequestsPerSecond!\n this.client = rateLimit(this.client, {\n maxRequests: maxRequests,\n perMilliseconds: 1000,\n maxRPS: maxRequests\n })\n\n this.logger?.debug?.('Rate limiting configured', {\n maxRequestsPerSecond: maxRequests\n })\n }\n\n /**\n * Actualizar el token de autenticación\n *\n * Permite cambiar el token de autenticación dinámicamente sin crear\n * una nueva instancia del cliente. Detecta automáticamente el formato\n * del token (JWT vs Database Token).\n *\n * @param token - Nuevo token de autenticación\n *\n * @example\n * ```typescript\n * // Cambiar a Database Token\n * client.setAuthToken('new-database-token-123')\n *\n * // Cambiar a JWT\n * client.setAuthToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...')\n * ```\n *\n * @since 1.0.0\n */\n setAuthToken(token: string): void {\n if (token) {\n this.client.defaults.headers.common['Authorization'] = this.getAuthHeader(token)\n }\n }\n\n /**\n * Limpiar el token de autenticación\n *\n * Remueve el header de autenticación del cliente.\n * Útil para testing o cuando se necesita acceso anónimo.\n *\n * @example\n * ```typescript\n * client.clearAuthToken()\n * // Ahora las requests no incluirán Authorization header\n * ```\n *\n * @since 1.0.0\n */\n clearAuthToken(): void {\n delete this.client.defaults.headers.common['Authorization']\n }\n\n /**\n * Agregar interceptor de respuesta personalizado\n *\n * Permite agregar lógica personalizada para procesar respuestas\n * o manejar errores específicos. Se ejecuta después de los\n * interceptors internos del cliente.\n *\n * @param onFulfilled - Función para procesar respuestas exitosas\n * @param onRejected - Función para manejar errores\n *\n * @example\n * ```typescript\n * client.addResponseInterceptor(\n * (response) => {\n * console.log('Response received:', response.status)\n * return response\n * },\n * (error) => {\n * console.error('Request failed:', error.message)\n * return Promise.reject(error)\n * }\n * )\n * ```\n *\n * @since 1.0.0\n */\n addResponseInterceptor(\n onFulfilled: (response: AxiosResponse) => AxiosResponse,\n onRejected: (error: any) => any\n ): void {\n this.client.interceptors.response.use(onFulfilled, onRejected)\n }\n\n /**\n * Realizar petición GET\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param endpoint - Endpoint relativo a la baseURL\n * @param params - Parámetros de query string\n * @returns Promise con los datos de la respuesta\n *\n * @example\n * ```typescript\n * const tables = await client.get<Table[]>('/database/tables/database/123/')\n * const rows = await client.get('/database/rows/table/456/', { page: 1, size: 100 })\n * ```\n *\n * @since 1.0.0\n */\n async get<T = any>(endpoint: string, params?: Record<string, any>): Promise<T> {\n const response = await this.client.get(endpoint, { params })\n return response.data\n }\n\n /**\n * Realizar petición POST\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param endpoint - Endpoint relativo a la baseURL\n * @param data - Datos a enviar en el body\n * @param params - Parámetros de query string\n * @returns Promise con los datos de la respuesta\n *\n * @example\n * ```typescript\n * const newTable = await client.post<Table>('/database/tables/database/123/', {\n * name: 'Nueva Tabla'\n * })\n * ```\n *\n * @since 1.0.0\n */\n async post<T = any>(endpoint: string, data?: any, params?: Record<string, any>): Promise<T> {\n const response = await this.client.post(endpoint, data, { params })\n return response.data\n }\n\n /**\n * Realizar petición PUT\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param endpoint - Endpoint relativo a la baseURL\n * @param data - Datos a enviar en el body\n * @param params - Parámetros de query string\n * @returns Promise con los datos de la respuesta\n *\n * @since 1.0.0\n */\n async put<T = any>(endpoint: string, data?: any, params?: Record<string, any>): Promise<T> {\n const response = await this.client.put(endpoint, data, { params })\n return response.data\n }\n\n /**\n * Realizar petición PATCH\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param endpoint - Endpoint relativo a la baseURL\n * @param data - Datos a enviar en el body\n * @param params - Parámetros de query string\n * @returns Promise con los datos de la respuesta\n *\n * @example\n * ```typescript\n * const updated = await client.patch<Table>('/database/tables/456/', {\n * name: 'Nuevo Nombre'\n * })\n * ```\n *\n * @since 1.0.0\n */\n async patch<T = any>(endpoint: string, data?: any, params?: Record<string, any>): Promise<T> {\n const response = await this.client.patch(endpoint, data, { params })\n return response.data\n }\n\n /**\n * Realizar petición DELETE\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param endpoint - Endpoint relativo a la baseURL\n * @param params - Parámetros de query string\n * @returns Promise con los datos de la respuesta\n *\n * @example\n * ```typescript\n * await client.delete('/database/tables/456/')\n * ```\n *\n * @since 1.0.0\n */\n async delete<T = any>(endpoint: string, params?: Record<string, any>): Promise<T> {\n const response = await this.client.delete(endpoint, { params })\n return response.data\n }\n\n /**\n * Crear instancia HttpClient optimizada para testing\n *\n * Crea un cliente con configuración conservadora optimizada para tests:\n * - Rate limiting muy bajo (1 req/s) para no saturar el servidor\n * - Timeout alto (60s) para operaciones lentas en CI/CD\n * - Menos reintentos (2) para tests más rápidos\n *\n * @param config - Configuración base (sin maxRequestsPerSecond)\n * @returns Instancia optimizada para testing\n *\n * @example\n * ```typescript\n * const testClient = HttpClient.forTesting({\n * baseURL: process.env.TEST_BASEROW_URL,\n * token: process.env.TEST_TOKEN\n * })\n * // Rate limiting: 1 req/s, timeout: 60s, retries: 2\n * ```\n *\n * @since 1.0.0\n */\n static forTesting(config: Omit<HttpClientConfig, 'maxRequestsPerSecond'>): HttpClient {\n return new HttpClient({\n ...config,\n maxRequestsPerSecond: 1, // Máximo 1 request por segundo en tests (más conservador)\n enableRateLimiting: true,\n retries: 2, // Menos reintentos en tests\n timeout: 60000 // Timeout más alto para tests (60s)\n })\n }\n\n /**\n * Acceso directo a la instancia de Axios para casos avanzados\n *\n * Proporciona acceso a la instancia configurada de Axios para\n * casos que requieren funcionalidad no cubierta por los métodos\n * simplificados del cliente.\n *\n * @returns Instancia configurada de Axios\n *\n * @example\n * ```typescript\n * // Usar funcionalidades avanzadas de Axios\n * const response = await client.axios({\n * method: 'post',\n * url: '/custom-endpoint',\n * headers: { 'Custom-Header': 'value' }\n * })\n * ```\n *\n * @since 1.0.0\n */\n get axios(): AxiosInstance {\n return this.client\n }\n\n /**\n * Realizar petición personalizada usando configuración de Axios\n *\n * Permite realizar peticiones con configuración avanzada de Axios\n * manteniendo todos los beneficios del cliente (retry, rate limiting, etc.).\n *\n * @template T - Tipo de datos esperado en la respuesta\n * @param config - Configuración completa de Axios\n * @returns Promise con los datos de la respuesta\n *\n * @example\n * ```typescript\n * const result = await client.request<CustomType>({\n * method: 'POST',\n * url: '/special-endpoint',\n * data: complexData,\n * headers: { 'Special-Header': 'value' },\n * timeout: 60000\n * })\n * ```\n *\n * @since 1.0.0\n */\n async request<T = any>(config: AxiosRequestConfig): Promise<T> {\n const response = await this.client.request(config)\n return response.data\n }\n\n /**\n * Cerrar las conexiones HTTP y limpiar recursos\n *\n * Limpia los recursos del cliente HTTP. En Node.js 20+ con keepAlive\n * automático, no requiere gestión manual de connection pools.\n * Principalmente útil para testing y cleanup de recursos.\n *\n * @example\n * ```typescript\n * // En tests o al cerrar la aplicación\n * client.destroy()\n * ```\n *\n * @since 1.0.0\n * @since 1.1.0 - Simplificado para Node.js 20+ keepAlive nativo\n */\n destroy(): void {\n this.logger?.debug?.('Destroying HTTP client')\n // En Node.js 20+ con keepAlive automático, no necesitamos gestión manual de agentes\n this.logger?.debug?.('HTTP client destroyed successfully')\n }\n}\n","import type { BaserowPerformanceOptions } from '../types'\n\n/**\n * Defaults centralizados de performance para toda la librería\n *\n * Fuente única de verdad para valores por defecto de configuración de performance.\n * Usado por PerformanceManager y HttpClient para evitar duplicación.\n */\nexport const PERFORMANCE_DEFAULTS: BaserowPerformanceOptions = {\n timeout: 30000,\n retries: 3,\n maxRequestsPerSecond: 10,\n enableRateLimiting: true\n} as const\n\n/**\n * Gestor centralizado de configuración de performance para toda la librería Baserow\n *\n * Proporciona una API unificada para gestionar la configuración global de performance\n * que se aplicará a todas las instancias de clientes Baserow.\n *\n * @internal\n * @since 1.0.0\n */\nexport class PerformanceManager {\n /**\n * Configuración global por defecto de performance\n */\n private static globalDefaults: BaserowPerformanceOptions = { ...PERFORMANCE_DEFAULTS }\n\n /**\n * Establece la configuración global de performance\n *\n * @param defaults - Configuración parcial para sobrescribir defaults\n *\n * @example\n * ```typescript\n * PerformanceManager.setGlobal({\n * timeout: 60000,\n * retries: 5\n * })\n * ```\n */\n static setGlobal(defaults: Partial<BaserowPerformanceOptions>): void {\n PerformanceManager.globalDefaults = {\n ...PerformanceManager.globalDefaults,\n ...defaults\n }\n }\n\n /**\n * Obtiene la configuración global actual de performance\n *\n * @returns Copia de la configuración global actual\n *\n * @example\n * ```typescript\n * const current = PerformanceManager.getGlobal()\n * console.log('Timeout global:', current.timeout)\n * ```\n */\n static getGlobal(): BaserowPerformanceOptions {\n return { ...PerformanceManager.globalDefaults }\n }\n\n /**\n * Resetea la configuración global a los valores por defecto de la librería\n *\n * @example\n * ```typescript\n * PerformanceManager.reset()\n * ```\n */\n static reset(): void {\n PerformanceManager.globalDefaults = { ...PERFORMANCE_DEFAULTS }\n }\n\n /**\n * Combina la configuración global con la configuración específica de instancia\n *\n * La configuración de instancia tiene prioridad sobre la global.\n *\n * @param instanceConfig - Configuración específica de la instancia (opcional)\n * @returns Configuración final combinada\n *\n * @example\n * ```typescript\n * // Solo configuración global\n * const config1 = PerformanceManager.merge()\n *\n * // Configuración global + override de instancia\n * const config2 = PerformanceManager.merge({\n * retries: 1, // Override\n * timeout: 5000 // Override\n * // maxRequestsPerSecond y enableRateLimiting vienen de global\n * })\n * ```\n */\n static merge(instanceConfig?: Partial<BaserowPerformanceOptions>): BaserowPerformanceOptions {\n return {\n ...PerformanceManager.globalDefaults,\n ...instanceConfig\n }\n }\n}\n","/**\n * Tipos relacionados con Database Tokens de Baserow\n */\n\n/**\n * Prefijo único para identificar tokens creados por la librería\n * vs tokens creados manualmente por usuarios\n */\nexport const LIBRARY_TOKEN_PREFIX = 'br-lib:'\n\nexport interface DatabaseTokenPermissions {\n create: boolean\n read: boolean\n update: boolean\n delete: boolean\n}\n\nexport interface DatabaseToken {\n id: number\n name: string\n token: string\n workspace: number\n permissions: DatabaseTokenPermissions\n created_on: string\n}\n\nexport interface CreateDatabaseTokenRequest {\n name: string\n permissions: DatabaseTokenPermissions\n}\n\nexport interface UpdateDatabaseTokenRequest {\n name?: string\n permissions?: DatabaseTokenPermissions\n rotate_token?: boolean\n}\n","/**\n * Clases de error específicas para Baserow\n */\n\nexport class BaserowError extends Error {\n public config?: any // Axios request config para permitir retry en interceptores\n\n constructor(\n message: string,\n public status?: number,\n public code?: string,\n public details?: any,\n config?: any\n ) {\n super(message)\n this.name = 'BaserowError'\n this.config = config\n }\n}\n\nexport class BaserowValidationError extends BaserowError {\n constructor(\n message: string,\n public fieldErrors: Record<string, string[]>\n ) {\n super(message, 400, 'VALIDATION_ERROR', fieldErrors)\n this.name = 'BaserowValidationError'\n }\n}\n\nexport class BaserowNotFoundError extends BaserowError {\n constructor(resource: string, id: number | string) {\n super(`${resource} with id ${id} not found`, 404, 'NOT_FOUND')\n this.name = 'BaserowNotFoundError'\n }\n}\n\nexport class BaserowRateLimitError extends BaserowError {\n constructor(public retryAfter?: number) {\n super('Rate limit exceeded', 429, 'RATE_LIMIT', { retryAfter })\n this.name = 'BaserowRateLimitError'\n }\n}\n\nexport class BaserowNetworkError extends BaserowError {\n constructor(message: string, originalError?: any) {\n super(`Network error: ${message}`, 0, 'NETWORK_ERROR', { originalError })\n this.name = 'BaserowNetworkError'\n }\n}\n\nexport class BaserowTimeoutError extends BaserowError {\n constructor(timeoutMs: number) {\n super(`Request timeout after ${timeoutMs}ms`, 0, 'TIMEOUT', { timeoutMs })\n this.name = 'BaserowTimeoutError'\n }\n}\n\nexport class BaserowConfigError extends BaserowError {\n constructor(message: string, configField?: string) {\n super(`Configuration error: ${message}`, 0, 'CONFIG_ERROR', { configField })\n this.name = 'BaserowConfigError'\n }\n}\n\nexport class BaserowWorkspaceError extends BaserowError {\n constructor(message: string, workspaceId?: number) {\n super(`Workspace error: ${message}`, 0, 'WORKSPACE_ERROR', { workspaceId })\n this.name = 'BaserowWorkspaceError'\n }\n}\n\nexport class BaserowAuthTokenExpiredError extends BaserowError {\n constructor(tokenType: 'access' | 'refresh' = 'refresh') {\n super(\n `${tokenType === 'access' ? 'Access' : 'Refresh'} token has expired. Please login again.`,\n 401,\n 'AUTH_TOKEN_EXPIRED',\n { tokenType }\n )\n this.name = 'BaserowAuthTokenExpiredError'\n }\n}\n\nexport class BaserowAuthTokenInvalidError extends BaserowError {\n constructor(message: string = 'Invalid or corrupted authentication token', details?: any) {\n super(message, 401, 'AUTH_TOKEN_INVALID', details)\n this.name = 'BaserowAuthTokenInvalidError'\n }\n}\n\nexport class BaserowReLoginRequiredError extends BaserowError {\n constructor(\n reason: 'refresh_token_expired' | 'session_expired' | 'tokens_lost' = 'refresh_token_expired',\n details?: any\n ) {\n const messages = {\n refresh_token_expired: 'Refresh token has expired after period of inactivity. Re-authentication required.',\n session_expired: 'Session has expired. Please login again with your credentials.',\n tokens_lost: 'Authentication tokens were lost (process restart or memory cleared). Re-authentication required.'\n }\n super(messages[reason], 401, 'RE_LOGIN_REQUIRED', { reason, ...details })\n this.name = 'BaserowReLoginRequiredError'\n }\n}\n","/**\n * Factory centralizado para creación de HttpClient\n *\n * Elimina duplicación de código en la inicialización de HttpClient\n * proporcionando una fuente única de verdad para la configuración.\n *\n * @internal\n * @since 1.0.0\n */\n\nimport { HttpClient } from './axios'\nimport { PerformanceManager } from './performance'\nimport type { Logger, BaserowPerformanceOptions } from '../types'\n\n/**\n * Configuración para crear HttpClient\n */\nexport interface HttpClientFactoryConfig {\n /** URL base de Baserow (ej: 'https://baserow.com') */\n url: string\n /** Token de autenticación (Database Token o JWT) */\n token: string\n /** Logger opcional para debug */\n logger?: Logger\n /** Configuración de performance opcional */\n performance?: Partial<BaserowPerformanceOptions>\n}\n\n/**\n * Crea una instancia de HttpClient con configuración estandarizada\n *\n * Centraliza la lógica de:\n * - Sanitización de URL base\n * - Configuración de performance (merge con defaults globales)\n * - Configuración de token y logger\n *\n * @param config - Configuración del cliente HTTP\n * @returns Instancia configurada de HttpClient\n *\n * @example\n * ```typescript\n * // Para Database Token\n * const client = createHttpClient({\n * url: 'https://baserow.com',\n * token: 'db_token_123',\n * logger: console\n * })\n *\n * // Para JWT (durante login)\n * const tempClient = createHttpClient({\n * url: 'https://baserow.com',\n * token: '', // Sin token inicial\n * logger: console,\n * performance: { timeout: 15000 }\n * })\n * ```\n */\nexport function createHttpClient(config: HttpClientFactoryConfig): HttpClient {\n return new HttpClient({\n baseURL: `${config.url.replace(/\\/$/, '')}/api`,\n token: config.token,\n logger: config.logger,\n // Combinar defaults globales con configuración específica de instancia\n ...PerformanceManager.merge(config.performance)\n })\n}\n","/**\n * Utilidades de validación para la librería @gzl10/baserow\n *\n * Proporciona funciones de validación reutilizables para parámetros,\n * configuraciones y datos de entrada. Todas las funciones lanzan\n * BaserowValidationError con mensajes estructurados para facilitar\n * el debugging y manejo de errores.\n *\n * **Funciones disponibles:**\n * - Validación de campos requeridos y tipos básicos\n * - Validación de URLs y tokens\n * - Sanitización de nombres de campos\n * - Validación de tipos de campos de Baserow\n * - Utilidades para procesamiento bulk (chunking)\n *\n * @since 1.0.0\n */\n\nimport { BaserowValidationError } from '../types'\n\n/**\n * Valida que un valor no sea null, undefined o string vacío\n *\n * Función genérica que garantiza que un valor requerido esté presente.\n * Utilizada en toda la librería para validar parámetros obligatorios.\n *\n * @template T - Tipo del valor a validar\n * @param value - Valor a validar\n * @param fieldName - Nombre del campo para mensajes de error\n * @returns El valor validado (mismo tipo)\n *\n * @throws {BaserowValidationError} Si el valor es null, undefined o string vacío\n *\n * @example\n * ```typescript\n * // Validar parámetros requeridos\n * const name = validateRequired(userInput.name, 'name')\n * const id = validateRequired(params.id, 'table ID')\n *\n * // Usada internamente en servicios\n * validateRequired(tableId, 'table ID') // Lanza error si tableId es null/undefined\n * ```\n *\n * @since 1.0.0\n */\nexport function validateRequired<T>(value: T, fieldName: string): T {\n if (value === undefined || value === null || value === '') {\n throw new BaserowValidationError(`${fieldName} is required`, {\n [fieldName]: [`${fieldName} is required`]\n })\n }\n return value\n}\n\n/**\n * Valida que un número sea positivo (mayor que 0)\n *\n * Utilizada para validar IDs, cantidades, tamaños de página y otros\n * valores numéricos que deben ser positivos en la API de Baserow.\n *\n * @param value - Número a validar\n * @param fieldName - Nombre del campo para mensajes de error\n * @returns El número validado\n *\n * @throws {BaserowValidationError} Si no es un número o es menor/igual que 0\n *\n * @example\n * ```typescript\n * // Validar IDs\n * const tableId = validatePositiveNumber(params.tableId, 'table ID')\n * const fieldId = validatePositiveNumber(data.fieldId, 'field ID')\n *\n * // Validar opciones de paginación\n * const pageSize = validatePositiveNumber(options.size, 'page size')\n * ```\n *\n * @since 1.0.0\n */\nexport function validatePositiveNumber(value: number, fieldName: string): number {\n if (typeof value !== 'number' || value <= 0) {\n throw new BaserowValidationError(`${fieldName} must be a positive number`, {\n [fieldName]: [`${fieldName} must be a positive number`]\n })\n }\n return value\n}\n\n/**\n * Valida que un valor sea un string con longitud mínima\n *\n * Verifica tipo string y longitud mínima configurable.\n * Utilizada para validar nombres, descripciones y otros campos de texto.\n *\n * @param value - Valor a validar\n * @param fieldName - Nombre del campo para mensajes de error\n * @param minLength - Longitud mínima requerida (default: 1)\n * @returns El string validado\n *\n * @throws {BaserowValidationError} Si no es string o no cumple longitud mínima\n *\n * @example\n * ```typescript\n * // Validar nombres de recursos\n * const tableName = validateString(data.name, 'table name')\n * const fieldName = validateString(field.name, 'field name')\n *\n * // Validar con longitud mínima específica\n * const description = validateString(data.desc, 'description', 10)\n * ```\n *\n * @since 1.0.0\n */\nexport function validateString(value: any, fieldName: string, minLength = 1): string {\n if (typeof value !== 'string' || value.length < minLength) {\n throw new BaserowValidationError(`${fieldName} must be a string with at least ${minLength} character(s)`, {\n [fieldName]: [`${fieldName} must be a string with at least ${minLength} character(s)`]\n })\n }\n return value\n}\n\n/**\n * Valida que un string sea una URL válida\n *\n * Utiliza el constructor URL nativo de JavaScript para validar formato.\n * Soporta todos los protocolos válidos (http, https, ftp, etc.).\n *\n * @param url - String URL a validar\n * @param fieldName - Nombre del campo para mensajes de error (default: 'url')\n * @returns La URL validada\n *\n * @throws {BaserowValidationError} Si el formato de URL es inválido\n *\n * @example\n * ```typescript\n * // Validar URLs de configuración\n * const baseUrl = validateUrl(config.url, 'Baserow URL')\n * const webhookUrl = validateUrl(data.webhook_url, 'webhook URL')\n *\n * // Validar campos URL de Baserow\n * const siteUrl = validateUrl(fieldData.url, 'website URL')\n * ```\n *\n * @since 1.0.0\n */\nexport function validateUrl(url: string, fieldName: string = 'url'): string {\n try {\n new URL(url)\n return url\n } catch {\n throw new BaserowValidationError(`${fieldName} must be a valid URL`, {\n [fieldName]: [`${fieldName} must be a valid URL`]\n })\n }\n}\n\n/**\n * Verifica si un string es una URL válida sin lanzar errores\n *\n * Versión no-throw de validateUrl() que retorna boolean.\n * Útil para validaciones condicionales y filtros.\n *\n * @param url - String URL a verificar\n * @returns true si es URL válida, false si no\n *\n * @example\n * ```typescript\n * // Verificación condicional\n * if (isValidUrl(userInput)) {\n * // Procesar como URL\n * } else {\n * // Manejar como texto plano\n * }\n *\n * // Filtrar URLs válidas\n * const validUrls = urls.filter(isValidUrl)\n * ```\n *\n * @since 1.0.0\n */\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Valida un token de autenticación (Database Token o JWT)\n *\n * Verifica que el token no esté vacío y remueve espacios extra.\n * Soporta tanto Database Tokens como JWT tokens de Baserow.\n *\n * @param token - Token a validar\n * @returns El token validado y sanitizado (sin espacios)\n *\n * @throws {BaserowValidationError} Si el token está vacío o no es string\n *\n * @example\n * ```typescript\n * // Validar tokens de configuración\n * const dbToken = validateToken(config.token)\n * const jwtToken = validateToken(process.env.BASEROW_JWT)\n *\n * // Se auto-sanitiza\n * const clean = validateToken(' token123 ') // returns 'token123'\n * ```\n *\n * @since 1.0.0\n */\nexport function validateToken(token: string): string {\n if (!token || typeof token !== 'string' || token.trim().length === 0) {\n throw new BaserowValidationError('Token is required and cannot be empty', {\n token: ['Token is required and cannot be empty']\n })\n }\n return token.trim()\n}\n\n/**\n * Valida la configuración completa de un cliente Baserow\n *\n * Valida que la configuración tenga estructura correcta con URL y token válidos.\n * Utilizada por todos los clientes (BaserowClient, BaserowAdmin*) durante la inicialización.\n *\n * @param config - Configuración a validar\n * @returns void (lanza error si es inválida)\n *\n * @throws {BaserowValidationError} Si faltan campos requeridos o son inválidos\n *\n * @example\n * ```typescript\n * // Validación en constructores de clientes\n * validateConfig({\n * url: 'https://baserow.example.com',\n * token: 'valid-token-123'\n * }) // OK\n *\n * validateConfig({\n * url: 'invalid-url',\n * token: ''\n * }) // Lanza BaserowValidationError\n * ```\n *\n * @since 1.0.0\n */\nexport function validateConfig(config: any): void {\n if (!config || typeof config !== 'object') {\n throw new BaserowValidationError('Config must be an object', {\n config: ['Config must be an object']\n })\n }\n\n validateRequired(config.url, 'url')\n validateRequired(config.token, 'token')\n\n if (!isValidUrl(config.url)) {\n throw new BaserowValidationError('URL must be a valid URL', {\n url: ['URL must be a valid URL']\n })\n }\n\n validateToken(config.token)\n}\n\n/**\n * Sanitiza y valida nombres de campos de Baserow\n *\n * Aplica las reglas de nombres de campos de Baserow:\n * - Debe ser string no vacío\n * - Máximo 255 caracteres\n * - Remueve espacios al inicio y final\n *\n * @param name - Nombre de campo a sanitizar\n * @returns El nombre sanitizado y validado\n *\n * @throws {BaserowValidationError} Si el nombre es inválido\n *\n * @example\n * ```typescript\n * // Sanitizar nombres de entrada del usuario\n * const fieldName = sanitizeFieldName(' Nombre del Campo ') // 'Nombre del Campo'\n * const cleanName = sanitizeFieldName(userInput.fieldName)\n *\n * // Validar antes de crear campos\n * const safeName = sanitizeFieldName(data.name)\n * await fieldService.createTextField(tableId, safeName)\n * ```\n *\n * @since 1.0.0\n */\nexport function sanitizeFieldName(name: string): string {\n if (typeof name !== 'string' || name == null) {\n throw new BaserowValidationError('Field name must be a string', {\n name: ['Field name must be a string']\n })\n }\n\n // Eliminar espacios al inicio y final\n name = name.trim()\n\n if (name.length === 0) {\n throw new BaserowValidationError('Field name cannot be empty', {\n name: ['Field name cannot be empty']\n })\n }\n\n if (name.length > 255) {\n throw new BaserowValidationError('Field name cannot exceed 255 characters', {\n name: ['Field name cannot exceed 255 characters']\n })\n }\n\n return name\n}\n\n/**\n * Divide un array en chunks (trozos) de tamaño específico\n *\n * Utilizada para operaciones bulk optimizadas que procesan datos en lotes\n * para evitar timeouts y limitar el uso de memoria. Esencial para\n * createBulk(), updateBulk() y deleteBulk().\n *\n * @template T - Tipo de elementos del array\n * @param array - Array a dividir en chunks\n * @param chunkSize - Tamaño de cada chunk (debe ser positivo)\n * @returns Array de arrays (chunks) con el tamaño especificado\n *\n * @throws {BaserowValidationError} Si chunkSize es menor o igual que 0\n *\n * @example\n * ```typescript\n * // Procesar rows en lotes de 50\n * const rows = [...] // 1000 rows\n * const chunks = chunkArray(rows, 50) // [[50 rows], [50 rows], ...]\n *\n * for (const chunk of chunks) {\n * await rowService.createBulk(tableId, chunk)\n * }\n *\n * // Procesar IDs para delete bulk\n * const ids = [1, 2, 3, ..., 100]\n * const idChunks = chunkArray(ids, 25) // [[25 IDs], [25 IDs], ...]\n * ```\n *\n * @since 1.0.0\n */\nexport function chunkArray<T>(array: T[], chunkSize: number): T[][] {\n if (chunkSize <= 0) {\n throw new BaserowValidationError('Chunk size must be positive', {\n chunkSize: ['Chunk size must be positive']\n })\n }\n\n const chunks: T[][] = []\n for (let i = 0; i < array.length; i += chunkSize) {\n chunks.push(array.slice(i, i + chunkSize))\n }\n return chunks\n}\n\n/**\n * Crea una pausa (delay) asíncrona por un tiempo especificado\n *\n * Utilizada para retry logic, rate limiting manual y esperas\n * entre operaciones. Especialmente útil en tests y operaciones\n * que requieren delays entre requests.\n *\n * @param ms - Millisegundos a esperar\n * @returns Promise que se resuelve después del delay\n *\n * @example\n * ```typescript\n * // Esperar entre operaciones\n * await createRow(data1)\n * await delay(1000) // Esperar 1 segundo\n * await createRow(data2)\n *\n * // Retry logic con delay\n * for (let i = 0; i < 3; i++) {\n * try {\n * return await operation()\n * } catch (error) {\n * if (i < 2) await delay(1000 * (i + 1)) // Backoff: 1s, 2s\n * }\n * }\n * ```\n *\n * @since 1.0.0\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nconst VALID_FIELD_TYPES = [\n 'text',\n 'long_text',\n 'url',\n 'email',\n 'phone_number',\n 'number',\n 'rating',\n 'boolean',\n 'date',\n 'last_modified',\n 'last_modified_by',\n 'created_on',\n 'created_by',\n 'single_select',\n 'multiple_select',\n 'file',\n 'multiple_collaborators',\n 'link_row',\n 'count',\n 'rollup',\n 'lookup',\n 'formula',\n 'autonumber'\n] as const\n\n/**\n * Valida que un tipo de campo sea soportado por Baserow\n *\n * Verifica que el tipo esté en la lista de 21 tipos soportados por la librería.\n * Lista actualizada con los nuevos tipos avanzados (file, autonumber, count, rollup, lookup).\n *\n * **Tipos soportados (21/22):**\n * - **Texto**: text, long_text, url, email, phone_number\n * - **Numéricos**: number, rating, boolean\n * - **Fecha/Auditoría**: date, last_modified, last_modified_by, created_on, created_by\n * - **Selección**: single_select, multiple_select\n * - **Relacionales**: link_row, formula\n * - **Avanzados**: file, autonumber, count, rollup, lookup\n * - **Otros**: multiple_collaborators\n *\n * @param type - Tipo de campo a validar\n * @param fieldName - Nombre del campo para mensajes de error (default: 'type')\n * @returns El tipo validado\n *\n * @throws {BaserowValidationError} Si el tipo no está soportado\n *\n * @example\n * ```typescript\n * // Validar antes de crear campos\n * const fieldType = validateFieldType('text', 'field type')\n * const advancedType = validateFieldType('rollup', 'rollup field type')\n *\n * // Usar en FieldService\n * validateFieldType(data.type) // Lanza error si no es soportado\n * ```\n *\n * @since 1.0.0\n */\nexport function validateFieldType(type: string, fieldName: string = 'type'): string {\n if (!VALID_FIELD_TYPES.includes(type as any)) {\n throw new BaserowValidationError(`Invalid field type: ${type}`, {\n [fieldName]: [`Field type must be one of: ${VALID_FIELD_TYPES.join(', ')}`]\n })\n }\n return type\n}\n","/**\n * Clase base para todos los servicios de Baserow\n * Proporciona funcionalidades comunes como HTTP client, logging y validación\n */\n\nimport { HttpClient } from '../../utils/axios'\nimport { Logger } from '../../types/index'\n\nexport abstract class BaseService {\n protected logger?: Logger\n\n constructor(\n protected http: HttpClient,\n logger?: Logger\n ) {\n this.logger = logger\n }\n\n /**\n * Log de información\n */\n protected logInfo(message: string, ...params: any[]): void {\n this.logger?.info?.(`[${this.constructor.name}] ${message}`, ...params)\n }\n\n /**\n * Log de warnings\n */\n protected logWarn(message: string, ...params: any[]): void {\n this.logger?.warn?.(`[${this.constructor.name}] ${message}`, ...params)\n }\n\n /**\n * Log de errores\n */\n protected logError(message: string, error?: any, ...params: any[]): void {\n this.logger?.error?.(`[${this.constructor.name}] ${message}`, error, ...params)\n }\n\n /**\n * Log de debug\n */\n protected logDebug(message: string, ...params: any[]): void {\n this.logger?.debug?.(`[${this.constructor.name}] ${message}`, ...params)\n }\n\n /**\n * Método helper para construir URLs de endpoints\n */\n protected buildEndpoint(...segments: (string | number)[]): string {\n const cleanedSegments = segments.map(segment => String(segment).replace(/^\\/|\\/$/g, ''))\n return cleanedSegments.join('/') + '/'\n }\n\n /**\n * Método helper para manejar errores HTTP de forma consistente\n */\n protected handleHttpError(error: any, operation: string, resourceId?: number | string): never {\n const context = resourceId ? `${operation} for resource ${resourceId}` : operation\n this.logError(`HTTP error during ${context}`, error)\n throw error\n }\n\n /**\n * Log de operación exitosa\n */\n protected logSuccess(operation: string, resourceId?: number | string, details?: any): void {\n const context = resourceId ? `${operation} for resource ${resourceId}` : operation\n this.logInfo(`✅ ${context} completed successfully`, details)\n }\n}\n","/**\n * Servicio HTTP especializado para operaciones CRUD comunes\n * Elimina duplicación de código HTTP entre servicios\n */\n\nimport { BaseService } from './BaseService'\nimport { BaserowResponse, BaserowNotFoundError } from '../../types/index'\n\nexport abstract class HttpService extends BaseService {\n /**\n * GET genérico para listar recursos\n */\n protected async getList<T>(endpoint: string, params?: Record<string, any>): Promise<T[]> {\n try {\n this.logDebug(`Fetching list from ${endpoint}`, params)\n const response = await this.http.get<BaserowResponse<T>>(endpoint, { params })\n this.logSuccess(`list operation`, undefined, { count: response.count })\n return response.results\n } catch (error) {\n this.handleHttpError(error, 'list operation')\n }\n }\n\n /**\n * GET genérico para obtener un recurso por ID\n */\n protected async getById<T>(endpoint: string, id: number): Promise<T> {\n try {\n this.logDebug(`Fetching resource ${id} from ${endpoint}`)\n const response = await this.http.get<T>(this.buildEndpoint(endpoint, id))\n this.logSuccess('get operation', id)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n const resourceName = endpoint.split('/').pop() || 'resource'\n throw new BaserowNotFoundError(resourceName, id)\n }\n this.handleHttpError(error, 'get operation', id)\n }\n }\n\n /**\n * POST genérico para crear recursos\n */\n protected async createResource<T, C = any>(endpoint: string, data: C): Promise<T> {\n try {\n this.logDebug(`Creating resource at ${endpoint}`, data)\n const response = await this.http.post<T>(endpoint, data)\n this.logSuccess('create operation', (response as any).id || 'new')\n return response\n } catch (error) {\n this.handleHttpError(error, 'create operation')\n }\n }\n\n /**\n * PATCH genérico para actualizar recursos\n */\n protected async updateResource<T, U = any>(endpoint: string, id: number, data: U): Promise<T> {\n try {\n this.logDebug(`Updating resource ${id} at ${endpoint}`, data)\n const response = await this.http.patch<T>(this.buildEndpoint(endpoint, id), data)\n this.logSuccess('update operation', id)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n const resourceName = endpoint.split('/').pop() || 'resource'\n throw new BaserowNotFoundError(resourceName, id)\n }\n this.handleHttpError(error, 'update operation', id)\n }\n }\n\n /**\n * DELETE genérico para eliminar recursos\n */\n protected async deleteById(endpoint: string, id: number): Promise<void> {\n try {\n this.logDebug(`Deleting resource ${id} from ${endpoint}`)\n await this.http.delete(this.buildEndpoint(endpoint, id))\n this.logSuccess('delete operation', id)\n } catch (error) {\n if ((error as any).status === 404) {\n const resourceName = endpoint.split('/').pop() || 'resource'\n throw new BaserowNotFoundError(resourceName, id)\n }\n this.handleHttpError(error, 'delete operation', id)\n }\n }\n\n /**\n * Método helper para buscar recursos por nombre\n */\n protected async findResourceByName<T extends { id: number; name: string }>(\n endpoint: string,\n name: string,\n listParams?: Record<string, any>\n ): Promise<T | null> {\n try {\n this.logDebug(`Searching for resource with name \"${name}\" in ${endpoint}`)\n const items = await this.getList<T>(endpoint, listParams)\n const found = items.find(item => item.name === name) || null\n\n if (found) {\n this.logSuccess(`find by name \"${name}\"`, found.id)\n } else {\n this.logDebug(`No resource found with name \"${name}\"`)\n }\n\n return found\n } catch (error) {\n this.handleHttpError(error, `find by name \"${name}\"`)\n }\n }\n}\n","/**\n * Servicio de validación centralizado\n * Elimina duplicación de validaciones entre servicios\n */\n\nimport { BaseService } from './BaseService'\nimport { BaserowConfigError, BaserowValidationError } from '../../types/index'\nimport { validateRequired, validatePositiveNumber, validateString, validateUrl } from '../../utils/validation'\n\nexport class ValidationService extends BaseService {\n /**\n * Valida configuración básica de Baserow\n */\n validateBaserowConfig(url: string, token?: string): void {\n try {\n validateRequired(url, 'url')\n validateUrl(url, 'url')\n\n if (token) {\n validateRequired(token, 'token')\n validateString(token, 'token')\n }\n } catch (error) {\n throw new BaserowConfigError(`Invalid configuration: ${(error as Error).message}`)\n }\n }\n\n /**\n * Valida credenciales de usuario\n */\n validateCredentials(email: string, password: string): void {\n try {\n validateRequired(email, 'email')\n validateString(email, 'email')\n validateRequired(password, 'password')\n validateString(password, 'password')\n\n // Validación básica de email\n if (!email.includes('@')) {\n throw new Error('Invalid email format')\n }\n } catch (error) {\n throw new BaserowValidationError(`Invalid credentials: ${(error as Error).message}`, {\n email: email ? [] : ['Email is required'],\n password: password ? [] : ['Password is required']\n })\n }\n }\n\n /**\n * Valida IDs numéricos\n */\n validateId(id: number, fieldName: string): void {\n try {\n validateRequired(id, fieldName)\n validatePositiveNumber(id, fieldName)\n } catch (error) {\n throw new BaserowValidationError(`Invalid ${fieldName}: ${(error as Error).message}`, {\n [fieldName]: [(error as Error).message]\n })\n }\n }\n\n /**\n * Valida nombres de recursos\n */\n validateResourceName(name: string, resourceType: string): void {\n try {\n validateRequired(name, 'name')\n validateString(name, 'name')\n\n if (name.trim() !== name) {\n throw new Error('Name cannot start or end with whitespace')\n }\n\n if (name.length > 255) {\n throw new Error('Name cannot exceed 255 characters')\n }\n } catch (error) {\n throw new BaserowValidationError(`Invalid ${resourceType} name: ${(error as Error).message}`, {\n name: [(error as Error).message]\n })\n }\n }\n\n /**\n * Valida datos de tabla de creación\n */\n validateTableData(data: Array<Array<string>>): void {\n if (!Array.isArray(data)) {\n throw new BaserowValidationError('Table data must be an array', {\n data: ['Must be an array of arrays']\n })\n }\n\n if (data.length === 0) {\n throw new BaserowValidationError('Table data cannot be empty', {\n data: ['At least one row is required']\n })\n }\n\n const firstRowLength = data[0]?.length || 0\n if (firstRowLength === 0) {\n throw new BaserowValidationError('First row cannot be empty', {\n data: ['First row must have at least one column']\n })\n }\n\n // Verificar que todas las filas tengan la misma longitud\n for (let i = 0; i < data.length; i++) {\n if (!Array.isArray(data[i])) {\n throw new BaserowValidationError(`Row ${i} must be an array`, {\n data: [`Row ${i} is not an array`]\n })\n }\n\n if (data[i].length !== firstRowLength) {\n throw new BaserowValidationError(`All rows must have the same number of columns`, {\n data: [`Row ${i} has ${data[i].length} columns, expected ${firstRowLength}`]\n })\n }\n }\n }\n\n /**\n * Combina múltiples validaciones en una sola llamada\n */\n validateMultiple(validations: Array<() => void>): void {\n const errors: string[] = []\n\n for (const validation of validations) {\n try {\n validation()\n } catch (error) {\n if (error instanceof BaserowValidationError) {\n errors.push(error.message)\n } else {\n errors.push((error as Error).message || 'Unknown validation error')\n }\n }\n }\n\n if (errors.length > 0) {\n throw new BaserowValidationError(`Multiple validation errors: ${errors.join(', ')}`, {\n multiple: errors\n })\n }\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { ValidationService } from './core/ValidationService'\nimport {\n Table,\n CreateTableRequest,\n UpdateTableRequest,\n BaserowResponse,\n BaserowNotFoundError,\n Field,\n Logger,\n TableFromAllTables\n} from '../types/index'\n\n/**\n * Servicio para operaciones CRUD de tablas de Baserow\n *\n * Proporciona operaciones completas de tablas incluyendo CRUD básico,\n * creación asíncrona, duplicación, reordenamiento y análisis de esquemas.\n * Las tablas son contenedores de campos y filas en la jerarquía de Baserow.\n *\n * **Características:**\n * - CRUD completo: create, read, update, delete\n * - Creación síncrona y asíncrona (mejor para archivos grandes)\n * - Búsqueda de tablas por nombre\n * - Operaciones avanzadas: duplicar, reordenar\n * - Análisis de esquemas y estadísticas\n * - Validación de nombres y IDs\n *\n * **Jerarquía de Baserow:**\n * ```\n * Workspace → Database → Table → Field/Row\n * ```\n *\n * @example\n * ```typescript\n * // Operaciones básicas\n * const tables = await tableService.findMany(databaseId)\n * const table = await tableService.get(tableId)\n *\n * // Buscar tabla por nombre o ID\n * const found = await tableService.findUnique(databaseId, 'usuarios')\n *\n * // Crear tabla con datos iniciales\n * const newTable = await tableService.create(databaseId, {\n * name: 'Usuarios',\n * data: [['Nombre', 'Email'], ['Juan', 'juan@example.com']],\n * first_row_header: true\n * })\n *\n * // Operaciones avanzadas\n * const duplicated = await tableService.duplicate(tableId, 'Copia de Usuarios')\n * const stats = await tableService.getStats(tableId)\n * ```\n *\n * @since 1.0.0\n */\nexport class TableService extends HttpService {\n private validationService: ValidationService\n\n constructor(http: any, logger?: Logger) {\n super(http, logger)\n this.validationService = new ValidationService(http, logger)\n }\n\n // ===== PUBLIC API (Prisma-style) =====\n\n /**\n * Listar todas las tablas de una database\n *\n * Obtiene todas las tablas de una database específica con sus metadatos.\n * Incluye información de campos si está disponible en la respuesta.\n *\n * @param databaseId - ID numérico de la database\n * @returns Promise con array de tablas\n *\n * @throws {BaserowValidationError} Si el databaseId es inválido\n *\n * @example\n * ```typescript\n * const tables = await tableService.findMany(123)\n * tables.forEach(table => {\n * console.log(`${table.name} (ID: ${table.id})`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n async findMany(databaseId: number): Promise<Table[]> {\n this.validationService.validateId(databaseId, 'database ID')\n\n try {\n const response = await this.http.get<BaserowResponse<Table> | Table[]>(`/database/tables/database/${databaseId}/`)\n\n // La API puede devolver array directo o {results: []}\n const tables = Array.isArray(response) ? response : response.results || []\n this.logSuccess('find many tables', databaseId, { count: tables.length })\n return tables\n } catch (error) {\n this.handleHttpError(error, 'find many tables', databaseId)\n }\n }\n\n /**\n * Obtener tabla por ID\n *\n * Recupera una tabla específica por su ID con metadatos completos.\n * Útil para obtener información detallada de una tabla.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Promise con datos completos de la tabla\n *\n * @throws {BaserowNotFoundError} Si la tabla no existe\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * const table = await tableService.get(456)\n * console.log(`Tabla: ${table.name} - Database: ${table.database_id}`)\n * ```\n *\n * @since 1.0.0\n */\n async get(tableId: number): Promise<Table> {\n this.validationService.validateId(tableId, 'table ID')\n return this.getById<Table>('/database/tables', tableId)\n }\n\n /**\n * Buscar tabla por ID o nombre en una database\n *\n * Busca una tabla específica por su ID o nombre dentro de una database.\n * Útil para operaciones dinámicas donde se conoce el identificador pero no se sabe si es ID o nombre.\n *\n * @param databaseId - ID numérico de la database\n * @param identifier - ID numérico o nombre de la tabla a buscar\n * @returns Promise con la tabla encontrada o null si no existe\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * const table = await tableService.findUnique(123, 'usuarios')\n * if (table) {\n * console.log(`Tabla encontrada: ${table.id}`)\n * } else {\n * console.log('Tabla no encontrada')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async findUnique(databaseId: number, identifier: string | number): Promise<Table | null> {\n this.validationService.validateId(databaseId, 'database ID')\n\n if (typeof identifier === 'number') {\n // Buscar por ID\n try {\n return await this.get(identifier)\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return null\n }\n throw error\n }\n } else {\n // Buscar por nombre\n this.validationService.validateResourceName(identifier, 'table')\n const tables = await this.findMany(databaseId)\n const found = tables.find(table => table.name === identifier) || null\n\n if (found) {\n this.logSuccess(`find table by name \"${identifier}\"`, found.id)\n } else {\n this.logDebug(`No table found with name \"${identifier}\" in database ${databaseId}`)\n }\n\n return found\n }\n }\n\n /**\n * Crear nueva tabla - API pública\n *\n * Crea una nueva tabla en la database especificada. Soporta creación\n * con datos iniciales y configuración de esquema. Modo síncrono ideal\n * para tablas pequeñas y medianas.\n *\n * **⚠️ Comportamiento importante de Baserow:**\n * Cuando se crea una tabla SIN datos iniciales (`data` vacío o undefined),\n * Baserow automáticamente agrega campos y filas de ejemplo. Esta librería\n * **automáticamente limpia** este contenido usando `ensureTableEmpty()`\n * para garantizar tablas mínimas (solo campo primario, 0 filas).\n *\n * **Nota**: Baserow NO permite eliminar el campo primario, por lo que las\n * tablas \"vacías\" tendrán siempre 1 campo primario como mínimo.\n *\n * @param databaseId - ID numérico de la database\n * @param data - Configuración de la tabla\n * @param data.name - Nombre de la tabla\n * @param data.data - Datos iniciales (array 2D, opcional)\n * @param data.first_row_header - Primera fila como headers (default: false)\n * @param data.skipDefaultCleanup - Saltarse limpieza automática del contenido por defecto de Baserow (default: false)\n * @returns Promise con la tabla creada (garantizada mínima si no hay datos)\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * // Tabla mínima (auto-limpiada)\n * const emptyTable = await tableService.create(123, {\n * name: 'Nueva Tabla'\n * })\n * // Resultado: 1 campo primario, 0 filas (limpieza automática)\n *\n * // Tabla con contenido por defecto de Baserow (sin limpieza)\n * const defaultTable = await tableService.create(123, {\n * name: 'Tabla con Defaults',\n * skipDefaultCleanup: true\n * })\n * // Resultado: campos y filas de ejemplo creados por Baserow\n *\n * // Tabla con datos iniciales (sin limpieza)\n * const dataTable = await tableService.create(123, {\n * name: 'Usuarios',\n * data: [\n * ['Nombre', 'Email', 'Activo'],\n * ['Juan', 'juan@example.com', true],\n * ['María', 'maria@example.com', false]\n * ],\n * first_row_header: true\n * })\n * // Resultado: 3 campos, 2 filas (sin limpieza automática)\n * ```\n *\n * @since 1.0.0\n */\n async create(databaseId: number, data: CreateTableRequest): Promise<Table> {\n return this.createTableInternal(databaseId, data)\n }\n\n // ===== PRIVATE METHODS =====\n\n /**\n * Crear nueva tabla (método interno)\n * @private - Solo para uso interno del servicio\n */\n private async createTableInternal(databaseId: number, data: CreateTableRequest): Promise<Table> {\n this.validationService.validateId(databaseId, 'database ID')\n this.validationService.validateResourceName(data.name, 'table')\n\n try {\n this.logDebug(`Creating table \"${data.name}\" in database ${databaseId}`, data)\n const response = await this.http.post<Table>(`/database/tables/database/${databaseId}/`, data)\n\n // Limpiar contenido por defecto de Baserow si es necesario\n if (!data.data?.length && !data.skipDefaultCleanup) {\n const cleanupStats = await this.ensureTableEmpty((response as any).id, data.name)\n this.logDebug(`Table cleanup completed`, cleanupStats)\n } else if (data.skipDefaultCleanup) {\n this.logDebug(`Skipping default cleanup for table \"${data.name}\" as requested`)\n }\n\n this.logSuccess('create table', (response as any).id, { name: data.name })\n return response\n } catch (error) {\n this.handleHttpError(error, 'create table', databaseId)\n }\n }\n\n /**\n * Crear nueva tabla (modo asíncrono)\n *\n * Crea una nueva tabla de forma asíncrona, ideal para archivos grandes\n * o datasets extensos. Retorna un job_id para monitorear el progreso.\n *\n * @param databaseId - ID numérico de la database\n * @param data - Configuración de la tabla (igual que createTable)\n * @returns Promise con job_id para monitorear la creación\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * // Crear tabla asíncrona para archivo CSV grande\n * const job = await tableService.createAsync(123, {\n * name: 'Datos Masivos',\n * data: largeCsvData,\n * first_row_header: true\n * })\n *\n * console.log(`Job iniciado: ${job.job_id}`)\n * // Usar job_id para monitorear progreso\n * ```\n *\n * @since 1.0.0\n */\n async createAsync(databaseId: number, data: CreateTableRequest): Promise<{ job_id: string }> {\n this.validationService.validateId(databaseId, 'database ID')\n this.validationService.validateResourceName(data.name, 'table')\n\n try {\n this.logDebug(`Creating table async \"${data.name}\" in database ${databaseId}`, data)\n const response = await this.http.post<{ job_id: string }>(\n `/database/tables/database/${databaseId}/create-async/`,\n data\n )\n\n // NOTA: createAsync no puede usar ensureTableEmpty() porque retorna job_id, no table_id\n // El usuario debe verificar el estado del job y limpiar la tabla manualmente si es necesario\n\n this.logSuccess('create table async', `job_${response.job_id}`, { name: data.name })\n return response\n } catch (error) {\n this.handleHttpError(error, 'create table async', databaseId)\n }\n }\n\n /**\n * Actualizar tabla existente (método interno)\n * @private - Solo para uso por TableContext\n */\n private async updateTableInternal(tableId: number, data: UpdateTableRequest): Promise<Table> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (data.name !== undefined) {\n this.validationService.validateResourceName(data.name, 'table')\n }\n\n try {\n this.logDebug(`Updating table ${tableId}`, data)\n const response = await this.http.patch<Table>(`/database/tables/${tableId}/`, data)\n this.logSuccess('update table', tableId)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Table', tableId)\n }\n this.handleHttpError(error, 'update table', tableId)\n }\n }\n\n /**\n * Eliminar tabla (método interno)\n * @private - Solo para uso por TableContext\n */\n private async deleteTableInternal(tableId: number): Promise<void> {\n this.validationService.validateId(tableId, 'table ID')\n return this.deleteById('/database/tables', tableId)\n }\n\n /**\n * Verificar si una tabla existe\n *\n * Verifica la existencia de una tabla sin cargar todos sus metadatos.\n * Útil para validaciones antes de operaciones.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Promise que resuelve a true si existe, false si no\n *\n * @example\n * ```typescript\n * const exists = await tableService.exists(456)\n * if (exists) {\n * console.log('La tabla existe')\n * } else {\n * console.log('La tabla no existe')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async exists(tableId: number): Promise<boolean> {\n try {\n await this.get(tableId)\n return true\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return false\n }\n throw error\n }\n }\n\n /**\n * Obtener esquema completo de una tabla\n *\n * Recupera el esquema completo de una tabla incluyendo sus metadatos\n * y todos los campos con sus definiciones. Útil para análisis de estructura.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Promise con esquema completo (tabla + campos)\n *\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * const schema = await tableService.getSchema(456)\n * console.log(`Tabla: ${schema.table.name}`)\n * console.log(`Campos: ${schema.fields.length}`)\n * schema.fields.forEach(field => {\n * console.log(`- ${field.name} (${field.type})`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n async getSchema(tableId: number): Promise<{\n table: Table\n fields: Field[]\n }> {\n this.validationService.validateId(tableId, 'table ID')\n\n try {\n this.logDebug(`Fetching schema for table ${tableId}`)\n const [table, fieldsResponse] = await Promise.all([\n this.get(tableId),\n this.http.get<BaserowResponse<Field>>(`/database/fields/`, {\n params: { table_id: tableId }\n })\n ])\n\n this.logSuccess('get table schema', tableId, { fieldCount: fieldsResponse.results.length })\n return {\n table,\n fields: fieldsResponse.results\n }\n } catch (error) {\n this.handleHttpError(error, 'get table schema', tableId)\n }\n }\n\n /**\n * Duplicar tabla\n *\n * Crea una copia exacta de una tabla existente incluyendo estructura\n * y datos. Opcionalmente permite especificar un nuevo nombre.\n *\n * @param tableId - ID numérico de la tabla a duplicar\n * @param newName - Nombre para la tabla duplicada (opcional)\n * @returns Promise con la tabla duplicada\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * const duplicated = await tableService.duplicate(456, 'Copia de Usuarios')\n * console.log(`Tabla duplicada: ${duplicated.id}`)\n * ```\n *\n * @since 1.0.0\n */\n async duplicate(tableId: number, newName?: string): Promise<Table> {\n this.validationService.validateId(tableId, 'table ID')\n\n const originalTable = await this.get(tableId)\n const name = newName || `${originalTable.name} (copy)`\n\n if (newName) {\n this.validationService.validateResourceName(newName, 'table')\n }\n\n try {\n this.logDebug(`Duplicating table ${tableId} with name \"${name}\"`, { originalName: originalTable.name })\n const response = await this.http.post<Table>(`/database/tables/${tableId}/duplicate/`, { name })\n this.logSuccess('duplicate table', (response as any).id, { originalId: tableId, newName: name })\n return response\n } catch (error) {\n this.handleHttpError(error, 'duplicate table', tableId)\n }\n }\n\n /**\n * Reordenar tabla en la database\n *\n * Cambia la posición de una tabla en el orden de visualización\n * dentro de la database. Permite especificar posición relativa.\n *\n * @param tableId - ID numérico de la tabla a reordenar\n * @param beforeId - ID de tabla antes de la cual colocar (opcional)\n * @returns Promise con la tabla reordenada\n *\n * @throws {BaserowNotFoundError} Si alguna tabla no existe\n * @throws {BaserowValidationError} Si los IDs son inválidos\n *\n * @example\n * ```typescript\n * // Mover tabla al final\n * await tableService.reorder(456)\n *\n * // Mover tabla antes de otra específica\n * await tableService.reorder(456, 123)\n * ```\n *\n * @since 1.0.0\n */\n async reorder(tableId: number, beforeId?: number): Promise<Table> {\n this.validationService.validateId(tableId, 'table ID')\n\n const data: any = {}\n if (beforeId !== undefined) {\n this.validationService.validateId(beforeId, 'before table ID')\n data.before_id = beforeId\n }\n\n try {\n this.logDebug(`Reordering table ${tableId}`, data)\n const response = await this.http.patch<Table>(`/database/tables/${tableId}/move/`, data)\n this.logSuccess('reorder table', tableId)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Table', tableId)\n }\n this.handleHttpError(error, 'reorder table', tableId)\n }\n }\n\n /**\n * Obtener estadísticas de una tabla\n *\n * Recopila información estadística sobre una tabla incluyendo\n * número de campos y filas. Útil para dashboards y monitoreo.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Promise con estadísticas de la tabla\n *\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * const stats = await tableService.getStats(456)\n * console.log(`Campos: ${stats.fieldCount}`)\n * console.log(`Filas: ${stats.rowCount}`)\n * ```\n *\n * @since 1.0.0\n */\n async getStats(tableId: number): Promise<{\n fieldCount: number\n rowCount: number\n }> {\n this.validationService.validateId(tableId, 'table ID')\n\n try {\n this.logDebug(`Fetching stats for table ${tableId}`)\n const [schema, rowCountResponse] = await Promise.all([\n this.getSchema(tableId),\n this.http.get<BaserowResponse<any>>(`/database/rows/table/${tableId}/`, { params: { size: 1 } })\n ])\n\n const stats = {\n fieldCount: schema.fields.length,\n rowCount: rowCountResponse.count\n }\n\n this.logSuccess('get table stats', tableId, stats)\n return stats\n } catch (error) {\n this.handleHttpError(error, 'get table stats', tableId)\n }\n }\n\n /**\n * Asegurar que una tabla esté completamente vacía (sin campos ni filas)\n *\n * **Contexto**: Baserow automáticamente crea campos y filas de ejemplo cuando\n * se crea una tabla sin datos iniciales. Este método limpia ese contenido\n * para garantizar tablas mínimas (solo campo primario).\n *\n * **Proceso de limpieza**:\n * 1. Obtiene todos los campos de la tabla\n * 2. Elimina campos secundarios (preserva campo primario obligatorio)\n * 3. Verifica que no queden filas residuales\n * 4. Registra estadísticas de limpieza\n *\n * **Limitación de Baserow**: El campo primario NO se puede eliminar\n * (ERROR_CANNOT_DELETE_PRIMARY_FIELD), por lo que las tablas \"vacías\"\n * siempre tendrán al menos 1 campo primario.\n *\n * @param tableId - ID de la tabla a limpiar\n * @param tableName - Nombre de la tabla (para logging)\n * @returns Promise que resuelve cuando la limpieza está completa\n *\n * @private\n * @since 1.0.0\n */\n private async ensureTableEmpty(\n tableId: number,\n tableName: string\n ): Promise<{\n wasAlreadyEmpty: boolean\n removedFields: number\n finalRowCount: number\n cleanupErrors: number\n }> {\n const stats = {\n wasAlreadyEmpty: false,\n removedFields: 0,\n finalRowCount: 0,\n cleanupErrors: 0\n }\n\n try {\n this.logDebug(`[BASEROW CLEANUP] Ensuring table \"${tableName}\" (ID: ${tableId}) is empty`)\n\n // 1. Verificar y limpiar campos por defecto de Baserow\n const fieldsResponse = await this.http.get<BaserowResponse<Field> | Field[]>(`/database/fields/table/${tableId}/`)\n const fields = Array.isArray(fieldsResponse) ? fieldsResponse : fieldsResponse.results || []\n\n stats.removedFields = fields.length\n\n if (fields.length <= 1 && fields.every(f => f.primary)) {\n stats.wasAlreadyEmpty = true\n this.logDebug(`[BASEROW CLEANUP] Table \"${tableName}\" only has primary field (no extra default fields created)`)\n } else {\n this.logger?.info(\n `[BASEROW CLEANUP] Table \"${tableName}\" created with ${fields.length} default fields by Baserow - removing them`,\n {\n tableId,\n fieldNames: fields.map(f => f.name),\n fieldTypes: fields.map(f => f.type)\n }\n )\n\n // Eliminar todos los campos EXCEPTO el campo primario\n // Baserow no permite eliminar el campo primario (ERROR_CANNOT_DELETE_PRIMARY_FIELD)\n const nonPrimaryFields = fields.filter(field => !field.primary)\n const primaryFields = fields.filter(field => field.primary)\n\n this.logger?.info(\n `[BASEROW CLEANUP] Found ${fields.length} fields: ${primaryFields.length} primary, ${nonPrimaryFields.length} secondary`,\n {\n primaryFields: primaryFields.map(f => ({ name: f.name, id: f.id, type: f.type })),\n secondaryFields: nonPrimaryFields.map(f => ({ name: f.name, id: f.id, type: f.type }))\n }\n )\n\n // Solo eliminar campos no-primarios\n for (const field of nonPrimaryFields) {\n try {\n await this.http.delete(`/database/fields/${field.id}/`)\n this.logDebug(\n `[BASEROW CLEANUP] Removed secondary field \"${field.name}\" (type: ${field.type}, ID: ${field.id})`\n )\n } catch (deleteError) {\n stats.cleanupErrors++\n this.logger?.error(`[BASEROW CLEANUP] Failed to remove field \"${field.name}\" from table \"${tableName}\":`, {\n fieldId: field.id,\n fieldType: field.type,\n error: deleteError\n })\n }\n }\n\n // Actualizar stats para reflejar solo campos eliminables\n stats.removedFields = nonPrimaryFields.length\n }\n\n // 2. Limpiar filas por defecto que crea Baserow\n const rowsResponse = await this.http.get<BaserowResponse<any>>(`/database/rows/table/${tableId}/`, {\n params: { size: 100 } // Obtener hasta 100 filas para eliminar\n })\n\n if (rowsResponse.count > 0) {\n this.logger?.info(\n `[BASEROW CLEANUP] Table \"${tableName}\" has ${rowsResponse.count} default rows - removing them`,\n {\n tableId,\n rowIds: rowsResponse.results?.slice(0, 5).map((r: any) => r.id) || []\n }\n )\n\n // Eliminar todas las filas por defecto\n const allRows = rowsResponse.results || []\n for (const row of allRows) {\n try {\n await this.http.delete(`/database/rows/table/${tableId}/${row.id}/`)\n this.logDebug(`[BASEROW CLEANUP] Removed default row ID ${row.id} from table \"${tableName}\"`)\n } catch (deleteError) {\n stats.cleanupErrors++\n this.logger?.error(`[BASEROW CLEANUP] Failed to remove row ${row.id} from table \"${tableName}\":`, {\n rowId: row.id,\n error: deleteError\n })\n }\n }\n\n // Verificar estado final después de limpiar filas\n const finalRowsResponse = await this.http.get<BaserowResponse<any>>(`/database/rows/table/${tableId}/`, {\n params: { size: 1 }\n })\n stats.finalRowCount = finalRowsResponse.count\n } else {\n stats.finalRowCount = 0\n }\n\n // 3. Log resultado final\n const resultLevel = stats.cleanupErrors > 0 ? 'warn' : 'info'\n const message = stats.wasAlreadyEmpty\n ? `[BASEROW CLEANUP] Table \"${tableName}\" only had primary field`\n : `[BASEROW CLEANUP] Successfully cleaned table \"${tableName}\" (primary field preserved)`\n\n this.logger?.[resultLevel]?.(message, {\n tableId,\n tableName,\n wasAlreadyEmpty: stats.wasAlreadyEmpty,\n removedFields: stats.removedFields,\n finalRowCount: stats.finalRowCount,\n cleanupErrors: stats.cleanupErrors,\n summary: `Removed ${stats.removedFields} fields, ${stats.finalRowCount} rows remaining, ${stats.cleanupErrors} errors`\n })\n\n this.logSuccess('ensured table empty', tableId, stats)\n return stats\n } catch (error) {\n // Log error pero no fallar la creación de la tabla\n this.logger?.error(`[BASEROW CLEANUP] Failed to verify/clean table \"${tableName}\":`, {\n tableId,\n tableName,\n error: error,\n note: 'Table creation succeeded but cleanup failed'\n })\n\n return {\n ...stats,\n cleanupErrors: stats.cleanupErrors + 1\n }\n }\n }\n\n /**\n * Listar todas las tablas accesibles con Database Token\n *\n * Endpoint específico para Database Tokens que devuelve todas las tablas\n * a las que el token tiene acceso, sin necesidad de especificar database.\n *\n * @returns Promise con array de tablas con información básica\n * @throws {BaserowError} Si hay error de red o el token no es válido\n *\n * @example\n * ```typescript\n * const tables = await tableService.findAllTablesWithToken()\n * console.log(`Token tiene acceso a ${tables.length} tablas`)\n * tables.forEach(table => {\n * console.log(`Tabla: ${table.name} (DB: ${table.database_id})`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n async findAllTablesWithToken(): Promise<TableFromAllTables[]> {\n try {\n this.logDebug('Fetching all tables accessible with database token')\n const response = await this.http.get<TableFromAllTables[]>('/database/tables/all-tables/')\n\n // La respuesta es directamente un array de tablas\n const tables = Array.isArray(response) ? response : []\n\n this.logSuccess('fetch all tables with token', undefined, { count: tables.length })\n return tables\n } catch (error) {\n this.handleHttpError(error, 'fetch all tables with token')\n }\n }\n\n // ===== FRIEND ACCESS PATTERN FOR TABLE CONTEXT =====\n // Symbol-based access que no aparece en la API pública pero permite acceso interno\n\n /**\n * Friend access para TableContext\n * No aparece en intellisense normal ni en la API pública\n * @internal\n */\n get [Symbol.for('tableContext')]() {\n return {\n createTable: this.createTableInternal.bind(this),\n updateTable: this.updateTableInternal.bind(this),\n deleteTable: this.deleteTableInternal.bind(this)\n }\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { ValidationService } from './core/ValidationService'\nimport {\n Field,\n CreateFieldRequest,\n UpdateFieldRequest,\n SelectOption,\n NumberSeparator,\n RatingStyle,\n RatingColor,\n FieldConstraint,\n FieldAdvancedOptions,\n ConstraintType,\n BaserowResponse,\n BaserowNotFoundError,\n BaserowValidationError,\n Logger\n} from '../types/index'\nimport { sanitizeFieldName, validateFieldType } from '../utils/validation'\n\n/**\n * Servicio para operaciones CRUD de campos de Baserow\n *\n * Proporciona operaciones completas de campos incluyendo CRUD básico,\n * métodos de creación fluidos para todos los tipos de campo soportados,\n * y operaciones de gestión avanzadas como duplicación y reordenamiento.\n *\n * **Cobertura de Tipos de Campo (21/22 - 95%):**\n * - **Texto (5)**: text, long_text, url, email, phone_number\n * - **Numéricos (3)**: number, boolean, rating\n * - **Fecha/Auditoría (5)**: date, last_modified, last_modified_by, created_on, created_by\n * - **Selección (2)**: single_select, multiple_select\n * - **Relacionales (2)**: link_row, formula\n * - **Avanzados (5)**: file, autonumber, count, rollup, lookup\n *\n * **Características:**\n * - API fluida para cada tipo de campo con parámetros específicos\n * - Validación automática de tipos y configuraciones\n * - Sanitización de nombres de campo\n * - Operaciones avanzadas: duplicar, reordenar\n * - Búsqueda de campos por nombre\n * - **Soporte v1.35+**: Índices para performance y constraints para integridad\n *\n * @example\n * ```typescript\n * // Operaciones básicas\n * const fields = await fieldService.findMany(tableId)\n * const field = await fieldService.get(fieldId)\n * const found = await fieldService.findUnique(tableId, 'email')\n *\n * // API fluida por tipo de campo (MANTENER - Sin cambios)\n * const textField = await fieldService.createTextField(tableId, 'nombre', 'default')\n * const numberField = await fieldService.createNumberField(tableId, 'precio', 2, true)\n * const selectField = await fieldService.createSelectField(tableId, 'estado', [\n * { value: 'active', color: 'blue' },\n * { value: 'inactive', color: 'red' }\n * ])\n *\n * // Campos avanzados (MANTENER - Sin cambios)\n * const linkField = await fieldService.createLinkField(tableId, 'relacionado', targetTableId)\n * const formulaField = await fieldService.createFormulaField(tableId, 'total', 'field(\"precio\") * field(\"cantidad\")')\n *\n * // Campos con índices y constraints (v1.35+)\n * const emailField = await fieldService.createEmailField(tableId, 'email', undefined, {\n * index: true,\n * constraints: [{ type: 'unique_with_empty', active: true }]\n * })\n *\n * // Gestión de índices y constraints\n * await fieldService.setFieldIndex(fieldId, true)\n * await fieldService.addFieldConstraint(fieldId, { type: 'unique', active: true })\n * ```\n *\n * @since 1.0.0\n */\nexport class FieldService extends HttpService {\n private validationService: ValidationService\n\n constructor(http: any, logger?: Logger) {\n super(http, logger)\n this.validationService = new ValidationService(http, logger)\n }\n\n // ===== PUBLIC API (Prisma-style) =====\n\n /**\n * Listar todos los campos de una tabla\n *\n * Obtiene todos los campos de una tabla específica con sus definiciones\n * completas incluyendo tipo, configuración y metadatos.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Promise con array de campos de la tabla\n *\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * const fields = await fieldService.findMany(123)\n * fields.forEach(field => {\n * console.log(`${field.name} (${field.type}): ${field.id}`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n async findMany(tableId: number): Promise<Field[]> {\n this.validationService.validateId(tableId, 'table ID')\n\n try {\n const response = await this.http.get<BaserowResponse<Field> | Field[]>(`/database/fields/table/${tableId}/`)\n\n // La API puede devolver array directo o {results: []}\n const fields = Array.isArray(response) ? response : response.results || []\n this.logSuccess('find many fields', tableId, { count: fields.length })\n return fields\n } catch (error) {\n this.handleHttpError(error, 'find many fields', tableId)\n }\n }\n\n /**\n * Obtener campo por ID\n *\n * Recupera un campo específico por su ID con toda su configuración\n * y metadatos. Útil para inspeccionar configuraciones detalladas.\n *\n * @param fieldId - ID numérico del campo\n * @returns Promise con datos completos del campo\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si el fieldId es inválido\n *\n * @example\n * ```typescript\n * const field = await fieldService.get(456)\n * console.log(`Campo: ${field.name} - Tipo: ${field.type}`)\n * ```\n *\n * @since 1.0.0\n */\n async get(fieldId: number): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n return this.getById<Field>('/database/fields', fieldId)\n }\n\n /**\n * Buscar campo por ID o nombre en una tabla\n *\n * Busca un campo específico por su ID o nombre dentro de una tabla.\n * Útil para operaciones dinámicas donde se conoce el identificador pero no se sabe si es ID o nombre.\n *\n * @param tableId - ID numérico de la tabla\n * @param identifier - ID numérico o nombre del campo a buscar\n * @returns Promise con el campo encontrado o null si no existe\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * const field = await fieldService.findUnique(123, 'email')\n * if (field) {\n * console.log(`Campo encontrado: ${field.id}`)\n * } else {\n * console.log('Campo no encontrado')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async findUnique(tableId: number, identifier: string | number): Promise<Field | null> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (typeof identifier === 'number') {\n // Buscar por ID\n try {\n return await this.get(identifier)\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return null\n }\n throw error\n }\n } else {\n // Buscar por nombre\n this.validationService.validateResourceName(identifier, 'field')\n const fields = await this.findMany(tableId)\n const found = fields.find(field => field.name === identifier) || null\n\n if (found) {\n this.logSuccess(`find field by name \"${identifier}\"`, found.id)\n } else {\n this.logDebug(`No field found with name \"${identifier}\" in table ${tableId}`)\n }\n\n return found\n }\n }\n\n /**\n * Crear nuevo campo - API pública\n *\n * Crea un nuevo campo en la tabla especificada. Este es el método base\n * que usan todos los métodos de creación específicos por tipo.\n *\n * @param tableId - ID numérico de la tabla\n * @param data - Configuración completa del campo\n * @returns Promise con el campo creado\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const field = await fieldService.create(123, {\n * name: 'Mi Campo',\n * type: 'text',\n * text_default: 'Valor por defecto',\n * description: 'Descripción del campo'\n * })\n * ```\n *\n * @since 1.0.0\n */\n async create(tableId: number, data: CreateFieldRequest): Promise<Field> {\n return this.createFieldInternal(tableId, data)\n }\n\n // ===== PRIVATE METHODS =====\n\n /**\n * Crear nuevo campo (método interno)\n * @private - Solo para uso interno del servicio\n */\n private async createFieldInternal(tableId: number, data: CreateFieldRequest): Promise<Field> {\n this.validationService.validateId(tableId, 'table ID')\n data.name = sanitizeFieldName(data.name)\n this.validationService.validateResourceName(data.name, 'field')\n validateFieldType(data.type, 'type')\n\n const payload = {\n table_id: tableId,\n ...data\n }\n\n try {\n this.logDebug(`Creating field \"${data.name}\" of type ${data.type} in table ${tableId}`, payload)\n const response = await this.http.post<Field>(`/database/fields/table/${tableId}/`, payload)\n this.logSuccess('create field', response.id, { name: data.name, type: data.type })\n return response\n } catch (error) {\n this.handleHttpError(error, 'create field', tableId)\n }\n }\n\n /**\n * Actualizar campo (método interno)\n * @private - Solo para uso por FieldContext\n */\n private async updateFieldInternal(fieldId: number, data: UpdateFieldRequest): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n if (data.name !== undefined) {\n data.name = sanitizeFieldName(data.name)\n this.validationService.validateResourceName(data.name, 'field')\n }\n\n try {\n this.logDebug(`Updating field ${fieldId}`, data)\n const response = await this.http.patch<Field>(`/database/fields/${fieldId}/`, data)\n this.logSuccess('update field', fieldId)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Field', fieldId)\n }\n this.handleHttpError(error, 'update field', fieldId)\n }\n }\n\n /**\n * Eliminar campo (método interno)\n * @private - Solo para uso por FieldContext\n */\n private async deleteFieldInternal(fieldId: number): Promise<void> {\n this.validationService.validateId(fieldId, 'field ID')\n return this.deleteById('/database/fields', fieldId)\n }\n\n /**\n * Duplicar campo\n *\n * Crea una copia exacta de un campo existente con un nuevo nombre.\n * Mantiene toda la configuración del campo original.\n *\n * @param fieldId - ID numérico del campo a duplicar\n * @param newName - Nombre para el campo duplicado (opcional)\n * @returns Promise con el campo duplicado\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * const duplicated = await fieldService.duplicate(456, 'Copia del campo')\n * console.log(`Campo duplicado: ${duplicated.id}`)\n * ```\n *\n * @since 1.0.0\n */\n async duplicate(fieldId: number, newName?: string): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n const originalField = await this.get(fieldId)\n const name = newName ? sanitizeFieldName(newName) : `${originalField.name} (copy)`\n\n if (newName) {\n this.validationService.validateResourceName(name, 'field')\n }\n\n try {\n this.logDebug(`Duplicating field ${fieldId} with name \"${name}\"`, { originalName: originalField.name })\n const response = await this.http.post<Field>(`/database/fields/${fieldId}/duplicate/`, { name })\n this.logSuccess('duplicate field', response.id, { originalId: fieldId, newName: name })\n return response\n } catch (error) {\n this.handleHttpError(error, 'duplicate field', fieldId)\n }\n }\n\n /**\n * Reordenar campos de una tabla\n *\n * Cambia el orden de visualización de los campos en una tabla.\n * El orden se define por la secuencia de IDs proporcionada.\n *\n * @param tableId - ID numérico de la tabla\n * @param fieldIds - Array de IDs de campos en el nuevo orden deseado\n * @returns Promise que resuelve cuando el reordenamiento se completa\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * // Reordenar campos: primero ID 3, luego 1, luego 2\n * await fieldService.reorder(123, [3, 1, 2])\n * console.log('Campos reordenados exitosamente')\n * ```\n *\n * @since 1.0.0\n */\n async reorder(tableId: number, fieldIds: number[]): Promise<void> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (!Array.isArray(fieldIds) || fieldIds.length === 0) {\n throw new Error('fieldIds must be a non-empty array')\n }\n\n fieldIds.forEach(id => this.validationService.validateId(id, 'field ID'))\n\n try {\n this.logDebug(`Reordering ${fieldIds.length} fields in table ${tableId}`, { fieldIds })\n await this.http.patch(`/database/tables/${tableId}/order-fields/`, {\n field_ids: fieldIds\n })\n this.logSuccess('reorder fields', tableId, { fieldCount: fieldIds.length })\n } catch (error) {\n this.handleHttpError(error, 'reorder fields', tableId)\n }\n }\n\n // ===== HELPERS PARA TIPOS ESPECÍFICOS =====\n\n /**\n * Crear campo de texto\n *\n * Crea un campo de texto simple con valor por defecto opcional.\n * El tipo de campo más básico para almacenar cadenas de texto cortas.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param defaultValue - Valor por defecto (opcional)\n * @param description - Descripción del campo (opcional)\n * @param advancedOptions - Opciones avanzadas: índice y constraints (v1.35+)\n * @returns Promise con el campo de texto creado\n *\n * @example\n * ```typescript\n * const nameField = await fieldService.createTextField(123, 'nombre', 'Sin nombre')\n * const titleField = await fieldService.createTextField(123, 'titulo')\n *\n * // Con índice y constraint (v1.35+)\n * const codeField = await fieldService.createTextField(123, 'codigo', undefined, undefined, {\n * index: true,\n * constraints: [{ type: 'unique_with_empty', active: true }]\n * })\n * ```\n *\n * @since 1.0.0\n */\n async createTextField(\n tableId: number,\n name: string,\n defaultValue?: string,\n description?: string,\n advancedOptions?: FieldAdvancedOptions\n ): Promise<Field> {\n const baseRequest = {\n name,\n type: 'text' as const,\n text_default: defaultValue || '',\n description\n }\n\n return this.create(tableId, this.applyAdvancedOptions(baseRequest, advancedOptions))\n }\n\n /**\n * Crear campo de texto largo\n *\n * Crea un campo de texto largo (multilinea) para almacenar texto extenso.\n * Ideal para descripciones, comentarios o contenido largo.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param defaultValue - Valor por defecto (opcional)\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de texto largo creado\n *\n * @example\n * ```typescript\n * const descField = await fieldService.createLongTextField(123, 'descripcion')\n * const notesField = await fieldService.createLongTextField(123, 'notas', 'Sin notas')\n * ```\n *\n * @since 1.0.0\n */\n async createLongTextField(\n tableId: number,\n name: string,\n defaultValue?: string,\n description?: string\n ): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'long_text',\n text_default: defaultValue || '',\n description\n })\n }\n\n /**\n * Crear campo numérico\n *\n * Crea un campo numérico con configuración completa de formato y validación.\n * Soporta decimales, prefijos/sufijos, separadores y valores por defecto.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param decimalPlaces - Número de decimales (0-10, default: 0)\n * @param allowNegative - Permitir números negativos (default: true)\n * @param defaultValue - Valor numérico por defecto (opcional)\n * @param prefix - Prefijo para mostrar (ej: '$', default: undefined)\n * @param suffix - Sufijo para mostrar (ej: '%', default: undefined)\n * @param separator - Separador de miles (default: 'COMMA_PERIOD')\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo numérico creado\n *\n * @throws {BaserowValidationError} Si decimalPlaces no está entre 0-10\n *\n * @example\n * ```typescript\n * // Campo de precio con 2 decimales y símbolo $\n * const priceField = await fieldService.createNumberField(123, 'precio', 2, true, 0, '$')\n *\n * // Campo de porcentaje\n * const percentField = await fieldService.createNumberField(123, 'descuento', 1, false, 0, undefined, '%')\n *\n * // Campo entero simple\n * const ageField = await fieldService.createNumberField(123, 'edad', 0)\n * ```\n *\n * @since 1.0.0\n */\n async createNumberField(\n tableId: number,\n name: string,\n decimalPlaces = 0,\n allowNegative = true,\n defaultValue?: number,\n prefix?: string,\n suffix?: string,\n separator: NumberSeparator = 'COMMA_PERIOD',\n description?: string\n ): Promise<Field> {\n // Validar number_decimal_places (debe estar entre 0-10)\n if (!Number.isInteger(decimalPlaces) || decimalPlaces < 0 || decimalPlaces > 10) {\n throw new BaserowValidationError(`Decimal places must be an integer between 0 and 10, got: ${decimalPlaces}`, {\n number_decimal_places: [`Value must be between 0 and 10, got: ${decimalPlaces}`]\n })\n }\n\n return this.create(tableId, {\n name,\n type: 'number',\n number_decimal_places: decimalPlaces,\n number_negative: allowNegative,\n number_default: defaultValue !== undefined ? defaultValue.toFixed(decimalPlaces) : undefined,\n number_prefix: prefix,\n number_suffix: suffix,\n number_separator: separator,\n description\n })\n }\n\n /**\n * Crear campo booleano\n *\n * Crea un campo booleano (checkbox) para valores verdadero/falso.\n * Ideal para estados, flags o configuraciones binarias.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param defaultValue - Valor por defecto (default: false)\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo booleano creado\n *\n * @example\n * ```typescript\n * const activeField = await fieldService.createBooleanField(123, 'activo', true)\n * const verifiedField = await fieldService.createBooleanField(123, 'verificado')\n * ```\n *\n * @since 1.0.0\n */\n async createBooleanField(tableId: number, name: string, defaultValue = false, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'boolean',\n boolean_default: defaultValue,\n description\n })\n }\n\n /**\n * Crear campo de selección simple\n *\n * Crea un campo de selección simple donde solo se puede elegir una opción.\n * Las opciones incluyen valor y color para visualización.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param options - Array de opciones con value y color\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de selección simple creado\n *\n * @throws {Error} Si no se proporcionan opciones\n *\n * @example\n * ```typescript\n * const statusField = await fieldService.createSelectField(123, 'estado', [\n * { value: 'pendiente', color: 'yellow' },\n * { value: 'completado', color: 'green' },\n * { value: 'cancelado', color: 'red' }\n * ])\n * ```\n *\n * @since 1.0.0\n */\n async createSelectField(\n tableId: number,\n name: string,\n options: SelectOption[],\n description?: string\n ): Promise<Field> {\n if (!options || !Array.isArray(options) || options.length === 0) {\n throw new Error('Select field must have at least one option')\n }\n\n if (!Array.isArray(options) || options.length === 0) {\n throw new Error('Select field must have at least one option')\n }\n\n return this.create(tableId, {\n name,\n type: 'single_select',\n select_options: options,\n description\n })\n }\n\n /**\n * Crear campo de selección múltiple\n *\n * Crea un campo de selección múltiple donde se pueden elegir varias opciones.\n * Útil para tags, categorías o cualquier clasificación múltiple.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param options - Array de opciones con value y color\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de selección múltiple creado\n *\n * @throws {Error} Si no se proporcionan opciones\n *\n * @example\n * ```typescript\n * const tagsField = await fieldService.createMultiSelectField(123, 'tags', [\n * { value: 'importante', color: 'red' },\n * { value: 'urgente', color: 'orange' },\n * { value: 'revision', color: 'blue' }\n * ])\n * ```\n *\n * @since 1.0.0\n */\n async createMultiSelectField(\n tableId: number,\n name: string,\n options: SelectOption[],\n description?: string\n ): Promise<Field> {\n if (!options || !Array.isArray(options) || options.length === 0) {\n throw new Error('Multi-select field must have at least one option')\n }\n\n if (!Array.isArray(options) || options.length === 0) {\n throw new Error('Multi-select field must have at least one option')\n }\n\n return this.create(tableId, {\n name,\n type: 'multiple_select',\n select_options: options,\n description\n })\n }\n\n /**\n * Crear campo de fecha\n *\n * Crea un campo de fecha con configuración completa de formato y zona horaria.\n * Soporta fechas simples o fechas con hora, y configuración de timezone.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param includeTime - Incluir hora además de fecha (default: false)\n * @param dateFormat - Formato de fecha (default: 'ISO')\n * @param timeFormat - Formato de hora (default: '24')\n * @param showTzInfo - Mostrar información de timezone (default: false)\n * @param forceTimezone - Timezone forzado (opcional)\n * @param forceTimezoneOffset - Offset de timezone forzado (opcional)\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de fecha creado\n *\n * @example\n * ```typescript\n * // Fecha simple\n * const birthField = await fieldService.createDateField(123, 'nacimiento')\n *\n * // Fecha con hora\n * const createdField = await fieldService.createDateField(123, 'creado', true, 'ISO', '24')\n * ```\n *\n * @since 1.0.0\n */\n async createDateField(\n tableId: number,\n name: string,\n includeTime = false,\n dateFormat = 'ISO',\n timeFormat = '24',\n showTzInfo = false,\n forceTimezone?: string,\n forceTimezoneOffset?: number,\n description?: string\n ): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'date',\n date_include_time: includeTime,\n date_format: dateFormat,\n date_time_format: timeFormat,\n date_show_tzinfo: showTzInfo,\n date_force_timezone: forceTimezone,\n date_force_timezone_offset: forceTimezoneOffset,\n description\n })\n }\n\n /**\n * Crear campo de enlace a otra tabla\n *\n * Crea un campo de relación que enlaza con otra tabla (foreign key).\n * Permite crear relaciones entre tablas para modelar datos relacionales.\n *\n * @param tableId - ID numérico de la tabla origen\n * @param name - Nombre del campo\n * @param targetTableId - ID numérico de la tabla destino\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de enlace creado\n *\n * @throws {BaserowValidationError} Si targetTableId es inválido\n *\n * @example\n * ```typescript\n * // Relacionar tabla de pedidos con tabla de clientes\n * const customerField = await fieldService.createLinkField(ordersTableId, 'cliente', customersTableId)\n * ```\n *\n * @since 1.0.0\n */\n async createLinkField(tableId: number, name: string, targetTableId: number, description?: string): Promise<Field> {\n this.validationService.validateId(targetTableId, 'target table ID')\n\n return this.create(tableId, {\n name,\n type: 'link_row',\n link_row_table_id: targetTableId,\n description\n })\n }\n\n /**\n * Crear campo de fórmula\n *\n * Crea un campo calculado que usa una fórmula para derivar su valor\n * de otros campos de la misma fila. El valor se actualiza automáticamente.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param formula - Fórmula de cálculo (sintaxis de Baserow)\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de fórmula creado\n *\n * @throws {Error} Si la fórmula está vacía\n *\n * @example\n * ```typescript\n * // Campo que calcula el total como precio * cantidad\n * const totalField = await fieldService.createFormulaField(\n * 123,\n * 'total',\n * 'field(\"precio\") * field(\"cantidad\")'\n * )\n *\n * // Campo que concatena nombre y apellido\n * const fullNameField = await fieldService.createFormulaField(\n * 123,\n * 'nombre_completo',\n * 'concat(field(\"nombre\"), \" \", field(\"apellido\"))'\n * )\n * ```\n *\n * @since 1.0.0\n */\n async createFormulaField(tableId: number, name: string, formula: string, description?: string): Promise<Field> {\n if (!formula || typeof formula !== 'string' || formula.trim() === '') {\n throw new Error('Formula is required and must be a non-empty string')\n }\n\n return this.create(tableId, {\n name,\n type: 'formula',\n formula,\n description\n })\n }\n\n /**\n * Crear campo de URL\n *\n * Crea un campo especializado para almacenar URLs con validación automática.\n * Los valores se validan como URLs válidas y se muestran como enlaces.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de URL creado\n *\n * @example\n * ```typescript\n * const websiteField = await fieldService.createUrlField(123, 'sitio_web')\n * const linkedinField = await fieldService.createUrlField(123, 'perfil_linkedin')\n * ```\n *\n * @since 1.0.0\n */\n async createUrlField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'url',\n description\n })\n }\n\n /**\n * Crear campo de email\n *\n * Crea un campo especializado para almacenar direcciones de email\n * con validación automática de formato.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @param advancedOptions - Opciones avanzadas: índice y constraints (v1.35+)\n * @returns Promise con el campo de email creado\n *\n * @example\n * ```typescript\n * const emailField = await fieldService.createEmailField(123, 'email')\n * const contactField = await fieldService.createEmailField(123, 'email_contacto')\n *\n * // Con índice y restricción de unicidad (v1.35+)\n * const uniqueEmailField = await fieldService.createEmailField(123, 'email', undefined, {\n * index: true,\n * constraints: [{ type: 'unique', active: true }]\n * })\n * ```\n *\n * @since 1.0.0\n */\n async createEmailField(\n tableId: number,\n name: string,\n description?: string,\n advancedOptions?: FieldAdvancedOptions\n ): Promise<Field> {\n const baseRequest = {\n name,\n type: 'email' as const,\n description\n }\n\n return this.create(tableId, this.applyAdvancedOptions(baseRequest, advancedOptions))\n }\n\n /**\n * Crear campo de teléfono\n *\n * Crea un campo especializado para almacenar números de teléfono\n * con formateo automático y validación.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de teléfono creado\n *\n * @example\n * ```typescript\n * const phoneField = await fieldService.createPhoneField(123, 'telefono')\n * const mobileField = await fieldService.createPhoneField(123, 'movil')\n * ```\n *\n * @since 1.0.0\n */\n async createPhoneField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'phone_number',\n description\n })\n }\n\n /**\n * Crear campo de rating\n *\n * Crea un campo de calificación visual con estrellas, corazones u otros íconos.\n * Ideal para ratings, puntuaciones o evaluaciones.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param maxValue - Valor máximo (1-10, default: 5)\n * @param color - Color del rating (default: 'yellow')\n * @param style - Estilo del ícono (default: 'star')\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de rating creado\n *\n * @throws {Error} Si maxValue no está entre 1-10\n *\n * @example\n * ```typescript\n * // Rating de 5 estrellas amarillas\n * const ratingField = await fieldService.createRatingField(123, 'calificacion')\n *\n * // Rating personalizado de 10 corazones rojos\n * const loveField = await fieldService.createRatingField(123, 'amor', 10, 'red', 'heart')\n * ```\n *\n * @since 1.0.0\n */\n async createRatingField(\n tableId: number,\n name: string,\n maxValue: number = 5,\n color: RatingColor = 'yellow',\n style: RatingStyle = 'star',\n description?: string\n ): Promise<Field> {\n this.validationService.validateId(tableId, 'table ID')\n this.validationService.validateResourceName(name, 'field')\n\n if (maxValue < 1 || maxValue > 10) {\n throw new Error('maxValue must be between 1 and 10')\n }\n\n // Validación removida - ahora usamos RatingColor enum que tiene valores válidos\n\n return this.create(tableId, {\n name,\n type: 'rating',\n max_value: maxValue,\n color: color,\n style: style,\n description\n })\n }\n\n /**\n * Crear campo de última modificación (fecha)\n *\n * Crea un campo de auditoría que se actualiza automáticamente\n * con la fecha de la última modificación de la fila.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de última modificación creado\n *\n * @example\n * ```typescript\n * const updatedField = await fieldService.createLastModifiedField(123, 'actualizado')\n * ```\n *\n * @since 1.0.0\n */\n async createLastModifiedField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'last_modified',\n description\n })\n }\n\n /**\n * Crear campo de última modificación por usuario\n *\n * Crea un campo de auditoría que se actualiza automáticamente\n * con el usuario que realizó la última modificación de la fila.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de última modificación por usuario creado\n *\n * @example\n * ```typescript\n * const modifiedByField = await fieldService.createLastModifiedByField(123, 'modificado_por')\n * ```\n *\n * @since 1.0.0\n */\n async createLastModifiedByField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'last_modified_by',\n description\n })\n }\n\n /**\n * Crear campo de fecha de creación\n *\n * Crea un campo de auditoría que se establece automáticamente\n * con la fecha de creación de la fila (no se modifica después).\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de fecha de creación creado\n *\n * @example\n * ```typescript\n * const createdField = await fieldService.createCreatedOnField(123, 'creado')\n * ```\n *\n * @since 1.0.0\n */\n async createCreatedOnField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'created_on',\n description\n })\n }\n\n /**\n * Crear campo de usuario creador\n *\n * Crea un campo de auditoría que se establece automáticamente\n * con el usuario que creó la fila (no se modifica después).\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de usuario creador creado\n *\n * @example\n * ```typescript\n * const createdByField = await fieldService.createCreatedByField(123, 'creado_por')\n * ```\n *\n * @since 1.0.0\n */\n async createCreatedByField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'created_by',\n description\n })\n }\n\n /**\n * Crear campo de archivo\n *\n * Crea un campo para subir y almacenar archivos adjuntos.\n * Soporta múltiples archivos por fila con previsualización automática.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de archivo creado\n *\n * @example\n * ```typescript\n * const attachmentField = await fieldService.createFileField(123, 'documentos')\n * const photoField = await fieldService.createFileField(123, 'fotos')\n * ```\n *\n * @since 1.0.0\n */\n async createFileField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'file',\n description\n })\n }\n\n /**\n * Crear campo de numeración automática\n *\n * Crea un campo que asigna automáticamente números secuenciales únicos\n * a cada nueva fila. Útil para IDs de tickets, números de orden, etc.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de numeración automática creado\n *\n * @example\n * ```typescript\n * const ticketField = await fieldService.createAutonumberField(123, 'ticket_id')\n * const orderField = await fieldService.createAutonumberField(123, 'numero_orden')\n * ```\n *\n * @since 1.0.0\n */\n async createAutonumberField(tableId: number, name: string, description?: string): Promise<Field> {\n return this.create(tableId, {\n name,\n type: 'autonumber',\n description\n })\n }\n\n /**\n * Crear campo de conteo\n *\n * Crea un campo que cuenta automáticamente el número de registros\n * relacionados a través de un campo de enlace. Se actualiza automáticamente.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param throughFieldId - ID del campo de enlace a través del cual contar\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de conteo creado\n *\n * @throws {BaserowValidationError} Si throughFieldId es inválido\n *\n * @example\n * ```typescript\n * // Contar número de pedidos por cliente\n * const orderCountField = await fieldService.createCountField(\n * customersTableId,\n * 'total_pedidos',\n * customerLinkFieldId\n * )\n * ```\n *\n * @since 1.0.0\n */\n async createCountField(tableId: number, name: string, throughFieldId: number, description?: string): Promise<Field> {\n this.validationService.validateId(throughFieldId, 'through field ID')\n\n return this.create(tableId, {\n name,\n type: 'count',\n through_field_id: throughFieldId,\n description\n })\n }\n\n /**\n * Crear campo de rollup (agregación)\n *\n * Crea un campo que agrega valores de registros relacionados usando\n * funciones como sum, avg, min, max, etc. Se actualiza automáticamente.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param throughFieldId - ID del campo de enlace para la relación\n * @param targetFieldId - ID del campo a agregar en la tabla relacionada\n * @param rollupFunction - Función de agregación (default: 'sum')\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de rollup creado\n *\n * @throws {BaserowValidationError} Si los IDs son inválidos\n *\n * @example\n * ```typescript\n * // Sumar total de ventas por cliente\n * const totalSalesField = await fieldService.createRollupField(\n * customersTableId,\n * 'ventas_totales',\n * customerLinkFieldId,\n * orderAmountFieldId,\n * 'sum'\n * )\n *\n * // Calcular promedio de calificaciones\n * const avgRatingField = await fieldService.createRollupField(\n * productsTableId,\n * 'rating_promedio',\n * productLinkFieldId,\n * ratingFieldId,\n * 'avg'\n * )\n * ```\n *\n * @since 1.0.0\n */\n async createRollupField(\n tableId: number,\n name: string,\n throughFieldId: number,\n targetFieldId: number,\n rollupFunction: string = 'sum',\n description?: string\n ): Promise<Field> {\n this.validationService.validateId(throughFieldId, 'through field ID')\n this.validationService.validateId(targetFieldId, 'target field ID')\n\n return this.create(tableId, {\n name,\n type: 'rollup',\n through_field_id: throughFieldId,\n target_field_id: targetFieldId,\n rollup_function: rollupFunction,\n description\n })\n }\n\n /**\n * Crear campo de lookup (búsqueda)\n *\n * Crea un campo que muestra valores de un campo en registros relacionados.\n * Similar a un VLOOKUP en spreadsheets. Se actualiza automáticamente.\n *\n * @param tableId - ID numérico de la tabla\n * @param name - Nombre del campo\n * @param throughFieldId - ID del campo de enlace para la relación\n * @param targetFieldId - ID del campo a mostrar en la tabla relacionada\n * @param description - Descripción del campo (opcional)\n * @returns Promise con el campo de lookup creado\n *\n * @throws {BaserowValidationError} Si los IDs son inválidos\n *\n * @example\n * ```typescript\n * // Mostrar nombre del cliente en tabla de pedidos\n * const customerNameField = await fieldService.createLookupField(\n * ordersTableId,\n * 'nombre_cliente',\n * customerLinkFieldId,\n * customerNameFieldId\n * )\n *\n * // Mostrar categoría del producto en pedidos\n * const productCategoryField = await fieldService.createLookupField(\n * ordersTableId,\n * 'categoria_producto',\n * productLinkFieldId,\n * categoryFieldId\n * )\n * ```\n *\n * @since 1.0.0\n */\n async createLookupField(\n tableId: number,\n name: string,\n throughFieldId: number,\n targetFieldId: number,\n description?: string\n ): Promise<Field> {\n this.validationService.validateId(throughFieldId, 'through field ID')\n this.validationService.validateId(targetFieldId, 'target field ID')\n\n return this.create(tableId, {\n name,\n type: 'lookup',\n through_field_id: throughFieldId,\n target_field_id: targetFieldId,\n description\n })\n }\n\n /**\n * Verificar si un campo existe\n *\n * Verifica la existencia de un campo sin cargar todos sus metadatos.\n * Útil para validaciones antes de operaciones.\n *\n * @param fieldId - ID numérico del campo\n * @returns Promise que resuelve a true si existe, false si no\n *\n * @example\n * ```typescript\n * const exists = await fieldService.exists(456)\n * if (exists) {\n * console.log('El campo existe')\n * } else {\n * console.log('El campo no existe')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async exists(fieldId: number): Promise<boolean> {\n try {\n await this.get(fieldId)\n return true\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return false\n }\n throw error\n }\n }\n\n // ===== GESTIÓN DE ÍNDICES Y CONSTRAINTS (v1.35+) =====\n\n /**\n * Configurar índice en campo para mejorar performance\n *\n * Habilita o deshabilita el índice en un campo para acelerar\n * las operaciones de filtrado hasta 10x más rápido.\n *\n * @param fieldId - ID numérico del campo\n * @param enabled - true para habilitar índice, false para deshabilitar\n * @returns Promise con el campo actualizado\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si el fieldId es inválido\n *\n * @example\n * ```typescript\n * // Habilitar índice para mejorar filtros en campo email\n * const field = await fieldService.setFieldIndex(emailFieldId, true)\n * console.log(`Índice ${field.index ? 'habilitado' : 'deshabilitado'}`)\n *\n * // Deshabilitar índice\n * await fieldService.setFieldIndex(fieldId, false)\n * ```\n *\n * @since 1.1.0\n */\n async setFieldIndex(fieldId: number, enabled: boolean): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n try {\n this.logDebug(`${enabled ? 'Enabling' : 'Disabling'} index for field ${fieldId}`)\n const response = await this.http.patch<Field>(`/database/fields/${fieldId}/`, {\n index: enabled\n })\n this.logSuccess(`set field index`, fieldId, { enabled })\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Field', fieldId)\n }\n this.handleHttpError(error, 'set field index', fieldId)\n }\n }\n\n /**\n * Agregar restricción de valor a campo\n *\n * Agrega una nueva restricción de integridad al campo para\n * garantizar calidad de datos (ej: valores únicos).\n *\n * @param fieldId - ID numérico del campo\n * @param constraint - Configuración de la restricción\n * @returns Promise con el campo actualizado\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * // Agregar restricción de unicidad (excluyendo vacíos)\n * const field = await fieldService.addFieldConstraint(emailFieldId, {\n * type: 'unique',\n * active: true\n * })\n *\n * // Agregar restricción de unicidad (incluyendo vacíos)\n * await fieldService.addFieldConstraint(codeFieldId, {\n * type: 'unique_with_empty',\n * active: true\n * })\n * ```\n *\n * @since 1.1.0\n */\n async addFieldConstraint(fieldId: number, constraint: FieldConstraint): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n if (!constraint.type || typeof constraint.active !== 'boolean') {\n throw new BaserowValidationError('Invalid constraint configuration', {\n constraint: ['type and active are required']\n })\n }\n\n // Obtener constraints existentes\n const currentField = await this.get(fieldId)\n const existingConstraints = currentField.constraints || []\n\n // Verificar si ya existe una constraint del mismo tipo\n const existingIndex = existingConstraints.findIndex(c => c.type === constraint.type)\n let updatedConstraints: FieldConstraint[]\n\n if (existingIndex >= 0) {\n // Actualizar constraint existente\n updatedConstraints = [...existingConstraints]\n updatedConstraints[existingIndex] = constraint\n } else {\n // Agregar nueva constraint\n updatedConstraints = [...existingConstraints, constraint]\n }\n\n try {\n this.logDebug(`Adding constraint ${constraint.type} to field ${fieldId}`, { constraint })\n const response = await this.http.patch<Field>(`/database/fields/${fieldId}/`, {\n constraints: updatedConstraints\n })\n this.logSuccess('add field constraint', fieldId, { type: constraint.type })\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Field', fieldId)\n }\n this.handleHttpError(error, 'add field constraint', fieldId)\n }\n }\n\n /**\n * Eliminar restricción de valor de campo\n *\n * Elimina una restricción específica del campo por tipo.\n *\n * @param fieldId - ID numérico del campo\n * @param constraintType - Tipo de restricción a eliminar\n * @returns Promise con el campo actualizado\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si el fieldId es inválido\n *\n * @example\n * ```typescript\n * // Eliminar restricción de unicidad\n * const field = await fieldService.removeFieldConstraint(emailFieldId, 'unique')\n * console.log(`Constraints restantes: ${field.constraints?.length || 0}`)\n * ```\n *\n * @since 1.1.0\n */\n async removeFieldConstraint(fieldId: number, constraintType: ConstraintType): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n // Obtener constraints existentes\n const currentField = await this.get(fieldId)\n const existingConstraints = currentField.constraints || []\n\n // Filtrar constraint a eliminar\n const updatedConstraints = existingConstraints.filter(c => c.type !== constraintType)\n\n try {\n this.logDebug(`Removing constraint ${constraintType} from field ${fieldId}`)\n const response = await this.http.patch<Field>(`/database/fields/${fieldId}/`, {\n constraints: updatedConstraints\n })\n this.logSuccess('remove field constraint', fieldId, { type: constraintType })\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Field', fieldId)\n }\n this.handleHttpError(error, 'remove field constraint', fieldId)\n }\n }\n\n /**\n * Obtener restricciones de campo\n *\n * Recupera todas las restricciones activas de un campo específico.\n *\n * @param fieldId - ID numérico del campo\n * @returns Promise con array de restricciones del campo\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si el fieldId es inválido\n *\n * @example\n * ```typescript\n * const constraints = await fieldService.getFieldConstraints(emailFieldId)\n * constraints.forEach(constraint => {\n * console.log(`Constraint ${constraint.type}: ${constraint.active ? 'activa' : 'inactiva'}`)\n * })\n * ```\n *\n * @since 1.1.0\n */\n async getFieldConstraints(fieldId: number): Promise<FieldConstraint[]> {\n this.validationService.validateId(fieldId, 'field ID')\n\n const field = await this.get(fieldId)\n return field.constraints || []\n }\n\n /**\n * Eliminar todas las restricciones de un campo\n *\n * Remueve completamente todas las constraints configuradas en un campo específico,\n * dejándolo sin ninguna restricción de integridad. Esta operación es útil para\n * limpiar campos que han acumulado múltiples constraints o para resetear\n * la configuración de restricciones.\n *\n * @param fieldId - ID del campo del cual eliminar todas las constraints\n * @returns Promise con el campo actualizado sin constraints\n *\n * @throws {BaserowValidationError} Si fieldId es inválido\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowError} Si hay error en la comunicación con la API\n *\n * @example\n * ```typescript\n * // Eliminar todas las constraints de un campo\n * const field = await fieldService.removeAllFieldConstraints(emailFieldId)\n * console.log(`Campo \"${field.name}\" ahora sin constraints`)\n *\n * // Verificar que se eliminaron todas\n * const remainingConstraints = await fieldService.getFieldConstraints(emailFieldId)\n * console.log(`Constraints restantes: ${remainingConstraints.length}`) // 0\n * ```\n *\n * @since 1.1.0\n */\n async removeAllFieldConstraints(fieldId: number): Promise<Field> {\n this.validationService.validateId(fieldId, 'field ID')\n\n try {\n this.logDebug(`Removing all constraints from field ${fieldId}`)\n\n const updatedField = await this.updateFieldInternal(fieldId, {\n constraints: []\n })\n\n this.logSuccess('remove all field constraints', fieldId, { removedAll: true })\n return updatedField\n } catch (error) {\n this.handleHttpError(error, 'remove all field constraints', fieldId)\n throw error\n }\n }\n\n /**\n * Aplicar opciones avanzadas a solicitud de creación de campo\n *\n * Método helper interno que aplica índices y constraints a la configuración\n * de creación de campo.\n *\n * @param baseRequest - Solicitud base de creación\n * @param advancedOptions - Opciones avanzadas (índice y constraints)\n * @returns Solicitud enriquecida con opciones avanzadas\n *\n * @private\n * @since 1.1.0\n */\n private applyAdvancedOptions(\n baseRequest: CreateFieldRequest,\n advancedOptions?: FieldAdvancedOptions\n ): CreateFieldRequest {\n if (!advancedOptions) return baseRequest\n\n const enrichedRequest = { ...baseRequest }\n\n if (advancedOptions.index !== undefined) {\n enrichedRequest.index = advancedOptions.index\n }\n\n if (advancedOptions.constraints && advancedOptions.constraints.length > 0) {\n enrichedRequest.constraints = advancedOptions.constraints\n }\n\n return enrichedRequest\n }\n\n // ===== FRIEND ACCESS PATTERN FOR FIELD CONTEXT =====\n // Symbol-based access que no aparece en la API pública pero permite acceso interno\n\n /**\n * Friend access para FieldContext\n * No aparece en intellisense normal ni en la API pública\n * @internal\n */\n get [Symbol.for('fieldContext')]() {\n return {\n createField: this.createFieldInternal.bind(this),\n updateField: this.updateFieldInternal.bind(this),\n deleteField: this.deleteFieldInternal.bind(this)\n }\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { ValidationService } from './core/ValidationService'\nimport {\n Row,\n CreateRowRequest,\n UpdateRowRequest,\n QueryOptions,\n BulkOptions,\n BaserowResponse,\n BaserowNotFoundError,\n Logger\n} from '../types/index'\nimport { chunkArray, delay } from '../utils/validation'\n\n/**\n * Servicio para operaciones CRUD de filas de Baserow\n *\n * Proporciona operaciones completas de filas incluyendo CRUD básico,\n * operaciones bulk optimizadas y métodos de utilidad avanzados.\n * Maneja paginación automática, rate limiting y validación robusta.\n *\n * **Características:**\n * - CRUD completo: create, read, update, delete\n * - Operaciones bulk optimizadas con chunking automático\n * - `listAll()` con paginación automática para descargar tablas completas\n * - Rate limiting automático entre requests\n * - Validación de datos y IDs\n * - Manejo robusto de errores\n *\n * @example\n * ```typescript\n * // Operaciones básicas\n * const rows = await rowService.list(tableId, { size: 50 })\n * const row = await rowService.get(tableId, rowId)\n * const newRow = await rowService.createRow(tableId, { name: 'John', email: 'john@example.com' })\n * await rowService.updateRow(tableId, rowId, { name: 'Jane' })\n * await rowService.delete(tableId, rowId)\n *\n * // Operaciones bulk\n * const allRows = await rowService.listAll(tableId) // Descarga tabla completa\n * const createdRows = await rowService.createBulk(tableId, [\n * { name: 'Alice', email: 'alice@example.com' },\n * { name: 'Bob', email: 'bob@example.com' }\n * ])\n * ```\n *\n * @since 1.0.0\n */\nexport class RowService extends HttpService {\n private validationService: ValidationService\n\n constructor(http: any, logger?: Logger) {\n super(http, logger)\n this.validationService = new ValidationService(http, logger)\n }\n\n /**\n * Obtener filas de una tabla con opciones de filtrado y paginación\n *\n * Lista filas de una tabla específica con soporte completo para filtrado,\n * ordenamiento, búsqueda y paginación. Retorna metadatos de paginación.\n *\n * @param tableId - ID numérico de la tabla\n * @param options - Opciones de consulta\n * @param options.page - Número de página (default: 1)\n * @param options.size - Tamaño de página (default: 100)\n * @param options.search - Término de búsqueda en todos los campos\n * @param options.order_by - Campo para ordenar (prefijo '-' para desc)\n * @param options.filters - Filtros por campo específico\n * @param options.user_field_names - Usar nombres de usuario para campos (default: true)\n * @returns Promise con filas y metadatos de paginación\n *\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * // Lista básica\n * const result = await rowService.list(123)\n * console.log(`${result.count} filas total, mostrando ${result.rows.length}`)\n *\n * // Con filtros y ordenamiento\n * const filtered = await rowService.list(123, {\n * search: 'John',\n * order_by: '-created_at',\n * filters: { status: 'active' },\n * size: 50\n * })\n * ```\n *\n * @since 1.0.0\n */\n async list(\n tableId: number,\n options: QueryOptions = {}\n ): Promise<{\n rows: Row[]\n count: number\n next: string | null\n previous: string | null\n }> {\n this.validationService.validateId(tableId, 'table ID')\n\n const params: any = {\n user_field_names: options.user_field_names ?? true,\n ...options\n }\n\n // Aplanar filtros directamente en query params para Baserow API\n if (options.filters && typeof options.filters === 'object' && Object.keys(options.filters).length > 0) {\n Object.assign(params, options.filters)\n delete params.filters // No enviar el objeto anidado\n }\n\n // Preservar filter_type si existe (para OR queries)\n if (options.filter_type) {\n params.filter_type = options.filter_type\n }\n\n try {\n this.logDebug(`Fetching rows from table ${tableId}`, options)\n this.logDebug(`Final query params being sent to Baserow API:`, params)\n const response = await this.http.get<BaserowResponse<Row>>(`/database/rows/table/${tableId}/`, params)\n\n const result = {\n rows: response.results,\n count: response.count,\n next: response.next,\n previous: response.previous\n }\n\n this.logSuccess('list rows', tableId, { count: result.count, pageSize: result.rows.length })\n return result\n } catch (error) {\n this.handleHttpError(error, 'list rows', tableId)\n }\n }\n\n /**\n * Obtener todas las filas de una tabla (maneja paginación automáticamente)\n *\n * Descarga la tabla completa manejando paginación automáticamente.\n * Ideal para exportar datos o análisis completo de tablas.\n * Incluye rate limiting automático entre páginas.\n *\n * @aiUsage\n * **✅ Usar cuando:**\n * - Necesitas exportar/analizar la tabla completa (< 50K filas)\n * - Generar reportes o backups completos\n * - Sincronización de datos entre sistemas\n * - Análisis de datos que requiere dataset completo\n *\n * **❌ NO usar cuando:**\n * - Tabla tiene > 50K filas (alto consumo de memoria)\n * - Solo necesitas primeras N filas (usa `list()` con paginación)\n * - Datos cambian frecuentemente durante la carga (inconsistencias)\n * - Aplicación tiene límites de memoria estrictos\n *\n * @aiWarning\n * **⚠️ Consumo de Memoria:**\n * - Cada fila ocupa ~1-5 KB en memoria (depende de campos)\n * - 10,000 filas ≈ 10-50 MB\n * - 50,000 filas ≈ 50-250 MB\n * - 100,000+ filas → ⚠️ Posible OutOfMemory en Node.js\n *\n * **⚠️ Tiempo de Ejecución:**\n * - ~200 filas/segundo (aprox. 5 páginas/segundo con pageSize=200)\n * - 10,000 filas → ~50 segundos\n * - 50,000 filas → ~4-5 minutos\n *\n * @aiAlternative\n * **Para tablas grandes (>50K filas), usa paginación manual:**\n * ```typescript\n * // Procesar en streaming sin cargar todo en memoria\n * let page = 1\n * while (true) {\n * const result = await rowService.list(tableId, { page, size: 200 })\n *\n * // Procesar chunk inmediatamente\n * await processChunk(result.rows)\n *\n * if (!result.next) break\n * page++\n * }\n * ```\n *\n * @aiPerformance\n * **Optimizaciones:**\n * - `size: 200` (default) → Balance ideal entre requests y memoria\n * - `delay: 50ms` (default) → Evita rate limiting\n * - Para tablas pequeñas (<5K): aumentar `size: 500` y reducir `delay: 0`\n * - Para tablas grandes (>20K): reducir `size: 100` y aumentar `delay: 100ms`\n *\n * @param tableId - ID numérico de la tabla\n * @param options - Opciones de consulta (sin page/size)\n * @param options.search - Término de búsqueda\n * @param options.order_by - Campo para ordenar\n * @param options.filters - Filtros por campo\n * @param options.delay - Delay entre páginas en ms (default: 50)\n * @param options.size - Tamaño de página interna (default: 200)\n * @returns Promise con array de todas las filas\n *\n * @throws {BaserowValidationError} Si el tableId es inválido\n *\n * @example\n * ```typescript\n * // Descargar tabla completa\n * const allRows = await rowService.listAll(123)\n * console.log(`Descargadas ${allRows.length} filas`)\n *\n * // Con filtros aplicados\n * const activeUsers = await rowService.listAll(123, {\n * filters: { status: 'active' },\n * order_by: 'name'\n * })\n *\n * // Optimizado para tabla grande (>20K filas)\n * const allRows = await rowService.listAll(123, {\n * size: 100, // Chunks más pequeños\n * delay: 100 // Más tiempo entre requests\n * })\n * ```\n *\n * @since 1.0.0\n */\n async listAll(tableId: number, options: Omit<QueryOptions, 'page' | 'size'> = {}): Promise<Row[]> {\n this.validationService.validateId(tableId, 'table ID')\n\n const allRows: Row[] = []\n let page = 1\n const size = (options as any).size || 200 // Tamaño de página configurable\n\n this.logInfo(`Starting listAll for table ${tableId}`, { pageSize: size })\n\n while (true) {\n const result = await this.list(tableId, {\n ...options,\n page,\n size\n })\n\n allRows.push(...result.rows)\n this.logDebug(`Fetched page ${page}`, { rowsThisPage: result.rows.length, totalRows: allRows.length })\n\n if (!result.next) {\n break\n }\n\n page++\n await delay((options as any).delay || 50) // Rate limiting configurable\n }\n\n this.logSuccess('listAll completed', tableId, { totalRows: allRows.length, pages: page })\n return allRows\n }\n\n /**\n * Obtener fila específica por ID\n *\n * Recupera una fila individual por su ID con datos completos.\n * Útil para operaciones de lectura específicas.\n *\n * @param tableId - ID numérico de la tabla\n * @param rowId - ID numérico de la fila\n * @param userFieldNames - Usar nombres de usuario para campos (default: true)\n * @returns Promise con datos completos de la fila\n *\n * @throws {BaserowNotFoundError} Si la fila no existe\n * @throws {BaserowValidationError} Si los IDs son inválidos\n *\n * @example\n * ```typescript\n * const row = await rowService.get(123, 456)\n * console.log(`Usuario: ${row.name} - ${row.email}`)\n * ```\n *\n * @since 1.0.0\n */\n async get(tableId: number, rowId: number, userFieldNames = true): Promise<Row> {\n this.validationService.validateId(tableId, 'table ID')\n this.validationService.validateId(rowId, 'row ID')\n\n try {\n this.logDebug(`Fetching row ${rowId} from table ${tableId}`, { userFieldNames })\n const response = await this.http.get<Row>(`/database/rows/table/${tableId}/${rowId}/`, {\n user_field_names: userFieldNames\n })\n this.logSuccess('get row', rowId)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Row', rowId)\n }\n this.handleHttpError(error, 'get row', rowId)\n }\n }\n\n /**\n * Crear nueva fila\n *\n * Crea una nueva fila en la tabla especificada con los datos proporcionados.\n * Valida tipos de datos y estructura según el esquema de la tabla.\n *\n * @param tableId - ID numérico de la tabla\n * @param data - Datos de la nueva fila (usar nombres de campo de usuario)\n * @returns Promise con la fila creada (incluye ID asignado)\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const newRow = await rowService.createRow(123, {\n * name: 'John Doe',\n * email: 'john@example.com',\n * age: 30,\n * active: true\n * })\n * console.log(`Fila creada con ID: ${newRow.id}`)\n * ```\n *\n * @since 1.0.0\n */\n async createRow(tableId: number, data: CreateRowRequest): Promise<Row> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (!data || typeof data !== 'object') {\n throw new Error('Row data is required and must be an object')\n }\n\n try {\n this.logDebug(`Creating row in table ${tableId}`, data)\n const response = await this.http.post<Row>(`/database/rows/table/${tableId}/`, data, {\n user_field_names: true\n })\n\n this.logSuccess('create row', response.id)\n return { ...response, id: response.id }\n } catch (error) {\n this.handleHttpError(error, 'create row', tableId)\n }\n }\n\n /**\n * Actualizar fila existente\n *\n * Actualiza una fila existente con nuevos datos. Solo actualiza\n * los campos proporcionados (actualización parcial).\n *\n * @param tableId - ID numérico de la tabla\n * @param rowId - ID numérico de la fila a actualizar\n * @param data - Datos a actualizar (solo campos modificados)\n * @returns Promise con la fila actualizada\n *\n * @throws {BaserowNotFoundError} Si la fila no existe\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const updatedRow = await rowService.updateRow(123, 456, {\n * email: 'newemail@example.com',\n * active: false\n * })\n * console.log(`Fila actualizada: ${updatedRow.name}`)\n * ```\n *\n * @since 1.0.0\n */\n async updateRow(tableId: number, rowId: number, data: UpdateRowRequest): Promise<Row> {\n this.validationService.validateId(tableId, 'table ID')\n this.validationService.validateId(rowId, 'row ID')\n\n if (!data || typeof data !== 'object') {\n throw new Error('Row data is required and must be an object')\n }\n\n try {\n this.logDebug(`Updating row ${rowId} in table ${tableId}`, data)\n const response = await this.http.patch<Row>(`/database/rows/table/${tableId}/${rowId}/`, data, {\n user_field_names: true\n })\n this.logSuccess('update row', rowId)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Row', rowId)\n }\n this.handleHttpError(error, 'update row', rowId)\n }\n }\n\n /**\n * Eliminar fila\n *\n * Elimina permanentemente una fila de la tabla.\n * Esta operación no se puede deshacer.\n *\n * @param tableId - ID numérico de la tabla\n * @param rowId - ID numérico de la fila a eliminar\n * @returns Promise que resuelve cuando la fila es eliminada\n *\n * @throws {BaserowNotFoundError} Si la fila no existe\n * @throws {BaserowValidationError} Si los IDs son inválidos\n *\n * @example\n * ```typescript\n * await rowService.delete(123, 456)\n * console.log('Fila eliminada exitosamente')\n * ```\n *\n * @since 1.0.0\n */\n async delete(tableId: number, rowId: number): Promise<void> {\n this.validationService.validateId(tableId, 'table ID')\n this.validationService.validateId(rowId, 'row ID')\n\n try {\n this.logDebug(`Deleting row ${rowId} from table ${tableId}`)\n await this.http.delete(`/database/rows/table/${tableId}/${rowId}/`)\n this.logSuccess('delete row', rowId)\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Row', rowId)\n }\n this.handleHttpError(error, 'delete row', rowId)\n }\n }\n\n // ===== OPERACIONES BULK =====\n\n /**\n * Crear múltiples filas en lotes (bulk create)\n *\n * Crea múltiples filas de forma eficiente usando operaciones batch.\n * Procesa en chunks para optimizar rendimiento y evitar timeouts.\n * Incluye rate limiting automático entre lotes.\n *\n * @aiUsage\n * **✅ Usar cuando:**\n * - Importar datos desde CSV, JSON o bases de datos externas\n * - Migración de datos entre tablas o sistemas\n * - Seed de datos de prueba o inicialización\n * - Inserción masiva desde formularios o APIs (>10 filas)\n *\n * **❌ NO usar cuando:**\n * - Solo 1-5 filas → Usar `createRow()` individual (más simple)\n * - Necesitas validación compleja fila por fila (usa loop con try/catch)\n * - Datos vienen de stream continuo (procesar en mini-batches)\n *\n * @aiPerformance\n * **Tamaño de Batch Óptimo (batchSize):**\n * - `100-200` → **Recomendado general** (balance velocidad/confiabilidad)\n * - `50-100` → Conexiones lentas o datos complejos (muchos campos)\n * - `200-500` → Conexiones rápidas y datos simples (pocos campos)\n * - `> 500` → ⚠️ Riesgo de timeout en Baserow API\n *\n * **Rendimiento Estimado:**\n * - ~1,000 filas/minuto con batchSize=200 (velocidad promedio)\n * - 10,000 filas → ~10 minutos\n * - 50,000 filas → ~50 minutos\n *\n * @aiErrorHandling\n * **Manejo de Errores Parciales:**\n * ```typescript\n * // Si un batch falla, TODA la operación se detiene\n * try {\n * const created = await rowService.createBulk(tableId, rows, { batchSize: 100 })\n * console.log(`✅ ${created.length} filas creadas`)\n * } catch (error) {\n * console.error('❌ Error en batch, algunas filas pueden haberse creado')\n * // Verificar cuántas filas se crearon antes del error\n * const currentCount = await rowService.count(tableId)\n * console.log(`Total filas actuales: ${currentCount}`)\n * }\n * ```\n *\n * **Para operaciones críticas, considera transacciones manuales:**\n * ```typescript\n * const created = []\n * const failed = []\n *\n * for (const chunk of chunks(rows, 100)) {\n * try {\n * const result = await rowService.createBulk(tableId, chunk, { batchSize: 100 })\n * created.push(...result)\n * } catch (error) {\n * failed.push({ chunk, error })\n * // Continuar o abortar según lógica de negocio\n * }\n * }\n *\n * console.log(`Creadas: ${created.length}, Fallidas: ${failed.length}`)\n * ```\n *\n * @aiWarning\n * **⚠️ Webhooks Deshabilitados por Default:**\n * - `send_webhook_events: false` (default) → Sin notificaciones\n * - Para habilitar webhooks: `{ send_webhook_events: true }`\n * - ⚠️ Con webhooks habilitados, la operación es ~20-30% más lenta\n *\n * @param tableId - ID numérico de la tabla\n * @param rows - Array de datos para crear filas\n * @param options - Opciones de la operación bulk\n * @param options.batchSize - Tamaño de lote (default: 200)\n * @param options.send_webhook_events - Enviar eventos webhook (default: false)\n * @param options.user_field_names - Usar nombres de usuario (default: true)\n * @returns Promise con array de filas creadas\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * // Importación básica\n * const newRows = await rowService.createBulk(123, [\n * { name: 'Alice', email: 'alice@example.com' },\n * { name: 'Bob', email: 'bob@example.com' },\n * // ... más filas\n * ], { batchSize: 100 })\n * console.log(`${newRows.length} filas creadas`)\n *\n * // Con webhooks habilitados (notificaciones activas)\n * const created = await rowService.createBulk(tableId, rows, {\n * batchSize: 150,\n * send_webhook_events: true\n * })\n *\n * // Importación desde CSV con logging\n * const csvData = await parseCsv('data.csv')\n * console.log(`Importando ${csvData.length} filas...`)\n *\n * const imported = await rowService.createBulk(tableId, csvData, {\n * batchSize: 200\n * })\n *\n * console.log(`✅ Importadas ${imported.length} de ${csvData.length} filas`)\n * ```\n *\n * @since 1.0.0\n */\n async createBulk(tableId: number, rows: CreateRowRequest[], options: BulkOptions = {}): Promise<Row[]> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (!rows || !Array.isArray(rows) || rows.length === 0) {\n throw new Error('Rows must be a non-empty array')\n }\n\n const { batchSize = 200, send_webhook_events = false, user_field_names = true } = options\n\n const chunks = chunkArray(rows, batchSize)\n const results: Row[] = []\n\n this.logger?.info('🔧 Bulk create iniciado', { tableId, rowCount: rows.length, batchCount: chunks.length })\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]\n const batchNum = i + 1\n\n try {\n this.logger?.info('Procesando lote', { batchNum, totalBatches: chunks.length, batchSize: chunk.length })\n\n const response = await this.http.post<{ items: Row[] }>(\n `/database/rows/table/${tableId}/batch/`,\n { items: chunk },\n {\n user_field_names,\n send_webhook_events\n }\n )\n\n results.push(...(response.items || []))\n\n // Rate limiting entre lotes\n if (i < chunks.length - 1) {\n await delay(100)\n }\n\n this.logger?.info('Lote procesado correctamente', { batchNum })\n } catch (error) {\n this.logger?.error('❌ Error en lote', { batchNum, totalBatches: chunks.length, error })\n throw error\n }\n }\n\n this.logger?.info('✅ Bulk create completado', { createdRows: results.length })\n return results\n }\n\n /**\n * Actualizar múltiples filas en lotes (bulk update)\n *\n * Actualiza múltiples filas de forma eficiente usando operaciones batch.\n * Cada elemento del array debe incluir el ID de la fila a actualizar.\n * Procesa en chunks con rate limiting automático.\n *\n * @aiUsage\n * **✅ Usar cuando:**\n * - Actualizar estado/status de múltiples registros (ej: marcar como procesado)\n * - Sincronización de datos desde sistema externo (>10 filas)\n * - Actualizar campos calculados en batch (ej: recalcular totales)\n * - Operaciones administrativas masivas (cambiar propietario, categoría, etc.)\n *\n * **❌ NO usar cuando:**\n * - Solo 1-5 filas → Usar `updateRow()` individual\n * - Updates dependen de estado previo (race conditions) → Actualizar secuencialmente\n * - Necesitas rollback complejo → Implementar transacciones manuales\n *\n * @aiPerformance\n * **Igual que `createBulk()`:**\n * - **Batch Size Recomendado:** 100-200 filas\n * - **Rendimiento:** ~1,000 actualizaciones/minuto\n *\n * @aiErrorHandling\n * **Validación Estricta de IDs:**\n * ```typescript\n * // ❌ INCORRECTO - Faltan IDs\n * await rowService.updateBulk(tableId, [\n * { status: 'active' } // Error: falta 'id'\n * ])\n *\n * // ✅ CORRECTO - Cada update tiene su ID\n * await rowService.updateBulk(tableId, [\n * { id: 1, status: 'active' },\n * { id: 2, status: 'inactive' }\n * ])\n * ```\n *\n * **Manejo de Errores con Rollback Parcial:**\n * ```typescript\n * const updates = [...] // Array grande de updates\n * const originalValues = await rowService.listAll(tableId) // Backup\n *\n * try {\n * const updated = await rowService.updateBulk(tableId, updates, { batchSize: 100 })\n * console.log(`✅ ${updated.length} filas actualizadas`)\n * } catch (error) {\n * console.error('❌ Error durante update, iniciando rollback...')\n *\n * // Rollback manual de filas afectadas\n * const rollbackData = originalValues.map(row => ({\n * id: row.id,\n * ...pickFields(row, changedFields)\n * }))\n *\n * await rowService.updateBulk(tableId, rollbackData)\n * console.log('Rollback completado')\n * }\n * ```\n *\n * @param tableId - ID numérico de la tabla\n * @param updates - Array de actualizaciones (cada una debe incluir 'id')\n * @param options - Opciones de la operación bulk\n * @param options.batchSize - Tamaño de lote (default: 200)\n * @param options.send_webhook_events - Enviar eventos webhook (default: false)\n * @param options.user_field_names - Usar nombres de usuario (default: true)\n * @returns Promise con array de filas actualizadas\n *\n * @throws {BaserowValidationError} Si faltan IDs o datos son inválidos\n *\n * @example\n * ```typescript\n * // Actualización básica de status\n * const updates = [\n * { id: 1, status: 'active' },\n * { id: 2, status: 'inactive' },\n * { id: 3, email: 'updated@example.com' }\n * ]\n *\n * const updatedRows = await rowService.updateBulk(123, updates)\n * console.log(`${updatedRows.length} filas actualizadas`)\n *\n * // Actualizar desde sistema externo\n * const externalData = await fetchFromAPI()\n * const updates = externalData.map(item => ({\n * id: item.baserowId,\n * name: item.fullName,\n * email: item.emailAddress,\n * updated_at: new Date().toISOString()\n * }))\n *\n * await rowService.updateBulk(tableId, updates, { batchSize: 150 })\n * ```\n *\n * @since 1.0.0\n */\n async updateBulk(\n tableId: number,\n updates: Array<{ id: number } & UpdateRowRequest>,\n options: BulkOptions = {}\n ): Promise<Row[]> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (!updates || !Array.isArray(updates) || updates.length === 0) {\n throw new Error('Updates must be a non-empty array')\n }\n\n // Validar que todas las actualizaciones tienen ID\n updates.forEach((update, index) => {\n if (!update.id || typeof update.id !== 'number' || update.id <= 0) {\n throw new Error(`Update at index ${index} must have a valid id`)\n }\n })\n\n const { batchSize = 200, send_webhook_events = false, user_field_names = true } = options\n\n const chunks = chunkArray(updates, batchSize)\n const results: Row[] = []\n\n this.logger?.info('Bulk update iniciado', { tableId, updateCount: updates.length, batchCount: chunks.length })\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]\n const batchNum = i + 1\n\n try {\n this.logger?.info('Procesando lote', { batchNum, totalBatches: chunks.length, batchSize: chunk.length })\n\n const response = await this.http.patch<{ items: Row[] }>(\n `/database/rows/table/${tableId}/batch/`,\n { items: chunk },\n {\n user_field_names,\n send_webhook_events\n }\n )\n\n results.push(...(response.items || []))\n\n // Rate limiting entre lotes\n if (i < chunks.length - 1) {\n await delay(100)\n }\n\n this.logger?.info('Lote procesado correctamente', { batchNum })\n } catch (error) {\n this.logger?.error('❌ Error en lote', { batchNum, totalBatches: chunks.length, error })\n throw error\n }\n }\n\n this.logger?.info('Bulk update completado', { updatedRows: results.length })\n return results\n }\n\n /**\n * Eliminar múltiples filas\n *\n * Elimina múltiples filas de forma eficiente. Procesa en lotes pequeños\n * para evitar problemas con URLs muy largas. Operación irreversible.\n *\n * @aiUsage\n * **✅ Usar cuando:**\n * - Limpieza de datos obsoletos (registros antiguos, duplicados)\n * - Eliminación administrativa masiva (purgar test data)\n * - Sincronización: eliminar filas que ya no existen en origen\n * - Operaciones de mantenimiento (>10 filas)\n *\n * **❌ NO usar cuando:**\n * - Solo 1-5 filas → Usar `delete()` individual\n * - Datos pueden recuperarse → Considerar soft delete (campo deleted=true)\n * - Operación puede fallar parcialmente → Implementar backup previo\n *\n * @aiWarning\n * **⚠️ OPERACIÓN IRREVERSIBLE:**\n * - **NO hay undo/rollback automático** en Baserow\n * - Una vez eliminadas, las filas NO se pueden recuperar\n * - Si la operación falla a mitad, algunas filas YA estarán eliminadas\n *\n * **⚠️ Batch Size Pequeño (50 default):**\n * - Usa batchSize=50 (más pequeño que create/update) para evitar URLs largas\n * - Baserow API usa DELETE con query params: `/table/123/1,2,3,4,5...`\n * - URLs muy largas (>2000 chars) pueden fallar en algunos proxies\n *\n * @aiErrorHandling\n * **Patrón de Backup Preventivo:**\n * ```typescript\n * // ✅ RECOMENDADO: Backup antes de eliminar\n * const rowsToDelete = [1, 2, 3, 4, 5]\n *\n * // 1. Crear backup de las filas\n * const backup = await Promise.all(\n * rowsToDelete.map(id => rowService.get(tableId, id))\n * )\n * console.log(`Backup creado: ${backup.length} filas`)\n *\n * // 2. Guardar backup en archivo o DB\n * await fs.writeFile('backup.json', JSON.stringify(backup, null, 2))\n *\n * // 3. Eliminar filas\n * try {\n * await rowService.deleteBulk(tableId, rowsToDelete)\n * console.log('✅ Eliminación exitosa')\n * } catch (error) {\n * console.error('❌ Error durante eliminación')\n * console.log('Backup disponible en backup.json para restauración manual')\n * throw error\n * }\n * ```\n *\n * **Confirmación Interactiva para Operaciones Peligrosas:**\n * ```typescript\n * async function safeBulkDelete(tableId: number, rowIds: number[]) {\n * console.log(`⚠️ Vas a eliminar ${rowIds.length} filas de forma PERMANENTE`)\n *\n * const confirmed = await promptUser('¿Estás seguro? (yes/no): ')\n * if (confirmed !== 'yes') {\n * console.log('Operación cancelada')\n * return\n * }\n *\n * const backup = await createBackup(tableId, rowIds)\n * console.log(`Backup creado: ${backup.length} filas`)\n *\n * await rowService.deleteBulk(tableId, rowIds)\n * console.log('✅ Eliminación completada')\n * }\n * ```\n *\n * @aiPerformance\n * **Rendimiento de Deletes:**\n * - Más lento que create/update (eliminación paralela en chunks)\n * - ~500-800 eliminaciones/minuto con batchSize=50\n * - 10,000 filas → ~12-20 minutos\n *\n * @param tableId - ID numérico de la tabla\n * @param rowIds - Array de IDs de filas a eliminar\n * @param options - Opciones de la operación\n * @param options.batchSize - Tamaño de lote (default: 50)\n * @returns Promise que resuelve cuando todas las filas son eliminadas\n *\n * @throws {BaserowValidationError} Si los IDs son inválidos\n * @throws {BaserowNotFoundError} Si alguna fila no existe\n *\n * @example\n * ```typescript\n * // Eliminación básica\n * const idsToDelete = [123, 456, 789]\n * await rowService.deleteBulk(tableId, idsToDelete)\n * console.log(`${idsToDelete.length} filas eliminadas`)\n *\n * // Con backup preventivo\n * const rowsToDelete = await rowService.list(tableId, {\n * filters: { status: 'archived' }\n * })\n *\n * const backup = rowsToDelete.rows\n * await fs.writeFile('archived_backup.json', JSON.stringify(backup))\n *\n * const ids = rowsToDelete.rows.map(r => r.id)\n * await rowService.deleteBulk(tableId, ids, { batchSize: 50 })\n *\n * console.log(`✅ ${ids.length} filas archivadas eliminadas (backup guardado)`)\n * ```\n *\n * @since 1.0.0\n */\n async deleteBulk(tableId: number, rowIds: number[], options?: BulkOptions): Promise<void> {\n this.validationService.validateId(tableId, 'table ID')\n\n if (!rowIds || !Array.isArray(rowIds) || rowIds.length === 0) {\n throw new Error('Row IDs must be a non-empty array')\n }\n\n rowIds.forEach(id => this.validationService.validateId(id, 'row ID'))\n\n // Eliminar en lotes más pequeños para evitar problemas con URLs muy largas\n const deleteBatchSize = options?.batchSize || 50\n const chunks = chunkArray(rowIds, deleteBatchSize)\n\n this.logger?.info('Bulk delete iniciado', { tableId, deleteCount: rowIds.length, batchCount: chunks.length })\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]\n const batchNum = i + 1\n\n try {\n this.logger?.info('Procesando lote', { batchNum, totalBatches: chunks.length, batchSize: chunk.length })\n\n // Eliminar una por una en paralelo (más eficiente que secuencial)\n await Promise.all(chunk.map(rowId => this.delete(tableId, rowId)))\n\n // Rate limiting entre lotes\n if (i < chunks.length - 1) {\n await delay(200)\n }\n\n this.logger?.info('Lote procesado correctamente', { batchNum })\n } catch (error) {\n this.logger?.error('❌ Error en lote', { batchNum, totalBatches: chunks.length, error })\n throw error\n }\n }\n\n this.logger?.info('Bulk delete completado', { deletedRows: rowIds.length })\n }\n\n /**\n * Buscar filas por criterios específicos\n *\n * Método de conveniencia para buscar filas usando un término de búsqueda.\n * Busca en todos los campos de texto de la tabla.\n *\n * @param tableId - ID numérico de la tabla\n * @param query - Término de búsqueda\n * @param options - Opciones adicionales de consulta (sin search)\n * @returns Promise con array de filas que coinciden\n *\n * @example\n * ```typescript\n * const results = await rowService.search(123, 'john', {\n * order_by: 'name',\n * size: 20\n * })\n * console.log(`Encontradas ${results.length} filas`)\n * ```\n *\n * @since 1.0.0\n */\n async search(tableId: number, query: string, options: Omit<QueryOptions, 'search'> = {}): Promise<Row[]> {\n return (await this.list(tableId, { ...options, search: query })).rows\n }\n\n /**\n * Verificar si una fila existe\n *\n * Verifica la existencia de una fila sin cargar todos sus datos.\n * Útil para validaciones antes de operaciones.\n *\n * @param tableId - ID numérico de la tabla\n * @param rowId - ID numérico de la fila\n * @returns Promise que resuelve a true si existe, false si no\n *\n * @example\n * ```typescript\n * const exists = await rowService.exists(123, 456)\n * if (exists) {\n * console.log('La fila existe')\n * } else {\n * console.log('La fila no existe')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async exists(tableId: number, rowId: number): Promise<boolean> {\n try {\n await this.get(tableId, rowId)\n return true\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return false\n }\n throw error\n }\n }\n\n /**\n * Contar filas en una tabla\n *\n * Obtiene el número total de filas en una tabla, opcionalmente\n * aplicando filtros. No carga los datos de las filas.\n *\n * @param tableId - ID numérico de la tabla\n * @param filters - Filtros opcionales para aplicar al conteo\n * @returns Promise con el número total de filas\n *\n * @example\n * ```typescript\n * const total = await rowService.count(123)\n * console.log(`Total de filas: ${total}`)\n *\n * const activeCount = await rowService.count(123, { status: 'active' })\n * console.log(`Filas activas: ${activeCount}`)\n * ```\n *\n * @since 1.0.0\n */\n async count(tableId: number, filters?: Record<string, any>): Promise<number> {\n const result = await this.list(tableId, {\n size: 1,\n filters\n })\n return result.count\n }\n}\n","import {\n QueryOptions,\n PrismaLikeQueryOptions,\n WhereClause,\n OrderByClause,\n SelectClause,\n FieldOperators\n} from '../types/requests'\nimport { FieldMetadata } from './field-cache'\n\n/**\n * Mapper para transformar entre sintaxis Prisma y Baserow API\n *\n * Esta clase proporciona métodos estáticos para convertir opciones de consulta\n * estilo Prisma a formato compatible con la API de Baserow, permitiendo\n * una experiencia de desarrollador familiar mientras manteniendo compatibilidad\n * con las capacidades específicas de Baserow.\n *\n * **Transformaciones principales:**\n * - `where` → `filters` con operadores Baserow\n * - `orderBy` → `order_by` string\n * - `select` → `include`/`exclude` arrays\n * - `take`/`skip` → `size`/`page`\n * - Validación y optimización de consultas\n *\n * @aiPattern\n * ## 🎯 5 Patrones de Uso Comunes\n *\n * **1. Búsqueda Simple con Paginación**\n * ```typescript\n * // Prisma-like query para buscar usuarios activos\n * const query = {\n * where: { status: 'active' },\n * orderBy: { created_at: 'desc' },\n * take: 20,\n * skip: 0\n * }\n *\n * const baserowQuery = PrismaBaserowMapper.transformPrismaToBaserow(query)\n * const rows = await client.rows.list(tableId, baserowQuery)\n * ```\n *\n * **2. Filtros Complejos con AND**\n * ```typescript\n * // Combinar múltiples condiciones (todas deben cumplirse)\n * const query = {\n * where: {\n * AND: [\n * { age: { gte: 18, lt: 65 } },\n * { email: { contains: '@company.com' } },\n * { status: 'active' }\n * ]\n * }\n * }\n *\n * const baserowQuery = PrismaBaserowMapper.transformPrismaToBaserow(query)\n * // Transforma a: filter__age__higher_than_or_equal=18&filter__age__lower_than=65&...\n * ```\n *\n * **3. Filtros OR (Solo Nivel Raíz)**\n * ```typescript\n * // ✅ OR simple a nivel raíz (soportado nativamente)\n * const query = {\n * where: {\n * OR: [\n * { status: 'active' },\n * { status: 'pending' }\n * ]\n * }\n * }\n *\n * const baserowQuery = PrismaBaserowMapper.transformPrismaToBaserow(query)\n * // Genera: filter__status__equal=active&filter__status__equal=pending&filter_type=OR\n * ```\n *\n * **4. Selección de Campos (Optimizar Transferencia)**\n * ```typescript\n * // Solo traer campos necesarios\n * const query = {\n * where: { department: 'Sales' },\n * select: {\n * id: true,\n * name: true,\n * email: true,\n * password: false // Excluir explícitamente\n * },\n * take: 50\n * }\n *\n * const baserowQuery = PrismaBaserowMapper.transformPrismaToBaserow(query)\n * // Genera: include=['id','name','email']&size=50\n * ```\n *\n * **5. Migración desde Prisma ORM**\n * ```typescript\n * // Migrar query existente de Prisma\n * const prismaQuery = await prisma.user.findMany({\n * where: { active: true },\n * orderBy: { createdAt: 'desc' },\n * take: 10,\n * skip: 20\n * })\n *\n * // Equivalente en Baserow\n * const baserowQuery = PrismaBaserowMapper.transformPrismaToBaserow({\n * where: { active: true },\n * orderBy: { createdAt: 'desc' },\n * take: 10,\n * skip: 20\n * })\n * const rows = await client.rows.list(tableId, baserowQuery)\n * // Datos en formato similar a Prisma\n * ```\n *\n * @aiLimitation\n * ## ⚠️ Limitaciones de Baserow API\n *\n * **1. OR Anidado NO Soportado**\n * ```typescript\n * // ❌ INCORRECTO - OR anidado dentro de AND\n * const complexOr = {\n * where: {\n * AND: [\n * { status: 'active' },\n * { OR: [{ age: { gt: 18 } }, { verified: true }] } // ❌ NO funciona\n * ]\n * }\n * }\n * // Solución: Reestructurar query o filtrar client-side\n * ```\n *\n * **2. IN/NOT IN con Múltiples Valores**\n * ```typescript\n * // ⚠️ LIMITADO - Solo usa el primer valor\n * const query = {\n * where: {\n * status: { in: ['active', 'pending', 'processing'] } // Solo usa 'active'\n * }\n * }\n * // Baserow API no soporta IN nativo, requiere client-side filtering\n * ```\n *\n * **3. DISTINCT Client-Side Only**\n * ```typescript\n * // ⚠️ WARNING - distinct requiere post-procesamiento\n * const query = {\n * distinct: ['department'],\n * where: { active: true }\n * }\n * // Genera warning: \"Distinct is not directly supported by Baserow API\"\n * ```\n *\n * @aiMigrationFrom\n * ## 🔄 Migración desde Prisma ORM\n *\n * **Mapeo de Operadores Prisma → Baserow:**\n * - `equals` → `equal`\n * - `not` → `not_equal`\n * - `contains` → `contains`\n * - `startsWith` → `starts_with`\n * - `endsWith` → `ends_with`\n * - `gt` → `higher_than`\n * - `gte` → `higher_than_or_equal`\n * - `lt` → `lower_than`\n * - `lte` → `lower_than_or_equal`\n * - `in` (múltiple) → ⚠️ Solo primer valor (limitación API)\n * - `isEmpty` / `empty` → `empty`\n * - `isNotEmpty` / `notEmpty` → `not_empty`\n *\n * **Conversiones Automáticas:**\n * - Dates: `new Date()` → ISO string automáticamente\n * - Select fields: `value` → `optionId` (con FieldMetadata)\n * - Pagination: `take/skip` → `size/page`\n * - OrderBy: `{ field: 'desc' }` → `-field`\n *\n * @aiPerformance\n * ## ⚡ Optimizaciones de Query\n *\n * **1. Limitar Page Size Automáticamente**\n * ```typescript\n * // Si size > 1000, se limita a 1000 con warning\n * const query = { take: 5000 } // ⚠️ Muy grande\n * const baserow = PrismaBaserowMapper.transformPrismaToBaserow(query)\n * // Result: size=1000 (limitado automáticamente)\n * ```\n *\n * **2. Usar Select para Reducir Payload**\n * ```typescript\n * // ✅ BUENO - Solo campos necesarios\n * const optimized = {\n * select: { id: true, name: true }, // Solo 2 campos\n * take: 1000\n * }\n *\n * // ❌ MALO - Todos los campos\n * const heavy = {\n * take: 1000 // Trae TODOS los campos (payload grande)\n * }\n * ```\n *\n * **3. Analizar Complejidad de Query**\n * ```typescript\n * const analysis = PrismaBaserowMapper.analyzeQueryComplexity(query)\n * console.log(`Score: ${analysis.score}`) // Score alto = query compleja\n * console.log('Warnings:', analysis.warnings) // Problemas potenciales\n * console.log('Suggestions:', analysis.suggestions) // Optimizaciones sugeridas\n * ```\n *\n * **4. Evitar OR Operators (Mejor Performance)**\n * ```typescript\n * // ⚡ RÁPIDO - Filtros AND (procesamiento server-side)\n * const fast = {\n * where: {\n * AND: [{ status: 'active' }, { verified: true }]\n * }\n * }\n *\n * // 🐌 LENTO - OR operators (puede requerir client-side)\n * const slow = {\n * where: {\n * OR: [{ status: 'active' }, { status: 'pending' }]\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * const prismaOptions = {\n * where: { status: 'active', age: { gte: 18 } },\n * orderBy: { created_at: 'desc' },\n * take: 20,\n * skip: 0\n * }\n *\n * const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(prismaOptions)\n * // Result: { filters: { status: 'active', age__gte: 18 }, order_by: '-created_at', size: 20, page: 1 }\n * ```\n *\n * @since 1.1.0\n */\nexport class PrismaBaserowMapper {\n /**\n * Transformar opciones Prisma completas a formato Baserow API\n *\n * Convierte una consulta estilo Prisma en opciones compatibles con\n * la API de Baserow, aplicando todas las transformaciones necesarias\n * y optimizaciones automáticas.\n *\n * @param options - Opciones de consulta estilo Prisma\n * @returns Opciones convertidas para Baserow API\n *\n * @example\n * ```typescript\n * const result = PrismaBaserowMapper.transformPrismaToBaserow({\n * where: {\n * AND: [\n * { status: 'active' },\n * { age: { gte: 18, lt: 65 } }\n * ]\n * },\n * orderBy: [{ created_at: 'desc' }, { name: 'asc' }],\n * take: 10,\n * skip: 20,\n * select: { id: true, name: true, password: false }\n * })\n * ```\n *\n * @since 1.2.0\n */\n static transformPrismaToBaserow(options?: PrismaLikeQueryOptions, fieldMetadata?: FieldMetadata): QueryOptions {\n if (!options) return {}\n\n const baserowOptions: QueryOptions = {}\n\n // Transformar paginación: take/skip → size/page\n const pagination = this.transformPagination(options.take, options.skip)\n Object.assign(baserowOptions, pagination)\n\n // Transformar filtros: where → aplanar filtros directamente\n if (options.where) {\n const filters = this.transformWhereToFilters(options.where, fieldMetadata)\n Object.assign(baserowOptions, filters)\n\n // Detectar si hay operador OR a nivel raíz y agregar filter_type\n if (this.hasRootOrOperator(options.where)) {\n baserowOptions.filter_type = 'OR'\n }\n }\n\n // Transformar ordenamiento: orderBy → order_by\n if (options.orderBy) {\n baserowOptions.order_by = this.transformOrderByToBaserow(options.orderBy)\n }\n\n // Transformar selección: select → include/exclude\n if (options.select) {\n const fieldSelection = this.transformSelectToFields(options.select)\n Object.assign(baserowOptions, fieldSelection)\n }\n\n // Transformar distinct\n if (options.distinct && options.distinct.length > 0) {\n // Baserow no soporta distinct directamente, pero podemos documentarlo\n // eslint-disable-next-line no-console\n console.warn('Distinct is not directly supported by Baserow API, filtering will be done client-side')\n baserowOptions.distinct = options.distinct as string[]\n }\n\n // Opciones específicas de Baserow\n baserowOptions.user_field_names = options.userFieldNames ?? true\n\n return this.validateAndOptimize(baserowOptions)\n }\n\n /**\n * Convertir valor a string compatible con Baserow API\n *\n * @param value - Valor a convertir\n * @returns String compatible con Baserow\n */\n private static valueToString(value: any): string {\n if (value instanceof Date) {\n return value.toISOString()\n }\n if (value === null) {\n return 'null'\n }\n if (value === undefined) {\n return 'undefined'\n }\n return String(value)\n }\n\n /**\n * Transformar cláusula WHERE a filtros Baserow\n *\n * Convierte la sintaxis de filtrado estilo Prisma a los filtros query parameters\n * que espera la API de Baserow usando el formato:\n * filter__field__type=value\n *\n * @param where - Cláusula WHERE estilo Prisma\n * @returns Record de filtros compatible con Baserow API\n *\n * @example\n * ```typescript\n * const filters = PrismaBaserowMapper.transformWhereToFilters({\n * name: 'John',\n * age: { gte: 18, lt: 65 },\n * email: { contains: '@company.com' }\n * })\n * // Result: {\n * // \"filter__name__equal\": \"John\",\n * // \"filter__age__higher_than_or_equal\": \"18\",\n * // \"filter__age__lower_than\": \"65\",\n * // \"filter__email__contains\": \"@company.com\"\n * // }\n * ```\n *\n * @since 1.2.0\n */\n static transformWhereToFilters(where?: WhereClause, fieldMetadata?: FieldMetadata): Record<string, any> {\n if (!where) return {}\n\n const filters: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(where)) {\n // Operadores lógicos especiales\n if (key === 'AND') {\n // Combinar múltiples condiciones AND\n const andFilters = (value as WhereClause[]).map(clause => this.transformWhereToFilters(clause, fieldMetadata))\n // Fusionar todos los filtros AND (aplican todos)\n andFilters.forEach(filterObj => Object.assign(filters, filterObj))\n continue\n }\n\n if (key === 'OR') {\n // Baserow soporta OR mediante filter_type=OR (solo a nivel raíz flat)\n // Aplanar filtros OR - el filter_type se agregará en transformPrismaToBaserow\n const orFilters = (value as WhereClause[]).map(clause => this.transformWhereToFilters(clause, fieldMetadata))\n\n // Verificar si hay múltiples campos en el OR (simple) o AND anidados (complejo)\n const hasNestedOperators = orFilters.some(f =>\n Object.keys(f).some(k => k.includes('__') && Object.keys(f).length > 1)\n )\n\n if (hasNestedOperators) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Complex OR with multiple conditions per branch may not work as expected. Baserow OR only supports flat filters.'\n )\n }\n\n orFilters.forEach(filterObj => Object.assign(filters, filterObj))\n continue\n }\n\n if (key === 'NOT') {\n // Implementar NOT usando operadores de negación\n const notFilters = this.transformWhereToFilters(value as WhereClause, fieldMetadata)\n for (const [filterKey, filterValue] of Object.entries(notFilters)) {\n // Convertir tipo a versión negativa\n const negatedKey = this.negateFilterKey(filterKey)\n filters[negatedKey] = filterValue\n }\n continue\n }\n\n // Operadores de campo (excluir Date que debe tratarse como valor simple)\n if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {\n const operators = value as FieldOperators\n const fieldType = fieldMetadata?.types[key] || 'text'\n\n if ('equals' in operators) {\n this.addFieldFilter(filters, key, fieldType, 'equal', operators.equals, fieldMetadata?.selectOptions)\n }\n if ('not' in operators) {\n this.addFieldFilter(filters, key, fieldType, 'not_equal', operators.not, fieldMetadata?.selectOptions)\n }\n if ('contains' in operators) {\n this.addFieldFilter(filters, key, fieldType, 'contains', operators.contains, fieldMetadata?.selectOptions)\n }\n if ('contains_not' in operators) {\n filters[`filter__${key}__contains_not`] = this.valueToString(operators.contains_not)\n }\n if ('startsWith' in operators) {\n filters[`filter__${key}__starts_with`] = this.valueToString(operators.startsWith)\n }\n if ('endsWith' in operators) {\n filters[`filter__${key}__ends_with`] = this.valueToString(operators.endsWith)\n }\n if ('gt' in operators) {\n filters[`filter__${key}__higher_than`] = this.valueToString(operators.gt)\n }\n if ('gte' in operators) {\n filters[`filter__${key}__higher_than_or_equal`] = this.valueToString(operators.gte)\n }\n if ('lt' in operators) {\n filters[`filter__${key}__lower_than`] = this.valueToString(operators.lt)\n }\n if ('lte' in operators) {\n filters[`filter__${key}__lower_than_or_equal`] = this.valueToString(operators.lte)\n }\n if ('in' in operators) {\n const values = Array.isArray(operators.in) ? operators.in : [operators.in]\n // Filtrar valores vacíos o undefined\n const validValues = values.filter(v => v !== undefined && v !== null && v !== '')\n if (validValues.length === 1) {\n filters[`filter__${key}__equal`] = this.valueToString(validValues[0])\n } else if (validValues.length > 1) {\n // eslint-disable-next-line no-console\n console.warn('IN with multiple values requires client-side filtering in Baserow')\n filters[`filter__${key}__equal`] = this.valueToString(validValues[0]) // Usar solo el primero\n }\n // Si no hay valores válidos, no crear filtro\n }\n if ('notIn' in operators) {\n const values = Array.isArray(operators.notIn) ? operators.notIn : [operators.notIn]\n // Filtrar valores vacíos o undefined\n const validValues = values.filter(v => v !== undefined && v !== null && v !== '')\n if (validValues.length === 1) {\n filters[`filter__${key}__not_equal`] = this.valueToString(validValues[0])\n } else if (validValues.length > 1) {\n // eslint-disable-next-line no-console\n console.warn('NOT IN with multiple values requires client-side filtering in Baserow')\n filters[`filter__${key}__not_equal`] = this.valueToString(validValues[0]) // Usar solo el primero\n }\n // Si no hay valores válidos, no crear filtro\n }\n // Soporte para isEmpty y su alias empty (sintaxis Baserow)\n if (('isEmpty' in operators && operators.isEmpty) || ('empty' in operators && operators.empty)) {\n filters[`filter__${key}__empty`] = ''\n }\n // Soporte para isNotEmpty y su alias notEmpty (sintaxis Baserow)\n if (('isNotEmpty' in operators && operators.isNotEmpty) || ('notEmpty' in operators && operators.notEmpty)) {\n filters[`filter__${key}__not_empty`] = ''\n }\n } else {\n // Valor simple (equals implícito)\n const fieldType = fieldMetadata?.types[key] || 'text'\n this.addFieldFilter(filters, key, fieldType, 'equal', value, fieldMetadata?.selectOptions)\n }\n }\n\n return filters\n }\n\n /**\n * Negar una clave de filtro de Baserow\n *\n * @param filterKey - Clave de filtro a negar (formato filter__field__type)\n * @returns Clave de filtro negada\n */\n private static negateFilterKey(filterKey: string): string {\n const negationMap: Record<string, string> = {\n equal: 'not_equal',\n not_equal: 'equal',\n contains: 'contains_not',\n contains_not: 'contains',\n higher_than: 'lower_than_or_equal',\n lower_than: 'higher_than_or_equal',\n higher_than_or_equal: 'lower_than',\n lower_than_or_equal: 'higher_than',\n empty: 'not_empty',\n not_empty: 'empty'\n }\n\n // Parsear la clave: filter__field__type\n const parts = filterKey.split('__')\n if (parts.length === 3 && parts[0] === 'filter') {\n const [, field, filterType] = parts\n const negatedType = negationMap[filterType] || `not_${filterType}`\n return `filter__${field}__${negatedType}`\n }\n\n return filterKey // Si no puede parsear, devolver original\n }\n\n /**\n * Transformar configuración orderBy a string order_by de Baserow\n *\n * Convierte la configuración de ordenamiento estilo Prisma a la\n * sintaxis de string que espera la API de Baserow, soportando\n * ordenamiento por múltiples campos.\n *\n * @param orderBy - Configuración de ordenamiento Prisma\n * @returns String de ordenamiento para Baserow API\n *\n * @example\n * ```typescript\n * // Ordenamiento simple\n * const result1 = PrismaBaserowMapper.transformOrderByToBaserow({ name: 'asc' })\n * // Result: 'name'\n *\n * // Ordenamiento múltiple\n * const result2 = PrismaBaserowMapper.transformOrderByToBaserow([\n * { created_at: 'desc' },\n * { name: 'asc' }\n * ])\n * // Result: '-created_at,name'\n * ```\n *\n * @since 1.2.0\n */\n static transformOrderByToBaserow(orderBy?: OrderByClause): string | undefined {\n if (!orderBy) return undefined\n\n const orders: string[] = []\n\n if (Array.isArray(orderBy)) {\n // Múltiples campos de ordenamiento\n for (const orderItem of orderBy) {\n for (const [field, direction] of Object.entries(orderItem)) {\n const prefix = direction === 'desc' ? '-' : ''\n orders.push(`${prefix}${field}`)\n }\n }\n } else {\n // Un solo objeto de ordenamiento\n for (const [field, direction] of Object.entries(orderBy)) {\n const prefix = direction === 'desc' ? '-' : ''\n orders.push(`${prefix}${field}`)\n }\n }\n\n return orders.length > 0 ? orders.join(',') : undefined\n }\n\n /**\n * Transformar configuración select a include/exclude de Baserow\n *\n * Convierte la selección de campos estilo Prisma a los arrays\n * include/exclude que utiliza la API de Baserow para filtrar campos.\n *\n * @param select - Configuración de selección Prisma\n * @returns Objeto con arrays include/exclude para Baserow\n *\n * @example\n * ```typescript\n * const result = PrismaBaserowMapper.transformSelectToFields({\n * id: true,\n * name: true,\n * email: true,\n * password: false,\n * internal_notes: false\n * })\n * // Result: { include: ['id', 'name', 'email'] }\n * ```\n *\n * @since 1.2.0\n */\n static transformSelectToFields(select?: SelectClause): { include?: string[]; exclude?: string[] } {\n if (!select) return {}\n\n const include: string[] = []\n const exclude: string[] = []\n\n for (const [field, selected] of Object.entries(select)) {\n if (selected === true) {\n include.push(field)\n } else if (selected === false) {\n exclude.push(field)\n }\n }\n\n // Estrategia: si hay campos incluidos, usar include. Si no, usar exclude\n if (include.length > 0) {\n return { include }\n } else if (exclude.length > 0) {\n return { exclude }\n }\n\n return {}\n }\n\n /**\n * Transformar take/skip a page/size de Baserow\n *\n * Convierte la paginación estilo Prisma (take/skip) al formato\n * page/size que utiliza la API de Baserow.\n *\n * @param take - Número máximo de registros (equivale a size)\n * @param skip - Número de registros a saltar\n * @returns Objeto con page/size para Baserow\n *\n * @example\n * ```typescript\n * const result = PrismaBaserowMapper.transformPagination(20, 40)\n * // Result: { size: 20, page: 3 } // skip 40 with size 20 = page 3\n * ```\n *\n * @since 1.2.0\n */\n static transformPagination(take?: number, skip?: number): { page?: number; size?: number } {\n const result: { page?: number; size?: number } = {}\n\n if (take !== undefined) {\n result.size = take\n }\n\n if (skip !== undefined && take !== undefined && take > 0) {\n result.page = Math.floor(skip / take) + 1\n } else if (skip !== undefined) {\n // Si solo hay skip sin take, asumir size por defecto\n const defaultSize = 100\n result.size = defaultSize\n result.page = Math.floor(skip / defaultSize) + 1\n }\n\n return result\n }\n\n /**\n * Validar y optimizar consulta final\n *\n * Aplica validaciones y optimizaciones automáticas a las opciones\n * de consulta convertidas para mejorar rendimiento y evitar errores.\n *\n * @param options - Opciones de consulta a optimizar\n * @returns Opciones optimizadas y validadas\n *\n * @example\n * ```typescript\n * const optimized = PrismaBaserowMapper.validateAndOptimize({\n * page: 0, // Se corrige a 1\n * size: 2000, // Se limita a 1000\n * filters: {}, // Se elimina\n * include: [] // Se elimina\n * })\n * // Result: { page: 1, size: 1000 }\n * ```\n *\n * @since 1.2.0\n */\n static validateAndOptimize(options: QueryOptions): QueryOptions {\n const optimized = { ...options }\n\n // Validar paginación\n if (optimized.page !== undefined && optimized.page < 1) {\n optimized.page = 1\n }\n\n if (optimized.size !== undefined && optimized.size > 1000) {\n // eslint-disable-next-line no-console\n console.warn('Large page size detected, limiting to 1000. Consider using pagination.')\n optimized.size = 1000\n }\n\n if (optimized.size !== undefined && optimized.size < 1) {\n optimized.size = 1\n }\n\n // Limpiar filtros vacíos\n if (optimized.filters && Object.keys(optimized.filters).length === 0) {\n delete optimized.filters\n }\n\n // Limpiar arrays vacíos\n if (optimized.include && optimized.include.length === 0) {\n delete optimized.include\n }\n if (optimized.exclude && optimized.exclude.length === 0) {\n delete optimized.exclude\n }\n if (optimized.distinct && optimized.distinct.length === 0) {\n delete optimized.distinct\n }\n\n // Validar conflictos include/exclude\n if (optimized.include && optimized.exclude) {\n // eslint-disable-next-line no-console\n console.warn('Both include and exclude specified, using include only')\n delete optimized.exclude\n }\n\n return optimized\n }\n\n /**\n * Crear where clause a partir de filtros legacy de Baserow\n *\n * Función de utilidad para convertir en dirección opuesta:\n * de filtros Baserow existentes a sintaxis where de Prisma.\n *\n * @param filters - Filtros en formato Baserow\n * @returns Cláusula WHERE estilo Prisma\n *\n * @example\n * ```typescript\n * const where = PrismaBaserowMapper.transformFiltersToWhere({\n * name: 'John',\n * age__gte: 18,\n * email__contains: '@company.com'\n * })\n * // Result: { name: 'John', age: { gte: 18 }, email: { contains: '@company.com' } }\n * ```\n *\n * @since 1.2.0\n */\n static transformFiltersToWhere(filters?: Record<string, any>): WhereClause {\n if (!filters) return {}\n\n const where: WhereClause = {}\n\n for (const [key, value] of Object.entries(filters)) {\n // Detectar operadores Baserow y convertir a Prisma\n if (key.includes('__')) {\n const [field, operator] = key.split('__', 2)\n\n if (!where[field] || typeof where[field] !== 'object') {\n where[field] = {}\n }\n const fieldWhere = where[field] as any\n\n switch (operator) {\n case 'contains':\n fieldWhere.contains = value\n break\n case 'startswith':\n fieldWhere.startsWith = value\n break\n case 'endswith':\n fieldWhere.endsWith = value\n break\n case 'gt':\n fieldWhere.gt = value\n break\n case 'gte':\n fieldWhere.gte = value\n break\n case 'lt':\n fieldWhere.lt = value\n break\n case 'lte':\n fieldWhere.lte = value\n break\n case 'in':\n fieldWhere.in = typeof value === 'string' ? value.split(',') : value\n break\n case 'not_in':\n fieldWhere.notIn = typeof value === 'string' ? value.split(',') : value\n break\n case 'not':\n fieldWhere.not = value\n break\n case 'empty':\n fieldWhere.isEmpty = Boolean(value)\n break\n case 'not_empty':\n fieldWhere.isNotEmpty = Boolean(value)\n break\n default:\n // Operador no reconocido, mantener como filtro legacy\n where[key] = value\n }\n } else {\n // Campo simple sin operador\n where[key] = value\n }\n }\n\n return where\n }\n\n /**\n * Detectar si hay operador OR a nivel raíz (para filter_type)\n *\n * Verifica si la consulta tiene un operador OR en el nivel raíz,\n * lo cual permite usar filter_type=OR en Baserow API.\n *\n * @param where - Cláusula WHERE a analizar\n * @returns true si contiene OR a nivel raíz\n *\n * @since 1.3.0\n */\n static hasRootOrOperator(where?: WhereClause): boolean {\n if (!where) return false\n return where.OR !== undefined && Array.isArray(where.OR) && where.OR.length > 0\n }\n\n /**\n * Detectar si hay operadores OR en la consulta (incluye anidados)\n *\n * Utilidad para verificar si una consulta contiene operadores OR\n * que requieren procesamiento especial en el cliente.\n *\n * @param where - Cláusula WHERE a analizar\n * @returns true si contiene operadores OR\n *\n * @since 1.2.0\n */\n static hasOrOperators(where?: WhereClause): boolean {\n if (!where) return false\n\n if (where.OR && Array.isArray(where.OR) && where.OR.length > 0) {\n return true\n }\n\n // Buscar OR anidados\n for (const value of Object.values(where)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n if (this.hasOrOperators(value as WhereClause)) {\n return true\n }\n }\n }\n\n return false\n }\n\n /**\n * Estimar complejidad de la consulta\n *\n * Analiza una consulta para determinar su complejidad y\n * sugerir optimizaciones si es necesario.\n *\n * @param options - Opciones de consulta a analizar\n * @returns Métricas de complejidad\n *\n * @since 1.2.0\n */\n static analyzeQueryComplexity(options?: PrismaLikeQueryOptions): {\n score: number\n warnings: string[]\n suggestions: string[]\n } {\n const warnings: string[] = []\n const suggestions: string[] = []\n let score = 0\n\n if (!options) return { score: 0, warnings, suggestions }\n\n // Analizar filtros\n if (options.where) {\n const filterCount = Object.keys(options.where).length\n score += filterCount\n\n if (this.hasOrOperators(options.where)) {\n score += 5\n warnings.push('OR operators require client-side processing')\n suggestions.push('Consider restructuring query to avoid OR operators')\n }\n\n if (filterCount > 10) {\n warnings.push('High number of filters may impact performance')\n suggestions.push('Consider using fewer, more specific filters')\n }\n }\n\n // Analizar paginación\n if (options.take && options.take > 500) {\n score += 3\n warnings.push('Large page size may impact performance')\n suggestions.push('Consider using smaller page sizes with pagination')\n }\n\n // Analizar ordenamiento\n if (options.orderBy) {\n const orderCount = Array.isArray(options.orderBy) ? options.orderBy.length : 1\n score += orderCount\n if (orderCount > 3) {\n warnings.push('Multiple order fields may impact performance')\n }\n }\n\n return { score, warnings, suggestions }\n }\n\n /**\n * Agregar filtro específico según tipo de campo con resolución automática value → ID\n *\n * @private\n * @param filters - Objeto de filtros a modificar\n * @param fieldName - Nombre del campo\n * @param fieldType - Tipo del campo (text, single_select, etc.)\n * @param operator - Operador lógico (equal, contains, etc.)\n * @param value - Valor del filtro\n * @param selectOptions - Mapa de opciones select para resolución value → ID\n */\n private static addFieldFilter(\n filters: Record<string, any>,\n fieldName: string,\n fieldType: string,\n operator: string,\n value: any,\n selectOptions?: Record<string, Record<string, number>>\n ): void {\n let baserowOperator: string\n let resolvedValue = value\n\n // Resolver value → ID para campos select automáticamente\n if (['single_select', 'multiple_select'].includes(fieldType)) {\n const fieldOptions = selectOptions?.[fieldName]\n if (fieldOptions && typeof value === 'string') {\n const optionId = fieldOptions[value]\n if (optionId !== undefined) {\n resolvedValue = optionId\n }\n }\n }\n\n // Mapear operadores específicos por tipo de campo\n switch (fieldType) {\n case 'single_select':\n baserowOperator = operator === 'equal' ? 'single_select_equal' : `single_select_${operator}`\n break\n case 'multiple_select':\n // Para multiple_select, mapear operadores específicos\n if (operator === 'equal' || operator === 'contains') {\n baserowOperator = 'multiple_select_has'\n } else {\n baserowOperator = `multiple_select_${operator}`\n }\n break\n default:\n // Para campos normales, usar operador estándar\n baserowOperator = operator\n }\n\n filters[`filter__${fieldName}__${baserowOperator}`] = this.valueToString(resolvedValue)\n }\n}\n","import { FieldService } from '../services/FieldService'\nimport { Field, Logger } from '../types'\n\n/**\n * Metadata de campos con tipos y opciones select\n */\nexport interface FieldMetadata {\n /** Mapa de nombre de campo → tipo de campo */\n types: Record<string, string>\n /** Mapa de nombre de campo → {valor: id} para campos select */\n selectOptions: Record<string, Record<string, number>>\n}\n\n/**\n * Cache centralizado para metadata de campos de tablas\n *\n * Proporciona un sistema de cache eficiente para tipos de campo y opciones\n * de campos select/multiselect, permitiendo resolver automáticamente\n * valores de texto a IDs de opción para filtros de Baserow.\n *\n * **Características:**\n * - Cache por tabla (tableId) para evitar conflictos\n * - Mapeo automático value → ID para campos select\n * - Invalidación granular por tabla o completa\n * - Logging opcional para debugging\n * - Performance optimizada con Map interna\n *\n * @example\n * ```typescript\n * const cache = new FieldCache(logger)\n * const metadata = await cache.getFieldMetadata(fieldService, 123)\n *\n * // Tipos de campo: metadata.types['categoria'] // 'single_select'\n * // Opciones select: metadata.selectOptions['categoria']['Premium'] // 45\n * ```\n *\n * @since 1.2.0\n */\nexport class FieldCache {\n private cache = new Map<number, FieldMetadata>()\n private logger?: Logger\n\n /**\n * Crear nueva instancia de FieldCache\n *\n * @param logger - Logger opcional para debugging y trazabilidad\n */\n constructor(logger?: Logger) {\n this.logger = logger\n }\n\n /**\n * Obtener metadata completa de campos para una tabla\n *\n * Retorna tipos de campo y opciones de select con cache automático.\n * Si los datos no están en cache, hace fetch automático y los almacena.\n *\n * @param fieldService - Servicio de campos para hacer fetch si necesario\n * @param tableId - ID numérico de la tabla\n * @returns Promise con metadata completa de campos\n *\n * @example\n * ```typescript\n * const metadata = await cache.getFieldMetadata(fieldService, 123)\n *\n * // Usar tipos\n * const fieldType = metadata.types['categoria'] // 'single_select'\n *\n * // Resolver valor a ID\n * const optionId = metadata.selectOptions['categoria']['Premium'] // 45\n * ```\n */\n async getFieldMetadata(fieldService: FieldService, tableId: number): Promise<FieldMetadata> {\n if (!this.cache.has(tableId)) {\n const fields = await fieldService.findMany(tableId)\n const metadata = this.buildMetadata(fields, tableId)\n this.cache.set(tableId, metadata)\n }\n return this.cache.get(tableId)!\n }\n\n /**\n * Construir metadata a partir de array de campos\n *\n * @private\n * @param fields - Array de campos de la tabla\n * @param tableId - ID de la tabla (para logging)\n * @returns Metadata construida\n */\n private buildMetadata(fields: Field[], tableId: number): FieldMetadata {\n const types: Record<string, string> = {}\n const selectOptions: Record<string, Record<string, number>> = {}\n\n for (const field of fields) {\n // Mapear nombre → tipo\n types[field.name] = field.type\n\n // Para campos select, construir mapeo value → id\n if (['single_select', 'multiple_select'].includes(field.type) && field.select_options) {\n selectOptions[field.name] = Object.fromEntries(\n field.select_options.map(option => [option.value, option.id]).filter(([, id]) => id !== undefined)\n )\n }\n }\n\n // Logging detallado para debugging\n this.logger?.debug?.(`Field metadata cached for table ${tableId}`, {\n fieldCount: fields.length,\n selectFieldCount: Object.keys(selectOptions).length,\n typeDistribution: Object.values(types).reduce(\n (acc, type) => {\n acc[type] = (acc[type] || 0) + 1\n return acc\n },\n {} as Record<string, number>\n ),\n selectFields: Object.keys(selectOptions)\n })\n\n return { types, selectOptions }\n }\n\n /**\n * Invalidar cache para una tabla específica o completo\n *\n * Útil cuando se sabe que los campos de una tabla han cambiado\n * (campos agregados, eliminados, o opciones select modificadas).\n *\n * @param tableId - ID de tabla específica a invalidar. Si no se proporciona, limpia todo el cache\n *\n * @example\n * ```typescript\n * // Invalidar tabla específica\n * cache.clearCache(123)\n *\n * // Limpiar todo el cache\n * cache.clearCache()\n * ```\n */\n clearCache(tableId?: number): void {\n if (tableId !== undefined) {\n const wasDeleted = this.cache.delete(tableId)\n this.logger?.debug?.(`Cache cleared for table ${tableId}`, { wasDeleted })\n } else {\n const cacheSize = this.cache.size\n this.cache.clear()\n this.logger?.debug?.(`Full cache cleared`, { clearedTables: cacheSize })\n }\n }\n\n /**\n * Obtener estadísticas del cache\n *\n * Útil para debugging y monitoreo de performance.\n *\n * @returns Estadísticas del cache\n */\n getCacheStats(): { cachedTables: number; tableIds: number[] } {\n return {\n cachedTables: this.cache.size,\n tableIds: Array.from(this.cache.keys())\n }\n }\n}\n","import { Logger, Table, Row, PrismaLikeQueryOptions } from '../types'\nimport { TableService } from '../services/TableService'\nimport { FieldService } from '../services/FieldService'\nimport { RowService } from '../services/RowService'\nimport { PrismaBaserowMapper } from '../utils/prisma-mapper'\nimport { FieldCache } from '../utils/field-cache'\n\n/**\n * Context para operaciones en una tabla específica con BaserowClient\n * Proporciona API jerárquica para Database Token operations a nivel table\n */\nexport class TableClientContext {\n private databaseId: number\n private tableId: number\n private resolvedTable?: Table\n private logger?: Logger\n private fieldCache: FieldCache\n\n constructor(\n databaseId: number,\n tableId: number,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n logger?: Logger\n ) {\n this.databaseId = databaseId\n this.tableId = tableId\n this.logger = logger\n this.fieldCache = new FieldCache(logger)\n }\n\n /**\n * Obtener metadata completa de campos con cache\n *\n * @private\n * @returns FieldMetadata con tipos y opciones select\n */\n private async getFieldMetadata() {\n return await this.fieldCache.getFieldMetadata(this.fieldService, this.tableId)\n }\n\n /**\n * Operaciones de rows en esta table (API Prisma-like)\n */\n get rows() {\n return {\n // === MÉTODOS ESTILO PRISMA ===\n\n /**\n * Buscar múltiples filas con opciones avanzadas estilo Prisma\n *\n * Equivalente a Prisma findMany(), proporciona filtrado avanzado,\n * ordenamiento, paginación y selección de campos con sintaxis familiar.\n *\n * @param options - Opciones de consulta estilo Prisma\n * @returns Promise con array de filas que cumplen los criterios\n *\n * @example\n * ```typescript\n * const users = await table.rows.findMany({\n * where: {\n * status: 'active',\n * age: { gte: 18 }\n * },\n * select: {\n * id: true,\n * name: true,\n * email: true\n * },\n * orderBy: { created_at: 'desc' },\n * take: 20\n * })\n * ```\n */\n findMany: async <T = Row>(options?: PrismaLikeQueryOptions<T>): Promise<T[]> => {\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(options, fieldMetadata)\n const response = await this.rowService.list(this.tableId, baserowOptions)\n return response.rows as T[]\n },\n\n /**\n * Buscar TODAS las filas de la tabla con paginación automática\n *\n * Similar a findMany() pero sin límites de paginación. Descarga\n * automáticamente todas las filas de la tabla aplicando los filtros\n * especificados. Ideal para operaciones de exportación o análisis completo.\n *\n * @param options - Opciones de consulta (sin take/skip ya que obtiene todas)\n * @returns Promise con TODAS las filas que cumplen los criterios\n *\n * @example\n * ```typescript\n * // Descargar tabla completa\n * const allUsers = await client.table(123).rows.findAll()\n *\n * // Con filtros aplicados\n * const allActiveUsers = await client.table(123).rows.findAll({\n * where: { status: 'active' },\n * orderBy: { created_at: 'desc' }\n * })\n * ```\n *\n * @since 1.1.0\n */\n findAll: async <T = Row>(options?: Omit<PrismaLikeQueryOptions<T>, 'take' | 'skip'>): Promise<T[]> => {\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(options, fieldMetadata)\n return (await this.rowService.listAll(this.tableId, baserowOptions)) as T[]\n },\n\n /**\n * Buscar la primera fila que cumpla los criterios\n *\n * @param options - Opciones de consulta estilo Prisma\n * @returns Promise con la primera fila encontrada o null\n */\n findFirst: async <T = Row>(options?: PrismaLikeQueryOptions<T>): Promise<T | null> => {\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow({ ...options, take: 1 }, fieldMetadata)\n const response = await this.rowService.list(this.tableId, baserowOptions)\n return response.rows.length > 0 ? (response.rows[0] as T) : null\n },\n\n /**\n * Buscar fila específica por ID o criterios únicos\n *\n * @param idOrOptions - ID de la fila o opciones de búsqueda\n * @returns Promise con la fila encontrada o null\n */\n findUnique: async <T = Row>(idOrOptions: number | PrismaLikeQueryOptions<T>): Promise<T | null> => {\n if (typeof idOrOptions === 'number') {\n try {\n const row = await this.rowService.get(this.tableId, idOrOptions)\n return row as T\n } catch {\n return null\n }\n } else {\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(\n { ...idOrOptions, take: 1 },\n fieldMetadata\n )\n const response = await this.rowService.list(this.tableId, baserowOptions)\n return response.rows.length > 0 ? (response.rows[0] as T) : null\n }\n },\n\n /**\n * Crear una fila individual\n *\n * @param data - Datos para crear la fila\n * @returns Promise con la fila creada\n */\n create: async <T = Row>(data: Partial<T> | Record<string, any>): Promise<T> => {\n return (await this.rowService.createRow(this.tableId, data as Record<string, any>)) as T\n },\n\n /**\n * Crear múltiples filas\n *\n * @param data - Array de datos para crear filas\n * @param options - Opciones de creación\n * @returns Promise con las filas creadas\n */\n createMany: async <T = Row>(data: Partial<T>[] | Record<string, any>[], options?: any): Promise<T[]> => {\n return (await this.rowService.createBulk(this.tableId, data as Record<string, any>[], options)) as T[]\n },\n\n /**\n * Actualizar múltiples filas\n *\n * @param updates - Array de actualizaciones con ID\n * @param options - Opciones de actualización\n * @returns Promise con las filas actualizadas\n */\n updateMany: async <T = Row>(\n updates: Array<{ id: number } & Partial<T>> | Array<{ id: number } & Record<string, any>>,\n options?: any\n ): Promise<T[]> => {\n return (await this.rowService.updateBulk(\n this.tableId,\n updates as Array<{ id: number } & Record<string, any>>,\n options\n )) as T[]\n },\n\n /**\n * Eliminar múltiples filas\n *\n * @param ids - Array de IDs a eliminar\n * @param options - Opciones de eliminación\n * @returns Promise que resuelve cuando se completa\n */\n deleteMany: async (ids: number[], options?: any): Promise<void> => {\n await this.rowService.deleteBulk(this.tableId, ids, options)\n },\n\n // === MÉTODOS ESPECÍFICOS (sin equivalente Prisma directo) ===\n\n /**\n * Buscar rows por texto\n */\n search: async (query: string, options?: any) => {\n return await this.rowService.search(this.tableId, query, options)\n },\n\n /**\n * Contar rows con filtros opcionales (sintaxis Prisma)\n */\n count: async (options?: { where?: Record<string, any> }): Promise<number> => {\n const fieldMetadata = await this.getFieldMetadata()\n const filters = PrismaBaserowMapper.transformWhereToFilters(options?.where, fieldMetadata)\n return await this.rowService.count(this.tableId, filters)\n },\n\n /**\n * Listar todas las rows de la tabla (con paginación automática)\n */\n listAll: async (options?: any) => {\n return await this.rowService.listAll(this.tableId, options)\n }\n }\n }\n\n /**\n * Acceder a una fila específica en esta table\n */\n row(rowId: number) {\n return {\n /**\n * Obtener fila por ID\n */\n get: async (options?: any) => {\n return await this.rowService.get(this.tableId, rowId, options)\n },\n\n /**\n * Actualizar fila\n */\n update: async (data: Record<string, any>) => {\n return await this.rowService.updateRow(this.tableId, rowId, data)\n },\n\n /**\n * Eliminar fila\n */\n delete: async () => {\n return await this.rowService.delete(this.tableId, rowId)\n },\n\n /**\n * Verificar si fila existe\n */\n exists: async () => {\n return await this.rowService.exists(this.tableId, rowId)\n }\n }\n }\n\n /**\n * Obtener información de la table (lazy loading)\n */\n async get(): Promise<Table> {\n if (this.resolvedTable) {\n return this.resolvedTable\n }\n\n this.resolvedTable = await this.tableService.get(this.tableId)\n\n if (this.logger) {\n this.logger.debug?.(`Resolved table: \"${this.resolvedTable.name}\" (ID: ${this.resolvedTable.id})`)\n }\n\n return this.resolvedTable\n }\n\n /**\n * Verificar si la table existe\n */\n async exists(): Promise<boolean> {\n try {\n await this.get()\n return true\n } catch {\n return false\n }\n }\n}\n","import { Logger, Row } from '../types'\nimport { RowService } from '../services/RowService'\n\n/**\n * Context para operaciones en una fila específica con TableOnlyContext\n * Proporciona API simplificada para operaciones CRUD de una sola fila\n */\nexport class RowOnlyContext {\n constructor(\n private tableId: number | (() => Promise<number>),\n private rowId: number,\n private rowService: RowService,\n private logger?: Logger\n ) {}\n\n private async getTableId(): Promise<number> {\n if (typeof this.tableId === 'function') {\n return await this.tableId()\n }\n return this.tableId\n }\n\n /**\n * Obtener los datos de la fila\n *\n * @returns Promise con los datos de la fila\n *\n * @example\n * ```typescript\n * const rowData = await client.table(123).row(456).get()\n * console.log(rowData.name)\n * ```\n */\n async get(): Promise<Row> {\n const tableId = await this.getTableId()\n this.logger?.info(`[RowOnlyContext] Getting row ${this.rowId} from table ${tableId}`)\n return await this.rowService.get(tableId, this.rowId, true)\n }\n\n /**\n * Actualizar los datos de la fila\n *\n * @param data - Datos parciales para actualizar\n * @returns Promise con los datos actualizados de la fila\n *\n * @example\n * ```typescript\n * const updated = await client.table(123).row(456).update({\n * name: 'Nuevo nombre',\n * active: false\n * })\n * ```\n */\n async update(data: Partial<Row>): Promise<Row> {\n const tableId = await this.getTableId()\n this.logger?.info(`[RowOnlyContext] Updating row ${this.rowId} in table ${tableId}`)\n return await this.rowService.updateRow(tableId, this.rowId, data)\n }\n\n /**\n * Eliminar la fila\n *\n * @returns Promise que se resuelve cuando la fila es eliminada\n *\n * @example\n * ```typescript\n * await client.table(123).row(456).delete()\n * console.log('Fila eliminada')\n * ```\n */\n async delete(): Promise<void> {\n const tableId = await this.getTableId()\n this.logger?.info(`[RowOnlyContext] Deleting row ${this.rowId} from table ${tableId}`)\n await this.rowService.delete(tableId, this.rowId)\n }\n\n /**\n * Verificar si la fila existe\n *\n * @returns Promise con true si existe, false si no\n *\n * @example\n * ```typescript\n * const exists = await client.table(123).row(456).exists()\n * if (exists) {\n * console.log('La fila existe')\n * }\n * ```\n */\n async exists(): Promise<boolean> {\n try {\n const tableId = await this.getTableId()\n await this.rowService.get(tableId, this.rowId)\n return true\n } catch {\n return false\n }\n }\n}\n","import { Logger } from '../types'\nimport { TableClientContext } from './TableClientContext'\nimport { RowOnlyContext } from './RowContext'\nimport { TableService } from '../services/TableService'\nimport { FieldService } from '../services/FieldService'\nimport { RowService } from '../services/RowService'\n\n/**\n * Context para acceso directo a una tabla específica con Database Token\n *\n * A diferencia de TableContext que requiere database context, este contexto\n * permite acceso directo a una tabla específica usando solo su ID.\n * Diseñado para Database Tokens que ya conocen los IDs de tabla específicos.\n */\nexport class TableOnlyContext {\n private tableId: number\n private logger?: Logger\n\n constructor(\n tableId: number,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n logger?: Logger\n ) {\n this.tableId = tableId\n this.logger = logger\n }\n\n /**\n * Obtener información de la tabla\n */\n async get() {\n return await this.tableService.get(this.tableId)\n }\n\n /**\n * Verificar si la tabla existe\n */\n async exists(): Promise<boolean> {\n return await this.tableService.exists(this.tableId)\n }\n\n /**\n * Acceso a operaciones de filas de esta tabla\n */\n get rows() {\n return new TableClientContext(\n 0, // databaseId no se usa en operaciones de rows con Database Token\n this.tableId,\n this.tableService,\n this.fieldService,\n this.rowService,\n this.logger\n ).rows\n }\n\n /**\n * Acceso a una fila específica por ID\n *\n * @param rowId - ID de la fila\n * @returns Contexto de fila individual para operaciones CRUD\n *\n * @example\n * ```typescript\n * const row = await client.table(123).row(456).get()\n * await client.table(123).row(456).update({ name: 'Nuevo nombre' })\n * await client.table(123).row(456).delete()\n * ```\n */\n row(rowId: number): RowOnlyContext {\n return new RowOnlyContext(this.tableId, rowId, this.rowService, this.logger)\n }\n}\n","/**\n * Clase base abstracta para todos los clientes de Baserow\n * Proporciona funcionalidades comunes como getConfig() unificado\n */\n\nimport { BaseService } from './BaseService'\nimport { HttpClient } from '../../utils/axios'\nimport { Logger } from '../../types/index'\n\nexport abstract class BaseClient<TConfig = any, TExcludeKeys extends keyof TConfig = never> extends BaseService {\n protected config: TConfig\n\n constructor(config: TConfig, http: HttpClient, logger?: Logger) {\n super(http, logger)\n this.config = config\n }\n\n /**\n * Obtener configuración del cliente sin campos sensibles\n *\n * Implementación unificada que excluye automáticamente campos sensibles\n * como credenciales, manteniendo consistencia entre todos los clientes.\n *\n * @param excludeKeys - Campos adicionales a excluir de la configuración\n * @returns Configuración del cliente sin campos sensibles\n *\n * @example\n * ```typescript\n * const config = client.getConfig()\n * console.log('URL:', config.url)\n * console.log('Performance:', config.performance)\n * // credentials no aparece en el resultado\n * ```\n */\n protected getConfigBase<K extends keyof TConfig = TExcludeKeys>(\n excludeKeys: K[] = [] as K[]\n ): Readonly<Omit<TConfig, K | TExcludeKeys>> {\n const result = { ...this.config } as any\n\n // Excluir campos predefinidos para este tipo de cliente\n const defaultExcludes = this.getDefaultExcludeKeys()\n const allExcludes = [...defaultExcludes, ...excludeKeys]\n\n allExcludes.forEach(key => delete result[key])\n\n return result\n }\n\n /**\n * Campos que deben excluirse por defecto para este tipo de cliente\n * Subclases pueden sobrescribir este método para definir exclusiones específicas\n */\n protected abstract getDefaultExcludeKeys(): string[]\n\n /**\n * Verificar si el servidor Baserow está disponible (health check)\n *\n * Realiza una verificación de conectividad básica al servidor Baserow\n * sin requerir autenticación. Útil para verificar disponibilidad del servicio.\n *\n * @returns Promise que resuelve a true si el servidor está disponible\n *\n * @example\n * ```typescript\n * const isHealthy = await client.health()\n * if (!isHealthy) {\n * console.log('Servidor Baserow no disponible')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async health(): Promise<boolean> {\n try {\n // Usar endpoint de salud oficial (no requiere autenticación)\n const healthUrl = `${(this.config as any).url.replace(/\\/$/, '')}/api/_health/`\n const response = await fetch(healthUrl, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' }\n })\n\n return response.ok\n } catch (error) {\n this.logError('Health check failed', error)\n return false\n }\n }\n\n /**\n * Destruir cliente y liberar recursos\n *\n * Limpia todas las conexiones HTTP, timers y recursos del cliente.\n * Debe llamarse cuando el cliente ya no se va a usar para evitar memory leaks.\n *\n * @example\n * ```typescript\n * // Al final de la aplicación o cuando ya no se necesite el cliente\n * client.destroy()\n * ```\n *\n * @since 1.0.0\n */\n destroy(): void {\n this.logDebug(`Destroying ${this.constructor.name}`)\n this.http.destroy()\n }\n}\n","import { BaserowConfig, TableFromAllTables } from './types'\nimport { createHttpClient } from './utils/httpFactory'\nimport { validateConfig } from './utils/validation'\nimport { PerformanceManager } from './utils/performance'\nimport { TableService } from './services/TableService'\nimport { FieldService } from './services/FieldService'\nimport { RowService } from './services/RowService'\nimport { TableOnlyContext } from './contexts/TableOnlyContext'\nimport { BaseClient } from './services/core/BaseClient'\n\n/**\n * Cliente interno para operaciones CRUD de datos con Database Token\n *\n * ⚠️ **USO INTERNO**: Esta clase no debe usarse directamente.\n * Usar `BaserowClient.create()` en su lugar.\n *\n * Proporciona acceso de solo lectura/escritura a datos de Baserow sin capacidades administrativas.\n * Ideal para aplicaciones cliente que necesitan acceder a datos específicos de una base de datos.\n *\n * **Características:**\n * - Autenticación mediante Database Token (no requiere credenciales de usuario)\n * - API directa: `client.tables.findMany()` y `client.table(id).rows.list()`\n * - Solo operaciones de datos: CRUD de filas, lectura de esquemas\n * - Sin capacidades administrativas: no puede crear/modificar estructura\n * - Rate limiting automático y retry logic integrado\n *\n * @internal\n * @since 1.0.0\n */\nexport class ClientWithToken extends BaseClient<BaserowConfig> {\n // Servicios para operaciones directas de tablas\n private tableService: TableService\n private fieldService: FieldService\n private rowService: RowService\n\n // API pública de tables (acceso directo para Database Token)\n public tables: {\n findMany(): Promise<TableFromAllTables[]>\n findUnique(id: number): Promise<TableFromAllTables | null>\n }\n\n /**\n * Crear una nueva instancia de ClientWithToken\n *\n * ⚠️ **USO INTERNO**: Usar `BaserowClient.create()` en su lugar.\n *\n * @param config - Configuración del cliente\n * @param config.url - URL del servidor Baserow\n * @param config.token - Database Token para autenticación\n * @param config.logger - Logger opcional para debugging\n *\n * @example\n * ```typescript\n * // ❌ No usar directamente\n * const client = new ClientWithToken(config)\n *\n * // ✅ Usar la API unificada\n * const client = await BaserowClient.create({\n * url: 'https://baserow.example.com',\n * token: process.env.BASEROW_DB_TOKEN\n * })\n *\n * // Nueva API simplificada para Database Tokens\n * const tables = await client.tables.findMany()\n * const rows = await client.table(123).rows.findMany()\n * ```\n *\n * @internal\n * @since 1.0.0\n */\n constructor(config: BaserowConfig) {\n validateConfig(config)\n\n // Procesar performance con defaults antes de guardar\n const processedConfig = {\n ...config,\n performance: PerformanceManager.merge(config.performance)\n }\n\n const http = createHttpClient({\n url: processedConfig.url,\n token: processedConfig.token,\n logger: processedConfig.logger,\n performance: processedConfig.performance\n })\n\n super(processedConfig, http, processedConfig.logger)\n\n // Inicializar servicios para operaciones directas de tablas\n this.tableService = new TableService(this.http, this.logger)\n this.fieldService = new FieldService(this.http, this.logger)\n this.rowService = new RowService(this.http, this.logger)\n\n // Inicializar API de tables (acceso directo para Database Token)\n this.tables = {\n /**\n * Listar todas las tablas accesibles por el Database Token\n * Usa el endpoint /database/tables/all-tables/ específico para tokens\n */\n findMany: async (): Promise<TableFromAllTables[]> => {\n return await this.tableService.findAllTablesWithToken()\n },\n\n /**\n * Buscar tabla específica por ID\n * Busca en las tablas accesibles al Database Token\n */\n findUnique: async (id: number): Promise<TableFromAllTables | null> => {\n const tables = await this.tableService.findAllTablesWithToken()\n return tables.find(table => table.id === id) || null\n }\n }\n }\n\n /**\n * Campos que deben excluirse por defecto para cliente con token\n * Para ClientWithToken no hay campos sensibles a excluir\n */\n protected getDefaultExcludeKeys(): string[] {\n return []\n }\n\n /**\n * Acceder a una tabla específica (API directa)\n *\n * Crea un contexto de tabla que permite operaciones en filas y campos.\n * El Database Token debe tener permisos para la tabla especificada.\n *\n * @param tableId - ID numérico de la tabla\n * @returns Contexto de tabla con acceso a filas y campos\n *\n * @example\n * ```typescript\n * // Acceso directo a operaciones de tabla\n * const table = await client.table(456).get()\n * const rows = await client.table(456).rows.findMany()\n * const newRow = await client.table(456).rows.createMany([{\n * name: 'John',\n * active: true\n * }])\n *\n * // Acceso a información de campos\n * const fields = await client.table(456).fields.findMany()\n * ```\n *\n * @since 1.0.0\n */\n table(tableId: number): TableOnlyContext {\n return new TableOnlyContext(tableId, this.tableService, this.fieldService, this.rowService, this.logger)\n }\n\n /**\n * Obtener la configuración actual del cliente\n *\n * Retorna una copia de solo lectura de la configuración para prevenir modificaciones accidentales.\n *\n * @returns Configuración actual del cliente (solo lectura)\n *\n * @example\n * ```typescript\n * const config = client.getConfig()\n * console.log('URL servidor:', config.url)\n * console.log('Token:', config.token.substring(0, 10) + '...')\n * ```\n *\n * @since 1.0.0\n */\n getConfig(): Readonly<BaserowConfig> {\n return this.getConfigBase()\n }\n}\n","/**\n * Utilidades para decodificar JWT tokens sin dependencias externas\n *\n * Proporciona funciones para extraer información de JWT tokens usando\n * solo decodificación Base64 nativa de Node.js/Browser.\n *\n * **Características:**\n * - Sin dependencias externas\n * - Manejo robusto de errores\n * - Compatible con Node.js y navegadores\n * - Extrae claim `exp` (expiration) del payload\n *\n * @example\n * ```typescript\n * const token = 'eyJhbGc...'\n * const expiry = decodeJwtExpiry(token)\n * if (expiry) {\n * console.log('Token expira en:', expiry)\n * }\n * ```\n *\n * @since 1.0.0\n */\n\nimport { Logger } from '../types'\n\n/**\n * Decodifica el claim `exp` (expiration) de un JWT token\n *\n * Lee el payload del JWT token y extrae el timestamp de expiración\n * sin validar la firma (solo para leer metadatos del token).\n *\n * **JWT Structure:**\n * - JWT = `header.payload.signature` (3 partes separadas por `.`)\n * - `payload` está en Base64URL encoding\n * - `exp` claim es Unix timestamp en **segundos** (no milisegundos)\n *\n * **Implementación:**\n * 1. Split token por `.`\n * 2. Decodificar parte [1] (payload) desde Base64\n * 3. Parsear JSON del payload\n * 4. Extraer `exp` y convertir a Date (multiplicar por 1000 para ms)\n *\n * @param token - JWT token completo en formato string\n * @param logger - Logger opcional para debugging\n * @returns Date de expiración o undefined si no se puede decodificar\n *\n * @example\n * ```typescript\n * // Token válido\n * const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDkzOTY4MDB9.sig'\n * const expiry = decodeJwtExpiry(token)\n * // expiry = Date(2024-03-02T12:00:00.000Z)\n *\n * // Token sin exp claim\n * const tokenNoExp = 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.sig'\n * const expiry2 = decodeJwtExpiry(tokenNoExp)\n * // expiry2 = undefined\n *\n * // Token malformado\n * const badToken = 'invalid-token'\n * const expiry3 = decodeJwtExpiry(badToken)\n * // expiry3 = undefined\n * ```\n *\n * @since 1.0.0\n */\nexport function decodeJwtExpiry(token: string, logger?: Logger): Date | undefined {\n try {\n // Validar que el token tenga formato JWT (3 partes separadas por puntos)\n const parts = token.split('.')\n if (parts.length !== 3) {\n logger?.warn?.(`❌ JWT malformado: esperaba 3 partes, recibió ${parts.length}`)\n return undefined\n }\n\n // Decodificar payload (parte [1]) desde Base64\n // atob() es global en navegadores y Node.js 16+\n // Para Node.js < 16, usar Buffer.from(parts[1], 'base64').toString()\n const payload = JSON.parse(\n typeof atob !== 'undefined'\n ? atob(parts[1]) // Browser / Node.js 16+\n : Buffer.from(parts[1], 'base64').toString() // Node.js < 16\n )\n\n // Extraer claim 'exp' (expiration time)\n // exp está en formato Unix timestamp (segundos desde epoch)\n if (payload.exp === undefined || payload.exp === null || typeof payload.exp !== 'number') {\n logger?.warn?.('❌ JWT sin claim exp válido')\n return undefined\n }\n\n // Convertir Unix timestamp (segundos) a Date (milisegundos)\n const expiryDate = new Date(payload.exp * 1000)\n\n // Validar que la fecha sea válida\n if (isNaN(expiryDate.getTime())) {\n logger?.warn?.(`❌ Claim exp inválido: ${payload.exp}`)\n return undefined\n }\n\n logger?.debug?.(`✅ JWT exp decodificado: ${expiryDate.toISOString()}`)\n return expiryDate\n } catch (error) {\n logger?.warn?.('❌ Error decodificando JWT exp:', error)\n return undefined\n }\n}\n\n/**\n * Decodifica el payload completo de un JWT token\n *\n * Útil para debugging o para acceder a otros claims del token\n * además de `exp`.\n *\n * @param token - JWT token completo en formato string\n * @param logger - Logger opcional para debugging\n * @returns Payload del JWT o undefined si no se puede decodificar\n *\n * @example\n * ```typescript\n * const token = 'eyJhbGc...'\n * const payload = decodeJwtPayload(token)\n * console.log(payload)\n * // { user_id: 123, exp: 1709396800, iat: 1709393200 }\n * ```\n *\n * @since 1.0.0\n */\nexport function decodeJwtPayload(token: string, logger?: Logger): Record<string, any> | undefined {\n try {\n const parts = token.split('.')\n if (parts.length !== 3) {\n logger?.warn?.(`❌ JWT malformado: esperaba 3 partes, recibió ${parts.length}`)\n return undefined\n }\n\n const payload = JSON.parse(\n typeof atob !== 'undefined' ? atob(parts[1]) : Buffer.from(parts[1], 'base64').toString()\n )\n\n return payload\n } catch (error) {\n logger?.warn?.('❌ Error decodificando JWT payload:', error)\n return undefined\n }\n}\n\n/**\n * Verifica si un JWT token está expirado\n *\n * Compara el claim `exp` del token con el tiempo actual.\n * Útil para validaciones rápidas sin decodificar manualmente.\n *\n * @param token - JWT token completo en formato string\n * @param bufferMs - Buffer de tiempo en ms para considerar \"casi expirado\" (default: 0)\n * @param logger - Logger opcional para debugging\n * @returns true si está expirado, false si es válido, undefined si no se puede decodificar\n *\n * @example\n * ```typescript\n * const token = 'eyJhbGc...'\n *\n * // Verificar si está expirado ahora\n * if (isJwtExpired(token)) {\n * console.log('Token expirado')\n * }\n *\n * // Verificar si expira en los próximos 5 minutos\n * const fiveMinutes = 5 * 60 * 1000\n * if (isJwtExpired(token, fiveMinutes)) {\n * console.log('Token expira pronto, renovar')\n * }\n * ```\n *\n * @since 1.0.0\n */\nexport function isJwtExpired(token: string, bufferMs: number = 0, logger?: Logger): boolean | undefined {\n const expiry = decodeJwtExpiry(token, logger)\n if (!expiry) {\n return undefined // No se pudo decodificar\n }\n\n const now = Date.now()\n const expiryWithBuffer = expiry.getTime() - bufferMs\n\n return now >= expiryWithBuffer\n}\n","/**\n * Utilidades centralizadas para manejo de JWT tokens\n *\n * Elimina duplicación de lógica de configuración de tokens JWT\n * entre ClientWithCreds y ClientWithCredsWs.\n *\n * @internal\n * @since 1.0.0\n */\n\nimport type { LoginResponse } from '../types'\nimport type { AuthService } from '../services/AuthService'\nimport { decodeJwtExpiry } from './jwt-decoder'\n\n/**\n * Configura los tokens JWT en un AuthService existente\n *\n * Centraliza la lógica de transferencia de tokens desde LoginResponse\n * al AuthService interno, eliminando duplicación entre clientes.\n *\n * **Importante:** La expiración se decodifica del claim `exp` del JWT,\n * no se asume un TTL hardcoded.\n *\n * @param authService - Instancia de AuthService donde configurar tokens\n * @param loginResponse - Respuesta del login con tokens JWT\n *\n * @example\n * ```typescript\n * // En ClientWithCreds.create()\n * const admin = new ClientWithCreds(config, loginResponse)\n * setupJwtTokens(admin.auth, loginResponse)\n * ```\n */\nexport function setupJwtTokens(authService: AuthService, loginResponse: LoginResponse): void {\n // Acceder a propiedades privadas del AuthService\n // Esto es necesario para transferir tokens del login temporal al AuthService final\n const authServiceAny = authService as any\n\n authServiceAny['accessToken'] = loginResponse.access_token\n authServiceAny['refreshToken'] = loginResponse.refresh_token\n\n // Decodificar expiración desde el claim 'exp' del JWT (estándar)\n const expiry = decodeJwtExpiry(loginResponse.access_token)\n if (!expiry) {\n // Fallback conservador si no se puede decodificar\n authServiceAny['tokenExpiry'] = new Date(Date.now() + 10 * 60 * 1000) // 10 min\n } else {\n authServiceAny['tokenExpiry'] = expiry\n }\n}\n","import { AxiosResponse } from 'axios'\nimport { HttpService } from './core/HttpService'\nimport { HttpClient } from '../utils/axios'\nimport {\n LoginResponse,\n RefreshTokenRequest,\n RefreshTokenResponse,\n BaserowError,\n BaserowAuthTokenInvalidError,\n BaserowReLoginRequiredError,\n BaserowCredentials,\n Logger\n} from '../types'\nimport { validateString } from '../utils/validation'\nimport { decodeJwtExpiry } from '../utils/jwt-decoder'\n\nexport class AuthService extends HttpService {\n private accessToken?: string\n private refreshToken?: string\n private tokenExpiry?: Date\n private keepAliveTimer?: NodeJS.Timeout\n private credentials?: BaserowCredentials\n\n constructor(http: HttpClient, logger?: Logger) {\n super(http, logger)\n }\n\n /**\n * Realizar login con credenciales y obtener JWT tokens\n *\n * Autentica al usuario con Baserow y almacena los tokens de acceso y refresh en memoria.\n * Si keep-alive está habilitado, también almacena las credenciales para re-login automático.\n *\n * @param credentials - Email y password del usuario Baserow\n * @returns Respuesta de login con tokens y datos del usuario\n * @throws {BaserowError} Si las credenciales son inválidas (401)\n *\n * @example\n * ```typescript\n * const response = await authService.login({\n * email: 'user@example.com',\n * password: 'securepassword'\n * })\n * console.log(`Logged in as: ${response.user.username}`)\n * ```\n *\n * @since 1.0.0\n */\n async login(credentials: BaserowCredentials): Promise<LoginResponse> {\n validateString(credentials.email, 'email')\n validateString(credentials.password, 'password')\n\n this.logger?.info?.(`🔐 Attempting login for user: ${credentials.email}`)\n\n const loginData = {\n username: credentials.email,\n password: credentials.password\n }\n\n try {\n const response = await this.http.post<LoginResponse>('/user/token-auth/', loginData)\n\n // Almacenar tokens en memoria\n this.accessToken = response.access_token\n this.refreshToken = response.refresh_token\n\n // Almacenar credenciales para keep-alive (re-login automático)\n this.credentials = credentials\n\n // Decodificar expiración desde el claim 'exp' del JWT (estándar)\n this.tokenExpiry = decodeJwtExpiry(response.access_token, this.logger)\n if (!this.tokenExpiry) {\n this.logger?.warn?.('⚠️ No se pudo decodificar exp del JWT, usando fallback de 10 min')\n this.tokenExpiry = new Date(Date.now() + 10 * 60 * 1000) // Fallback conservador\n }\n\n // Actualizar el token en HttpClient\n this.http.setAuthToken(response.access_token)\n\n this.logger?.info?.(`✅ Login successful for user: ${response.user.username} (ID: ${response.user.id})`)\n this.logger?.debug?.(`Access token expires at: ${this.tokenExpiry.toISOString()}`)\n this.logger?.debug?.(`Refresh token stored (expires ~7 days from now)`)\n\n return response\n } catch (error) {\n if (error instanceof BaserowError && error.status === 401) {\n this.logger?.error?.(`❌ Login failed: Invalid credentials for ${credentials.email}`)\n throw new BaserowError('Invalid credentials', 401, 'INVALID_CREDENTIALS')\n }\n this.logger?.error?.('❌ Login failed with unexpected error:', error)\n throw error\n }\n }\n\n /**\n * Renovar access token usando refresh token\n *\n * IMPORTANTE: En Baserow, el refresh token NO se renueva al hacer refresh (ROTATE_REFRESH_TOKENS=False).\n * Solo se obtiene un nuevo access_token válido por 10 minutos. El refresh_token expira 7 días después\n * del login original y NO se extiende con actividad.\n *\n * Para backends 24/7, usar keep-alive con re-login automático en lugar de depender de refresh token.\n *\n * @returns Nuevo access token válido\n * @throws {BaserowReLoginRequiredError} Si el refresh token expiró (401) o no está disponible\n * @throws {BaserowAuthTokenInvalidError} Si el refresh token es inválido (400)\n *\n * @example\n * ```typescript\n * try {\n * const newToken = await authService.refreshAccessToken()\n * console.log('Token renovado exitosamente')\n * } catch (error) {\n * if (error instanceof BaserowReLoginRequiredError) {\n * console.log('Refresh token expirado, se requiere re-login')\n * await authService.login(credentials)\n * }\n * }\n * ```\n *\n * @since 1.0.0\n */\n async refreshAccessToken(): Promise<string> {\n if (!this.refreshToken) {\n this.logger?.error?.('❌ No refresh token available - tokens may have been lost')\n throw new BaserowReLoginRequiredError('tokens_lost', {\n message: 'No refresh token available. This can happen after process restart or if tokens were manually cleared.'\n })\n }\n\n const refreshData: RefreshTokenRequest = {\n refresh_token: this.refreshToken\n }\n\n try {\n this.logger?.debug?.('🔄 Attempting to refresh access token...')\n\n const response = await this.http.post<RefreshTokenResponse>('/user/token-refresh/', refreshData)\n\n // Actualizar tokens almacenados\n this.accessToken = response.access_token\n // El refresh token se mantiene igual, no se renueva\n\n // Decodificar expiración desde el claim 'exp' del JWT (estándar)\n this.tokenExpiry = decodeJwtExpiry(response.access_token, this.logger)\n if (!this.tokenExpiry) {\n this.logger?.warn?.('⚠️ No se pudo decodificar exp del JWT, usando fallback de 10 min')\n this.tokenExpiry = new Date(Date.now() + 10 * 60 * 1000) // Fallback conservador\n }\n\n // Actualizar el token en HttpClient\n this.http.setAuthToken(response.access_token)\n\n this.logger?.info?.('✅ Refresh token successful - new access token obtained')\n this.logger?.debug?.(`New token expires at: ${this.tokenExpiry.toISOString()}`)\n\n return response.access_token\n } catch (error) {\n // Si el refresh falla, limpiar tokens\n this.clearTokens()\n\n // Proporcionar mensajes de error específicos y accionables\n if (error instanceof BaserowError) {\n if (error.status === 401) {\n // 401 en refresh significa que el refresh token expiró (típicamente después de ~7 días de inactividad)\n this.logger?.error?.('❌ Refresh token has EXPIRED - Re-authentication required')\n this.logger?.error?.(' Refresh tokens expire after ~7 days of inactivity in Baserow')\n this.logger?.error?.(' User must login again with email/password')\n\n throw new BaserowReLoginRequiredError('refresh_token_expired', {\n originalError: error.message,\n hint: 'Baserow refresh tokens expire after approximately 7 days. User must re-authenticate with credentials.'\n })\n } else if (error.status === 400) {\n this.logger?.error?.('❌ Refresh token invalid or corrupted')\n throw new BaserowAuthTokenInvalidError('Refresh token is invalid or corrupted', {\n originalStatus: error.status,\n originalCode: error.code\n })\n }\n }\n\n this.logger?.error?.('❌ Refresh token failed with unexpected error:', error)\n throw error\n }\n }\n\n /**\n * Verificar si el token actual ha expirado\n *\n * Considera el token expirado si:\n * - No hay token almacenado\n * - No hay fecha de expiración conocida\n * - Expira en los próximos 5 minutos (margen de seguridad)\n *\n * @returns `true` si el token está expirado o próximo a expirar, `false` si es válido\n *\n * @example\n * ```typescript\n * if (authService.isTokenExpired()) {\n * await authService.refreshAccessToken()\n * }\n * ```\n *\n * @since 1.0.0\n */\n isTokenExpired(): boolean {\n if (!this.tokenExpiry || !this.accessToken) {\n return true\n }\n\n // Verificar si expira en los próximos 5 minutos\n const fiveMinutesFromNow = new Date(Date.now() + 5 * 60 * 1000)\n return this.tokenExpiry <= fiveMinutesFromNow\n }\n\n /**\n * Obtener token válido, renovándolo automáticamente si ha expirado\n *\n * Verifica la expiración del token y lo renueva automáticamente si es necesario.\n * Ideal para usar antes de realizar requests a la API.\n *\n * @returns Access token válido\n * @throws {BaserowError} Si no hay token disponible y se requiere login\n * @throws {BaserowReLoginRequiredError} Si el refresh token expiró\n *\n * @example\n * ```typescript\n * const token = await authService.getValidToken()\n * // Usar token en headers de request\n * ```\n *\n * @since 1.0.0\n */\n async getValidToken(): Promise<string> {\n if (!this.accessToken) {\n throw new BaserowError('No access token available, login required', 401, 'NO_ACCESS_TOKEN')\n }\n\n if (this.isTokenExpired()) {\n await this.refreshAccessToken()\n }\n\n return this.accessToken!\n }\n\n /**\n * Verificar si el usuario está autenticado con token válido\n *\n * @returns `true` si hay token válido y no expirado, `false` en caso contrario\n *\n * @example\n * ```typescript\n * if (!authService.isAuthenticated()) {\n * await authService.login(credentials)\n * }\n * ```\n *\n * @since 1.0.0\n */\n isAuthenticated(): boolean {\n return !!this.accessToken && !this.isTokenExpired()\n }\n\n /**\n * Obtener el access token actual (sin validar expiración)\n *\n * Retorna el token almacenado en memoria sin verificar si está expirado.\n * Para obtener un token válido garantizado, usar `getValidToken()`.\n *\n * @returns Access token actual o `undefined` si no hay token\n *\n * @example\n * ```typescript\n * const token = authService.getCurrentToken()\n * if (token) {\n * console.log('Token disponible')\n * }\n * ```\n *\n * @since 1.0.0\n */\n getCurrentToken(): string | undefined {\n return this.accessToken\n }\n\n /**\n * Limpiar todos los tokens y credenciales almacenadas\n *\n * Limpia access token, refresh token, credenciales y detiene el keep-alive si está activo.\n * También limpia el token del HttpClient.\n *\n * @example\n * ```typescript\n * authService.clearTokens()\n * console.log('Sesión limpiada')\n * ```\n *\n * @since 1.0.0\n */\n clearTokens(): void {\n this.stopKeepAlive()\n this.accessToken = undefined\n this.refreshToken = undefined\n this.tokenExpiry = undefined\n this.credentials = undefined\n this.http.clearAuthToken()\n }\n\n /**\n * Cerrar sesión y limpiar tokens\n *\n * Alias de `clearTokens()`. Limpia todos los tokens, credenciales y detiene keep-alive.\n * No hace petición al servidor (Baserow no tiene endpoint de logout).\n *\n * @example\n * ```typescript\n * authService.logout()\n * console.log('Sesión cerrada')\n * ```\n *\n * @since 1.0.0\n */\n logout(): void {\n this.clearTokens()\n }\n\n /**\n * Iniciar keep-alive automático para evitar expiración de refresh token\n *\n * IMPORTANTE: El refresh token de Baserow expira 7 días después del login (fijo, NO se renueva con refresh).\n * Keep-alive ejecuta RE-LOGIN completo periódicamente para obtener tokens frescos (access + refresh)\n * y mantener la sesión activa indefinidamente en aplicaciones backend 24/7.\n *\n * ⚠️ SEGURIDAD: Las credenciales se almacenan en memoria del proceso.\n * Solo usar en backends propios/confiables, NO en frontends o apps compartidas.\n *\n * @param intervalMinutes - Intervalo en minutos para re-login (default: 7200 = 5 días, margen 2 días)\n *\n * @example\n * ```typescript\n * // Re-login cada 5 días (recomendado para backends 24/7)\n * authService.startKeepAlive()\n *\n * // Re-login cada 3 días (más conservador, margen 4 días)\n * authService.startKeepAlive(4320)\n * ```\n *\n * @since 1.1.0\n */\n startKeepAlive(intervalMinutes: number = 7200): void {\n // Prevenir múltiples timers simultáneos\n if (this.keepAliveTimer) {\n this.logger?.warn?.('⚠️ Keep-alive ya está activo, deteniendo timer anterior')\n this.stopKeepAlive()\n }\n\n if (!this.credentials) {\n this.logger?.error?.('❌ Keep-alive requiere credenciales almacenadas')\n return\n }\n\n const intervalMs = intervalMinutes * 60 * 1000\n const days = (intervalMinutes / 1440).toFixed(1)\n\n this.logger?.info?.(`🔄 Keep-alive iniciado: re-login cada ${days} días (${intervalMinutes} min)`)\n\n this.keepAliveTimer = setInterval(async () => {\n if (!this.credentials) {\n this.logger?.error?.('❌ Keep-alive: credenciales no disponibles')\n return\n }\n\n try {\n this.logger?.debug?.('🔄 Keep-alive: ejecutando re-login preventivo...')\n await this.login(this.credentials)\n this.logger?.info?.('✅ Keep-alive: Re-login exitoso, tokens renovados (válidos 7 días más)')\n } catch (error) {\n this.logger?.error?.('❌ Keep-alive: error en re-login preventivo:', error)\n // No detenemos el timer, siguiente intento puede funcionar\n }\n }, intervalMs)\n\n // Prevenir que el timer mantenga el proceso Node.js vivo indefinidamente\n if (this.keepAliveTimer.unref) {\n this.keepAliveTimer.unref()\n }\n }\n\n /**\n * Detener keep-alive automático\n *\n * Limpia el timer de keep-alive. Se llama automáticamente en `logout()` y `clearTokens()`.\n *\n * @example\n * ```typescript\n * // Detener manualmente el keep-alive\n * authService.stopKeepAlive()\n * ```\n *\n * @since 1.1.0\n */\n stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer)\n this.keepAliveTimer = undefined\n this.logger?.info?.('🛑 Keep-alive detenido')\n }\n }\n\n /**\n * Configurar interceptor para auto-refresh en 401 usando interceptors nativos de Axios\n *\n * Intercepta respuestas 401 (Unauthorized) y automáticamente intenta renovar el token\n * usando refresh token. Si la renovación es exitosa, reintenta la petición original.\n *\n * NO intercepta endpoints de autenticación (`/user/token-*`) para evitar loops infinitos.\n *\n * @example\n * ```typescript\n * await authService.login(credentials)\n * authService.setupAutoRefresh()\n * // Ahora los 401 se manejan automáticamente\n * ```\n *\n * @since 1.0.0\n */\n setupAutoRefresh(): void {\n this.http.addResponseInterceptor(\n (response: AxiosResponse) => response,\n async (error: any) => {\n // El config puede venir del error original de Axios o del BaserowError transformado\n const originalRequest = error.config\n\n // NO hacer auto-refresh en endpoints de autenticación (previene loops infinitos)\n const isAuthEndpoint = originalRequest?.url?.includes('/user/token-')\n\n // Detectar 401 de cualquier fuente (error raw de Axios o BaserowError transformado)\n const is401Error = error.response?.status === 401 || (error instanceof BaserowError && error.status === 401)\n\n // Si recibimos 401 y tenemos refresh token, intentar renovar\n // EXCEPTO en endpoints de autenticación (login, refresh)\n if (is401Error && !isAuthEndpoint && this.refreshToken && originalRequest && !originalRequest._retry) {\n originalRequest._retry = true\n\n this.logger?.info?.('🔄 Auto-refresh: Detectado 401, intentando renovar token...')\n\n try {\n const newToken = await this.refreshAccessToken()\n\n this.logger?.info?.('✅ Auto-refresh: Token renovado, reintentando request original')\n\n // Actualizar el header Authorization con el nuevo token\n if (originalRequest.headers && newToken) {\n originalRequest.headers.Authorization = `JWT ${newToken}`\n }\n\n // Reintentar la petición original con el nuevo token actualizado\n return this.http.axios.request(originalRequest)\n } catch {\n // Si el refresh falla, limpiar tokens y propagar error original\n this.logger?.error?.('❌ Auto-refresh: Falló la renovación del token')\n this.clearTokens()\n throw error\n }\n }\n\n throw error\n }\n )\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { Workspace, BaserowResponse, BaserowNotFoundError, BaserowError } from '../types'\nimport { validateRequired, validatePositiveNumber, validateString } from '../utils/validation'\n\nexport interface CreateWorkspaceRequest {\n name: string\n}\n\nexport interface UpdateWorkspaceRequest {\n name?: string\n}\n\nexport class WorkspaceService extends HttpService {\n // ===== PUBLIC API (Prisma-style) =====\n\n /**\n * Listar todos los workspaces del usuario\n */\n async findMany(): Promise<Workspace[]> {\n const response = await this.http.get<BaserowResponse<Workspace> | Workspace[]>('/workspaces/')\n return Array.isArray(response) ? response : response.results\n }\n\n /**\n * Buscar workspace por ID o nombre\n */\n async findUnique(identifier: string | number): Promise<Workspace | null> {\n if (typeof identifier === 'number') {\n // Buscar por ID\n const workspaces = await this.findMany()\n return workspaces.find(ws => ws.id === identifier) || null\n } else {\n // Buscar por nombre\n validateRequired(identifier, 'workspace identifier')\n const workspaces = await this.findMany()\n return workspaces.find(ws => ws.name === identifier) || null\n }\n }\n\n /**\n * Crear workspace - API pública\n */\n async create(data: CreateWorkspaceRequest): Promise<Workspace> {\n return this.createWorkspaceInternal(data)\n }\n\n // ===== INTERNAL METHODS (Private) =====\n\n /**\n * Crear nuevo workspace (método interno)\n * @private - Solo para uso interno del servicio\n */\n private async createWorkspaceInternal(data: CreateWorkspaceRequest): Promise<Workspace> {\n validateString(data.name, 'name')\n\n return await this.http.post<Workspace>('/workspaces/', {\n name: data.name\n })\n }\n\n /**\n * Actualizar workspace (método interno)\n * @private - Solo para uso por WorkspaceContext\n */\n private async updateWorkspaceInternal(workspaceId: number, data: UpdateWorkspaceRequest): Promise<Workspace> {\n validatePositiveNumber(workspaceId, 'workspaceId')\n\n if (data.name !== undefined) {\n validateString(data.name, 'name')\n }\n\n try {\n return await this.http.patch<Workspace>(`/workspaces/${workspaceId}/`, data)\n } catch (error) {\n if (error instanceof BaserowError && error.status === 404) {\n throw new BaserowNotFoundError('Workspace', workspaceId)\n }\n throw error\n }\n }\n\n /**\n * Eliminar workspace (método interno)\n * @private - Solo para uso por WorkspaceContext\n */\n private async deleteWorkspaceInternal(workspaceId: number): Promise<void> {\n validatePositiveNumber(workspaceId, 'workspaceId')\n\n try {\n await this.http.delete(`/workspaces/${workspaceId}/`)\n } catch (error) {\n if (error instanceof BaserowError && error.status === 404) {\n throw new BaserowNotFoundError('Workspace', workspaceId)\n }\n throw error\n }\n }\n\n // ===== FRIEND ACCESS PATTERN FOR WORKSPACE CONTEXT =====\n // Symbol-based access que no aparece en la API pública pero permite acceso interno\n\n /**\n * Friend access para WorkspaceContext\n * No aparece en intellisense normal ni en la API pública\n * @internal\n */\n get [Symbol.for('workspaceContext')]() {\n return {\n updateWorkspace: this.updateWorkspaceInternal.bind(this),\n deleteWorkspace: this.deleteWorkspaceInternal.bind(this)\n }\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { ValidationService } from './core/ValidationService'\nimport { Database, BaserowResponse, BaserowNotFoundError, Logger } from '../types/index'\n\n/**\n * Servicio para operaciones CRUD de databases de Baserow\n *\n * Proporciona operaciones completas de databases (aplicaciones) incluyendo\n * CRUD básico, búsqueda por nombre y gestión dentro de workspaces.\n * Las databases son contenedores de tablas en la jerarquía de Baserow.\n *\n * **Características:**\n * - CRUD completo: create, read, update, delete\n * - Búsqueda de databases por nombre\n * - Gestión por workspace\n * - Validación de nombres y IDs\n * - Manejo robusto de errores\n *\n * **Jerarquía de Baserow:**\n * ```\n * Workspace → Database → Table → Field/Row\n * ```\n *\n * @example\n * ```typescript\n * // Operaciones básicas\n * const databases = await databaseService.list()\n * const database = await databaseService.findUnique(123)\n *\n * // Búsqueda y gestión\n * const found = await databaseService.findDatabaseByName('Mi Database')\n * const newDb = await databaseService.createDatabase(workspaceId, 'Nueva DB')\n * await databaseService.updateDatabase(dbId, { name: 'Nuevo Nombre' })\n * await databaseService.delete(dbId)\n * ```\n *\n * @since 1.0.0\n */\nexport class DatabaseService extends HttpService {\n private validationService: ValidationService\n private defaultWorkspaceId?: number\n\n constructor(http: any, logger?: Logger, defaultWorkspaceId?: number) {\n super(http, logger)\n this.validationService = new ValidationService(http, logger)\n this.defaultWorkspaceId = defaultWorkspaceId\n }\n\n // ===== PUBLIC API (Prisma-style) =====\n\n /**\n * Listar todas las databases accesibles\n *\n * Obtiene todas las databases a las que el token tiene acceso.\n * Con JWT devuelve todas las databases del usuario, con Database Token\n * solo las databases permitidas por el token.\n *\n * @returns Promise con array de databases\n *\n * @example\n * ```typescript\n * const databases = await databaseService.findMany()\n * databases.forEach(db => {\n * console.log(`${db.name} (ID: ${db.id}) - ${db.tables?.length || 0} tablas`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n async findMany(): Promise<Database[]> {\n try {\n // JWT API devuelve array directo, Database Token API devuelve {results: [...]}\n const response = await this.http.get<BaserowResponse<Database> | Database[]>('/applications/')\n const databases = Array.isArray(response) ? response : response.results\n\n this.logSuccess('list databases', undefined, { count: databases.length })\n return databases || []\n } catch (error) {\n this.handleHttpError(error, 'list databases')\n }\n }\n\n /**\n * Buscar database por ID o nombre\n *\n * Busca una database específica por su ID o nombre dentro de todas las\n * databases accesibles. Útil para operaciones dinámicas donde se\n * conoce el identificador pero no se sabe si es ID o nombre.\n *\n * @param identifier - ID numérico o nombre de la database a buscar\n * @returns Promise con la database encontrada o null si no existe\n *\n * @throws {BaserowValidationError} Si el identificador es inválido\n *\n * @example\n * ```typescript\n * const database = await databaseService.findUnique('CRM')\n * if (database) {\n * console.log(`Database encontrada: ${database.id}`)\n * } else {\n * console.log('Database no encontrada')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async findUnique(identifier: string | number): Promise<Database | null> {\n if (typeof identifier === 'number') {\n // Buscar por ID\n try {\n this.validationService.validateId(identifier, 'database ID')\n return await this.getById<Database>('/applications', identifier)\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return null\n }\n throw error\n }\n } else {\n // Buscar por nombre\n this.validationService.validateResourceName(identifier, 'database')\n const databases = await this.findMany()\n const found = databases.find(db => db.name === identifier) || null\n\n if (found) {\n this.logSuccess(`find database by name \"${identifier}\"`, found.id)\n } else {\n this.logDebug(`No database found with name \"${identifier}\"`)\n }\n\n return found\n }\n }\n\n /**\n * Crear nueva database - API pública\n *\n * Crea una nueva database dentro del workspace especificado o usa el workspace por defecto.\n * La database se crea vacía sin tablas iniciales.\n *\n * @param workspaceIdOrName - ID numérico del workspace donde crear la database, o nombre si hay workspace por defecto\n * @param name - Nombre de la nueva database (opcional si el primer parámetro es nombre)\n * @returns Promise con la database creada\n *\n * @throws {BaserowValidationError} Si los parámetros son inválidos\n *\n * @example\n * ```typescript\n * // Con workspace específico\n * const newDatabase = await databaseService.create(456, 'Nueva Database')\n *\n * // Con workspace por defecto (ClientWithCredsWs)\n * const newDatabase = await databaseService.create('Nueva Database')\n * ```\n *\n * @since 1.0.0\n */\n async create(workspaceIdOrName: number | string, name?: string): Promise<Database> {\n if (typeof workspaceIdOrName === 'number') {\n // Caso tradicional: create(workspaceId, name)\n if (!name) {\n throw new Error('Database name is required when providing workspace ID')\n }\n return this.createDatabaseInternal(workspaceIdOrName, name)\n } else {\n // Caso nuevo: create(name) usando workspace por defecto\n if (!this.defaultWorkspaceId) {\n throw new Error('No default workspace configured. Use create(workspaceId, name) instead')\n }\n return this.createDatabaseInternal(this.defaultWorkspaceId, workspaceIdOrName)\n }\n }\n\n // ===== PRIVATE METHODS =====\n\n /**\n * Crear nueva database (método interno)\n * @private - Solo para uso interno del servicio\n */\n private async createDatabaseInternal(workspaceId: number, name: string): Promise<Database> {\n this.validationService.validateId(workspaceId, 'workspace ID')\n this.validationService.validateResourceName(name, 'database')\n\n const requestData = {\n type: 'database',\n name\n }\n\n try {\n this.logDebug(`Creating database \"${name}\" in workspace ${workspaceId}`, requestData)\n const response = await this.http.post<Database>(`/applications/workspace/${workspaceId}/`, requestData)\n this.logSuccess('create database', response.id, { name: name })\n return response\n } catch (error) {\n this.handleHttpError(error, 'create database')\n }\n }\n\n /**\n * Actualizar database existente (método interno)\n * @private - Solo para uso por DatabaseContext\n */\n private async updateDatabaseInternal(id: number, data: { name?: string; workspace_id?: number }): Promise<Database> {\n this.validationService.validateId(id, 'database ID')\n\n if (data.name) {\n this.validationService.validateResourceName(data.name, 'database')\n }\n\n try {\n this.logDebug(`Updating database ${id}`, data)\n const response = await this.http.patch<Database>(`/applications/${id}/`, data)\n this.logSuccess('update database', id)\n return response\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Database', id)\n }\n this.handleHttpError(error, 'update database', id)\n }\n }\n\n /**\n * Eliminar database (método interno)\n * @private - Solo para uso por DatabaseContext\n */\n private async deleteDatabaseInternal(id: number): Promise<void> {\n this.validationService.validateId(id, 'database ID')\n return this.deleteById('/applications', id)\n }\n\n // ===== FRIEND ACCESS PATTERN FOR DATABASE CONTEXT =====\n // Symbol-based access que no aparece en la API pública pero permite acceso interno\n\n /**\n * Friend access para DatabaseContext\n * No aparece en intellisense normal ni en la API pública\n * @internal\n */\n get [Symbol.for('databaseContext')]() {\n return {\n updateDatabase: this.updateDatabaseInternal.bind(this),\n deleteDatabase: this.deleteDatabaseInternal.bind(this)\n }\n }\n}\n","import { HttpService } from './core/HttpService'\nimport { ValidationService } from './core/ValidationService'\nimport {\n DatabaseToken,\n DatabaseTokenPermissions,\n CreateDatabaseTokenRequest,\n UpdateDatabaseTokenRequest,\n BaserowResponse,\n BaserowNotFoundError,\n Logger\n} from '../types/index'\nimport { validateString } from '../utils/validation'\n\nexport class DatabaseTokenService extends HttpService {\n private validationService: ValidationService\n\n constructor(http: any, logger?: Logger) {\n super(http, logger)\n this.validationService = new ValidationService(http, logger)\n }\n\n /**\n * Listar database tokens de un workspace\n */\n async list(workspaceId: number): Promise<DatabaseToken[]> {\n this.validationService.validateId(workspaceId, 'workspace ID')\n\n try {\n const response = await this.http.get<BaserowResponse<any> | any[]>(`/database/tokens/`, {\n params: { workspace_id: workspaceId }\n })\n\n // La API puede devolver array directo o {results: []}\n const rawTokens = Array.isArray(response) ? response : response.results || []\n // Mapear key → token para consistencia en la interfaz\n const tokens = rawTokens.map((token: any) => ({ ...token, token: token.key || '' }))\n this.logSuccess('list database tokens', workspaceId, { count: tokens.length })\n return tokens\n } catch (error) {\n this.handleHttpError(error, 'list database tokens', workspaceId)\n }\n }\n\n /**\n * Obtener database token por ID\n */\n async get(tokenId: number): Promise<DatabaseToken> {\n this.validationService.validateId(tokenId, 'token ID')\n const response = await this.getById<any>('/database/tokens', tokenId)\n return { ...response, token: response.key || '' }\n }\n\n /**\n * Obtener database token por su valor (key)\n * Busca en la lista de tokens del workspace correspondiente\n */\n async getToken(tokenValue: string): Promise<DatabaseToken> {\n validateString(tokenValue, 'token value')\n\n try {\n this.logDebug(`Getting database token by value`, { tokenPreview: tokenValue.substring(0, 8) + '...' })\n\n // Como no hay endpoint directo, buscamos en todas las listas de workspaces\n // Este es un método menos eficiente pero funcional\n throw new Error('getToken requires workspace ID - use context.getToken() instead')\n } catch (error) {\n this.handleHttpError(error, 'get database token by value', tokenValue)\n }\n }\n\n /**\n * Crear nuevo database token\n *\n * NOTA: La API de Baserow tiene un comportamiento específico:\n * 1. Al crear el token, siempre establece TODOS los permisos en `true`\n * 2. Para aplicar permisos específicos, se requiere una actualización posterior\n *\n * Esta implementación maneja automáticamente este comportamiento:\n * - POST /database/tokens/ → crea token con permisos completos\n * - PATCH /database/tokens/{id}/ → ajusta permisos al valor deseado\n */\n async createToken(workspaceId: number, data: CreateDatabaseTokenRequest): Promise<DatabaseToken> {\n this.validationService.validateId(workspaceId, 'workspace ID')\n this.validationService.validateResourceName(data.name, 'token')\n\n if (!data.permissions) {\n throw new Error('Database token permissions are required')\n }\n\n const payload = {\n workspace: workspaceId,\n ...data\n }\n\n try {\n this.logDebug(`Creating database token \"${data.name}\" for workspace ${workspaceId}`, payload)\n // 1. Crear token (la API establece automáticamente permisos completos)\n const response = await this.http.post<any>(`/database/tokens/`, payload)\n if (!response.key) throw new Error('Database token key is missing')\n response.token = response.key\n // 2. Actualizar permisos al valor deseado (si no son permisos completos)\n const needsPermissionUpdate = !this.hasFullPermissions(data.permissions)\n if (needsPermissionUpdate) {\n const updatedToken = await this.updateToken(response.id, { permissions: data.permissions })\n this.logSuccess('create database token', response.id, { name: data.name, workspace: workspaceId })\n return updatedToken\n }\n this.logSuccess('create database token', response.id, { name: data.name, workspace: workspaceId })\n return { ...response, token: response.key, permissions: data.permissions }\n } catch (error) {\n this.handleHttpError(error, 'create database token', workspaceId)\n }\n }\n\n /**\n * Verificar si los permisos dados son permisos completos (todos true)\n */\n private hasFullPermissions(permissions: DatabaseTokenPermissions): boolean {\n return permissions.create && permissions.read && permissions.update && permissions.delete\n }\n\n /**\n * Actualizar database token\n */\n async updateToken(tokenId: number, data: UpdateDatabaseTokenRequest): Promise<DatabaseToken> {\n this.validationService.validateId(tokenId, 'token ID')\n\n if (data.name !== undefined) {\n this.validationService.validateResourceName(data.name, 'token')\n }\n\n try {\n this.logDebug(`Updating database token ${tokenId}`, data)\n const response = await this.http.patch<any>(`/database/tokens/${tokenId}/`, data)\n this.logSuccess('update database token', tokenId)\n return { ...response, token: response.key || '' }\n } catch (error) {\n if ((error as any).status === 404) {\n throw new BaserowNotFoundError('Database Token', tokenId)\n }\n this.handleHttpError(error, 'update database token', tokenId)\n }\n }\n\n /**\n * Eliminar database token\n */\n async delete(tokenId: number): Promise<void> {\n this.validationService.validateId(tokenId, 'token ID')\n return this.deleteById('/database/tokens', tokenId)\n }\n\n /**\n * Verificar si un database token existe\n */\n async exists(tokenId: number): Promise<boolean> {\n try {\n await this.get(tokenId)\n return true\n } catch (error) {\n if (error instanceof BaserowNotFoundError) {\n return false\n }\n throw error\n }\n }\n\n /**\n * Buscar database token por nombre en un workspace\n */\n async findByName(workspaceId: number, name: string): Promise<DatabaseToken | null> {\n this.validationService.validateId(workspaceId, 'workspace ID')\n this.validationService.validateResourceName(name, 'token')\n\n const tokens = await this.list(workspaceId)\n const found = tokens.find(token => token.name === name) || null\n\n if (found) {\n this.logSuccess(`find database token by name \"${name}\"`, found.id)\n } else {\n this.logDebug(`No database token found with name \"${name}\" in workspace ${workspaceId}`)\n }\n\n return found\n }\n}\n","import { Logger, Field, BaserowNotFoundError, FieldConstraint, ConstraintType } from '../types'\nimport { FieldService } from '../services/FieldService'\nimport { validateRequired } from '../utils/validation'\n\n/**\n * Context para operaciones en un campo específico\n *\n * Proporciona una API fluida para operaciones específicas en un campo individual\n * identificado por nombre o ID. Permite operaciones contextuales como update, delete\n * y gestión avanzada de índices y restricciones con API intuitiva.\n *\n * **Características principales:**\n * - Resolución automática de campo por nombre o ID (lazy loading)\n * - API intuitiva para operaciones avanzadas: index(), constraint()\n * - Operaciones CRUD específicas: update(), delete()\n * - Cache interno para evitar resoluciones repetidas\n * - Logging opcional de todas las operaciones\n * - Validación automática de parámetros\n *\n * **Patrón de API Contextual:**\n * ```\n * field('nombre').update(data) // Actualizar campo específico\n * field('nombre').delete() // Eliminar campo específico\n * field('email').index(true) // Habilitar índice\n * field('codigo').constraint({ type: 'unique', active: true }) // Agregar restricción\n * ```\n *\n * @example\n * ```typescript\n * // Operaciones CRUD específicas\n * await table.field('nombre').update({ description: 'Nombre completo' })\n * await table.field('obsoleto').delete()\n *\n * // Gestión de índices (v1.35+)\n * await table.field('email').index(true) // Habilitar índice\n * await table.field('email').index(false) // Deshabilitar índice\n *\n * // Gestión de restricciones (v1.35+)\n * await table.field('email').constraint({ type: 'unique', active: true }) // Agregar\n * await table.field('email').constraint('unique') // Eliminar\n * const constraints = await table.field('codigo').getConstraints()\n *\n * // Funciona con ID también\n * await table.field(123).update({ name: 'Nuevo Nombre' })\n * await table.field(456).index(true)\n * ```\n *\n * @since 1.1.0\n */\nexport class FieldContext {\n private fieldIdentifier: string | number\n private resolvedField?: Field\n private logger?: Logger\n\n /**\n * Crea un nuevo context de campo\n *\n * @param fieldService - Servicio para operaciones de campos\n * @param getTableId - Función para obtener el ID de la tabla padre\n * @param fieldIdentifier - Nombre o ID del campo\n * @param logger - Logger opcional para debug y trazabilidad\n *\n * @since 1.1.0\n */\n constructor(\n private fieldService: FieldService,\n private getTableId: () => Promise<number>,\n fieldIdentifier: string | number,\n logger?: Logger\n ) {\n this.fieldIdentifier = fieldIdentifier\n this.logger = logger\n }\n\n /**\n * Actualizar este campo\n *\n * Actualiza los datos del campo. Solo actualiza los campos\n * proporcionados (actualización parcial). Actualiza el cache interno\n * si el nombre cambia.\n *\n * @param data - Datos a actualizar\n * @param data.name - Nuevo nombre del campo (opcional)\n * @param data.description - Nueva descripción (opcional)\n * @returns Promise con el campo actualizado\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const updated = await table.field('email').update({\n * name: 'Email Corporativo',\n * description: 'Email principal de contacto'\n * })\n * console.log(`Campo actualizado: ${updated.name}`)\n * ```\n *\n * @since 1.1.0\n */\n async update(data: any): Promise<Field> {\n const field = await this.get()\n const contextAccess = (this.fieldService as any)[Symbol.for('fieldContext')]\n const updatedField = await contextAccess.updateField(field.id, data)\n\n // Actualizar cache si el nombre cambió\n if (data.name && data.name !== field.name) {\n this.resolvedField = updatedField\n if (this.logger) {\n this.logger.info(`Field updated: \"${data.name}\" (ID: ${updatedField.id})`)\n }\n }\n\n return updatedField\n }\n\n /**\n * Eliminar este campo\n *\n * Elimina permanentemente el campo y todos sus datos.\n * Esta operación no se puede deshacer. Limpia el cache interno.\n *\n * @returns Promise que resuelve cuando el campo es eliminado\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n *\n * @example\n * ```typescript\n * await table.field('campo_obsoleto').delete()\n * console.log('Campo eliminado exitosamente')\n * ```\n *\n * @since 1.1.0\n */\n async delete(): Promise<void> {\n const field = await this.get()\n const contextAccess = (this.fieldService as any)[Symbol.for('fieldContext')]\n await contextAccess.deleteField(field.id)\n\n if (this.logger) {\n this.logger.info(`Field deleted: \"${field.name}\" (ID: ${field.id})`)\n }\n\n // Limpiar cache\n this.resolvedField = undefined\n }\n\n /**\n * Configurar índice en este campo\n *\n * Habilita o deshabilita el índice en el campo para mejorar performance\n * de filtros y búsquedas. API más intuitiva que setFieldIndex().\n *\n * @param enabled - true para habilitar índice, false para deshabilitar\n * @returns Promise con el campo actualizado\n *\n * @example\n * ```typescript\n * // Habilitar índice para mejor performance en filtros\n * await table.field('email').index(true)\n *\n * // Deshabilitar índice\n * await table.field('email').index(false)\n * ```\n *\n * @since 1.1.0\n */\n async index(enabled: boolean): Promise<Field> {\n const field = await this.get()\n const updatedField = await this.fieldService.setFieldIndex(field.id, enabled)\n\n // Actualizar cache\n this.resolvedField = updatedField\n\n if (this.logger) {\n this.logger.info(`Field index ${enabled ? 'enabled' : 'disabled'}: \"${field.name}\" (ID: ${field.id})`)\n }\n\n return updatedField\n }\n\n /**\n * Gestionar restricción de valor en este campo\n *\n * API unificada para agregar o eliminar restricciones de valor.\n * Sobrecarga de función para APIs intuitivas:\n * - constraint(object) = agregar restricción\n * - constraint(string) = eliminar restricción por tipo\n *\n * @param constraintOrType - Objeto de restricción para agregar, o string del tipo para eliminar\n * @returns Promise con el campo actualizado\n *\n * @example\n * ```typescript\n * // Agregar restricción unique\n * await table.field('email').constraint({ type: 'unique', active: true })\n *\n * // Agregar restricción unique_with_empty\n * await table.field('codigo').constraint({ type: 'unique_with_empty', active: true })\n *\n * // Eliminar restricción por tipo\n * await table.field('email').constraint('unique')\n * await table.field('codigo').constraint('unique_with_empty')\n * ```\n *\n * @since 1.1.0\n */\n async constraint(constraintOrType: FieldConstraint | ConstraintType): Promise<Field> {\n const field = await this.get()\n\n let updatedField: Field\n\n if (typeof constraintOrType === 'string') {\n // Eliminar restricción por tipo\n updatedField = await this.fieldService.removeFieldConstraint(field.id, constraintOrType)\n\n if (this.logger) {\n this.logger.info(`Field constraint removed: \"${constraintOrType}\" from \"${field.name}\" (ID: ${field.id})`)\n }\n } else {\n // Agregar restricción\n updatedField = await this.fieldService.addFieldConstraint(field.id, constraintOrType)\n\n if (this.logger) {\n this.logger.info(`Field constraint added: \"${constraintOrType.type}\" to \"${field.name}\" (ID: ${field.id})`)\n }\n }\n\n // Actualizar cache\n this.resolvedField = updatedField\n\n return updatedField\n }\n\n /**\n * Obtener todas las restricciones de este campo\n *\n * @returns Promise con array de restricciones activas en el campo\n *\n * @example\n * ```typescript\n * const constraints = await table.field('email').getConstraints()\n * constraints.forEach(constraint => {\n * console.log(`${constraint.type}: ${constraint.active ? 'activa' : 'inactiva'}`)\n * })\n * ```\n *\n * @since 1.1.0\n */\n async getConstraints(): Promise<FieldConstraint[]> {\n const field = await this.get()\n return await this.fieldService.getFieldConstraints(field.id)\n }\n\n /**\n * Eliminar todas las restricciones del campo\n *\n * Remueve completamente todas las constraints configuradas en este campo,\n * dejándolo sin ninguna restricción de integridad. Esta operación es útil\n * para limpiar campos que han acumulado múltiples constraints o para resetear\n * completamente la configuración de restricciones.\n *\n * @returns Promise con el campo actualizado sin constraints\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowError} Si hay error en la comunicación con la API\n *\n * @example\n * ```typescript\n * // Eliminar todas las constraints de un campo\n * const field = await table.field('email').deleteConstraints()\n * console.log(`Campo \"${field.name}\" ahora sin constraints`)\n *\n * // Verificar que se eliminaron todas\n * const constraints = await table.field('email').getConstraints()\n * console.log(`Constraints restantes: ${constraints.length}`) // 0\n * ```\n *\n * @since 1.1.0\n */\n async deleteConstraints(): Promise<Field> {\n const field = await this.get()\n const updatedField = await this.fieldService.removeAllFieldConstraints(field.id)\n\n if (this.logger) {\n this.logger.info(`All constraints removed from field \"${field.name}\" (ID: ${field.id})`)\n }\n\n // Actualizar cache\n this.resolvedField = updatedField\n\n return updatedField\n }\n\n /**\n * Obtener este campo completo\n *\n * Recupera el campo con todos sus datos y metadatos.\n * Utiliza lazy loading con cache para evitar resoluciones repetidas.\n * Soporta identificación por nombre o ID del campo.\n *\n * @returns Promise con el campo completo\n *\n * @throws {BaserowNotFoundError} Si el campo no existe\n * @throws {BaserowValidationError} Si el identificador es inválido\n *\n * @example Obtener por nombre\n * ```typescript\n * const field = await table.field('email').get()\n * console.log(`Campo: ${field.name} (ID: ${field.id})`)\n * console.log(`Tipo: ${field.type}`)\n * ```\n *\n * @example Obtener por ID\n * ```typescript\n * const field = await table.field(123).get()\n * console.log(`Nombre: ${field.name}`)\n * console.log(`Descripción: ${field.description || 'Sin descripción'}`)\n * ```\n *\n * @since 1.1.0\n */\n async get(): Promise<Field> {\n if (this.resolvedField) {\n return this.resolvedField\n }\n\n if (typeof this.fieldIdentifier === 'number') {\n // Es un ID numérico\n const field = await this.fieldService.get(this.fieldIdentifier)\n this.resolvedField = field\n } else {\n // Es un nombre string - necesitamos buscar en la tabla\n validateRequired(this.fieldIdentifier, 'field name')\n\n // Obtener tableId desde TableContext\n const tableId = await this.getTableId()\n\n // Buscar campo por nombre en la tabla\n const field = await this.fieldService.findUnique(tableId, this.fieldIdentifier)\n if (!field) {\n throw new BaserowNotFoundError('Field', this.fieldIdentifier)\n }\n\n this.resolvedField = field\n }\n\n if (this.logger) {\n this.logger.info(`Retrieved field: \"${this.resolvedField.name}\" (ID: ${this.resolvedField.id})`)\n }\n\n return this.resolvedField\n }\n\n /**\n * Verificar si este campo existe\n *\n * Método de utilidad para verificar la existencia del campo\n * sin cargar todos sus datos. Útil para validaciones previas.\n *\n * @returns Promise con true si existe, false si no\n *\n * @example Verificar existencia por nombre\n * ```typescript\n * const exists = await table.field('email').exists()\n * if (exists) {\n * console.log('El campo existe')\n * } else {\n * console.log('El campo no fue encontrado')\n * }\n * ```\n *\n * @example Verificar antes de crear\n * ```typescript\n * const fieldName = 'nuevo_campo'\n * const exists = await table.field(fieldName).exists()\n *\n * if (!exists) {\n * await table.fields.createText(fieldName, undefined, 'Campo creado dinámicamente')\n * console.log('Campo creado exitosamente')\n * } else {\n * console.log('El campo ya existe')\n * }\n * ```\n *\n * @since 1.1.0\n */\n async exists(): Promise<boolean> {\n try {\n await this.get()\n return true\n } catch (error) {\n if ((error as any).name === 'BaserowNotFoundError') {\n return false\n }\n throw error\n }\n }\n}\n","import {\n Logger,\n Database,\n Table,\n Field,\n Row,\n BaserowNotFoundError,\n CreateTableRequest,\n SelectOption,\n NumberSeparator,\n RatingStyle,\n RatingColor,\n QueryOptions,\n PrismaLikeQueryOptions,\n WhereClause,\n SelectClause,\n FieldAdvancedOptions,\n FieldType,\n CreateFieldByType\n} from '../types'\nimport { WorkspaceService } from '../services/WorkspaceService'\nimport { DatabaseService } from '../services/DatabaseService'\nimport { TableService } from '../services/TableService'\nimport { FieldService } from '../services/FieldService'\nimport { RowService } from '../services/RowService'\nimport { FieldContext } from './FieldContext'\nimport { RowOnlyContext } from './RowContext'\nimport { PrismaBaserowMapper } from '../utils/prisma-mapper'\nimport { FieldCache } from '../utils/field-cache'\nimport { validateRequired } from '../utils/validation'\n\n/**\n * Context para operaciones en una tabla específica\n *\n * Proporciona una API jerárquica fluida para operaciones administrativas\n * dentro de una tabla específica. Es el nivel más granular de la API\n * jerárquica y permite operaciones completas de campos y filas.\n *\n * **Características principales:**\n * - Resolución automática de tabla por nombre o ID (lazy loading)\n * - API fluida para 21 tipos de campos diferentes\n * - Operaciones CRUD completas de filas con soporte bulk\n * - Operaciones avanzadas: search, count, listAll\n * - Cache interno para evitar resoluciones repetidas\n * - Logging opcional de todas las operaciones\n * - Validación automática de parámetros y tipos\n *\n * **Tipos de Campos Soportados (21/22):**\n * - **Texto**: text, long_text, url, email, phone_number\n * - **Numéricos**: number, boolean, rating\n * - **Fecha/Auditoría**: date, last_modified, last_modified_by, created_on, created_by\n * - **Selección**: single_select, multiple_select\n * - **Relacionales**: link_row, formula\n * - **Avanzados**: file, autonumber, count, rollup, lookup\n *\n * **Patrón de API Jerárquica:**\n * ```\n * table.field.createText('nombre') // Crear campo\n * table.field.list() // Listar campos\n * table.rows.list() // Listar filas\n * table.rows.createBulk([...]) // Operaciones bulk\n * ```\n *\n * @example\n * ```typescript\n * // Operaciones de campos - todos los tipos soportados\n * const textField = await table.field.createText('nombre')\n * const numberField = await table.field.createNumber('precio', 2)\n * const selectField = await table.field.createSelect('estado', [\n * { value: 'activo', color: 'blue' },\n * { value: 'inactivo', color: 'red' }\n * ])\n * const linkField = await table.field.createLink('productos', targetTableId)\n *\n * // Campos avanzados\n * const fileField = await table.field.createFile('documentos')\n * const rollupField = await table.field.createRollup('total', linkFieldId, priceFieldId, 'sum')\n *\n * // Operaciones de filas\n * const rows = await table.rows.list({ page: 1, size: 100 })\n * const newRow = await table.rows.create({ nombre: 'Juan', precio: 100 })\n *\n * // Operaciones bulk optimizadas\n * const newRows = await table.rows.createBulk([\n * { nombre: 'Item 1', precio: 50 },\n * { nombre: 'Item 2', precio: 75 }\n * ], { batchSize: 50 })\n *\n * // Búsqueda y agregaciones\n * const found = await table.rows.search('texto a buscar')\n * const count = await table.rows.count({ estado: 'activo' })\n * const allRows = await table.rows.listAll() // Sin paginación\n *\n * // Gestión de la tabla\n * await table.update({ name: 'Nuevo Nombre' })\n * await table.delete()\n * ```\n *\n * @since 1.0.0\n */\nexport class TableContext {\n private tableIdentifier: string | number\n private resolvedTable?: Table\n private logger?: Logger\n private fieldCache: FieldCache\n\n /**\n * Crea un nuevo context de tabla\n *\n * @param workspaceService - Servicio para operaciones de workspace\n * @param workspaceIdentifier - Identificador del workspace padre (opcional)\n * @param databaseIdentifier - Identificador de la database padre\n * @param tableIdentifier - Nombre o ID de la tabla\n * @param databaseService - Servicio para operaciones de database\n * @param tableService - Servicio para operaciones de tabla\n * @param fieldService - Servicio para operaciones de campos\n * @param rowService - Servicio para operaciones de filas\n * @param logger - Logger opcional para debug y trazabilidad\n *\n * @since 1.0.0\n */\n constructor(\n private workspaceService: WorkspaceService,\n private workspaceIdentifier: string | number | undefined,\n private databaseIdentifier: string | number,\n tableIdentifier: string | number,\n private databaseService: DatabaseService,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n logger?: Logger\n ) {\n this.tableIdentifier = tableIdentifier\n this.logger = logger\n this.fieldCache = new FieldCache(logger)\n }\n\n /**\n * Obtener metadata completa de campos con cache\n *\n * @private\n * @returns FieldMetadata con tipos y opciones select\n */\n private async getFieldMetadata() {\n const table = await this.get()\n return await this.fieldCache.getFieldMetadata(this.fieldService, table.id)\n }\n\n /**\n * Acceder a un campo específico por nombre o ID\n *\n * Crea un context para operaciones específicas en un campo individual.\n * Permite operaciones contextuales como update, delete y gestión avanzada\n * de índices y restricciones con API intuitiva.\n *\n * **Operaciones Disponibles:**\n * - **CRUD Específico**: update(), delete()\n * - **Gestión Avanzada**: index(), constraint(), getConstraints()\n *\n * **Para Operaciones Masivas**: Usa `table.fields.*` para crear, listar, etc.\n *\n * @param fieldIdentifier - Nombre o ID del campo\n * @returns Context de campo para operaciones específicas\n *\n * @example\n * ```typescript\n * // Operaciones específicas por campo\n * await table.field('email').update({ description: 'Email corporativo' })\n * await table.field('campo_obsoleto').delete()\n * await table.field(123).update({ name: 'Nuevo Nombre' })\n *\n * // Gestión de índices (API intuitiva)\n * await table.field('email').index(true) // Habilitar índice\n * await table.field('email').index(false) // Deshabilitar índice\n *\n * // Gestión de restricciones (API intuitiva)\n * await table.field('email').constraint({ type: 'unique', active: true }) // Agregar\n * await table.field('email').constraint('unique') // Eliminar\n * const constraints = await table.field('codigo').getConstraints()\n *\n * // Para crear campos usar table.fields.*\n * const textField = await table.fields.createText('nombre')\n * const numberField = await table.fields.createNumber('precio', 2)\n * ```\n *\n * @since 1.1.0\n */\n field(fieldIdentifier: string | number): FieldContext {\n return new FieldContext(\n this.fieldService,\n async () => {\n const table = await this.get()\n return table.id\n },\n fieldIdentifier,\n this.logger\n )\n }\n\n /**\n * Acceder a una fila específica por ID\n *\n * Crea un context para operaciones específicas en una fila individual.\n * Permite operaciones contextuales como get, update y delete sin necesidad\n * de pasar el rowId en cada llamada.\n *\n * **Operaciones Disponibles:**\n * - **CRUD Específico**: get(), update(), delete()\n * - **Utilidades**: exists()\n *\n * **Para Operaciones Masivas**: Usa `table.rows.*` para crear, listar, bulk, etc.\n *\n * @param rowId - ID de la fila\n * @returns Context de fila para operaciones específicas\n *\n * @example\n * ```typescript\n * // Operaciones específicas por fila\n * const row = await table.row(123).get()\n * await table.row(123).update({ name: 'Juan Carlos', active: true })\n * await table.row(456).delete()\n * const exists = await table.row(789).exists()\n *\n * // Para operaciones masivas usar table.rows.*\n * const allRows = await table.rows.findMany()\n * const newRow = await table.rows.create({ name: 'Ana' })\n * await table.rows.createBulk([...])\n * ```\n *\n * @since 1.1.0\n */\n row(rowId: number): RowOnlyContext {\n return new RowOnlyContext(\n async () => {\n const table = await this.get()\n return table.id\n },\n rowId,\n this.rowService,\n this.logger\n )\n }\n\n /**\n * API unificada completa de campos (fields operations) - Prisma-style + Métodos Especializados\n *\n * Proporciona la API completa para gestionar campos combinando:\n * - **API Prisma-style**: findMany, findUnique, create (genérico con tipos dinámicos)\n * - **API Especializada**: Todos los métodos createText, createNumber, etc. (20+ métodos)\n *\n * Esta es la **API principal recomendada** para trabajar con campos.\n * Para operaciones CRUD básicas simples, usar `table.field.*`.\n *\n * **Características:**\n * - **Prisma-style**: Consistencia con otros servicios (findMany, findUnique)\n * - **Métodos Especializados**: API fluida por tipo de campo con parámetros tipados\n * - **Tipos Dinámicos**: create() genérico con IntelliSense contextual\n * - **21 Tipos de Campos**: Cobertura completa (95% de tipos Baserow)\n *\n * @returns Objeto con API unificada completa para operaciones de campos\n *\n * @example\n * ```typescript\n * // === API Prisma-style ===\n * const allFields = await table.fields.findMany()\n * const fieldByName = await table.fields.findUnique('precio')\n * const fieldById = await table.fields.findUnique(123)\n *\n * // create() dinámico con IntelliSense contextual\n * const dynamicField = await table.fields.create({\n * type: 'text',\n * name: 'titulo',\n * text_default: 'Sin título' // ✅ Solo opciones de texto\n * })\n *\n * // === API Especializada ===\n * const textField = await table.fields.createText('nombre', 'Default')\n * const numberField = await table.fields.createNumber('precio', 2, true, 0, '$')\n * const selectField = await table.fields.createSelect('estado', [\n * { value: 'activo', color: 'blue' },\n * { value: 'inactivo', color: 'red' }\n * ])\n * const ratingField = await table.fields.createRating('calificacion', 5, 'yellow', 'star')\n * const linkField = await table.fields.createLink('productos', targetTableId)\n * const rollupField = await table.fields.createRollup('total', linkId, fieldId, 'sum')\n *\n * // === Campos Avanzados ===\n * const fileField = await table.fields.createFile('documentos')\n * const autoField = await table.fields.createAutonumber('ticket_id')\n * const countField = await table.fields.createCount('items_count', linkFieldId)\n * ```\n *\n * @since 1.1.0\n */\n get fields() {\n return {\n /**\n * Listar todos los campos de la tabla\n *\n * Obtiene todos los campos que pertenecen a esta tabla.\n * Incluye metadatos completos de cada campo.\n * Equivalente a `field.list()` pero con naming Prisma-style.\n *\n * @returns Promise con array de campos de la tabla\n *\n * @example\n * ```typescript\n * const fields = await table.fields.findMany()\n * console.log(`Encontrados ${fields.length} campos`)\n * fields.forEach(field => {\n * console.log(`${field.name} (ID: ${field.id}, Type: ${field.type})`)\n * })\n * ```\n *\n * @since 1.1.0\n */\n findMany: async (): Promise<Field[]> => {\n const table = await this.get()\n return await this.fieldService.findMany(table.id)\n },\n\n /**\n * Buscar campo por ID o nombre en esta tabla\n *\n * Busca un campo específico por su ID o nombre dentro de la tabla.\n * Útil para operaciones donde se conoce el identificador pero no se sabe si es ID o nombre.\n * Retorna null si no se encuentra (no lanza error).\n *\n * @param identifier - ID numérico o nombre del campo a buscar\n * @returns Promise con el campo encontrado o null si no existe\n *\n * @example\n * ```typescript\n * const fieldByName = await table.fields.findUnique('precio')\n * const fieldById = await table.fields.findUnique(123)\n *\n * if (fieldByName) {\n * console.log(`Campo encontrado: ${fieldByName.id}`)\n * } else {\n * console.log('Campo no encontrado')\n * }\n * ```\n *\n * @since 1.1.0\n */\n findUnique: async (identifier: string | number): Promise<Field | null> => {\n const table = await this.get()\n return await this.fieldService.findUnique(table.id, identifier)\n },\n\n /**\n * Crear campo con API genérica y tipos dinámicos\n *\n * Crea un nuevo campo usando una API genérica que proporciona\n * IntelliSense contextual y type safety basado en el tipo seleccionado.\n * Cada tipo de campo expone solo las opciones relevantes.\n *\n * **Tipos Soportados con IntelliSense:**\n * - `text`, `long_text`, `url`, `email`, `phone_number`\n * - `number`, `boolean`, `rating`\n * - `date`, `last_modified`, `created_on`, etc.\n * - `single_select`, `multiple_select`\n * - `link_row`, `formula`\n * - `file`, `autonumber`, `count`, `rollup`, `lookup`\n *\n * @param data - Configuración del campo con tipos dinámicos\n * @returns Promise con el campo creado\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n * @throws {BaserowNotFoundError} Si la tabla no existe\n *\n * @example\n * ```typescript\n * // Campo de texto con valor por defecto\n * const textField = await table.fields.create({\n * type: 'text',\n * name: 'titulo',\n * text_default: 'Sin título',\n * description: 'Título del artículo'\n * })\n *\n * // Campo numérico con formato de moneda\n * const priceField = await table.fields.create({\n * type: 'number',\n * name: 'precio',\n * number_decimal_places: 2,\n * number_prefix: '$',\n * number_negative: false\n * })\n *\n * // Campo de selección con opciones\n * const statusField = await table.fields.create({\n * type: 'single_select',\n * name: 'estado',\n * select_options: [\n * { value: 'activo', color: 'blue' },\n * { value: 'pendiente', color: 'yellow' },\n * { value: 'cancelado', color: 'red' }\n * ]\n * })\n *\n * // Campo de relación a otra tabla\n * const linkField = await table.fields.create({\n * type: 'link_row',\n * name: 'productos',\n * link_row_table_id: 456\n * })\n *\n * // Campo con índice y restricciones (v1.35+)\n * const codeField = await table.fields.create({\n * type: 'text',\n * name: 'codigo',\n * index: true,\n * constraints: [\n * { type: 'unique', active: true }\n * ]\n * })\n * ```\n *\n * @since 1.1.0\n */\n create: async <T extends FieldType>(data: CreateFieldByType<T>): Promise<Field> => {\n const table = await this.get()\n return await this.fieldService.create(table.id, data as any)\n },\n\n // === MÉTODOS ESPECIALIZADOS DE CREACIÓN ===\n\n // Campos básicos\n createText: async (\n name: string,\n defaultValue?: string,\n description?: string,\n advancedOptions?: FieldAdvancedOptions\n ) => {\n const table = await this.get()\n return await this.fieldService.createTextField(table.id, name, defaultValue, description, advancedOptions)\n },\n\n createLongText: async (name: string, defaultValue?: string, description?: string) => {\n const table = await this.get()\n if (description !== undefined) {\n return await this.fieldService.createLongTextField(table.id, name, defaultValue, description)\n } else if (defaultValue !== undefined) {\n return await this.fieldService.createLongTextField(table.id, name, defaultValue)\n } else {\n return await this.fieldService.createLongTextField(table.id, name)\n }\n },\n\n createUrl: async (name: string, description?: string) => {\n const table = await this.get()\n if (description !== undefined) {\n return await this.fieldService.createUrlField(table.id, name, description)\n } else {\n return await this.fieldService.createUrlField(table.id, name)\n }\n },\n\n createEmail: async (name: string, description?: string, advancedOptions?: FieldAdvancedOptions) => {\n const table = await this.get()\n return await this.fieldService.createEmailField(table.id, name, description, advancedOptions)\n },\n\n createPhoneNumber: async (name: string, defaultValue?: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.create(table.id, {\n name,\n type: 'phone_number',\n text_default: defaultValue || '',\n description\n })\n },\n\n // Campos numéricos\n createNumber: async (\n name: string,\n decimalPlaces = 0,\n allowNegative = true,\n defaultValue?: number,\n prefix?: string,\n suffix?: string,\n separator: NumberSeparator = 'COMMA_PERIOD',\n description?: string\n ) => {\n const table = await this.get()\n return await this.fieldService.createNumberField(\n table.id,\n name,\n decimalPlaces,\n allowNegative,\n defaultValue,\n prefix,\n suffix,\n separator,\n description\n )\n },\n\n createBoolean: async (name: string, defaultValue = false, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createBooleanField(table.id, name, defaultValue, description)\n },\n\n createRating: async (\n name: string,\n maxValue = 5,\n color: RatingColor = 'yellow',\n style: RatingStyle = 'star',\n description?: string\n ) => {\n const table = await this.get()\n return await this.fieldService.createRatingField(table.id, name, maxValue, color, style, description)\n },\n\n // Campos de fecha\n createDate: async (\n name: string,\n includeTime = false,\n dateFormat = 'ISO',\n timeFormat = '24',\n showTzInfo = false,\n forceTimezone?: string,\n forceTimezoneOffset?: number,\n description?: string\n ) => {\n const table = await this.get()\n return await this.fieldService.createDateField(\n table.id,\n name,\n includeTime,\n dateFormat,\n timeFormat,\n showTzInfo,\n forceTimezone,\n forceTimezoneOffset,\n description\n )\n },\n\n // Campos de selección\n createSelect: async (name: string, options: SelectOption[], description?: string) => {\n const table = await this.get()\n return await this.fieldService.createSelectField(table.id, name, options, description)\n },\n\n createMultiSelect: async (name: string, options: SelectOption[], description?: string) => {\n const table = await this.get()\n return await this.fieldService.createMultiSelectField(table.id, name, options, description)\n },\n\n // Campos de relación\n createLink: async (name: string, targetTableId: number, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createLinkField(table.id, name, targetTableId, description)\n },\n\n createFormula: async (name: string, formula: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createFormulaField(table.id, name, formula, description)\n },\n\n // Campos de auditoría\n createLastModified: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createLastModifiedField(table.id, name, description)\n },\n\n createLastModifiedBy: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createLastModifiedByField(table.id, name, description)\n },\n\n createCreatedOn: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createCreatedOnField(table.id, name, description)\n },\n\n createCreatedBy: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createCreatedByField(table.id, name, description)\n },\n\n // Campos avanzados\n createFile: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createFileField(table.id, name, description)\n },\n\n createAutonumber: async (name: string, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createAutonumberField(table.id, name, description)\n },\n\n createCount: async (name: string, throughFieldId: number, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createCountField(table.id, name, throughFieldId, description)\n },\n\n createRollup: async (\n name: string,\n throughFieldId: number,\n targetFieldId: number,\n rollupFunction: string = 'sum',\n description?: string\n ) => {\n const table = await this.get()\n return await this.fieldService.createRollupField(\n table.id,\n name,\n throughFieldId,\n targetFieldId,\n rollupFunction,\n description\n )\n },\n\n createLookup: async (name: string, throughFieldId: number, targetFieldId: number, description?: string) => {\n const table = await this.get()\n return await this.fieldService.createLookupField(table.id, name, throughFieldId, targetFieldId, description)\n }\n }\n }\n\n /**\n * Rows - API para operaciones de masa y consultas de filas\n *\n * API para operaciones de masa en filas de esta tabla.\n * Incluye operaciones estilo Prisma, búsqueda, filtrado y operaciones bulk.\n * Para operaciones en filas específicas usar: table.row(id).operation()\n *\n * **Características principales:**\n * - Operaciones estilo Prisma: findMany(), findUnique(), create()\n * - Operaciones de masa: createMany(), updateMany(), deleteMany()\n * - Búsqueda y filtrado: search(), count(), findAll()\n * - Compatible con user field names\n * - Control granular de webhook events\n * - Paginación automática con findAll()\n *\n * @example\n * ```typescript\n * // Operaciones estilo Prisma\n * const rows = await table.rows.findMany({ page: 1, size: 10 })\n * const row = await table.rows.findUnique({ id: 123 })\n * const newRow = await table.rows.create({\n * nombre: 'Juan Pérez',\n * email: 'juan@email.com',\n * activo: true\n * })\n *\n * // Operaciones de masa optimizadas\n * const newRows = await table.rows.createMany([\n * { nombre: 'Ana', email: 'ana@email.com' },\n * { nombre: 'Luis', email: 'luis@email.com' }\n * ], { batchSize: 100, sendWebhookEvents: false })\n *\n * const updates = await table.rows.updateMany([\n * { id: 1, nombre: 'Ana Modificada' },\n * { id: 2, nombre: 'Luis Modificado' }\n * ], { batchSize: 50 })\n *\n * await table.rows.deleteMany([1, 2, 3], { batchSize: 25 })\n *\n * // Búsqueda y agregaciones\n * const found = await table.rows.search('juan', { order_by: 'nombre' })\n * const count = await table.rows.count({ activo: true })\n * const allRows = await table.rows.findAll({ filters: { estado: 'activo' } })\n *\n * // Para operaciones específicas usar API contextual:\n * await table.row(123).update({ nombre: 'Juan Carlos' })\n * await table.row(123).delete()\n * const exists = await table.row(123).exists()\n * ```\n *\n * @since 1.0.0\n */\n get rows() {\n return {\n // === MÉTODOS ESTILO PRISMA ===\n\n /**\n * Buscar múltiples filas con opciones avanzadas estilo Prisma\n *\n * Equivalente a Prisma findMany(), proporciona filtrado avanzado,\n * ordenamiento, paginación y selección de campos con sintaxis familiar.\n *\n * @param options - Opciones de consulta estilo Prisma\n * @returns Promise con array de filas que cumplen los criterios\n *\n * @example\n * ```typescript\n * // Consulta compleja estilo Prisma\n * const users = await table.rows.findMany({\n * where: {\n * AND: [\n * { status: 'active' },\n * { age: { gte: 18, lt: 65 } }\n * ],\n * email: { contains: '@company.com' }\n * },\n * select: {\n * id: true,\n * name: true,\n * email: true\n * },\n * orderBy: [\n * { created_at: 'desc' },\n * { name: 'asc' }\n * ],\n * take: 20,\n * skip: 0\n * })\n *\n * // Sintaxis legacy (compatible)\n * const legacy = await table.rows.findMany({\n * filters: { status: 'active' },\n * size: 10,\n * order_by: '-created_at'\n * })\n * ```\n *\n * @since 1.2.0\n */\n findMany: async <T = Row>(options?: PrismaLikeQueryOptions<T>): Promise<T[]> => {\n const table = await this.get()\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(options, fieldMetadata)\n const response = await this.rowService.list(table.id, baserowOptions)\n return response.rows as T[]\n },\n\n /**\n * Buscar la primera fila que cumpla los criterios\n *\n * Equivalente a Prisma findFirst(), retorna la primera fila\n * que coincida con los filtros especificados.\n *\n * @param options - Opciones de consulta estilo Prisma\n * @returns Promise con la primera fila encontrada o null\n *\n * @example\n * ```typescript\n * const admin = await table.rows.findFirst({\n * where: {\n * role: 'admin',\n * status: 'active'\n * },\n * select: {\n * id: true,\n * name: true,\n * email: true\n * }\n * })\n * ```\n *\n * @since 1.2.0\n */\n findFirst: async <T = Row>(options?: PrismaLikeQueryOptions<T>): Promise<T | null> => {\n const result = await this.rows.findMany({ ...options, take: 1 })\n return (result[0] as T) || null\n },\n\n /**\n * Buscar fila por ID único\n *\n * Equivalente a Prisma findUnique(), busca una fila específica\n * por su ID con opciones de selección de campos.\n *\n * @param idOrWhere - ID de la fila o objeto con ID\n * @param options - Opciones de selección y configuración\n * @returns Promise con la fila encontrada o null\n *\n * @example\n * ```typescript\n * // Por ID directo:\n * const user = await table.rows.findUnique(123)\n *\n * // Con objeto where:\n * const user = await table.rows.findUnique({ id: 123 })\n *\n * // Con opciones:\n * const user = await table.rows.findUnique(123, {\n * select: { id: true, name: true, email: true },\n * userFieldNames: true\n * })\n * ```\n *\n * @since 1.2.0\n */\n findUnique: async <T = Row>(\n idOrWhere: number | { id: number },\n options?: {\n select?: SelectClause<T>\n userFieldNames?: boolean\n }\n ): Promise<T | null> => {\n try {\n const table = await this.get()\n const id = typeof idOrWhere === 'number' ? idOrWhere : idOrWhere.id\n return (await this.rowService.get(table.id, id, options?.userFieldNames ?? true)) as T\n } catch (error) {\n if ((error as any).name === 'BaserowNotFoundError') {\n return null\n }\n throw error\n }\n },\n\n /**\n * Contar filas que cumplen criterios específicos\n *\n * Equivalente a Prisma count(), cuenta filas sin cargar los datos.\n * Más eficiente que findMany().length para obtener totales.\n *\n * @param options - Opciones con filtros WHERE\n * @returns Promise con el número de filas que cumplen los criterios\n *\n * @example\n * ```typescript\n * const activeUsers = await table.rows.count({\n * where: {\n * status: 'active',\n * age: { gte: 18 }\n * }\n * })\n * ```\n *\n * @since 1.2.0\n */\n count: async <T = Row>(options?: { where?: WhereClause<T> | Record<string, any> }): Promise<number> => {\n const table = await this.get()\n const fieldMetadata = await this.getFieldMetadata()\n const filters = PrismaBaserowMapper.transformWhereToFilters(options?.where, fieldMetadata)\n return await this.rowService.count(table.id, filters)\n },\n\n // === MÉTODOS DE ESCRITURA ESTILO PRISMA ===\n\n /**\n * Crear nueva fila\n *\n * Equivalente a Prisma create(), crea una nueva fila con los datos\n * proporcionados y retorna la fila creada.\n *\n * @param data - Datos de la nueva fila\n * @returns Promise con la fila creada\n *\n * @example\n * ```typescript\n * const newUser = await table.rows.create({\n * name: 'John Doe',\n * email: 'john@company.com',\n * status: 'active'\n * })\n * ```\n *\n * @since 1.2.0\n */\n create: async <T = Row>(data: Partial<T>): Promise<T> => {\n const table = await this.get()\n return (await this.rowService.createRow(table.id, data)) as T\n },\n\n /**\n * Crear múltiples filas en lote\n *\n * Equivalente a Prisma createMany(), crea múltiples filas\n * de forma optimizada usando procesamiento en lotes.\n *\n * @param data - Array de datos para las nuevas filas\n * @param options - Opciones de procesamiento en lotes\n * @returns Promise con array de filas creadas\n *\n * @example\n * ```typescript\n * const newUsers = await table.rows.createMany([\n * { name: 'Alice', email: 'alice@company.com' },\n * { name: 'Bob', email: 'bob@company.com' }\n * ], {\n * batchSize: 50,\n * skipDuplicates: true\n * })\n * ```\n *\n * @since 1.2.0\n */\n createMany: async <T = Row>(\n data: Partial<T>[],\n options?: {\n skipDuplicates?: boolean\n batchSize?: number\n sendWebhookEvents?: boolean\n }\n ): Promise<T[]> => {\n const table = await this.get()\n return (await this.rowService.createBulk(table.id, data, {\n batchSize: options?.batchSize,\n send_webhook_events: options?.sendWebhookEvents\n })) as T[]\n },\n\n /**\n * Actualizar múltiples filas por ID\n *\n * Actualiza múltiples filas especificadas por ID de forma\n * optimizada usando procesamiento en lotes.\n *\n * @param updates - Array con ID y datos a actualizar para cada fila\n * @param options - Opciones de procesamiento en lotes\n * @returns Promise con array de filas actualizadas\n *\n * @example\n * ```typescript\n * const updated = await table.rows.updateMany([\n * { id: 1, status: 'active' },\n * { id: 2, status: 'inactive' },\n * { id: 3, name: 'Updated Name' }\n * ])\n * ```\n *\n * @since 1.2.0\n */\n updateMany: async <T = Row>(\n updates: Array<{ id: number } & Partial<T>>,\n options?: {\n batchSize?: number\n sendWebhookEvents?: boolean\n }\n ): Promise<T[]> => {\n const table = await this.get()\n return (await this.rowService.updateBulk(table.id, updates, {\n batchSize: options?.batchSize,\n send_webhook_events: options?.sendWebhookEvents\n })) as T[]\n },\n\n /**\n * Eliminar múltiples filas por ID o criterios\n *\n * Elimina múltiples filas especificadas por ID o que cumplan\n * criterios específicos de forma optimizada usando procesamiento en lotes.\n *\n * @param idsOrWhere - Array de IDs o objeto where con criterios\n * @param options - Opciones de procesamiento en lotes\n * @returns Promise que resuelve cuando se completa la eliminación\n *\n * @example\n * ```typescript\n * // Por IDs:\n * await table.rows.deleteMany([1, 2, 3, 4, 5])\n *\n * // Por criterios (estilo Prisma):\n * await table.rows.deleteMany({\n * where: { status: 'inactive', lastLogin: { lt: '2023-01-01' } }\n * })\n * ```\n *\n * @since 1.2.0\n */\n deleteMany: async <T = Row>(\n idsOrWhere: number[] | { where: WhereClause<T> },\n options?: { batchSize?: number }\n ): Promise<void> => {\n const table = await this.get()\n\n if (Array.isArray(idsOrWhere)) {\n // Eliminar por IDs\n return await this.rowService.deleteBulk(table.id, idsOrWhere, options)\n } else {\n // Eliminar por criterios where - buscar IDs primero\n const rowsToDelete = await this.rows.findMany({\n where: idsOrWhere.where,\n select: { id: true } as any\n })\n const ids = rowsToDelete.map((row: any) => row.id)\n if (ids.length > 0) {\n return await this.rowService.deleteBulk(table.id, ids, options)\n }\n }\n },\n\n // === MÉTODOS AVANZADOS ===\n\n /**\n * Buscar filas con texto libre (búsqueda global)\n *\n * Busca texto en todos los campos de la tabla.\n * Útil para funcionalidades de búsqueda general.\n *\n * @param query - Texto a buscar\n * @param options - Opciones adicionales (sin search)\n * @returns Promise con filas que contienen el texto\n *\n * @example\n * ```typescript\n * const results = await table.rows.search('john@company.com', {\n * orderBy: { created_at: 'desc' },\n * take: 10\n * })\n * ```\n *\n * @since 1.2.0\n */\n search: async <T = Row>(query: string, options?: Omit<PrismaLikeQueryOptions<T>, 'where'>): Promise<T[]> => {\n const table = await this.get()\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(options, fieldMetadata)\n return (await this.rowService.search(table.id, query, baserowOptions)) as T[]\n },\n\n /**\n * Obtener todas las filas sin paginación\n *\n * Descarga toda la tabla usando paginación automática.\n * Útil para exportaciones o procesamiento completo.\n *\n * @param options - Opciones de filtrado y ordenamiento\n * @returns Promise con todas las filas de la tabla\n *\n * @example\n * ```typescript\n * const allActiveUsers = await table.rows.findAll({\n * where: { status: 'active' },\n * orderBy: { created_at: 'asc' }\n * })\n * ```\n *\n * @since 1.2.0\n */\n findAll: async <T = Row>(options?: Omit<PrismaLikeQueryOptions<T>, 'take' | 'skip'>): Promise<T[]> => {\n const table = await this.get()\n const fieldMetadata = await this.getFieldMetadata()\n const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(options, fieldMetadata)\n return (await this.rowService.listAll(table.id, baserowOptions)) as T[]\n },\n\n // === COMPATIBILIDAD LEGACY ===\n\n /**\n * Listar filas (sintaxis legacy)\n *\n * Método de compatibilidad para código existente.\n * Mantiene la API original intacta.\n *\n * @param options - Opciones en formato legacy\n * @returns Promise con respuesta paginada legacy\n *\n * @since 1.0.0\n * @deprecated Usar findMany() para nueva funcionalidad\n */\n list: async (options?: QueryOptions) => {\n const table = await this.get()\n return await this.rowService.list(table.id, options)\n }\n }\n }\n\n /**\n * Actualizar esta tabla\n *\n * Actualiza los datos de la tabla. Solo actualiza los campos\n * proporcionados (actualización parcial). Actualiza el cache interno\n * si el nombre cambia.\n *\n * @param data - Datos a actualizar\n * @param data.name - Nuevo nombre de la tabla (opcional)\n * @returns Promise con la tabla actualizada\n *\n * @throws {BaserowNotFoundError} Si la tabla no existe\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const updated = await table.update({\n * name: 'Nuevo Nombre Tabla'\n * })\n * console.log(`Tabla actualizada: ${updated.name}`)\n * ```\n *\n * @since 1.0.0\n */\n async update(data: Partial<CreateTableRequest>): Promise<Table> {\n const table = await this.get()\n const contextAccess = (this.tableService as any)[Symbol.for('tableContext')]\n const updatedTable = await contextAccess.updateTable(table.id, data)\n\n // Actualizar cache si el nombre cambió\n if (data.name && data.name !== table.name) {\n this.resolvedTable = updatedTable\n if (this.logger) {\n this.logger.info(`Table updated: \"${data.name}\" (ID: ${updatedTable.id})`)\n }\n }\n\n return updatedTable\n }\n\n /**\n * Eliminar esta tabla\n *\n * Elimina permanentemente la tabla y todos sus campos, filas y datos.\n * Esta operación no se puede deshacer. Limpia el cache interno.\n *\n * @returns Promise que resuelve cuando la tabla es eliminada\n *\n * @throws {BaserowNotFoundError} Si la tabla no existe\n *\n * @example\n * ```typescript\n * await table.delete()\n * console.log('Tabla eliminada exitosamente')\n * ```\n *\n * @since 1.0.0\n */\n async delete(): Promise<void> {\n const table = await this.get()\n const contextAccess = (this.tableService as any)[Symbol.for('tableContext')]\n await contextAccess.deleteTable(table.id)\n\n if (this.logger) {\n this.logger.info(`Table deleted: \"${table.name}\" (ID: ${table.id})`)\n }\n\n // Limpiar cache\n this.resolvedTable = undefined\n }\n\n /**\n * Obtener esta tabla completa\n *\n * Recupera la tabla con todos sus datos y metadatos.\n * Utiliza lazy loading con cache para evitar resoluciones repetidas.\n * Soporta identificación por nombre o ID de la tabla.\n *\n * @returns Promise con la tabla completa\n *\n * @throws {BaserowNotFoundError} Si la tabla no existe\n * @throws {BaserowValidationError} Si el identificador es inválido\n *\n * @example Obtener por nombre\n * ```typescript\n * const table = await admin.workspace('WS').database('DB').table('Mi Tabla').get()\n * console.log(`Tabla: ${table.name} (ID: ${table.id})`)\n * console.log(`Filas: ${table.rows_count || 0}`)\n * ```\n *\n * @example Obtener por ID\n * ```typescript\n * const table = await admin.workspace('WS').database('DB').table(123).get()\n * console.log(`Nombre: ${table.name}`)\n * console.log(`Database ID: ${table.database_id}`)\n * ```\n *\n * @since 1.1.0\n */\n async get(): Promise<Table> {\n if (this.resolvedTable) {\n return this.resolvedTable\n }\n\n if (typeof this.tableIdentifier === 'number') {\n // Es un ID numérico\n const table = await this.tableService.get(this.tableIdentifier)\n this.resolvedTable = table\n } else {\n // Es un nombre string - necesitamos resolver database primero\n validateRequired(this.tableIdentifier, 'table name')\n\n // Resolver database\n let database: Database\n if (typeof this.databaseIdentifier === 'number') {\n const foundDatabase = await this.databaseService.findUnique(this.databaseIdentifier)\n if (!foundDatabase) {\n throw new BaserowNotFoundError('Database', this.databaseIdentifier)\n }\n database = foundDatabase\n } else {\n const foundDatabase = await this.databaseService.findUnique(this.databaseIdentifier)\n if (!foundDatabase) {\n throw new BaserowNotFoundError('Database', this.databaseIdentifier)\n }\n database = foundDatabase\n }\n\n // Buscar tabla por nombre en la database\n const table = await this.tableService.findUnique(database.id, this.tableIdentifier)\n if (!table) {\n throw new BaserowNotFoundError('Table', this.tableIdentifier)\n }\n\n this.resolvedTable = table\n }\n\n if (this.logger) {\n this.logger.info(`Retrieved table: \"${this.resolvedTable.name}\" (ID: ${this.resolvedTable.id})`)\n }\n\n return this.resolvedTable\n }\n\n /**\n * Verificar si esta tabla existe\n *\n * Método de utilidad para verificar la existencia de la tabla\n * sin cargar todos sus datos. Útil para validaciones previas.\n *\n * @returns Promise con true si existe, false si no\n *\n * @example Verificar existencia por nombre\n * ```typescript\n * const exists = await admin.workspace('WS').database('DB').table('Mi Tabla').exists()\n * if (exists) {\n * console.log('La tabla existe')\n * } else {\n * console.log('La tabla no fue encontrada')\n * }\n * ```\n *\n * @example Verificar antes de crear\n * ```typescript\n * const tableName = 'Nueva Tabla'\n * const tableContext = admin.workspace('WS').database('DB').table(tableName)\n * const exists = await tableContext.exists()\n *\n * if (!exists) {\n * await tableContext.create()\n * console.log('Tabla creada exitosamente')\n * } else {\n * console.log('La tabla ya existe')\n * }\n * ```\n *\n * @since 1.1.0\n */\n async exists(): Promise<boolean> {\n try {\n await this.get()\n return true\n } catch (error) {\n if ((error as any).name === 'BaserowNotFoundError') {\n return false\n }\n throw error\n }\n }\n}\n","/**\n * Servicio para gestión de control de schemas\n *\n * Maneja la tabla especial __schema_control que almacena metadatos\n * sobre los schemas cargados en cada base de datos, permitiendo\n * detección de cambios, versionado y operaciones idempotentes.\n *\n */\n\nimport { createHash } from 'crypto'\nimport type { Logger } from '../types'\nimport type { DatabaseSchema, SchemaControl, LoadSchemaOptions, SchemaControlStatus } from '../types/schema'\nimport { TableService } from './TableService'\nimport { FieldService } from './FieldService'\nimport { RowService } from './RowService'\nimport { BaserowValidationError } from '../types/errors'\n\n/**\n * Nombre de la tabla de control de schemas\n */\nconst SCHEMA_CONTROL_TABLE_NAME = '__schema_control'\n\n/**\n * Estructura de la tabla de control\n */\ninterface SchemaControlRow {\n id?: number\n version: string\n schema_hash: string\n loaded_at: string\n tables_created: string // JSON array\n status: SchemaControlStatus\n load_options: string // JSON object\n}\n\n/**\n * Servicio para gestión de control de schemas\n */\nexport class SchemaControlService {\n private controlTableId?: number\n private controlTableName = SCHEMA_CONTROL_TABLE_NAME\n\n constructor(\n private databaseId: number,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n private logger?: Logger\n ) {}\n\n /**\n * Inicializar la tabla de control si no existe\n */\n async initialize(): Promise<void> {\n this.logger?.debug?.(`Initializing schema control for database ${this.databaseId}`)\n\n try {\n // Intentar encontrar la tabla de control existente\n const existingTable = await this.tableService.findUnique(this.databaseId, this.controlTableName)\n\n if (existingTable) {\n this.controlTableId = existingTable.id\n this.logger?.debug?.(`Found existing schema control table: ${this.controlTableId}`)\n return\n }\n\n // Crear la tabla de control\n await this.createControlTable()\n } catch (error) {\n this.logger?.error?.('Failed to initialize schema control:', error)\n throw new Error(`Failed to initialize schema control: ${(error as Error).message}`)\n }\n }\n\n /**\n * Crear la tabla de control de schemas\n */\n private async createControlTable(): Promise<void> {\n this.logger?.debug?.('Creating schema control table')\n\n // Crear la tabla vacía\n const table = await this.tableService.create(this.databaseId, {\n name: this.controlTableName\n })\n\n this.controlTableId = table.id\n this.logger?.debug?.(`Created schema control table: ${this.controlTableId}`)\n\n // Crear los campos con los tipos correctos desde el inicio\n await this.createControlTableFields()\n }\n\n /**\n * Crear los campos de la tabla de control con los tipos correctos\n */\n private async createControlTableFields(): Promise<void> {\n if (!this.controlTableId) {\n throw new Error('Schema control table not initialized')\n }\n\n // Crear campos uno por uno con los tipos correctos\n await this.fieldService.createTextField(this.controlTableId, 'version', undefined, 'Versión del schema cargado')\n await this.fieldService.createTextField(\n this.controlTableId,\n 'schema_hash',\n undefined,\n 'Hash del schema para detectar cambios'\n )\n await this.fieldService.createDateField(\n this.controlTableId,\n 'loaded_at',\n true,\n 'ISO',\n '24',\n false,\n undefined,\n undefined,\n 'Fecha y hora de carga del schema'\n )\n await this.fieldService.createLongTextField(\n this.controlTableId,\n 'tables_created',\n 'JSON array con nombres de tablas creadas'\n )\n await this.fieldService.createSelectField(\n this.controlTableId,\n 'status',\n [\n { value: 'active', color: 'green' },\n { value: 'outdated', color: 'yellow' },\n { value: 'error', color: 'red' },\n { value: 'loading', color: 'blue' }\n ],\n 'Estado del schema cargado'\n )\n await this.fieldService.createLongTextField(\n this.controlTableId,\n 'load_options',\n 'JSON con opciones usadas para cargar el schema'\n )\n\n this.logger?.debug?.('Schema control table fields created')\n }\n\n /**\n * Obtener el control de schema actual\n */\n async getCurrentControl(): Promise<SchemaControl | null> {\n await this.ensureInitialized()\n\n try {\n const rows = await this.rowService.list(this.controlTableId!, {\n order_by: '-loaded_at',\n size: 1\n })\n\n if (rows.rows.length === 0) {\n return null\n }\n\n const row = rows.rows[0] as any as SchemaControlRow\n return this.mapRowToControl(row)\n } catch (error) {\n this.logger?.error?.('Failed to get current schema control:', error)\n return null\n }\n }\n\n /**\n * Crear nuevo control de schema\n */\n async createControl(\n schema: DatabaseSchema,\n options: LoadSchemaOptions,\n tablesCreated: string[]\n ): Promise<SchemaControl> {\n await this.ensureInitialized()\n\n const schemaHash = this.generateSchemaHash(schema)\n const control: SchemaControl = {\n id: `${Date.now()}`,\n version: options.version || schema.version || '1.0.0',\n schemaHash,\n loadedAt: new Date(),\n tablesCreated,\n status: 'loading',\n loadOptions: options\n }\n\n try {\n // Marcar controles anteriores como outdated\n await this.markPreviousControlsAsOutdated()\n\n // Crear nuevo control\n const rowData = this.mapControlToRow(control)\n const createdRow = await this.rowService.createRow(this.controlTableId!, rowData)\n\n control.id = (createdRow as any).id?.toString() || control.id\n control.status = 'active'\n\n // Actualizar el estado a active\n await this.updateControlStatus(control.id, 'active')\n\n this.logger?.debug?.(`Created schema control: ${control.id}`)\n return control\n } catch (error) {\n this.logger?.error?.('Failed to create schema control:', error)\n throw new Error(`Failed to create schema control: ${(error as Error).message}`)\n }\n }\n\n /**\n * Actualizar el estado de un control\n */\n async updateControlStatus(controlId: string, status: SchemaControlStatus): Promise<void> {\n await this.ensureInitialized()\n\n try {\n const numericId = parseInt(controlId, 10)\n if (isNaN(numericId)) {\n throw new Error(`Invalid control ID: ${controlId}`)\n }\n\n await this.rowService.updateRow(this.controlTableId!, numericId, {\n status: status\n })\n\n this.logger?.debug?.(`Updated schema control ${controlId} status to ${status}`)\n } catch (error) {\n this.logger?.error?.(`Failed to update control status:`, error)\n throw new Error(`Failed to update control status: ${(error as Error).message}`)\n }\n }\n\n /**\n * Verificar si un schema ha cambiado\n */\n async hasSchemaChanged(schema: DatabaseSchema): Promise<boolean> {\n const currentControl = await this.getCurrentControl()\n if (!currentControl) {\n return true // No hay control previo, se considera cambio\n }\n\n const newHash = this.generateSchemaHash(schema)\n return currentControl.schemaHash !== newHash\n }\n\n /**\n * Generar hash único para un schema\n */\n generateSchemaHash(schema: DatabaseSchema): string {\n // Crear una representación normalizada del schema\n const normalized = {\n tables: schema.tables\n .map(table => ({\n name: table.name.toLowerCase(),\n fields: table.fields\n .map(field => ({\n name: field.name.toLowerCase(),\n type: field.type,\n config: field.config || null\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n }))\n .sort((a, b) => a.name.localeCompare(b.name)),\n relationships: (schema.relationships || [])\n .map(rel => ({\n name: rel.name.toLowerCase(),\n sourceTable: rel.sourceTable.toLowerCase(),\n targetTable: rel.targetTable.toLowerCase()\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n const schemaString = JSON.stringify(normalized)\n return createHash('sha256').update(schemaString).digest('hex').substring(0, 16)\n }\n\n /**\n * Marcar controles anteriores como desactualizados\n */\n private async markPreviousControlsAsOutdated(): Promise<void> {\n try {\n const rows = await this.rowService.list(this.controlTableId!, {\n filters: { status: 'active' }\n })\n\n for (const row of rows.rows) {\n await this.rowService.updateRow(this.controlTableId!, (row as any).id, {\n status: 'outdated'\n })\n }\n } catch (error) {\n this.logger?.warn?.('Failed to mark previous controls as outdated:', error)\n // No es crítico, continuar\n }\n }\n\n /**\n * Mapear fila de base de datos a objeto SchemaControl\n */\n private mapRowToControl(row: SchemaControlRow): SchemaControl {\n try {\n // El status puede venir como string o como objeto de single_select\n const status = typeof row.status === 'string' ? row.status : (row.status as any)?.value || 'unknown'\n\n return {\n id: row.id?.toString() || 'unknown',\n version: row.version,\n schemaHash: row.schema_hash,\n loadedAt: new Date(row.loaded_at),\n tablesCreated: JSON.parse(row.tables_created || '[]'),\n status,\n loadOptions: JSON.parse(row.load_options || '{}')\n }\n } catch (error) {\n this.logger?.error?.('Failed to parse schema control row:', error)\n throw new BaserowValidationError('Invalid schema control data', { row: ['Failed to parse schema control row'] })\n }\n }\n\n /**\n * Mapear objeto SchemaControl a fila de base de datos\n */\n private mapControlToRow(control: SchemaControl): Record<string, any> {\n return {\n version: control.version,\n schema_hash: control.schemaHash,\n loaded_at: control.loadedAt.toISOString(),\n tables_created: JSON.stringify(control.tablesCreated),\n status: control.status,\n load_options: JSON.stringify(control.loadOptions)\n }\n }\n\n /**\n * Asegurar que el servicio esté inicializado\n */\n private async ensureInitialized(): Promise<void> {\n if (!this.controlTableId) {\n await this.initialize()\n }\n }\n\n /**\n * Limpiar controles antiguos (mantener solo los últimos N)\n */\n async cleanupOldControls(keepLast: number = 10): Promise<void> {\n await this.ensureInitialized()\n\n try {\n const rows = await this.rowService.list(this.controlTableId!, {\n order_by: '-loaded_at'\n })\n\n if (rows.rows.length <= keepLast) {\n return // No hay suficientes para limpiar\n }\n\n const toDelete = rows.rows.slice(keepLast)\n const idsToDelete = toDelete.map((row: any) => row.id).filter(id => id)\n\n if (idsToDelete.length > 0) {\n await this.rowService.deleteBulk(this.controlTableId!, idsToDelete)\n this.logger?.debug?.(`Cleaned up ${idsToDelete.length} old schema controls`)\n }\n } catch (error) {\n this.logger?.warn?.('Failed to cleanup old controls:', error)\n // No es crítico, continuar\n }\n }\n\n /**\n * Obtener estadísticas del control de schemas\n */\n async getControlStats(): Promise<{\n totalControls: number\n activeControls: number\n outdatedControls: number\n errorControls: number\n }> {\n await this.ensureInitialized()\n\n try {\n const rows = await this.rowService.list(this.controlTableId!)\n\n const stats = {\n totalControls: rows.rows.length,\n activeControls: 0,\n outdatedControls: 0,\n errorControls: 0\n }\n\n for (const row of rows.rows) {\n const status = (row as any).status\n switch (status) {\n case 'active':\n stats.activeControls++\n break\n case 'outdated':\n stats.outdatedControls++\n break\n case 'error':\n stats.errorControls++\n break\n }\n }\n\n return stats\n } catch (error) {\n this.logger?.error?.('Failed to get control stats:', error)\n return {\n totalControls: 0,\n activeControls: 0,\n outdatedControls: 0,\n errorControls: 0\n }\n }\n }\n}\n","/**\n * Validadores Zod para el sistema de schemas\n *\n * Proporciona validación robusta de schemas antes de aplicarlos,\n * previniendo errores y asegurando consistencia en los datos.\n *\n */\n\nimport { z } from 'zod'\nimport type {\n DatabaseSchema,\n TableSchema,\n FieldSchema,\n RelationshipSchema,\n LoadSchemaOptions,\n SelectOption\n} from '../types/schema'\n\n/**\n * Colores válidos para opciones de select\n */\nconst selectOptionColors = [\n 'blue',\n 'green',\n 'red',\n 'yellow',\n 'orange',\n 'purple',\n 'pink',\n 'brown',\n 'gray',\n 'dark_blue',\n 'dark_green',\n 'dark_red',\n 'dark_yellow',\n 'dark_orange',\n 'dark_purple',\n 'dark_pink',\n 'dark_brown',\n 'dark_gray'\n] as const\n\n/**\n * Tipos de campos válidos de Baserow\n */\nconst fieldTypes = [\n // Texto\n 'text',\n 'long_text',\n 'url',\n 'email',\n 'phone_number',\n // Numéricos\n 'number',\n 'rating',\n // Fecha\n 'date',\n 'last_modified',\n 'created_on',\n 'last_modified_by',\n 'created_by',\n // Selección\n 'single_select',\n 'multiple_select',\n // Boolean\n 'boolean',\n // Relación\n 'link_row',\n 'formula',\n // Avanzados\n 'file',\n 'autonumber',\n 'count',\n 'rollup',\n 'lookup'\n] as const\n\n/**\n * Validador para opciones de select\n */\nexport const SelectOptionValidator = z.object({\n value: z.string().min(1, 'El valor de la opción no puede estar vacío'),\n color: z.enum(selectOptionColors, {\n errorMap: () => ({ message: 'Color de opción no válido' })\n })\n}) satisfies z.ZodType<SelectOption>\n\n/**\n * Validadores para configuraciones específicas de campos\n */\nexport const FieldConfigValidators = {\n text: z\n .object({\n default: z.string().optional(),\n placeholder: z.string().optional()\n })\n .optional(),\n\n number: z\n .object({\n decimals: z.number().int().min(0).max(10).optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n default: z.number().optional(),\n prefix: z.string().optional(),\n suffix: z.string().optional(),\n separators: z.enum(['COMMA_PERIOD', 'PERIOD_COMMA']).optional()\n })\n .optional(),\n\n date: z\n .object({\n includeTime: z.boolean().optional(),\n format: z.enum(['ISO', 'US', 'EU']).optional(),\n timeFormat: z.enum(['24', '12']).optional(),\n default: z.string().optional(),\n timezone: z.string().optional()\n })\n .optional(),\n\n single_select: z.object({\n options: z.array(SelectOptionValidator).min(1, 'Debe haber al menos una opción')\n }),\n\n multiple_select: z.object({\n options: z.array(SelectOptionValidator).min(1, 'Debe haber al menos una opción')\n }),\n\n boolean: z\n .object({\n default: z.boolean().optional()\n })\n .optional(),\n\n link_row: z\n .object({\n targetTableId: z.number().int().positive().optional(),\n targetTableName: z.string().min(1).optional()\n })\n .refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName'),\n\n formula: z.object({\n formula: z.string().min(1, 'La fórmula no puede estar vacía')\n }),\n\n file: z\n .object({\n allowedFileTypes: z.array(z.string()).optional(),\n maxFileSize: z.number().int().positive().optional()\n })\n .optional(),\n\n count: z\n .object({\n targetTableId: z.number().int().positive().optional(),\n targetTableName: z.string().min(1).optional()\n })\n .refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName'),\n\n rollup: z\n .object({\n targetTableId: z.number().int().positive().optional(),\n targetTableName: z.string().min(1).optional(),\n targetFieldId: z.number().int().positive().optional(),\n targetFieldName: z.string().min(1).optional(),\n aggregation: z.enum(['sum', 'avg', 'min', 'max', 'count'])\n })\n .refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName')\n .refine(data => data.targetFieldId || data.targetFieldName, 'Debe especificar targetFieldId o targetFieldName'),\n\n lookup: z\n .object({\n targetTableId: z.number().int().positive().optional(),\n targetTableName: z.string().min(1).optional(),\n targetFieldId: z.number().int().positive().optional(),\n targetFieldName: z.string().min(1).optional()\n })\n .refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName')\n .refine(data => data.targetFieldId || data.targetFieldName, 'Debe especificar targetFieldId o targetFieldName')\n}\n\n/**\n * Validador dinámico para configuración de campos\n * Valida la configuración específica según el tipo de campo\n */\nfunction createFieldConfigValidator(fieldType: string) {\n const validator = FieldConfigValidators[fieldType as keyof typeof FieldConfigValidators]\n return validator || z.any().optional()\n}\n\n// FieldConfigValidator removido - ahora se usa createFieldConfigValidator para validación tipada\n\n/**\n * Validador para schema de campo\n */\nexport const FieldSchemaValidator = z\n .object({\n name: z\n .string()\n .min(1, 'El nombre del campo no puede estar vacío')\n .max(255, 'El nombre del campo no puede exceder 255 caracteres')\n .regex(\n /^[a-zA-Z][a-zA-Z0-9_]*$/,\n 'El nombre del campo debe empezar con letra y contener solo letras, números y guiones bajos'\n ),\n\n type: z.enum(fieldTypes, {\n errorMap: () => ({ message: 'Tipo de campo no válido' })\n }),\n\n description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),\n\n config: z.any().optional()\n })\n .refine(\n field => {\n // Validar que la configuración sea apropiada para el tipo de campo usando validadores específicos\n try {\n if (field.config !== undefined) {\n const specificValidator = createFieldConfigValidator(field.type)\n specificValidator.parse(field.config)\n }\n\n // Verificar campos que requieren configuración obligatoria\n const requiredConfigTypes = [\n 'single_select',\n 'multiple_select',\n 'link_row',\n 'formula',\n 'count',\n 'rollup',\n 'lookup'\n ]\n\n if (requiredConfigTypes.includes(field.type) && !field.config) {\n return false\n }\n\n return true\n } catch {\n // Error de validación específica del tipo de campo\n return false\n }\n },\n {\n message: 'La configuración no es válida para este tipo de campo'\n }\n ) satisfies z.ZodType<FieldSchema>\n\n/**\n * Validador para schema de tabla\n */\nexport const TableSchemaValidator = z\n .object({\n name: z\n .string()\n .min(1, 'El nombre de la tabla no puede estar vacío')\n .max(255, 'El nombre de la tabla no puede exceder 255 caracteres')\n .regex(\n /^[a-zA-Z][a-zA-Z0-9_]*$/,\n 'El nombre de la tabla debe empezar con letra y contener solo letras, números y guiones bajos'\n ),\n\n description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),\n\n fields: z\n .array(FieldSchemaValidator)\n .min(1, 'La tabla debe tener al menos un campo')\n .max(200, 'La tabla no puede tener más de 200 campos')\n })\n .refine(\n table => {\n // Validar que no haya nombres de campos duplicados\n const fieldNames = table.fields.map(f => f.name.toLowerCase())\n const uniqueNames = new Set(fieldNames)\n return fieldNames.length === uniqueNames.size\n },\n {\n message: 'Los nombres de campos deben ser únicos dentro de la tabla'\n }\n ) satisfies z.ZodType<TableSchema>\n\n/**\n * Validador para schema de relación\n */\nexport const RelationshipSchemaValidator = z\n .object({\n name: z\n .string()\n .min(1, 'El nombre de la relación no puede estar vacío')\n .max(255, 'El nombre de la relación no puede exceder 255 caracteres')\n .regex(\n /^[a-zA-Z][a-zA-Z0-9_]*$/,\n 'El nombre de la relación debe empezar con letra y contener solo letras, números y guiones bajos'\n ),\n\n sourceTable: z.string().min(1, 'El nombre de la tabla origen no puede estar vacío'),\n\n targetTable: z.string().min(1, 'El nombre de la tabla destino no puede estar vacío'),\n\n description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional()\n })\n .refine(\n rel => {\n // No permitir auto-relaciones por simplicidad\n return rel.sourceTable !== rel.targetTable\n },\n {\n message: 'Las auto-relaciones no están soportadas'\n }\n ) satisfies z.ZodType<RelationshipSchema>\n\n/**\n * Validador para schema de base de datos completo\n */\nexport const DatabaseSchemaValidator = z\n .object({\n version: z.string().optional(),\n\n description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),\n\n tables: z\n .array(TableSchemaValidator)\n .min(1, 'El schema debe contener al menos una tabla')\n .max(100, 'El schema no puede contener más de 100 tablas'),\n\n relationships: z.array(RelationshipSchemaValidator).optional()\n })\n .refine(\n schema => {\n // Validar que no haya nombres de tablas duplicados\n const tableNames = schema.tables.map(t => t.name.toLowerCase())\n const uniqueNames = new Set(tableNames)\n return tableNames.length === uniqueNames.size\n },\n {\n message: 'Los nombres de tablas deben ser únicos'\n }\n )\n .refine(\n schema => {\n // Validar que las relaciones referencien tablas existentes\n if (!schema.relationships) return true\n\n const tableNames = new Set(schema.tables.map(t => t.name.toLowerCase()))\n\n for (const rel of schema.relationships) {\n if (!tableNames.has(rel.sourceTable.toLowerCase()) || !tableNames.has(rel.targetTable.toLowerCase())) {\n return false\n }\n }\n\n return true\n },\n {\n message: 'Las relaciones deben referenciar tablas existentes en el schema'\n }\n ) satisfies z.ZodType<DatabaseSchema>\n\n/**\n * Validador para opciones de carga de schema\n */\nexport const LoadSchemaOptionsValidator = z.object({\n mode: z.enum(['create-only', 'update', 'recreate']).default('create-only'),\n version: z.string().optional(),\n force: z.boolean().default(false),\n cleanup: z.boolean().default(false)\n})\n\n/**\n * Función helper para validar un schema completo\n */\nexport function validateDatabaseSchema(schema: unknown): DatabaseSchema {\n try {\n return DatabaseSchemaValidator.parse(schema)\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessages = error.errors.map(err => `${err.path.join('.')}: ${err.message}`).join('\\n')\n\n throw new Error(`Schema validation failed:\\n${errorMessages}`)\n }\n throw error\n }\n}\n\n/**\n * Función helper para validar opciones de carga\n */\nexport function validateLoadSchemaOptions(options: unknown): LoadSchemaOptions {\n return LoadSchemaOptionsValidator.parse(options || {})\n}\n\n/**\n * Función helper para obtener validador específico de configuración por tipo de campo\n */\nexport function getFieldConfigValidator(fieldType: string) {\n return createFieldConfigValidator(fieldType)\n}\n\n/**\n * Validador para nombres de campos y tablas seguros\n */\nexport const SafeNameValidator = z\n .string()\n .min(1, 'El nombre no puede estar vacío')\n .max(255, 'El nombre no puede exceder 255 caracteres')\n .regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, 'El nombre debe empezar con letra y contener solo letras, números y guiones bajos')\n\n/**\n * Validar si un nombre es seguro para usar en Baserow\n */\nexport function validateSafeName(name: string): boolean {\n try {\n SafeNameValidator.parse(name)\n return true\n } catch {\n return false\n }\n}\n","import { Logger, Database, BaserowNotFoundError } from '../types'\nimport { TableContext } from './TableContext'\nimport { WorkspaceService } from '../services/WorkspaceService'\nimport { DatabaseService } from '../services/DatabaseService'\nimport { TableService } from '../services/TableService'\nimport { FieldService } from '../services/FieldService'\nimport { RowService } from '../services/RowService'\nimport { SchemaControlService } from '../services/SchemaControlService'\nimport { validateRequired } from '../utils/validation'\nimport { validateDatabaseSchema, validateLoadSchemaOptions } from '../validators/schema'\nimport type {\n DatabaseSchema,\n LoadSchemaOptions,\n LoadSchemaResult,\n FieldSchema,\n SchemaChange,\n LoadSchemaStats,\n TableSchema,\n RelationshipSchema,\n LoadSchemaMode,\n SchemaControl,\n Table\n} from '../types/schema'\n\n/**\n * Context para operaciones en una database específica\n *\n * Proporciona una API jerárquica fluida para operaciones administrativas\n * dentro de una database específica. Permite el acceso encadenado\n * database → table → field/rows y operaciones CRUD completas.\n *\n * **Características principales:**\n * - Resolución automática de database por nombre o ID (lazy loading)\n * - API fluida para operaciones de tables: list, find, create, update, delete\n * - Acceso directo a table contexts específicos\n * - Operaciones CRUD completas de la database\n * - Cache interno para evitar resoluciones repetidas\n * - Logging opcional de todas las operaciones\n * - Validación automática de permisos y scope\n *\n * **Patrón de API Jerárquica:**\n * ```\n * database.tables.findMany() // Operaciones masivas\n * database.table('name') // Context específico\n * database.update({ name: 'nuevo' }) // Actualizar database\n * ```\n *\n * @example\n * ```typescript\n * // Operaciones básicas de database\n * const tables = await database.tables.findMany()\n * const newTable = await database.tables.create({ name: 'Nueva Tabla' })\n * const found = await database.tables.find('Existente')\n *\n * // Acceso jerárquico a tabla\n * const tableContext = database.table('Mi Tabla')\n * const fields = await tableContext.field.list()\n * const textField = await tableContext.field.createText('nombre')\n *\n * // Gestión de la database\n * await database.update({ name: 'Nuevo Nombre' })\n * await database.delete()\n * ```\n *\n * @since 1.0.0\n */\nexport class DatabaseContext {\n private databaseIdentifier: string | number\n private resolvedDatabase?: Database\n private logger?: Logger\n private schemaControlService?: SchemaControlService\n\n /**\n * Crea un nuevo context de database\n *\n * @param workspaceService - Servicio para operaciones de workspace\n * @param workspaceIdentifier - Identificador del workspace padre (opcional)\n * @param databaseIdentifier - Nombre o ID de la database\n * @param databaseService - Servicio para operaciones de database\n * @param tableService - Servicio para operaciones de tabla\n * @param fieldService - Servicio para operaciones de campos\n * @param rowService - Servicio para operaciones de filas\n * @param logger - Logger opcional para debug y trazabilidad\n *\n * @since 1.0.0\n */\n constructor(\n private workspaceService: WorkspaceService,\n private workspaceIdentifier: string | number | undefined,\n databaseIdentifier: string | number,\n private databaseService: DatabaseService,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n logger?: Logger\n ) {\n this.databaseIdentifier = databaseIdentifier\n this.logger = logger\n }\n\n /**\n * Acceder a una tabla específica en esta database\n *\n * Crea un context para operaciones específicas en una tabla.\n * Permite acceso jerárquico a campos y filas de la tabla.\n * El identificador puede ser nombre (string) o ID (number).\n *\n * @param tableIdentifier - Nombre o ID numérico de la tabla\n * @returns Context de tabla para operaciones específicas\n *\n * @example\n * ```typescript\n * // Acceso por nombre\n * const tableContext = database.table('Mi Tabla')\n *\n * // Acceso por ID\n * const tableContext2 = database.table(456)\n *\n * // Operaciones jerárquicas\n * const fields = await tableContext.field.list()\n * const textField = await tableContext.field.createText('nombre')\n * const rows = await tableContext.rows.list()\n * ```\n *\n * @since 1.0.0\n */\n table(tableIdentifier: string | number): TableContext {\n return new TableContext(\n this.workspaceService,\n this.workspaceIdentifier,\n this.databaseIdentifier,\n tableIdentifier,\n this.databaseService,\n this.tableService,\n this.fieldService,\n this.rowService,\n this.logger\n )\n }\n\n /**\n * Operaciones masivas de tables en esta database\n *\n * Proporciona métodos para gestionar tables de forma masiva:\n * listar todas, buscar por nombre, crear, actualizar y eliminar.\n * Todas las operaciones están restringidas a la database actual.\n *\n * @returns Objeto con métodos para operaciones de tables\n *\n * @example\n * ```typescript\n * // Listar todas las tables de la database\n * const tables = await database.tables.findMany()\n *\n * // Buscar table específica\n * const table = await database.tables.findUnique('Mi Tabla')\n *\n * // Crear nueva table\n * const newTable = await database.tables.create({\n * name: 'Nueva Tabla',\n * first_row_header: true\n * })\n *\n * // Operaciones específicas (usando API jerárquica)\n * await database.table(123).update({ name: 'Nuevo Nombre' })\n * await database.table(123).delete()\n * ```\n *\n * @since 1.0.0\n */\n get tables() {\n return {\n /**\n * Listar todas las tables de la database\n *\n * Obtiene todas las tables que pertenecen a esta database.\n * Incluye metadatos completos de cada tabla.\n *\n * @returns Promise con array de tables de la database\n *\n * @example\n * ```typescript\n * const tables = await database.tables.findMany()\n * console.log(`Encontradas ${tables.length} tables`)\n * tables.forEach(table => {\n * console.log(`${table.name} (ID: ${table.id})`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n findMany: async () => {\n const database = await this.get()\n return await this.tableService.findMany(database.id)\n },\n\n /**\n * Buscar table por ID o nombre en esta database\n *\n * Busca una table específica por su ID o nombre dentro de la database.\n * Útil para operaciones donde se conoce el identificador pero no se sabe si es ID o nombre.\n *\n * @param identifier - ID numérico o nombre de la table a buscar\n * @returns Promise con la table encontrada o null si no existe\n *\n * @example\n * ```typescript\n * const table = await database.tables.findUnique('Usuarios')\n * if (table) {\n * console.log(`Table encontrada: ${table.id}`)\n * } else {\n * console.log('Table no encontrada')\n * }\n * ```\n *\n * @since 1.0.0\n */\n findUnique: async (identifier: string | number) => {\n const database = await this.get()\n return await this.tableService.findUnique(database.id, identifier)\n },\n\n // NOTA: get() no está en tables - usar database.table(id).get() para operaciones individuales\n\n /**\n * Crear nueva table en esta database\n *\n * Crea una nueva table vacía en esta database.\n * La table se crea garantizando 0 campos y 0 filas.\n *\n * @param data - Datos de la nueva table\n * @param data.name - Nombre de la table\n * @param data.data - Datos iniciales (opcional)\n * @param data.first_row_header - Si la primera fila son headers (opcional)\n * @param data.skipDefaultCleanup - Saltarse limpieza automática del contenido por defecto de Baserow (opcional)\n * @returns Promise con la table creada\n *\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const newTable = await database.tables.create({\n * name: 'Usuarios',\n * first_row_header: true\n * })\n * console.log(`Table creada: ${newTable.id} - ${newTable.name}`)\n * ```\n *\n * @since 1.0.0\n */\n create: async (data: { name: string; data?: any; first_row_header?: boolean; skipDefaultCleanup?: boolean }) => {\n const database = await this.get()\n return await this.tableService.create(database.id, data)\n }\n }\n }\n\n /**\n * Actualizar esta database\n *\n * Actualiza los datos de la database. Solo actualiza los campos\n * proporcionados (actualización parcial). Actualiza el cache interno\n * si el nombre cambia.\n *\n * @param data - Datos a actualizar\n * @param data.name - Nuevo nombre de la database (opcional)\n * @param data.workspace_id - Nuevo workspace ID (opcional)\n * @returns Promise con la database actualizada\n *\n * @throws {BaserowNotFoundError} Si la database no existe\n * @throws {BaserowValidationError} Si los datos son inválidos\n *\n * @example\n * ```typescript\n * const updated = await database.update({\n * name: 'Nuevo Nombre CRM'\n * })\n * console.log(`Database actualizada: ${updated.name}`)\n * ```\n *\n * @since 1.0.0\n */\n async update(data: { name?: string; workspace_id?: number }): Promise<Database> {\n const database = await this.get()\n const contextAccess = (this.databaseService as any)[Symbol.for('databaseContext')]\n const updatedDatabase = await contextAccess.updateDatabase(database.id, data)\n\n // Actualizar cache si el nombre cambió\n if (data.name && data.name !== database.name) {\n this.resolvedDatabase = updatedDatabase\n if (this.logger) {\n this.logger.info(`Database updated: \"${data.name}\" (ID: ${updatedDatabase.id})`)\n }\n }\n\n return updatedDatabase\n }\n\n /**\n * Eliminar esta database\n *\n * Elimina permanentemente la database y todas sus tablas, campos y datos.\n * Esta operación no se puede deshacer. Limpia el cache interno.\n *\n * @returns Promise que resuelve cuando la database es eliminada\n *\n * @throws {BaserowNotFoundError} Si la database no existe\n *\n * @example\n * ```typescript\n * await database.delete()\n * console.log('Database eliminada exitosamente')\n * ```\n *\n * @since 1.0.0\n */\n async delete(): Promise<void> {\n const database = await this.get()\n const contextAccess = (this.databaseService as any)[Symbol.for('databaseContext')]\n await contextAccess.deleteDatabase(database.id)\n\n if (this.logger) {\n this.logger.info(`Database deleted: \"${database.name}\" (ID: ${database.id})`)\n }\n\n // Limpiar cache\n this.resolvedDatabase = undefined\n }\n\n /**\n * Obtener esta database completa\n *\n * Recupera la database con todos sus datos y metadatos.\n * Utiliza lazy loading con cache para evitar resoluciones repetidas.\n * Soporta identificación por nombre o ID de la database.\n *\n * @returns Promise con la database completa\n *\n * @throws {BaserowNotFoundError} Si la database no existe\n * @throws {BaserowValidationError} Si el identificador es inválido\n *\n * @example Obtener por nombre\n * ```typescript\n * const database = await admin.workspace('Mi WS').database('Mi DB').get()\n * console.log(`Database: ${database.name} (ID: ${database.id})`)\n * console.log(`Tablas: ${database.tables?.length || 0}`)\n * ```\n *\n * @example Obtener por ID\n * ```typescript\n * const database = await admin.workspace('Mi WS').database(123).get()\n * console.log(`Nombre: ${database.name}`)\n * console.log(`Workspace ID: ${database.workspace_id}`)\n * ```\n *\n */\n async get(): Promise<Database> {\n if (this.resolvedDatabase) {\n return this.resolvedDatabase\n }\n\n if (typeof this.databaseIdentifier === 'number') {\n // Es un ID numérico\n const database = await this.databaseService.findUnique(this.databaseIdentifier)\n if (!database) {\n throw new BaserowNotFoundError('Database', this.databaseIdentifier)\n }\n this.resolvedDatabase = database\n } else {\n // Es un nombre string\n validateRequired(this.databaseIdentifier, 'database name')\n const database = await this.databaseService.findUnique(this.databaseIdentifier)\n\n if (!database) {\n throw new BaserowNotFoundError('Database', this.databaseIdentifier)\n }\n\n this.resolvedDatabase = database\n }\n\n if (this.logger) {\n this.logger.info(`Retrieved database: \"${this.resolvedDatabase.name}\" (ID: ${this.resolvedDatabase.id})`)\n }\n\n return this.resolvedDatabase\n }\n\n /**\n * Cargar un schema completo de base de datos de forma declarativa\n *\n * Permite definir y aplicar esquemas completos de base de datos usando\n * un formato JSON/TypeScript que incluye tablas, campos y relaciones.\n * Proporciona control de versiones, detección de cambios y operaciones\n * idempotentes para facilitar migraciones y setup automatizado.\n *\n * @param schema - Schema de la base de datos a cargar\n * @param options - Opciones de carga (modo, versión, etc.)\n * @returns Promise con resultado detallado de la operación\n *\n * @example Cargar schema básico\n * ```typescript\n * const ecommerceSchema: DatabaseSchema = {\n * tables: [\n * {\n * name: \"clientes\",\n * fields: [\n * { name: \"nombre\", type: \"text\" },\n * { name: \"email\", type: \"email\" },\n * {\n * name: \"categoria\",\n * type: \"single_select\",\n * config: {\n * options: [\n * { value: \"Premium\", color: \"blue\" },\n * { value: \"Estándar\", color: \"green\" }\n * ]\n * }\n * }\n * ]\n * }\n * ]\n * }\n *\n * const result = await admin.database(\"mi-db\").loadSchema(ecommerceSchema)\n * console.log(`Creadas ${result.tables.length} tablas`)\n * ```\n *\n * @example Con control de versiones\n * ```typescript\n * const result = await admin.database(\"mi-db\").loadSchema(schema, {\n * mode: 'update',\n * version: '2.0.0',\n * force: false\n * })\n *\n * if (result.changes.length > 0) {\n * console.log('Cambios aplicados:', result.changes)\n * } else {\n * console.log('No hay cambios - schema actualizado')\n * }\n * ```\n */\n async loadSchema(schema: DatabaseSchema, options: LoadSchemaOptions = {}): Promise<LoadSchemaResult> {\n const startTime = Date.now()\n const stats: LoadSchemaStats = {\n duration: 0,\n tablesProcessed: 0,\n fieldsProcessed: 0,\n relationshipsProcessed: 0,\n errors: 0,\n warnings: 0\n }\n\n try {\n // 1. Validar schema y opciones\n const validatedSchema = validateDatabaseSchema(schema)\n const validatedOptions = validateLoadSchemaOptions(options)\n\n this.logger?.info?.(`Loading schema for database with ${validatedSchema.tables.length} tables`)\n\n // 2. Asegurar que la database existe\n const database = await this.get()\n\n // 3. Inicializar control de schema\n const schemaControl = await this.getSchemaControlService(database.id)\n await schemaControl.initialize()\n\n // 4. Verificar si hay cambios (a menos que sea force)\n if (!validatedOptions.force) {\n const hasChanges = await schemaControl.hasSchemaChanged(validatedSchema)\n if (!hasChanges) {\n this.logger?.info?.('No schema changes detected, skipping load')\n const currentControl = await schemaControl.getCurrentControl()\n return {\n tables: [],\n tablesByName: {},\n schemaControl: currentControl!,\n changes: [],\n stats: { ...stats, duration: Date.now() - startTime }\n }\n }\n }\n\n // 5. Aplicar schema según el modo\n const result = await this.applySchemaChanges(validatedSchema, validatedOptions, database.id, stats)\n\n // 6. Actualizar control de schema\n const tablesCreated = result.tables.map(t => t.name)\n const finalControl = await schemaControl.createControl(validatedSchema, validatedOptions, tablesCreated)\n\n const duration = Date.now() - startTime\n this.logger?.info?.(`Schema loaded successfully in ${duration}ms`)\n\n return {\n ...result,\n schemaControl: finalControl,\n stats: { ...stats, duration }\n }\n } catch (error) {\n stats.errors++\n this.logger?.error?.('Failed to load schema:', error)\n throw new Error(`Failed to load schema: ${(error as Error).message}`)\n }\n }\n\n /**\n * Aplicar cambios de schema según el modo especificado\n */\n private async applySchemaChanges(\n schema: DatabaseSchema,\n options: LoadSchemaOptions,\n databaseId: number,\n stats: LoadSchemaStats\n ): Promise<Omit<LoadSchemaResult, 'schemaControl' | 'stats'>> {\n // Aplicar defaults para opciones opcionales\n const mode = options.mode || 'create-only'\n // TODO: Implementar force y cleanup cuando se necesiten\n // const force = options.force || false\n // const cleanup = options.cleanup || false\n\n const tables: Table[] = []\n const tablesByName: Record<string, Table> = {}\n const changes: SchemaChange[] = []\n const tableNameToId = new Map<string, number>()\n\n // Fase 1: Crear/actualizar tablas y campos básicos\n for (const tableSchema of schema.tables) {\n try {\n const table = await this.processTable(tableSchema, mode, changes)\n tables.push(table)\n tablesByName[table.name] = table\n tableNameToId.set(tableSchema.name.toLowerCase(), table.id)\n stats.tablesProcessed++\n stats.fieldsProcessed += tableSchema.fields.length\n\n this.logger?.debug?.(`Processed table: ${table.name} (${tableSchema.fields.length} fields)`)\n } catch (error) {\n stats.errors++\n this.logger?.error?.(`Failed to process table ${tableSchema.name}:`, error)\n throw error\n }\n }\n\n // Fase 2: Crear relaciones (después de que todas las tablas existan)\n if (schema.relationships) {\n for (const relationshipSchema of schema.relationships) {\n try {\n await this.processRelationship(relationshipSchema, tableNameToId, changes)\n stats.relationshipsProcessed++\n\n this.logger?.debug?.(`Created relationship: ${relationshipSchema.name}`)\n } catch (error) {\n stats.errors++\n this.logger?.error?.(`Failed to create relationship ${relationshipSchema.name}:`, error)\n throw error\n }\n }\n }\n\n return { tables, tablesByName, changes }\n }\n\n /**\n * Procesar una tabla individual del schema\n */\n private async processTable(tableSchema: TableSchema, mode: LoadSchemaMode, changes: SchemaChange[]): Promise<Table> {\n // Verificar si la tabla ya existe\n const existingTable = await this.tableService.findUnique((await this.get()).id, tableSchema.name)\n\n let table: Table\n\n if (existingTable) {\n if (mode === 'recreate') {\n // TODO: Implementar delete cuando esté disponible públicamente\n throw new Error('Recreate mode not yet implemented - delete method not available')\n } else {\n // Actualizar tabla existente\n table = existingTable\n await this.updateTableFields(table.id, tableSchema.fields, mode, changes)\n changes.push({\n type: 'table_updated',\n target: tableSchema.name,\n description: `Updated table fields`\n })\n }\n } else {\n // Crear nueva tabla\n table = await this.createTableWithFields(tableSchema)\n changes.push({\n type: 'table_created',\n target: tableSchema.name,\n description: `Created new table with ${tableSchema.fields.length} fields`\n })\n }\n\n return table\n }\n\n /**\n * Crear tabla con todos sus campos\n */\n private async createTableWithFields(tableSchema: TableSchema): Promise<Table> {\n // Crear tabla vacía\n const table = await this.tables.create({\n name: tableSchema.name,\n data: [['temp']],\n first_row_header: false\n })\n\n // TODO: Eliminar campo temporal cuando delete esté disponible\n // const tempFields = await this.fieldService.findMany(table.id)\n // if (tempFields.length > 0) {\n // await this.fieldService.delete(tempFields[0].id)\n // }\n\n // Crear todos los campos\n for (const fieldSchema of tableSchema.fields) {\n await this.createFieldFromSchema(table.id, fieldSchema)\n }\n\n return table\n }\n\n /**\n * Actualizar campos de una tabla existente\n */\n private async updateTableFields(\n tableId: number,\n fieldSchemas: FieldSchema[],\n mode: LoadSchemaMode,\n changes: SchemaChange[]\n ): Promise<void> {\n const existingFields = await this.fieldService.findMany(tableId)\n const existingFieldsByName = new Map(existingFields.map(f => [f.name.toLowerCase(), f]))\n\n for (const fieldSchema of fieldSchemas) {\n const existingField = existingFieldsByName.get(fieldSchema.name.toLowerCase())\n\n if (existingField) {\n if (mode === 'update') {\n // Actualizar campo existente si es necesario\n // Por simplicidad, no implementamos actualización de campos en esta versión\n changes.push({\n type: 'field_updated',\n target: `${tableId}.${fieldSchema.name}`,\n description: `Field exists, no update implemented yet`\n })\n }\n } else {\n // Crear campo nuevo\n await this.createFieldFromSchema(tableId, fieldSchema)\n changes.push({\n type: 'field_created',\n target: `${tableId}.${fieldSchema.name}`,\n description: `Created new field: ${fieldSchema.type}`\n })\n }\n }\n }\n\n /**\n * Crear campo usando el factory method\n */\n private async createFieldFromSchema(tableId: number, fieldSchema: FieldSchema): Promise<void> {\n const tableContext = this.table(tableId)\n\n switch (fieldSchema.type) {\n case 'text':\n await tableContext.fields.createText(\n fieldSchema.name,\n '', // default value (removed dynamic access)\n fieldSchema.description\n )\n break\n\n case 'long_text':\n await tableContext.fields.createLongText(fieldSchema.name, fieldSchema.description)\n break\n\n case 'email':\n await tableContext.fields.createEmail(fieldSchema.name, fieldSchema.description)\n break\n\n case 'url':\n await tableContext.fields.createUrl(fieldSchema.name, fieldSchema.description)\n break\n\n case 'phone_number':\n await tableContext.fields.createPhoneNumber(fieldSchema.name, fieldSchema.description)\n break\n\n case 'number':\n const numberConfig = fieldSchema.config as any\n await tableContext.fields.createNumber(\n fieldSchema.name,\n numberConfig?.decimals || 0,\n false, // negative allowed\n 0, // default value (removed dynamic access)\n numberConfig?.prefix || '',\n numberConfig?.suffix || '',\n numberConfig?.separators || 'COMMA_PERIOD',\n fieldSchema.description\n )\n break\n\n case 'rating':\n await tableContext.fields.createRating(\n fieldSchema.name,\n 5, // max value\n 'yellow', // color\n 'star', // style\n fieldSchema.description\n )\n break\n\n case 'boolean':\n const boolConfig = fieldSchema.config as any\n await tableContext.fields.createBoolean(fieldSchema.name, boolConfig?.default || false, fieldSchema.description)\n break\n\n case 'date':\n const dateConfig = fieldSchema.config as any\n await tableContext.fields.createDate(\n fieldSchema.name,\n dateConfig?.includeTime || false,\n dateConfig?.format || 'ISO',\n dateConfig?.timeFormat || '24',\n false, // force timezone\n dateConfig?.timezone,\n dateConfig?.default,\n fieldSchema.description\n )\n break\n\n case 'single_select':\n const selectConfig = fieldSchema.config as any\n if (!selectConfig?.options) {\n throw new Error(`single_select field ${fieldSchema.name} requires options in config`)\n }\n await tableContext.fields.createSelect(fieldSchema.name, selectConfig.options, fieldSchema.description)\n break\n\n case 'multiple_select':\n const multiSelectConfig = fieldSchema.config as any\n if (!multiSelectConfig?.options) {\n throw new Error(`multiple_select field ${fieldSchema.name} requires options in config`)\n }\n await tableContext.fields.createMultiSelect(\n fieldSchema.name,\n multiSelectConfig.options,\n fieldSchema.description\n )\n break\n\n case 'file':\n await tableContext.fields.createFile(fieldSchema.name, fieldSchema.description)\n break\n\n case 'autonumber':\n await tableContext.fields.createAutonumber(fieldSchema.name, fieldSchema.description)\n break\n\n // Los campos de relación se manejan en la fase 2\n case 'link_row':\n // Skip - se procesa en processRelationship\n break\n\n default:\n throw new Error(`Unsupported field type: ${fieldSchema.type}`)\n }\n }\n\n /**\n * Procesar una relación entre tablas\n */\n private async processRelationship(\n relationshipSchema: RelationshipSchema,\n tableNameToId: Map<string, number>,\n changes: SchemaChange[]\n ): Promise<void> {\n const sourceTableId = tableNameToId.get(relationshipSchema.sourceTable.toLowerCase())\n const targetTableId = tableNameToId.get(relationshipSchema.targetTable.toLowerCase())\n\n if (!sourceTableId || !targetTableId) {\n throw new Error(\n `Cannot create relationship ${relationshipSchema.name}: ` +\n `source table ${relationshipSchema.sourceTable} or target table ${relationshipSchema.targetTable} not found`\n )\n }\n\n const sourceTableContext = this.table(sourceTableId)\n await sourceTableContext.fields.createLink(relationshipSchema.name, targetTableId, relationshipSchema.description)\n\n changes.push({\n type: 'relationship_created',\n target: relationshipSchema.name,\n description: `Created link from ${relationshipSchema.sourceTable} to ${relationshipSchema.targetTable}`\n })\n }\n\n /**\n * Obtener o crear el servicio de control de schema\n */\n private async getSchemaControlService(databaseId: number): Promise<SchemaControlService> {\n if (!this.schemaControlService) {\n this.schemaControlService = new SchemaControlService(\n databaseId,\n this.tableService,\n this.fieldService,\n this.rowService,\n this.logger\n )\n }\n return this.schemaControlService\n }\n\n /**\n * Obtener el control de schema actual de la database\n */\n async getSchemaControl(): Promise<SchemaControl | null> {\n const database = await this.get()\n const schemaControl = await this.getSchemaControlService(database.id)\n await schemaControl.initialize()\n return await schemaControl.getCurrentControl()\n }\n\n /**\n * Verificar si esta database existe\n *\n * Método de utilidad para verificar la existencia de la database\n * sin cargar todos sus datos. Útil para validaciones previas.\n *\n * @returns Promise con true si existe, false si no\n *\n * @example Verificar existencia por nombre\n * ```typescript\n * const exists = await admin.workspace('Mi WS').database('Mi DB').exists()\n * if (exists) {\n * console.log('La database existe')\n * } else {\n * console.log('La database no fue encontrada')\n * }\n * ```\n *\n * @example Verificar antes de crear\n * ```typescript\n * const databaseName = 'Nueva Database'\n * const exists = await admin.workspace('Mi WS').database(databaseName).exists()\n *\n * if (!exists) {\n * await admin.workspace('Mi WS').databases.create(databaseName)\n * console.log('Database creada exitosamente')\n * } else {\n * console.log('La database ya existe')\n * }\n * ```\n *\n */\n async exists(): Promise<boolean> {\n try {\n await this.get()\n return true\n } catch (error) {\n if ((error as any).name === 'BaserowNotFoundError') {\n return false\n }\n throw error\n }\n }\n}\n","import {\n Logger,\n DatabaseToken,\n CreateDatabaseTokenRequest,\n UpdateDatabaseTokenRequest,\n DatabaseTokenPermissions,\n LIBRARY_TOKEN_PREFIX\n} from '../types'\nimport { DatabaseTokenService } from '../services/DatabaseTokenService'\nimport { WorkspaceService } from '../services/WorkspaceService'\n\n/**\n * Context para operaciones de database tokens en un workspace específico\n *\n * Proporciona una API completa para gestionar database tokens que permiten\n * acceso limitado a databases y tablas específicas. Los tokens pueden\n * configurarse con diferentes niveles de permisos y scope.\n *\n * **Características principales:**\n * - Gestión completa de database tokens (CRUD)\n * - Control granular de permisos (create, read, update, delete)\n * - Sistema de prefijos para tokens de librería ('br-lib:')\n * - Métodos de conveniencia para casos comunes\n * - Limpieza automática de tokens de test\n * - Resolución automática de workspace por nombre o ID\n * - Validación de existencia y búsqueda por nombre o valor\n *\n * **Nomenclatura de Tokens:**\n * - `create()`: Control total del nombre (para uso directo del usuario)\n * - `createLibraryToken()`: Agrega prefijo 'br-lib:' automáticamente\n * - `createFullAccess()/createReadOnly()`: Métodos de conveniencia con prefijo 'br-lib:'\n *\n * **Tipos de Permisos:**\n * - **create**: Permite crear nuevas filas\n * - **read**: Permite leer datos existentes\n * - **update**: Permite modificar filas existentes\n * - **delete**: Permite eliminar filas\n *\n * @example\n * ```typescript\n * // Métodos de conveniencia\n * const fullToken = await workspace.databaseToken.createFullAccess('api-client')\n * const readToken = await workspace.databaseToken.createReadOnly('dashboard')\n *\n * // Control granular de permisos\n * const customToken = await workspace.databaseToken.create('custom', {\n * create: true,\n * read: true,\n * update: false,\n * delete: false\n * })\n *\n * // Gestión de tokens\n * const tokens = await workspace.databaseToken.list()\n * const token = await workspace.databaseToken.findByName('br-lib:api-client')\n * await workspace.databaseToken.update(token.id, { name: 'nuevo-nombre' })\n *\n * // Limpieza de tokens de test\n * const cleanup = await workspace.databaseToken.cleanupLibraryTokens()\n * console.log(`Eliminados: ${cleanup.deleted}, Errores: ${cleanup.errors}`)\n * ```\n *\n * @since 1.0.0\n */\nexport class DatabaseTokenContext {\n private workspaceIdentifier: string | number\n private resolvedWorkspaceId?: number\n private logger?: Logger\n\n /**\n * Crea un nuevo context de database tokens\n *\n * @param workspaceService - Servicio para operaciones de workspace\n * @param workspaceIdentifier - Nombre o ID del workspace donde gestionar tokens\n * @param databaseTokenService - Servicio para operaciones de database tokens\n * @param logger - Logger opcional para debug y trazabilidad\n *\n * @since 1.0.0\n */\n constructor(\n private workspaceService: WorkspaceService,\n workspaceIdentifier: string | number,\n private databaseTokenService: DatabaseTokenService,\n logger?: Logger\n ) {\n this.workspaceIdentifier = workspaceIdentifier\n this.logger = logger\n }\n\n /**\n * Listar todos los database tokens del workspace\n * @returns Promise<DatabaseToken[]> Lista de todos los database tokens\n * @throws {BaserowError} Si el workspace no existe o hay error de red\n */\n async list(): Promise<DatabaseToken[]> {\n const workspaceId = await this.resolveWorkspaceId()\n return await this.databaseTokenService.list(workspaceId)\n }\n\n /**\n * Obtener database token por ID\n * @param tokenId - ID numérico del database token\n * @returns Promise<DatabaseToken> Database token encontrado\n * @throws {BaserowNotFoundError} Si el token no existe\n * @throws {BaserowError} Si hay error de red o validación\n */\n async get(tokenId: number): Promise<DatabaseToken> {\n return await this.databaseTokenService.get(tokenId)\n }\n\n /**\n * Obtener database token por su valor (key)\n * Busca en la lista de tokens del workspace\n * @param tokenValue - Valor string del token (ej: \"abc123def456...\")\n * @returns Promise<DatabaseToken> Database token encontrado\n * @throws {Error} Si el token no se encuentra en el workspace\n * @throws {BaserowError} Si hay error de red\n */\n async getToken(tokenValue: string): Promise<DatabaseToken> {\n const workspaceId = await this.resolveWorkspaceId()\n const tokens = await this.databaseTokenService.list(workspaceId)\n const found = tokens.find(token => token.token === tokenValue)\n\n if (!found) {\n throw new Error(`Database token with value ${tokenValue.substring(0, 8)}... not found in workspace`)\n }\n\n return found\n }\n\n /**\n * Crear nuevo database token con control total del nombre\n * Para tokens de librería, usar createLibraryToken() o métodos de conveniencia\n * @param name - Nombre del token (sin modificar)\n * @param permissions - Permisos del token (create, read, update, delete)\n * @returns Promise<DatabaseToken> Database token creado\n * @throws {BaserowValidationError} Si el nombre o permisos son inválidos\n * @throws {BaserowError} Si hay error de red\n */\n async create(name: string, permissions: DatabaseTokenPermissions): Promise<DatabaseToken> {\n const workspaceId = await this.resolveWorkspaceId()\n const request: CreateDatabaseTokenRequest = {\n name,\n permissions\n }\n return await this.databaseTokenService.createToken(workspaceId, request)\n }\n\n /**\n * Crear database token con prefijo de librería para identificación\n * Equivalente a create() pero agrega automáticamente el prefijo 'br-lib:'\n * @param name - Nombre base del token (se le agregará 'br-lib:' automáticamente)\n * @param permissions - Permisos del token (create, read, update, delete)\n * @returns Promise<DatabaseToken> Database token creado con prefijo 'br-lib:'\n * @throws {BaserowValidationError} Si el nombre o permisos son inválidos\n * @throws {BaserowError} Si hay error de red\n */\n async createLibraryToken(name: string, permissions: DatabaseTokenPermissions): Promise<DatabaseToken> {\n const prefixedName = `${LIBRARY_TOKEN_PREFIX}${name}`\n return await this.create(prefixedName, permissions)\n }\n\n /**\n * Crear database token con permisos completos (CRUD)\n * Automáticamente agrega prefijo 'br-lib:' para identificación de librería\n * Equivalente a: createLibraryToken(name, {create: true, read: true, update: true, delete: true})\n * @param name - Nombre base del token (se le agregará 'br-lib:' automáticamente)\n * @returns Promise<DatabaseToken> Database token con permisos completos y prefijo 'br-lib:'\n * @throws {BaserowValidationError} Si el nombre es inválido\n * @throws {BaserowError} Si hay error de red\n */\n async createFullAccess(name: string): Promise<DatabaseToken> {\n const prefixedName = `${LIBRARY_TOKEN_PREFIX}${name}`\n return await this.create(prefixedName, {\n create: true,\n read: true,\n update: true,\n delete: true\n })\n }\n\n /**\n * Crear database token de solo lectura\n * Automáticamente agrega prefijo 'br-lib:' para identificación de librería\n * Equivalente a: createLibraryToken(name, {create: false, read: true, update: false, delete: false})\n * @param name - Nombre base del token (se le agregará 'br-lib:' automáticamente)\n * @returns Promise<DatabaseToken> Database token de solo lectura con prefijo 'br-lib:'\n * @throws {BaserowValidationError} Si el nombre es inválido\n * @throws {BaserowError} Si hay error de red\n */\n async createReadOnly(name: string): Promise<DatabaseToken> {\n const prefixedName = `${LIBRARY_TOKEN_PREFIX}${name}`\n return await this.create(prefixedName, {\n create: false,\n read: true,\n update: false,\n delete: false\n })\n }\n\n /**\n * Actualizar database token\n * @param tokenId - ID numérico del database token\n * @param data - Datos a actualizar (nombre y/o permisos)\n * @returns Promise<DatabaseToken> Database token actualizado\n * @throws {BaserowNotFoundError} Si el token no existe\n * @throws {BaserowValidationError} Si los datos son inválidos\n * @throws {BaserowError} Si hay error de red\n */\n async update(tokenId: number, data: UpdateDatabaseTokenRequest): Promise<DatabaseToken> {\n return await this.databaseTokenService.updateToken(tokenId, data)\n }\n\n /**\n * Eliminar database token\n * @param tokenId - ID numérico del database token a eliminar\n * @returns Promise<void>\n * @throws {BaserowNotFoundError} Si el token no existe\n * @throws {BaserowError} Si hay error de red\n */\n async delete(tokenId: number): Promise<void> {\n return await this.databaseTokenService.delete(tokenId)\n }\n\n /**\n * Verificar si un database token existe\n * @param tokenId - ID numérico del database token\n * @returns Promise<boolean> true si existe, false si no existe\n * @throws {BaserowError} Si hay error de red\n */\n async exists(tokenId: number): Promise<boolean> {\n return await this.databaseTokenService.exists(tokenId)\n }\n\n /**\n * Buscar database token por nombre\n * @param name - Nombre exacto del database token a buscar\n * @returns Promise<DatabaseToken | null> Token encontrado o null si no existe\n * @throws {BaserowError} Si hay error de red\n */\n async findByName(name: string): Promise<DatabaseToken | null> {\n const workspaceId = await this.resolveWorkspaceId()\n return await this.databaseTokenService.findByName(workspaceId, name)\n }\n\n /**\n * Listar solo tokens creados por la librería (con prefijo br-lib:)\n * @returns Promise<DatabaseToken[]> Lista de tokens que empiezan con 'br-lib:'\n * @throws {BaserowError} Si hay error de red\n */\n async listLibraryTokens(): Promise<DatabaseToken[]> {\n const allTokens = await this.list()\n return allTokens.filter(token => token.name.startsWith(LIBRARY_TOKEN_PREFIX))\n }\n\n /**\n * Verificar si un token fue creado por la librería\n * @param token - Database token a verificar\n * @returns boolean true si el token tiene prefijo 'br-lib:', false si no\n */\n isLibraryToken(token: DatabaseToken): boolean {\n return token.name.startsWith(LIBRARY_TOKEN_PREFIX)\n }\n\n /**\n * Limpiar todos los tokens creados por la librería en el workspace\n * Elimina solo tokens que empiecen con 'br-lib:'\n * @returns Promise<{deleted: number, errors: number}> Resumen de tokens eliminados y errores\n * @throws {BaserowError} Si hay error crítico de red\n */\n async cleanupLibraryTokens(): Promise<{ deleted: number; errors: number }> {\n const libraryTokens = await this.listLibraryTokens()\n let deleted = 0\n let errors = 0\n\n for (const token of libraryTokens) {\n try {\n await this.delete(token.id)\n deleted++\n if (this.logger) {\n this.logger.info(`Deleted library token: \"${token.name}\" (ID: ${token.id})`)\n }\n } catch (error) {\n errors++\n if (this.logger) {\n this.logger.error(`Error deleting library token \"${token.name}\":`, error)\n }\n }\n }\n\n return { deleted, errors }\n }\n\n /**\n * Resolver workspace ID (lazy loading)\n * Convierte nombre de workspace a ID numérico si es necesario\n * @returns Promise<number> ID numérico del workspace\n * @throws {BaserowNotFoundError} Si el workspace no existe\n * @throws {BaserowError} Si hay error de red\n * @private\n */\n private async resolveWorkspaceId(): Promise<number> {\n // Si ya tenemos un ID resuelto Y es un nombre string, podemos usar el cache\n if (this.resolvedWorkspaceId && typeof this.workspaceIdentifier === 'string') {\n return this.resolvedWorkspaceId\n }\n\n let workspace\n if (typeof this.workspaceIdentifier === 'number') {\n // Es un ID numérico - retornar directamente sin verificación adicional\n // La verificación se asume hecha por el caller (BaserowAdminWorkspace)\n if (this.logger?.debug) {\n this.logger.debug(`Using workspace ID for database tokens: ${this.workspaceIdentifier}`)\n }\n return this.workspaceIdentifier\n } else {\n // Es un nombre string - se puede cachear\n workspace = await this.workspaceService.findUnique(this.workspaceIdentifier)\n if (!workspace) {\n throw new Error(`Workspace \"${this.workspaceIdentifier}\" not found`)\n }\n this.resolvedWorkspaceId = workspace.id\n\n if (this.logger) {\n this.logger.info(`Resolved workspace for database tokens: \"${workspace.name}\" (ID: ${workspace.id})`)\n }\n\n return this.resolvedWorkspaceId!\n }\n }\n}\n","import { Logger, Workspace, BaserowNotFoundError, Database } from '../types'\nimport { DatabaseContext } from './DatabaseContext'\nimport { DatabaseTokenContext } from './DatabaseTokenContext'\nimport { WorkspaceService, UpdateWorkspaceRequest } from '../services/WorkspaceService'\nimport { DatabaseService } from '../services/DatabaseService'\nimport { DatabaseTokenService } from '../services/DatabaseTokenService'\nimport { TableService } from '../services/TableService'\nimport { FieldService } from '../services/FieldService'\nimport { RowService } from '../services/RowService'\n\n/**\n * Context para operaciones en un workspace específico\n *\n * Proporciona una API jerárquica fluida para operaciones administrativas\n * dentro de un workspace específico. Permite el acceso encadenado\n * workspace → database → table → field/rows y operaciones CRUD completas.\n *\n * **Características principales:**\n * - Resolución automática de workspace por nombre o ID (lazy loading)\n * - API fluida para operaciones de databases: list, find, create\n * - Operaciones CRUD del workspace: update, delete\n * - Acceso directo a database contexts específicos\n * - Gestión de database tokens del workspace\n * - Cache interno para evitar resoluciones repetidas\n * - Logging opcional de todas las operaciones\n *\n * **Patrón de API Jerárquica:**\n * ```\n * workspace.databases.list() // Operaciones masivas\n * workspace.database('name') // Context específico\n * workspace.databaseToken // Database tokens\n * workspace.update({ name: 'nuevo' }) // Actualizar workspace\n * workspace.delete() // Eliminar workspace\n * ```\n *\n * @example\n * ```typescript\n * // Gestión del workspace\n * const updated = await workspace.update({ name: 'Nuevo Nombre' })\n * await workspace.delete() // Elimina workspace y todo su contenido\n *\n * // Operaciones básicas de databases\n * const databases = await workspace.databases.list()\n * const newDb = await workspace.databases.create('Nueva Database')\n * const found = await workspace.databases.find('Existente')\n *\n * // Acceso jerárquico a database\n * const dbContext = workspace.database('Mi Database')\n * const table = await dbContext.tables.create({ name: 'Mi Tabla' })\n *\n * // Gestión de database tokens\n * const tokens = await workspace.databaseToken.list()\n * const token = await workspace.databaseToken.create('read', ['table1'])\n * ```\n *\n * @since 1.0.0\n */\nexport class WorkspaceContext {\n private workspaceIdentifier: string | number\n private resolvedWorkspace?: Workspace\n private logger?: Logger\n\n /**\n * Crea un nuevo context de workspace\n *\n * El constructor es interno y no debe ser llamado directamente.\n * Usar `admin.workspace(nameOrId)` para crear contextos de workspace.\n *\n * @param workspaceService - Servicio para operaciones de workspace (CRUD completo)\n * @param databaseService - Servicio para operaciones de database\n * @param databaseTokenService - Servicio para gestión de tokens\n * @param tableService - Servicio para operaciones de tabla\n * @param fieldService - Servicio para operaciones de campos\n * @param rowService - Servicio para operaciones de filas\n * @param workspaceIdentifier - Nombre (string) o ID (number) del workspace\n * @param logger - Logger opcional para debug y trazabilidad\n *\n * @example Uso típico del contexto\n * ```typescript\n * // Creación del contexto (interno)\n * const workspaceContext = admin.workspace('Mi Empresa')\n *\n * // Operaciones CRUD del workspace\n * const updated = await workspaceContext.update({ name: 'Nueva Empresa' })\n * await workspaceContext.delete()\n *\n * // Operaciones de databases\n * const databases = await workspaceContext.databases.list()\n * const dbContext = workspaceContext.database('CRM')\n * ```\n *\n * @internal\n * @since 1.0.0\n */\n constructor(\n private workspaceService: WorkspaceService,\n private databaseService: DatabaseService,\n private databaseTokenService: DatabaseTokenService,\n private tableService: TableService,\n private fieldService: FieldService,\n private rowService: RowService,\n workspaceIdentifier: string | number,\n logger?: Logger\n ) {\n this.workspaceIdentifier = workspaceIdentifier\n this.logger = logger\n }\n\n /**\n * Operaciones masivas de databases en este workspace\n *\n * Proporciona métodos para gestionar databases de forma masiva:\n * listar todas, buscar por nombre y crear nuevas databases.\n * Todas las operaciones están restringidas al workspace actual.\n *\n * @returns Objeto con métodos para operaciones de databases\n *\n * @example\n * ```typescript\n * // Listar todas las databases del workspace\n * const databases = await workspace.databases.list()\n *\n * // Buscar database específica\n * const db = await workspace.databases.find('Mi Database')\n *\n * // Crear nueva database\n * const newDb = await workspace.databases.create('Nueva Database')\n * ```\n *\n * @since 1.0.0\n */\n get databases() {\n return {\n /**\n * Listar todas las databases del workspace\n *\n * Obtiene todas las databases que pertenecen a este workspace.\n * Filtra automáticamente las databases globales para mostrar\n * solo las del workspace actual.\n *\n * @returns Promise con array de databases del workspace\n *\n * @example\n * ```typescript\n * const databases = await workspace.databases.list()\n * console.log(`Encontradas ${databases.length} databases`)\n * databases.forEach(db => {\n * console.log(`${db.name} (ID: ${db.id})`)\n * })\n * ```\n *\n * @since 1.0.0\n */\n list: async (): Promise<Database[]> => {\n const workspace = await this.get()\n const allDatabases = await this.databaseService.findMany()\n return allDatabases.filter(db => db.workspace?.id === workspace.id)\n },\n\n /**\n * Buscar database por nombre en este workspace\n *\n * Busca una database específica por nombre dentro del workspace.\n * Útil para operaciones donde se conoce el nombre pero no el ID.\n *\n * @param name - Nombre exacto de la database a buscar\n * @returns Promise con la database encontrada o null si no existe\n *\n * @example\n * ```typescript\n * const db = await workspace.databases.find('CRM')\n * if (db) {\n * console.log(`Database encontrada: ${db.id}`)\n * } else {\n * console.log('Database no encontrada')\n * }\n * ```\n *\n * @since 1.0.0\n */\n find: async (name: string): Promise<Database | null> => {\n const databases = await this.databases.list()\n return databases.find(db => db.name === name) || null\n },\n\n /**\n * Crear nueva database en este workspace\n *\n * Crea una nueva database vacía en este workspace.\n * La database se crea sin tablas iniciales.\n *\n * @param name - Nombre de la nueva database\n * @returns Promise con la database creada\n *\n * @throws {BaserowValidationError} Si el nombre es inválido\n *\n * @example\n * ```typescript\n * const newDb = await workspace.databases.create('Nueva Database')\n * console.log(`Database creada: ${newDb.id} - ${newDb.name}`)\n * ```\n *\n * @since 1.0.0\n */\n create: async (name: string): Promise<Database> => {\n const workspace = await this.get()\n return await this.databaseService.create(workspace.id, name)\n }\n }\n }\n\n /**\n * Acceder a una base de datos específica en este workspace\n *\n * Crea un context para operaciones específicas en una database.\n * Permite acceso jerárquico a tablas, campos y filas de la database.\n * El identificador puede ser nombre (string) o ID (number).\n *\n * @param databaseIdentifier - Nombre o ID numérico de la database\n * @returns Context de database para operaciones específicas\n *\n * @example\n * ```typescript\n * // Acceso por nombre\n * const dbContext = workspace.database('Mi Database')\n *\n * // Acceso por ID\n * const dbContext2 = workspace.database(123)\n *\n * // Operaciones jerárquicas\n * const tables = await dbContext.tables.findMany()\n * const table = await dbContext.tables.create({ name: 'Nueva Tabla' })\n * ```\n *\n * @since 1.0.0\n */\n database(databaseIdentifier: string | number): DatabaseContext {\n return new DatabaseContext(\n this.workspaceService,\n this.workspaceIdentifier,\n databaseIdentifier,\n this.databaseService,\n this.tableService,\n this.fieldService,\n this.rowService,\n this.logger\n )\n }\n\n /**\n * Operaciones de database tokens en este workspace\n *\n * Proporciona acceso al context de database tokens para gestionar\n * tokens de acceso específicos del workspace. Los database tokens\n * permiten acceso limitado a databases y tablas específicas.\n *\n * @returns Context de database tokens\n *\n * @example\n * ```typescript\n * // Listar tokens existentes\n * const tokens = await workspace.databaseToken.list()\n *\n * // Crear token de solo lectura\n * const readToken = await workspace.databaseToken.create(\n * 'read',\n * ['table1', 'table2']\n * )\n *\n * // Crear token de escritura\n * const writeToken = await workspace.databaseToken.create(\n * 'create',\n * ['table1']\n * )\n * ```\n *\n * @since 1.0.0\n */\n get databaseToken(): DatabaseTokenContext {\n return new DatabaseTokenContext(\n this.workspaceService,\n this.workspaceIdentifier,\n this.databaseTokenService,\n this.logger\n )\n }\n\n /**\n * Actualizar este workspace\n *\n * Actualiza las propiedades del workspace actual mediante API jerárquica.\n * El cache interno se actualiza automáticamente con los nuevos datos.\n * Soporta identificación por nombre o ID del workspace.\n *\n * @param data - Datos para actualizar el workspace\n * @param data.name - Nuevo nombre del workspace (opcional)\n * @returns Promise con el workspace actualizado con los nuevos datos\n *\n * @throws {BaserowNotFoundError} Si el workspace no existe o no se puede encontrar\n * @throws {BaserowValidationError} Si los datos proporcionados son inválidos\n *\n * @example Actualización por nombre\n * ```typescript\n * const updated = await admin.workspace('Mi Empresa').update({\n * name: 'Nueva Empresa S.A.'\n * })\n * console.log(`Workspace actualizado: ${updated.name}`)\n * ```\n *\n * @example Actualización por ID\n * ```typescript\n * const updated = await admin.workspace(123).update({\n * name: 'Empresa Actualizada'\n * })\n * console.log(`ID: ${updated.id}, Nombre: ${updated.name}`)\n * ```\n *\n * @since 1.0.0\n */\n async update(data: UpdateWorkspaceRequest): Promise<Workspace> {\n const workspace = await this.get()\n const contextAccess = (this.workspaceService as any)[Symbol.for('workspaceContext')]\n const updatedWorkspace = await contextAccess.updateWorkspace(workspace.id, data)\n\n // Actualizar cache interno\n this.resolvedWorkspace = updatedWorkspace\n\n if (this.logger) {\n this.logger.info(`Workspace updated: \"${updatedWorkspace.name}\" (ID: ${updatedWorkspace.id})`)\n }\n\n return updatedWorkspace\n }\n\n /**\n * Eliminar este workspace\n *\n * Elimina permanentemente el workspace y todas sus databases, tablas y datos asociados.\n * Esta operación es **irreversible** y limpia automáticamente el cache interno.\n * Soporta identificación por nombre o ID del workspace.\n *\n * ⚠️ **ADVERTENCIA**: Esta operación elimina todo el contenido del workspace:\n * - Todas las databases del workspace\n * - Todas las tablas y campos de esas databases\n * - Todos los datos (filas) almacenados\n * - Todos los database tokens asociados\n *\n * @returns Promise que resuelve cuando el workspace es completamente eliminado\n *\n * @throws {BaserowNotFoundError} Si el workspace no existe o no se puede encontrar\n * @throws {BaserowAuthError} Si no tienes permisos para eliminar el workspace\n *\n * @example Eliminación por nombre\n * ```typescript\n * // Eliminar workspace identificado por nombre\n * await admin.workspace('Mi Empresa').delete()\n * console.log('Workspace \"Mi Empresa\" eliminado exitosamente')\n * ```\n *\n * @example Eliminación por ID\n * ```typescript\n * // Eliminar workspace identificado por ID numérico\n * await admin.workspace(123).delete()\n * console.log('Workspace con ID 123 eliminado exitosamente')\n * ```\n *\n * @example Patrón de confirmación seguro\n * ```typescript\n * const workspaceName = 'Empresa Temporal'\n * const workspace = await admin.workspace(workspaceName).get()\n *\n * console.log(`¿Seguro que quieres eliminar \"${workspace.name}\"?`)\n * console.log(`Contiene ${workspace.databases?.length || 0} databases`)\n *\n * // Confirmar antes de eliminar\n * if (confirmDeletion) {\n * await admin.workspace(workspaceName).delete()\n * console.log('Workspace eliminado exitosamente')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async delete(): Promise<void> {\n const workspace = await this.get()\n const contextAccess = (this.workspaceService as any)[Symbol.for('workspaceContext')]\n await contextAccess.deleteWorkspace(workspace.id)\n\n if (this.logger) {\n this.logger.info(`Workspace deleted: \"${workspace.name}\" (ID: ${workspace.id})`)\n }\n\n // Limpiar cache\n this.resolvedWorkspace = undefined\n }\n\n /**\n * Obtener este workspace completo\n *\n * Recupera el workspace con todos sus datos y metadatos.\n * Utiliza lazy loading con cache para evitar resoluciones repetidas.\n * Soporta identificación por nombre o ID del workspace.\n *\n * @returns Promise con el workspace completo\n *\n * @throws {BaserowNotFoundError} Si el workspace no existe\n * @throws {BaserowValidationError} Si el identificador es inválido\n *\n * @example Obtener por nombre\n * ```typescript\n * const workspace = await admin.workspace('Mi Empresa').get()\n * console.log(`Workspace: ${workspace.name} (ID: ${workspace.id})`)\n * console.log(`Creado: ${workspace.created_on}`)\n * ```\n *\n * @example Obtener por ID\n * ```typescript\n * const workspace = await admin.workspace(123).get()\n * console.log(`Nombre: ${workspace.name}`)\n * console.log(`Usuarios: ${workspace.users?.length || 0}`)\n * ```\n *\n * @since 1.1.0\n */\n async get(): Promise<Workspace> {\n if (this.resolvedWorkspace) {\n return this.resolvedWorkspace\n }\n\n // Usar findUnique que maneja tanto ID como nombre\n const workspace = await this.workspaceService.findUnique(this.workspaceIdentifier)\n\n if (!workspace) {\n throw new BaserowNotFoundError('Workspace', this.workspaceIdentifier)\n }\n\n this.resolvedWorkspace = workspace\n\n if (this.logger) {\n this.logger.info(`Retrieved workspace: \"${workspace.name}\" (ID: ${workspace.id})`)\n }\n\n return workspace\n }\n\n /**\n * Verificar si este workspace existe\n *\n * Método de utilidad para verificar la existencia del workspace\n * sin cargar todos sus datos. Útil para validaciones previas.\n *\n * @returns Promise con true si existe, false si no\n *\n * @example Verificar existencia por nombre\n * ```typescript\n * const exists = await admin.workspace('Mi Empresa').exists()\n * if (exists) {\n * console.log('El workspace existe')\n * } else {\n * console.log('El workspace no fue encontrado')\n * }\n * ```\n *\n * @example Verificar antes de crear\n * ```typescript\n * const workspaceName = 'Nueva Empresa'\n * const exists = await admin.workspace(workspaceName).exists()\n *\n * if (!exists) {\n * await admin.workspaces.create({ name: workspaceName })\n * console.log('Workspace creado exitosamente')\n * } else {\n * console.log('El workspace ya existe')\n * }\n * ```\n *\n * @since 1.1.0\n */\n async exists(): Promise<boolean> {\n try {\n await this.get()\n return true\n } catch (error) {\n if ((error as any).name === 'BaserowNotFoundError') {\n return false\n }\n throw error\n }\n }\n}\n","/**\n * Clase base abstracta para clientes con autenticación JWT\n * Extiende BaseClient agregando funcionalidades de autenticación\n */\n\nimport { BaseClient } from './BaseClient'\nimport { HttpClient } from '../../utils/axios'\nimport { Logger } from '../../types/index'\nimport { AuthService } from '../AuthService'\n\nexport abstract class BaseAuthClient<TConfig = any, TExcludeKeys extends keyof TConfig = never> extends BaseClient<\n TConfig,\n TExcludeKeys\n> {\n protected auth: AuthService\n\n constructor(config: TConfig, http: HttpClient, auth: AuthService, logger?: Logger) {\n super(config, http, logger)\n this.auth = auth\n }\n\n /**\n * Verificar si el cliente está autenticado\n *\n * Comprueba si hay un token JWT válido y no expirado.\n * Si el token está próximo a expirar, el auto-refresh se encargará de renovarlo.\n *\n * @returns `true` si está autenticado con token válido, `false` en caso contrario\n *\n * @example\n * ```typescript\n * if (admin.isAuthenticated()) {\n * console.log('Cliente autenticado correctamente')\n * // Proceder con operaciones que requieren autenticación\n * } else {\n * console.log('Cliente no autenticado')\n * }\n * ```\n *\n * @since 1.0.0\n */\n isAuthenticated(): boolean {\n return this.auth.isAuthenticated()\n }\n\n /**\n * Obtener el token JWT actual\n *\n * Retorna el token de acceso actual si está disponible.\n * Útil para debugging o para usar el token en otras APIs.\n *\n * @returns Token JWT actual o undefined si no está autenticado\n *\n * @example\n * ```typescript\n * const token = admin.getCurrentToken()\n * if (token) {\n * console.log('Token actual:', token.substring(0, 10) + '...')\n * }\n * ```\n *\n * @since 1.0.0\n */\n getCurrentToken(): string | undefined {\n return this.auth.getCurrentToken()\n }\n\n /**\n * Refrescar el token JWT manualmente\n *\n * Normalmente el auto-refresh maneja esto automáticamente, pero este método\n * permite forzar un refresh del token antes de que expire.\n *\n * @returns Promise que resuelve al nuevo token de acceso\n * @throws {BaserowAuthError} Si el refresh falla (refresh token expirado/inválido)\n *\n * @example\n * ```typescript\n * try {\n * const newToken = await admin.refreshToken()\n * console.log('Token refrescado exitosamente')\n * } catch (error) {\n * console.error('Error refrescando token:', error.message)\n * // Re-autenticar usuario\n * }\n * ```\n *\n * @since 1.0.0\n */\n async refreshToken(): Promise<string> {\n return await this.auth.refreshAccessToken()\n }\n\n /**\n * Cerrar sesión y limpiar tokens\n *\n * Invalida el token JWT actual y limpia toda la información de autenticación.\n * Después de logout() el cliente no podrá realizar operaciones autenticadas.\n *\n * @example\n * ```typescript\n * admin.logout()\n * console.log(admin.isAuthenticated()) // false\n * ```\n *\n * @since 1.0.0\n */\n logout(): void {\n this.auth.logout()\n }\n}\n","import { BaserowAdminConfig, LoginResponse, BaserowConfigError } from './types'\nimport { createHttpClient } from './utils/httpFactory'\nimport { PerformanceManager } from './utils/performance'\nimport { setupJwtTokens } from './utils/jwtTokens'\nimport { validateRequired, validateString, validateUrl } from './utils/validation'\nimport { AuthService } from './services/AuthService'\nimport { WorkspaceService } from './services/WorkspaceService'\nimport { DatabaseService } from './services/DatabaseService'\nimport { DatabaseTokenService } from './services/DatabaseTokenService'\nimport { TableService } from './services/TableService'\nimport { FieldService } from './services/FieldService'\nimport { RowService } from './services/RowService'\nimport { WorkspaceContext } from './contexts/WorkspaceContext'\nimport { BaseAuthClient } from './services/core/BaseAuthClient'\n\n/**\n * Cliente administrativo con acceso global a todos los workspaces\n *\n * Proporciona acceso completo de administración usando JWT authentication con auto-refresh.\n * API jerárquica que siempre comienza por workspace para mantener contexto claro.\n * Ideal para scripts de administración y operaciones multi-workspace.\n *\n * **Características:**\n * - Autenticación JWT con auto-refresh automático\n * - API jerárquica: siempre `admin.workspace(name).database(name)`\n * - Acceso global a todos los workspaces del usuario\n * - Operaciones administrativas completas: crear/modificar estructura\n * - Resolución automática de nombres a IDs\n * - Validación de permisos y contextos\n *\n * **API Jerárquica:**\n * - `admin.workspaces` → operaciones masivas de workspaces\n * - `admin.workspace(name)` → contexto de workspace específico\n * - `admin.workspace(name).databases` → operaciones masivas de databases\n * - `admin.workspace(name).database(name)` → contexto de database específica\n *\n * @example\n * ```typescript\n * // Crear cliente admin global usando la API unificada\n * const admin = await BaserowClient.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'password123'\n * }\n * })\n *\n * // API jerárquica que siempre comienza por workspace\n * const workspaces = await admin.workspaces.findMany()\n * const workspace = await admin.workspaces.create({ name: 'New Workspace' })\n *\n * // Gestión completa de workspaces con API jerárquica\n * const updatedWS = await admin.workspace('Company').update({ name: 'New Company' })\n * await admin.workspace('Old Company').delete()\n *\n * // Operaciones específicas con contexto jerárquico\n * const database = await admin.workspace('Company')\n * .database('CRM')\n * .create()\n *\n * const table = await admin.workspace('Company')\n * .database('CRM')\n * .table('Customers')\n * .create({\n * data: [['Name', 'Email'], ['Alice', 'alice@example.com']],\n * first_row_header: true\n * })\n * ```\n *\n * @since 1.0.0\n */\nexport class ClientWithCreds extends BaseAuthClient<BaserowAdminConfig, 'credentials'> {\n // Servicios para operaciones globales\n public readonly workspaces: WorkspaceService\n private readonly databaseService: DatabaseService\n private readonly databaseTokenService: DatabaseTokenService\n private readonly tableService: TableService\n private readonly fieldService: FieldService\n private readonly rowService: RowService\n\n private constructor(config: BaserowAdminConfig, loginResponse: LoginResponse) {\n // Procesar performance con defaults antes de guardar\n const processedConfig = {\n ...config,\n performance: PerformanceManager.merge(config.performance)\n }\n\n // Inicializar HttpClient con JWT token\n const http = createHttpClient({\n url: processedConfig.url,\n token: loginResponse.access_token,\n logger: processedConfig.logger,\n performance: processedConfig.performance\n })\n\n // Inicializar AuthService\n const auth = new AuthService(http, processedConfig.logger)\n\n super(processedConfig, http, auth, processedConfig.logger)\n\n // Transferir tokens al AuthService ANTES de configurar auto-refresh\n setupJwtTokens(this.auth, loginResponse)\n\n // Configurar auto-refresh de tokens (ahora tiene los tokens almacenados)\n this.auth.setupAutoRefresh()\n\n // Iniciar keep-alive si está habilitado\n if (processedConfig.keepAlive?.enabled) {\n const intervalMinutes = processedConfig.keepAlive.intervalMinutes ?? 7200\n this.auth.startKeepAlive(intervalMinutes)\n }\n\n // Inicializar servicios\n this.workspaces = new WorkspaceService(this.http, this.logger)\n this.databaseService = new DatabaseService(this.http, this.logger)\n this.databaseTokenService = new DatabaseTokenService(this.http, this.logger)\n this.tableService = new TableService(this.http, this.logger)\n this.fieldService = new FieldService(this.http, this.logger)\n this.rowService = new RowService(this.http, this.logger)\n\n if (this.logger) {\n this.logger.info('ClientWithCreds initialized with JWT authentication (global mode)')\n }\n }\n\n /**\n * Campos que deben excluirse por defecto para cliente con credenciales\n * Excluye credentials por seguridad\n */\n protected getDefaultExcludeKeys(): string[] {\n return ['credentials']\n }\n\n /**\n * Crear instancia de ClientWithCreds con auto-login\n *\n * Factory method que maneja la autenticación automáticamente y retorna una instancia\n * lista para usar con JWT token válido y auto-refresh configurado.\n *\n * **Keep-Alive opcional** para backends 24/7:\n * - Habilitar con `keepAlive: { enabled: true }` para evitar expiración de refresh token\n * - Default: re-login cada 5 días (7200 min), margen de seguridad de 2 días\n * - ⚠️ Almacena credenciales en memoria (solo para backends confiables)\n *\n * @param config - Configuración del cliente administrativo\n * @param config.url - URL del servidor Baserow\n * @param config.credentials - Credenciales de usuario (email/password)\n * @param config.keepAlive - Opcional: configuración keep-alive para backends 24/7\n * @param config.logger - Logger opcional para debugging\n * @returns Promise que resuelve a instancia autenticada de ClientWithCreds\n *\n * @throws {BaserowConfigError} Si la configuración es inválida\n * @throws {BaserowAuthError} Si las credenciales son incorrectas\n *\n * @example\n * ```typescript\n * // Cliente básico sin keep-alive\n * const admin = await BaserowClient.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'secure-password'\n * }\n * })\n *\n * // Cliente con keep-alive para backend 24/7 (re-login cada 5 días)\n * const adminBackend = await BaserowClient.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'secure-password'\n * },\n * keepAlive: { enabled: true }\n * })\n *\n * // Cliente listo para usar\n * const workspaces = await admin.workspaces.findMany()\n * ```\n *\n * @since 1.0.0\n */\n static async create(config: BaserowAdminConfig): Promise<ClientWithCreds> {\n validateRequired(config, 'config')\n validateString(config.url, 'url')\n validateRequired(config.credentials, 'credentials')\n validateString(config.credentials.email, 'email')\n validateString(config.credentials.password, 'password')\n\n if (!validateUrl(config.url)) {\n throw new BaserowConfigError('Invalid URL format', 'url')\n }\n\n // Crear HttpClient temporal para login\n const tempHttp = createHttpClient({\n url: config.url,\n token: '', // Sin token inicial\n logger: config.logger,\n performance: config.performance\n })\n\n // Crear AuthService temporal\n const tempAuth = new AuthService(tempHttp, config.logger)\n\n // Realizar login\n const loginResponse = await tempAuth.login(config.credentials)\n\n if (config.logger) {\n config.logger.info(`ClientWithCreds: Logged in as ${loginResponse.user.username}`)\n }\n\n // Crear instancia con token válido\n // Los tokens son transferidos dentro del constructor\n const admin = new ClientWithCreds(config, loginResponse)\n\n return admin\n }\n\n /**\n * Verificar estado de salud del servidor Baserow\n *\n * Realiza un health check del servidor sin requerir autenticación.\n * Útil para verificar conectividad antes de realizar operaciones.\n *\n * @returns Promise que resuelve a `true` si el servidor está saludable, `false` en caso contrario\n *\n * @example\n * ```typescript\n * const isHealthy = await admin.health()\n * if (!isHealthy) {\n * console.log('Servidor Baserow no disponible')\n * return\n * }\n *\n * // Proceder con operaciones\n * const workspaces = await admin.workspaces.findMany()\n * ```\n *\n * @since 1.0.0\n */\n async health(): Promise<boolean> {\n try {\n // Usar endpoint de salud oficial (no requiere autenticación)\n const healthUrl = `${this.config.url.replace(/\\/$/, '')}/api/_health/`\n const response = await fetch(healthUrl, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' }\n })\n\n return response.ok\n } catch (error) {\n if (this.logger) {\n this.logger.error('ClientWithCreds health check failed:', error)\n }\n return false\n }\n }\n\n /**\n * Obtener la configuración actual del cliente (sin exponer credenciales)\n *\n * Retorna una copia de solo lectura de la configuración, excluyendo las credenciales\n * por seguridad. Útil para debugging y logging.\n *\n * @returns Configuración actual sin credenciales (solo lectura)\n *\n * @example\n * ```typescript\n * const config = admin.getConfig()\n * console.log('URL servidor:', config.url)\n * // Las credenciales no están incluidas por seguridad\n * ```\n *\n * @since 1.0.0\n */\n getConfig(): Readonly<Omit<BaserowAdminConfig, 'credentials'>> {\n return this.getConfigBase()\n }\n\n /**\n * Verificar si el cliente está autenticado\n *\n * Comprueba si hay un token JWT válido y no expirado.\n * Si el token está próximo a expirar, el auto-refresh se encargará de renovarlo.\n *\n * @returns `true` si está autenticado con token válido, `false` en caso contrario\n *\n * @example\n * ```typescript\n * if (admin.isAuthenticated()) {\n * const workspaces = await admin.workspaces.findMany()\n * } else {\n * console.log('Sesión expirada, necesario re-login')\n * }\n * ```\n *\n * @since 1.0.0\n */\n isAuthenticated(): boolean {\n return this.auth.isAuthenticated()\n }\n\n /**\n * Obtener el token JWT actual\n *\n * Retorna el token de acceso actual si está disponible.\n * Útil para debugging o integración con otros sistemas.\n *\n * @returns Token JWT actual o `undefined` si no está autenticado\n *\n * @example\n * ```typescript\n * const token = admin.getCurrentToken()\n * if (token) {\n * console.log('Token disponible:', token.substring(0, 20) + '...')\n * }\n * ```\n *\n * @since 1.0.0\n */\n getCurrentToken(): string | undefined {\n return this.auth.getCurrentToken()\n }\n\n /**\n * Renovar el token de acceso manualmente\n *\n * Fuerza la renovación del token JWT usando el refresh token.\n * Normalmente no es necesario llamar esto manualmente ya que el auto-refresh\n * se encarga automáticamente.\n *\n * @returns Promise que resuelve al nuevo token de acceso\n *\n * @throws {BaserowAuthError} Si el refresh token es inválido o expirado\n *\n * @example\n * ```typescript\n * try {\n * const newToken = await admin.refreshToken()\n * console.log('Token renovado exitosamente')\n * } catch (error) {\n * console.log('Error renovando token, necesario re-login')\n * }\n * ```\n *\n * @since 1.0.0\n */\n async refreshToken(): Promise<string> {\n return await this.auth.refreshAccessToken()\n }\n\n /**\n * Verificar si el cliente necesita re-autenticación\n *\n * Útil para detectar si los tokens se han perdido o el refresh token expiró,\n * permitiendo manejar proactivamente la re-autenticación antes de hacer requests.\n *\n * @returns `true` si necesita re-login, `false` si la sesión es válida\n *\n * @example\n * ```typescript\n * if (admin.needsReLogin()) {\n * console.log('Sesión expirada, solicitando credenciales...')\n * const newAdmin = await ClientWithCreds.create({\n * url: config.url,\n * credentials: { email, password }\n * })\n * } else {\n * const workspaces = await admin.workspaces.findMany()\n * }\n * ```\n *\n * @since 1.0.4\n */\n needsReLogin(): boolean {\n if (!this.auth.getCurrentToken() || !this.auth['refreshToken']) {\n return true\n }\n\n if (this.auth.isTokenExpired() && !this.auth['refreshToken']) {\n return true\n }\n\n return false\n }\n\n /**\n * Obtener estado detallado de autenticación\n *\n * @returns Objeto con información detallada del estado de autenticación\n *\n * @example\n * ```typescript\n * const status = admin.getAuthStatus()\n * console.log('Autenticado:', status.isAuthenticated)\n * console.log('Necesita re-login:', status.needsReLogin)\n * ```\n *\n * @since 1.0.4\n */\n getAuthStatus(): {\n isAuthenticated: boolean\n hasAccessToken: boolean\n hasRefreshToken: boolean\n tokenExpiry?: Date\n isTokenExpired: boolean\n needsReLogin: boolean\n } {\n const hasAccessToken = !!this.auth.getCurrentToken()\n const hasRefreshToken = !!this.auth['refreshToken']\n const tokenExpiry = this.auth['tokenExpiry'] as Date | undefined\n const isTokenExpired = this.auth.isTokenExpired()\n\n return {\n isAuthenticated: this.auth.isAuthenticated(),\n hasAccessToken,\n hasRefreshToken,\n tokenExpiry,\n isTokenExpired,\n needsReLogin: this.needsReLogin()\n }\n }\n\n /**\n * Cerrar sesión y limpiar tokens\n *\n * Invalida los tokens JWT, detiene el keep-alive y limpia el estado de autenticación.\n * Después de llamar este método, el cliente necesitará volver a autenticarse.\n *\n * @example\n * ```typescript\n * // Al final de la aplicación o cambio de usuario\n * admin.logout()\n *\n * // El cliente ya no está autenticado\n * console.log(admin.isAuthenticated()) // false\n * ```\n *\n * @since 1.0.0\n */\n logout(): void {\n this.auth.logout() // Esto internamente llama stopKeepAlive()\n if (this.logger) {\n this.logger.info('ClientWithCreds: Logged out')\n }\n }\n\n // ============================================================================\n // API JERÁRQUICA - SIEMPRE COMIENZA POR WORKSPACE\n // ============================================================================\n\n /**\n * Acceder a un workspace específico (API jerárquica)\n *\n * Crea un contexto de workspace que permite operaciones en databases y recursos anidados.\n * Acepta tanto nombres de workspace como IDs numéricos.\n *\n * @param workspaceIdentifier - Nombre del workspace o ID numérico\n * @returns Contexto de workspace con acceso a databases y operaciones\n *\n * @example\n * ```typescript\n * // Usando nombre del workspace\n * const databases = await admin.workspace('My Workspace').databases.list()\n * const database = await admin.workspace('My Workspace').databases.create({\n * name: 'New Database'\n * })\n *\n * // Usando ID numérico\n * const tables = await admin.workspace(123).database('My DB').tables.findMany()\n *\n * // API jerárquica completa\n * const table = await admin.workspace('Company')\n * .database('CRM')\n * .table('Customers')\n * .create({\n * data: [['Name', 'Email'], ['John', 'john@example.com']],\n * first_row_header: true\n * })\n * ```\n *\n * @since 1.0.0\n */\n workspace(workspaceIdentifier: string | number): WorkspaceContext {\n return new WorkspaceContext(\n this.workspaces,\n this.databaseService,\n this.databaseTokenService,\n this.tableService,\n this.fieldService,\n this.rowService,\n workspaceIdentifier,\n this.logger\n )\n }\n\n /**\n * Obtener estadísticas de uso global\n */\n async getUsageStats(): Promise<{\n totalWorkspaces: number\n totalDatabases: number\n totalTables: number\n isAuthenticated: boolean\n tokenValid: boolean\n mode: 'global'\n }> {\n const workspaces = await this.workspaces.findMany()\n\n // Contar databases y tables de todos los workspaces\n let totalDatabases = 0\n let totalTables = 0\n\n for (const workspace of workspaces) {\n const databases = await this.databaseService.findMany()\n const workspaceDatabases = databases.filter(db => db.workspace?.id === workspace.id)\n totalDatabases += workspaceDatabases.length\n\n // Usar las tablas que ya vienen en el objeto Database (más eficiente)\n totalTables += workspaceDatabases.reduce((sum, db) => sum + (db.tables?.length || 0), 0)\n }\n\n return {\n totalWorkspaces: workspaces.length,\n totalDatabases,\n totalTables,\n isAuthenticated: this.isAuthenticated(),\n tokenValid: !this.auth.isTokenExpired(),\n mode: 'global'\n }\n }\n\n /**\n * Cerrar conexiones HTTP y limpiar recursos\n *\n * Detiene el keep-alive y libera recursos del cliente HTTP.\n * Debe llamarse cuando el cliente ya no se necesite.\n *\n * @since 1.0.0\n */\n destroy(): void {\n this.logger?.debug?.('Destroying ClientWithCreds')\n this.auth.stopKeepAlive()\n this.http.destroy()\n }\n}\n","import { BaserowAdminConfig, LoginResponse, Workspace, BaserowConfigError } from './types'\nimport { createHttpClient } from './utils/httpFactory'\nimport { PerformanceManager } from './utils/performance'\nimport { setupJwtTokens } from './utils/jwtTokens'\nimport { validateRequired, validateString, validateUrl } from './utils/validation'\nimport { AuthService } from './services/AuthService'\nimport { DatabaseService } from './services/DatabaseService'\nimport { DatabaseTokenService } from './services/DatabaseTokenService'\nimport { TableService } from './services/TableService'\nimport { FieldService } from './services/FieldService'\nimport { RowService } from './services/RowService'\nimport { WorkspaceService } from './services/WorkspaceService'\nimport { DatabaseContext } from './contexts/DatabaseContext'\nimport { DatabaseTokenContext } from './contexts/DatabaseTokenContext'\nimport { BaseAuthClient } from './services/core/BaseAuthClient'\n\n/**\n * Cliente administrativo restringido a workspace específico\n *\n * Proporciona acceso administrativo completo pero limitado a un workspace específico.\n * API simplificada sin prefijo workspace, ideal para aplicaciones con scope limitado.\n * Se autentica con JWT y auto-refresh, pero valida permisos solo para el workspace configurado.\n *\n * **Características:**\n * - Autenticación JWT con auto-refresh automático\n * - API simplificada: `admin.databases.create(name)` (workspace automático)\n * - Acceso restringido al workspace especificado en configuración\n * - Operaciones administrativas completas dentro del workspace\n * - Resolución automática de nombres a IDs\n * - Validación automática de permisos de workspace\n *\n * **API Simplificada:**\n * - `admin.databases` → operaciones de databases del workspace\n * - `admin.database(name)` → contexto de database específica\n * - `admin.databaseToken` → gestión de Database Tokens del workspace\n *\n * @example\n * ```typescript\n * const admin = await BaserowWithCredentialsWorkspace.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'password123'\n * },\n * workspace: 'My Workspace'\n * })\n *\n * // API simplificada sin prefijo workspace\n * const databases = await admin.databases.findMany()\n * const database = await admin.databases.create('My Database')\n *\n * // Acceso directo a operaciones de database\n * const table = await admin.database('My Database')\n * .table('Users')\n * .create({\n * data: [['Name', 'Email'], ['John', 'john@example.com']],\n * first_row_header: true\n * })\n *\n * // Gestión de Database Tokens del workspace\n * const token = await admin.databaseToken.createFullAccess('app-token')\n * ```\n *\n * @since 1.0.0\n */\nexport class ClientWithCredsWs extends BaseAuthClient<BaserowAdminConfig, 'credentials'> {\n private defaultWorkspace: Workspace\n\n // Servicios que tienen sentido a nivel workspace\n public databases: DatabaseService\n\n // Servicios internos (no públicos)\n private workspaceService: WorkspaceService\n private databaseTokenService: DatabaseTokenService\n\n private constructor(config: BaserowAdminConfig, loginResponse: LoginResponse, workspace: Workspace) {\n // Procesar performance con defaults antes de guardar\n const processedConfig = {\n ...config,\n performance: PerformanceManager.merge(config.performance)\n }\n\n // Inicializar HttpClient con JWT token\n const http = createHttpClient({\n url: processedConfig.url,\n token: loginResponse.access_token,\n logger: processedConfig.logger,\n performance: processedConfig.performance\n })\n\n // Inicializar AuthService\n const auth = new AuthService(http, processedConfig.logger)\n\n super(processedConfig, http, auth, processedConfig.logger)\n this.defaultWorkspace = workspace\n\n // Transferir tokens al AuthService ANTES de configurar auto-refresh\n setupJwtTokens(this.auth, loginResponse)\n\n // Configurar auto-refresh de tokens (ahora tiene los tokens almacenados)\n this.auth.setupAutoRefresh()\n\n // Iniciar keep-alive si está habilitado\n if (processedConfig.keepAlive?.enabled) {\n const intervalMinutes = processedConfig.keepAlive.intervalMinutes ?? 7200\n this.auth.startKeepAlive(intervalMinutes)\n }\n\n // Inicializar servicios\n this.workspaceService = new WorkspaceService(this.http, this.logger)\n this.databaseTokenService = new DatabaseTokenService(this.http, this.logger)\n this.databases = new DatabaseService(this.http, this.logger, workspace.id)\n\n if (this.logger) {\n this.logger.info(\n `BaserowWithCredentialsWorkspace initialized with JWT authentication (workspace-restricted to \"${workspace.name}\")`\n )\n }\n }\n\n /**\n * Campos que deben excluirse por defecto para cliente con credenciales workspace\n * Excluye credentials por seguridad\n */\n protected getDefaultExcludeKeys(): string[] {\n return ['credentials']\n }\n\n /**\n * Crear instancia de BaserowWithCredentialsWorkspace con auto-login y validación de workspace\n *\n * Factory method que maneja la autenticación automáticamente y valida/crea el workspace.\n * Retorna una instancia lista para usar con JWT token válido y workspace configurado.\n *\n * **Keep-Alive opcional** para backends 24/7:\n * - Habilitar con `keepAlive: { enabled: true }` para evitar expiración de refresh token\n * - Default: re-login cada 5 días (7200 min), margen de seguridad de 2 días\n * - ⚠️ Almacena credenciales en memoria (solo para backends confiables)\n *\n * @param config - Configuración del cliente administrativo\n * @param config.url - URL del servidor Baserow\n * @param config.credentials - Credenciales de usuario (email/password)\n * @param config.workspace - Nombre del workspace a usar (se crea si no existe)\n * @param config.keepAlive - Opcional: configuración keep-alive para backends 24/7\n * @param config.logger - Logger opcional para debugging\n * @returns Promise que resuelve a instancia autenticada de BaserowWithCredentialsWorkspace\n *\n * @throws {BaserowConfigError} Si la configuración es inválida o falta workspace\n * @throws {BaserowAuthError} Si las credenciales son incorrectas\n *\n * @example\n * ```typescript\n * // Cliente básico sin keep-alive\n * const admin = await BaserowWithCredentialsWorkspace.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'secure-password'\n * },\n * workspace: 'My Company Workspace'\n * })\n *\n * // Cliente con keep-alive para backend 24/7 (re-login cada 5 días)\n * const adminBackend = await BaserowWithCredentialsWorkspace.create({\n * url: 'https://baserow.example.com',\n * credentials: {\n * email: 'admin@example.com',\n * password: 'secure-password'\n * },\n * workspace: 'My Company Workspace',\n * keepAlive: { enabled: true }\n * })\n *\n * // Cliente listo para usar (restringido al workspace)\n * const databases = await admin.databases.list()\n * const stats = await admin.getUsageStats() // Solo del workspace configurado\n * ```\n *\n * @since 1.0.0\n */\n static async create(config: BaserowAdminConfig): Promise<ClientWithCredsWs> {\n validateRequired(config, 'config')\n validateString(config.url, 'url')\n validateRequired(config.credentials, 'credentials')\n validateString(config.credentials.email, 'email')\n validateString(config.credentials.password, 'password')\n\n if (!config.workspace) {\n throw new BaserowConfigError('workspace is required for BaserowWithCredentialsWorkspace', 'workspace')\n }\n\n if (!validateUrl(config.url)) {\n throw new BaserowConfigError('Invalid URL format', 'url')\n }\n\n // Crear HttpClient temporal para login\n const tempHttp = createHttpClient({\n url: config.url,\n token: '', // Sin token inicial\n logger: config.logger,\n performance: config.performance\n })\n\n // Crear AuthService temporal\n const tempAuth = new AuthService(tempHttp, config.logger)\n\n // Realizar login\n const loginResponse = await tempAuth.login(config.credentials)\n\n if (config.logger) {\n config.logger.info(`BaserowWithCredentialsWorkspace: Logged in as ${loginResponse.user.username}`)\n }\n\n // Resolver workspace por nombre\n const tempWorkspaceService = new WorkspaceService(tempHttp, config.logger)\n let workspace = await tempWorkspaceService.findUnique(config.workspace)\n\n // Si no existe, crearlo\n if (!workspace) {\n if (config.logger) {\n config.logger.info(`BaserowWithCredentialsWorkspace: Creating workspace \"${config.workspace}\"`)\n }\n workspace = await tempWorkspaceService.create({ name: config.workspace })\n }\n\n // Crear instancia con token válido y workspace resuelto\n // Los tokens son transferidos dentro del constructor\n const admin = new ClientWithCredsWs(config, loginResponse, workspace)\n\n return admin\n }\n\n /**\n * Verificar estado de salud del servidor Baserow\n *\n * Realiza un health check del servidor sin requerir autenticación.\n * Útil para verificar conectividad antes de realizar operaciones.\n *\n * @returns Promise que resuelve a `true` si el servidor está saludable, `false` en caso contrario\n *\n * @example\n * ```typescript\n * const isHealthy = await admin.health()\n * if (!isHealthy) {\n * console.log('Servidor Baserow no disponible')\n * return\n * }\n *\n * // Proceder con operaciones del workspace\n * const databases = await admin.databases.list()\n * ```\n *\n * @since 1.0.0\n */\n async health(): Promise<boolean> {\n try {\n // Usar endpoint de salud oficial (no requiere autenticación)\n const healthUrl = `${this.config.url.replace(/\\/$/, '')}/api/_health/`\n const response = await fetch(healthUrl, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' }\n })\n\n return response.ok\n } catch (error) {\n if (this.logger) {\n this.logger.error('BaserowWithCredentialsWorkspace health check failed:', error)\n }\n return false\n }\n }\n\n /**\n * Obtener la configuración actual del cliente (sin exponer credenciales)\n *\n * Retorna una copia de solo lectura de la configuración, excluyendo las credenciales\n * por seguridad. Incluye el workspace configurado.\n *\n * @returns Configuración actual sin credenciales (solo lectura)\n *\n * @example\n * ```typescript\n * const config = admin.getConfig()\n * console.log('URL servidor:', config.url)\n * console.log('Workspace:', config.workspace)\n * // Las credenciales no están incluidas por seguridad\n * ```\n *\n * @since 1.0.0\n */\n getConfig(): Readonly<Omit<BaserowAdminConfig, 'credentials'>> {\n return this.getConfigBase()\n }\n\n /**\n * Verificar si el cliente está autenticado\n *\n * Comprueba si hay un token JWT válido y no expirado.\n * Si el token está próximo a expirar, el auto-refresh se encargará de renovarlo.\n *\n * @returns `true` si está autenticado con token válido, `false` en caso contrario\n *\n * @example\n * ```typescript\n * if (admin.isAuthenticated()) {\n * const databases = await admin.databases.list()\n * } else {\n * console.log('Sesión expirada, necesario re-login')\n * }\n * ```\n *\n * @since 1.0.0\n */\n isAuthenticated(): boolean {\n return this.auth.isAuthenticated()\n }\n\n /**\n * Obtener el token JWT actual\n *\n * Retorna el token de acceso actual si está disponible.\n * Útil para debugging o integración con otros sistemas.\n *\n * @returns Token JWT actual o `undefined` si no está autenticado\n *\n * @example\n * ```typescript\n * const token = admin.getCurrentToken()\n * if (token) {\n * console.log('Token disponible:', token.substring(0, 20) + '...')\n * }\n * ```\n *\n * @since 1.0.0\n */\n getCurrentToken(): string | undefined {\n return this.auth.getCurrentToken()\n }\n\n /**\n * Renovar el token de acceso manualmente\n *\n * Fuerza la renovación del token JWT usando el refresh token.\n * Normalmente no es necesario llamar esto manualmente ya que el auto-refresh\n * se encarga automáticamente.\n *\n * @returns Promise que resuelve al nuevo token de acceso\n *\n * @throws {BaserowReLoginRequiredError} Si el refresh token ha expirado (~7 días)\n * @throws {BaserowAuthTokenInvalidError} Si el refresh token es inválido\n *\n * @example\n * ```typescript\n * try {\n * const newToken = await admin.refreshToken()\n * console.log('Token renovado exitosamente')\n * } catch (error) {\n * if (error instanceof BaserowReLoginRequiredError) {\n * console.log('Sesión expirada, necesario re-login con credenciales')\n * // Redirigir a login o solicitar credenciales nuevamente\n * }\n * }\n * ```\n *\n * @since 1.0.0\n */\n async refreshToken(): Promise<string> {\n return await this.auth.refreshAccessToken()\n }\n\n /**\n * Verificar si el cliente necesita re-autenticación\n *\n * Útil para detectar si los tokens se han perdido o el refresh token expiró,\n * permitiendo manejar proactivamente la re-autenticación antes de hacer requests.\n *\n * @aiUsage\n * **✅ Usar cuando:**\n * - Antes de operaciones críticas (validación proactiva de sesión)\n * - Después de períodos largos de inactividad (>6 horas)\n * - Al iniciar aplicación (verificar si sesión previa sigue válida)\n * - Después de errores 401 (distinguir si es recoverable con refresh)\n * - En background jobs/cron tasks (sesiones de larga duración)\n *\n * **❌ NO usar cuando:**\n * - Ya tienes try/catch con BaserowReLoginRequiredError (manejo reactivo suficiente)\n * - Llamando cada request (overhead innecesario, auto-refresh ya maneja)\n * - Solo para logging (usar `getAuthStatus()` que da más info)\n *\n * @aiPattern\n * ## 🎯 Patrón: Wrapper con Auto-Relogin\n *\n * Crea una función wrapper que automáticamente reautentica si la sesión expiró:\n *\n * ```typescript\n * async function withAutoRelogin<T>(\n * admin: ClientWithCredsWs,\n * credentials: { email: string; password: string },\n * operation: (client: ClientWithCredsWs) => Promise<T>\n * ): Promise<T> {\n * // 1. Verificar proactivamente si necesita re-login\n * if (admin.needsReLogin()) {\n * console.log('🔄 Sesión expirada, reautenticando...')\n *\n * admin = await ClientWithCredsWs.create({\n * url: admin.getConfig().url,\n * credentials,\n * workspace: admin.getConfig().workspace!\n * })\n *\n * console.log('✅ Re-autenticación exitosa')\n * }\n *\n * // 2. Ejecutar operación con manejo de errores\n * try {\n * return await operation(admin)\n * } catch (error) {\n * // 3. Si falla con re-login required, reintentar UNA vez\n * if (error instanceof BaserowReLoginRequiredError) {\n * console.log('🔄 Token refresh falló, reautenticando...')\n *\n * admin = await ClientWithCredsWs.create({\n * url: admin.getConfig().url,\n * credentials,\n * workspace: admin.getConfig().workspace!\n * })\n *\n * return await operation(admin)\n * }\n * throw error\n * }\n * }\n *\n * // Uso del wrapper\n * const result = await withAutoRelogin(\n * admin,\n * { email: 'user@example.com', password: 'secret' },\n * async (client) => {\n * const databases = await client.databases.findMany()\n * return databases\n * }\n * )\n * ```\n *\n * @aiPattern\n * ## 🔄 Patrón: Session Manager con Polling\n *\n * Para aplicaciones de larga duración, monitorea la sesión periódicamente:\n *\n * ```typescript\n * class SessionManager {\n * private checkInterval: NodeJS.Timeout | null = null\n *\n * constructor(\n * private admin: ClientWithCredsWs,\n * private credentials: { email: string; password: string }\n * ) {}\n *\n * // Monitorear sesión cada N minutos\n * startMonitoring(intervalMinutes: number = 30) {\n * this.checkInterval = setInterval(async () => {\n * if (this.admin.needsReLogin()) {\n * console.warn('⚠️ Sesión expirada detectada, reautenticando...')\n *\n * try {\n * this.admin = await ClientWithCredsWs.create({\n * url: this.admin.getConfig().url,\n * credentials: this.credentials,\n * workspace: this.admin.getConfig().workspace!\n * })\n *\n * console.log('✅ Sesión renovada automáticamente')\n * } catch (error) {\n * console.error('❌ Error renovando sesión:', error)\n * // Notificar al usuario o detener el servicio\n * }\n * }\n * }, intervalMinutes * 60 * 1000)\n * }\n *\n * stopMonitoring() {\n * if (this.checkInterval) {\n * clearInterval(this.checkInterval)\n * }\n * }\n *\n * getClient(): ClientWithCredsWs {\n * return this.admin\n * }\n * }\n *\n * // Uso\n * const sessionManager = new SessionManager(admin, credentials)\n * sessionManager.startMonitoring(30) // Check cada 30 min\n *\n * // Usar cliente desde manager\n * const client = sessionManager.getClient()\n * const data = await client.databases.findMany()\n * ```\n *\n * @aiErrorHandling\n * ## ⚠️ Casos de Re-Login Necesario\n *\n * **Caso 1: Tokens perdidos (reinicio de proceso)**\n * ```typescript\n * // Al iniciar aplicación, verificar sesión previa\n * if (admin.needsReLogin()) {\n * console.log('Tokens perdidos (reinicio), necesario login')\n * // needsReLogin() = true porque no hay tokens en memoria\n * }\n * ```\n *\n * **Caso 2: Refresh token expirado (~7 días inactividad)**\n * ```typescript\n * // Después de 7+ días sin uso\n * if (admin.needsReLogin()) {\n * console.log('Refresh token expirado, necesario login')\n * // Access token expirado + no refresh token disponible\n * }\n * ```\n *\n * **Caso 3: Sesión válida (NO necesita re-login)**\n * ```typescript\n * if (!admin.needsReLogin()) {\n * // Access token válido O refresh token disponible para renovar\n * const data = await admin.databases.findMany()\n * // Auto-refresh manejará la renovación si access token expira\n * }\n * ```\n *\n * @aiWarning\n * **⚠️ Limitaciones:**\n * - No detecta refresh token inválido (solo detecta ausencia o access token expirado)\n * - Si refresh token está corrupto, `needsReLogin()` retorna `false` pero el refresh fallará\n * - Para validación completa, usar try/catch con `BaserowReLoginRequiredError`\n *\n * **⚠️ Credenciales en Memoria:**\n * - El wrapper `withAutoRelogin()` requiere credenciales en memoria\n * - Evalúa riesgos de seguridad vs conveniencia\n * - Alternativa: solicitar credenciales al usuario cuando `needsReLogin()` = true\n *\n * @returns `true` si necesita re-login, `false` si la sesión es válida\n *\n * @example\n * ```typescript\n * // Validación proactiva simple\n * if (admin.needsReLogin()) {\n * console.log('Sesión expirada, solicitando credenciales...')\n * // Mostrar formulario de login o solicitar credenciales\n * const newAdmin = await ClientWithCredsWs.create({\n * url: config.url,\n * credentials: { email, password },\n * workspace: config.workspace\n * })\n * } else {\n * // Sesión válida, continuar normalmente\n * const databases = await admin.databases.findMany()\n * }\n *\n * // Con wrapper de auto-relogin\n * const databases = await withAutoRelogin(\n * admin,\n * credentials,\n * async (client) => client.databases.findMany()\n * )\n * ```\n *\n * @since 1.0.4\n */\n needsReLogin(): boolean {\n // No hay tokens = necesita login\n if (!this.auth.getCurrentToken() || !this.auth['refreshToken']) {\n return true\n }\n\n // Si el access token está expirado y no tenemos refresh token, necesita login\n if (this.auth.isTokenExpired() && !this.auth['refreshToken']) {\n return true\n }\n\n return false\n }\n\n /**\n * Obtener estado detallado de autenticación\n *\n * Proporciona información completa sobre el estado actual de la sesión,\n * útil para debugging y monitoreo de sesiones.\n *\n * @returns Objeto con información detallada del estado de autenticación\n *\n * @example\n * ```typescript\n * const status = admin.getAuthStatus()\n * console.log('Autenticado:', status.isAuthenticated)\n * console.log('Token expira:', status.tokenExpiry)\n * console.log('Tiene refresh token:', status.hasRefreshToken)\n * console.log('Necesita re-login:', status.needsReLogin)\n * ```\n *\n * @since 1.0.4\n */\n getAuthStatus(): {\n isAuthenticated: boolean\n hasAccessToken: boolean\n hasRefreshToken: boolean\n tokenExpiry?: Date\n isTokenExpired: boolean\n needsReLogin: boolean\n } {\n const hasAccessToken = !!this.auth.getCurrentToken()\n const hasRefreshToken = !!this.auth['refreshToken']\n const tokenExpiry = this.auth['tokenExpiry'] as Date | undefined\n const isTokenExpired = this.auth.isTokenExpired()\n\n return {\n isAuthenticated: this.auth.isAuthenticated(),\n hasAccessToken,\n hasRefreshToken,\n tokenExpiry,\n isTokenExpired,\n needsReLogin: this.needsReLogin()\n }\n }\n\n /**\n * Cerrar sesión y limpiar tokens\n *\n * Invalida los tokens JWT, detiene el keep-alive y limpia el estado de autenticación.\n * Después de llamar este método, el cliente necesitará volver a autenticarse.\n *\n * @example\n * ```typescript\n * // Al final de la aplicación o cambio de usuario\n * admin.logout()\n *\n * // El cliente ya no está autenticado\n * console.log(admin.isAuthenticated()) // false\n * ```\n *\n * @since 1.0.0\n */\n logout(): void {\n this.auth.logout() // Esto internamente llama stopKeepAlive()\n if (this.logger) {\n this.logger.info('BaserowWithCredentialsWorkspace: Logged out')\n }\n }\n\n /**\n * Obtener el workspace configurado\n *\n * Retorna la información completa del workspace al que está restringido este cliente.\n * Útil para obtener ID, nombre y metadatos del workspace actual.\n *\n * @returns Información completa del workspace configurado\n *\n * @example\n * ```typescript\n * const workspace = admin.getDefaultWorkspace()\n * console.log(`Workspace actual: ${workspace.name} (ID: ${workspace.id})`)\n * ```\n *\n * @since 1.0.0\n */\n getDefaultWorkspace(): Workspace {\n return this.defaultWorkspace\n }\n\n /**\n * Obtener ID del workspace configurado\n *\n * Retorna únicamente el ID numérico del workspace al que está restringido este cliente.\n * Método de conveniencia para obtener solo el ID sin metadatos adicionales.\n *\n * @returns ID numérico del workspace configurado\n *\n * @example\n * ```typescript\n * const workspaceId = admin.getWorkspaceId()\n * console.log(`Operando en workspace ID: ${workspaceId}`)\n * ```\n *\n * @since 1.0.0\n */\n getWorkspaceId(): number {\n return this.defaultWorkspace.id\n }\n\n // ============================================================================\n // API DIRECTO - SIN WORKSPACE PREFIX\n // ============================================================================\n\n /**\n * Acceder a una database específica del workspace configurado (API simplificada)\n *\n * Crea un contexto de database que permite operaciones en tablas y filas.\n * Solo puede acceder a databases del workspace configurado.\n * Acepta tanto nombres de database como IDs numéricos.\n *\n * @param databaseIdentifier - Nombre de la database o ID numérico\n * @returns Contexto de database con acceso a tablas y operaciones\n *\n * @example\n * ```typescript\n * // Usando nombre de la database\n * const tables = await admin.database('My Database').tables.findMany()\n * const table = await admin.database('My Database').table('Users').create()\n *\n * // Usando ID numérico\n * const rows = await admin.database(123).table('Users').rows.list()\n *\n * // API simplificada completa\n * const newTable = await admin.database('CRM')\n * .table('Customers')\n * .create({\n * data: [['Name', 'Email'], ['John', 'john@example.com']],\n * first_row_header: true\n * })\n *\n * // Gestión de campos\n * const field = await admin.database('CRM')\n * .table('Customers')\n * .field.createText('Company', undefined, 'Nombre de la empresa')\n * ```\n *\n * @since 1.0.0\n */\n database(databaseIdentifier: string | number): DatabaseContext {\n // Crear instancias de servicios según se necesiten para el contexto\n const tableService = new TableService(this.http, this.logger)\n const fieldService = new FieldService(this.http, this.logger)\n const rowService = new RowService(this.http, this.logger)\n\n return new DatabaseContext(\n this.workspaceService,\n this.defaultWorkspace.id,\n databaseIdentifier,\n this.databases,\n tableService,\n fieldService,\n rowService,\n this.logger\n )\n }\n\n /**\n * Acceder a gestión de Database Tokens del workspace configurado\n *\n * Proporciona operaciones completas de Database Tokens limitadas al workspace actual.\n * Los Database Tokens creados solo tendrán acceso a databases de este workspace.\n *\n * @returns Contexto de Database Token con operaciones CRUD\n *\n * @example\n * ```typescript\n * // Crear token con acceso completo al workspace\n * const fullToken = await admin.databaseToken.createFullAccess('app-token')\n *\n * // Crear token con permisos específicos\n * const readOnlyToken = await admin.databaseToken.create('readonly-token', {\n * databases: [{ database_id: 123, permissions: 'read' }]\n * })\n *\n * // Listar todos los tokens del workspace\n * const tokens = await admin.databaseToken.list()\n *\n * // Gestionar token específico\n * await admin.databaseToken.update(tokenId, { name: 'New Name' })\n * await admin.databaseToken.delete(tokenId)\n * ```\n *\n * @since 1.0.0\n */\n get databaseToken(): DatabaseTokenContext {\n return new DatabaseTokenContext(\n this.workspaceService,\n this.defaultWorkspace.id,\n this.databaseTokenService,\n this.logger\n )\n }\n\n /**\n * Obtener estadísticas de uso del workspace configurado\n *\n * Recopila información sobre recursos y estado del workspace al que está\n * restringido este cliente. Útil para dashboards y monitoreo.\n *\n * @returns Promise con estadísticas completas del workspace\n *\n * @example\n * ```typescript\n * const stats = await admin.getUsageStats()\n * console.log(`Workspace: ${stats.workspace.name}`)\n * console.log(`Databases: ${stats.totalDatabases}`)\n * console.log(`Tables: ${stats.totalTables}`)\n * console.log(`Autenticado: ${stats.isAuthenticated}`)\n * console.log(`Token válido: ${stats.tokenValid}`)\n * console.log(`Modo: ${stats.mode}`) // 'workspace-restricted'\n * ```\n *\n * @since 1.0.0\n */\n async getUsageStats(): Promise<{\n totalDatabases: number\n totalTables: number\n isAuthenticated: boolean\n tokenValid: boolean\n workspace: { id: number; name: string }\n mode: 'workspace-restricted'\n }> {\n // Filtrar databases del workspace configurado\n const allDatabases = await this.databases.findMany()\n const databases = allDatabases.filter(db => db.workspace?.id === this.defaultWorkspace.id)\n\n // Usar las tablas que ya vienen en el objeto Database (más eficiente)\n const totalTables = databases.reduce((sum, db) => sum + (db.tables?.length || 0), 0)\n\n return {\n totalDatabases: databases.length,\n totalTables,\n isAuthenticated: this.isAuthenticated(),\n tokenValid: !this.auth.isTokenExpired(),\n mode: 'workspace-restricted',\n workspace: {\n id: this.defaultWorkspace.id,\n name: this.defaultWorkspace.name\n }\n }\n }\n\n /**\n * Cerrar conexiones HTTP y limpiar recursos\n *\n * Detiene el keep-alive, libera recursos del cliente HTTP y cierra conexiones pendientes.\n * Debe llamarse cuando el cliente ya no se necesite para evitar memory leaks.\n *\n * @example\n * ```typescript\n * // Al final de la aplicación o cuando ya no se necesite el cliente\n * admin.destroy()\n * ```\n *\n * @since 1.0.0\n */\n destroy(): void {\n this.logger?.debug?.('Destroying BaserowWithCredentialsWorkspace')\n this.auth.stopKeepAlive()\n this.http.destroy()\n }\n}\n","import { HttpClient } from './axios'\nimport { BaserowCredentials, BaserowError, LoginResponse, Logger } from '../types'\nimport { validateRequired, validateString, validateUrl } from './validation'\n\n/**\n * Opciones para la verificación de login\n */\nexport interface VerifyLoginOptions {\n /** Timeout en milisegundos (default: 10000) */\n timeout?: number\n /** Logger para debug (opcional) */\n logger?: Logger\n}\n\n/**\n * Verificar si las credenciales de Baserow son válidas sin crear cliente\n *\n * Esta función es útil para:\n * - Validar credenciales en formularios de login\n * - Health checks de credenciales\n * - Verificación de configuración antes de inicializar la aplicación\n *\n * @param url URL base de Baserow (ej: 'https://baserow.gzl10.com')\n * @param credentials Credenciales de usuario (email y password)\n * @param options Opciones adicionales (timeout, logger)\n * @returns Promise<boolean> - true si las credenciales son válidas\n *\n * @example\n * ```typescript\n * import { verifyLogin } from '@gzl10/baserow'\n *\n * const isValid = await verifyLogin('https://baserow.gzl10.com', {\n * email: 'user@example.com',\n * password: 'password123'\n * })\n *\n * if (isValid) {\n * // Proceder a crear BaserowAdmin\n * const admin = await BaserowAdmin.create({ ... })\n * } else {\n * // Mostrar error en UI\n * console.error('Credenciales inválidas')\n * }\n * ```\n */\nexport async function verifyLogin(\n url: string,\n credentials: BaserowCredentials,\n options: VerifyLoginOptions = {}\n): Promise<boolean> {\n // Validación de parámetros\n validateRequired(url, 'url')\n validateRequired(credentials, 'credentials')\n validateString(credentials.email, 'email')\n validateString(credentials.password, 'password')\n\n if (!validateUrl(url)) {\n throw new Error('url must be a valid URL')\n }\n\n const { timeout = 10000, logger } = options\n\n // Crear HttpClient temporal (sin almacenar estado)\n let tempHttp: HttpClient | undefined\n\n try {\n tempHttp = new HttpClient({\n baseURL: `${url.replace(/\\/$/, '')}/api`,\n token: '', // Sin token inicial\n timeout,\n logger\n })\n\n logger?.info?.('BaserowAuth: Verifying credentials...')\n\n // Intentar login con las credenciales proporcionadas\n const loginData = {\n username: credentials.email,\n password: credentials.password\n }\n\n const response = await tempHttp.post<LoginResponse>('/user/token-auth/', loginData)\n\n // Si llegamos aquí, el login fue exitoso\n logger?.info?.(`BaserowAuth: Credentials valid for user ${response.user.username}`)\n return true\n } catch (error) {\n // Manejar diferentes tipos de errores\n if (error instanceof BaserowError) {\n switch (error.status) {\n case 401:\n // Credenciales inválidas\n logger?.warn?.('BaserowAuth: Invalid credentials provided')\n return false\n\n case 400:\n // Bad request (formato incorrecto)\n logger?.warn?.('BaserowAuth: Bad request - invalid data format')\n return false\n\n case 403:\n // Usuario bloqueado o sin permisos\n logger?.warn?.('BaserowAuth: User forbidden or blocked')\n return false\n\n case 429:\n // Rate limit - técnicamente las credenciales podrían ser válidas\n logger?.warn?.('BaserowAuth: Rate limited - cannot verify at this time')\n return false\n\n case 500:\n case 502:\n case 503:\n case 504:\n // Errores del servidor\n logger?.error?.('BaserowAuth: Server error during login verification')\n return false\n\n default:\n // Otros errores HTTP\n logger?.error?.(`BaserowAuth: HTTP error ${error.status}: ${error.message}`)\n return false\n }\n }\n\n // Errores de red, timeout, etc.\n logger?.error?.('BaserowAuth: Network or connection error:', error)\n return false\n } finally {\n // Limpieza: el HttpClient temporal se garbage-collectea automáticamente\n // No necesitamos limpiar tokens porque no los almacenamos\n tempHttp = undefined\n }\n}\n\n/**\n * Verificar si una URL de Baserow es accesible (health check básico)\n *\n * @param url URL base de Baserow\n * @param options Opciones de verificación\n * @returns Promise<boolean> - true si el servidor es accesible\n */\nexport async function verifyBaserowHealth(\n url: string,\n options: Pick<VerifyLoginOptions, 'timeout' | 'logger'> = {}\n): Promise<boolean> {\n validateRequired(url, 'url')\n\n if (!validateUrl(url)) {\n throw new Error('url must be a valid URL')\n }\n\n const { timeout = 5000, logger } = options\n\n try {\n logger?.info?.('BaserowAuth: Checking Baserow health...')\n\n // Usar endpoint público de health que no requiere autenticación\n const healthUrl = `${url.replace(/\\/$/, '')}/api/_health/`\n const response = await fetch(healthUrl, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n signal: AbortSignal.timeout(timeout)\n })\n\n const isHealthy = response.ok\n logger?.info?.(`BaserowAuth: Baserow health check ${isHealthy ? 'passed' : 'failed'}`)\n\n return isHealthy\n } catch (error) {\n logger?.error?.('BaserowAuth: Health check failed:', error)\n return false\n }\n}\n","/**\n * Cliente unificado principal de Baserow API v1.35+ (2025)\n *\n * **Arquitectura de 3 clientes especializados con auto-detección inteligente**\n *\n * Detecta automáticamente el tipo de autenticación basado en la configuración\n * y devuelve el cliente apropiado con tipos TypeScript perfectos.\n *\n * **Características principales v1.1.0+:**\n * - 🎯 **Arquitectura de 3 Clientes**: Auto-detección Token/Admin Global/Admin Workspace\n * - 🗺️ **PrismaBaserowMapper**: Sintaxis familiar Prisma con 35+ tests de transformaciones\n * - 📋 **21/22 Tipos de Campos**: Cobertura prácticamente completa incluyendo campos avanzados\n * - 🚀 **HTTP Optimizado**: 36% menos código con axios-retry + axios-rate-limit oficiales\n * - ⚡ **Operaciones Bulk**: Procesamiento optimizado con retry logic y rate limiting\n * - 🧪 **Testing Robusto**: 35+ tests de integración + tests comprehensivos del mapper\n *\n * **Un solo método: `BaserowClient.create()`**\n * - Detecta automáticamente si usas token o credenciales\n * - Infiere el tipo correcto de cliente\n * - Sin necesidad de elegir entre múltiples clases\n *\n * @aiComparison\n * ## 📊 Matriz de Selección de Cliente por Escenario\n *\n * | Escenario | Cliente Recomendado | Razón | Config Example |\n * |-----------|-------------------|-------|----------------|\n * | **Frontend/Mobile App** | `ClientWithToken` | Máxima seguridad, scope limitado a tablas específicas | `{ token: 'db_xxx' }` |\n * | **Admin Dashboard Web** | `ClientWithCreds` (Global) | Gestión multi-workspace, crear/editar estructuras | `{ credentials: {...} }` |\n * | **Backend API** | `ClientWithToken` | Performance óptimo, solo CRUD de datos | `{ token: 'db_xxx' }` |\n * | **CLI Tool Admin** | `ClientWithCreds` (Global) | Operaciones admin en múltiples workspaces | `{ credentials: {...} }` |\n * | **Microservicio Single-Tenant** | `ClientWithCredsWs` | API simplificada, workspace fijo | `{ credentials: {...}, workspace: 'X' }` |\n * | **Background Job/Cron** | `ClientWithToken` | Sin riesgo de sesión expirada (token no expira) | `{ token: 'db_xxx' }` |\n * | **Data Migration Script** | `ClientWithCreds` (Global) | Crear workspaces/databases/tablas completas | `{ credentials: {...} }` |\n * | **Edge Functions** | `ClientWithToken` | Stateless, no maneja refresh tokens | `{ token: 'db_xxx' }` |\n * | **Desktop App (Electron)** | `ClientWithCredsWs` | UX simplificada, workspace predefinido | `{ credentials: {...}, workspace: 'X' }` |\n * | **Testing/E2E** | `ClientWithCreds` (Global) | Setup/teardown completo de fixtures | `{ credentials: {...} }` |\n * | **Webhook Handler** | `ClientWithToken` | Read-only verification, performance crítico | `{ token: 'db_xxx' }` |\n * | **Serverless Functions** | `ClientWithToken` | Cold start rápido, sin estado de sesión | `{ token: 'db_xxx' }` |\n *\n * @aiDecisionFactors\n * ## 🔑 5 Factores Clave para Elegir Cliente\n *\n * **1. Operaciones Requeridas**\n * - Solo CRUD de datos → `ClientWithToken`\n * - Crear/modificar estructura (tablas, campos) → `ClientWithCreds*`\n * - Gestión de workspaces → `ClientWithCreds` (Global)\n *\n * **2. Scope de Acceso**\n * - Single workspace → `ClientWithCredsWs` o `ClientWithToken`\n * - Multi-workspace → `ClientWithCreds` (Global)\n * - Tablas específicas → `ClientWithToken` (mejor seguridad)\n *\n * **3. Ambiente de Ejecución**\n * - Stateless (serverless, edge) → `ClientWithToken`\n * - Stateful (backend server) → Cualquiera\n * - Cliente (frontend, mobile) → `ClientWithToken`\n *\n * **4. Seguridad y Permisos**\n * - Máxima seguridad (least privilege) → `ClientWithToken`\n * - Admin operations trusted → `ClientWithCreds*`\n * - Credenciales en código → ⚠️ Evitar (usar env vars)\n *\n * **5. Mantenimiento de Sesión**\n * - Sin preocuparse de sesiones → `ClientWithToken` (no expira)\n * - Manejo de refresh tokens OK → `ClientWithCreds*`\n * - Background jobs largos → `ClientWithToken` (no requiere refresh)\n *\n * @aiComparison\n * ## 🎯 Ejemplos de Decisión por Caso de Uso\n *\n * **Caso: Aplicación React de E-Commerce**\n * ```typescript\n * // ✅ CORRECTO - Token limitado a tablas de productos/carritos\n * const client = await BaserowClient.create({\n * token: process.env.VITE_BASEROW_TOKEN\n * })\n * // Scope: Solo products + carts (configurado en Baserow)\n * const products = await client.database(DB_ID).table(PRODUCTS_TABLE).rows.list()\n * ```\n *\n * **Caso: Dashboard Admin Multi-Tenant**\n * ```typescript\n * // ✅ CORRECTO - Credenciales globales para gestión completa\n * const admin = await BaserowClient.create({\n * credentials: {\n * email: process.env.ADMIN_EMAIL,\n * password: process.env.ADMIN_PASSWORD\n * }\n * })\n * // Puede crear/eliminar workspaces, databases, tables\n * const workspace = await admin.workspaces.create({ name: 'Nuevo Cliente' })\n * ```\n *\n * **Caso: Microservicio de Reportes para Empresa X**\n * ```typescript\n * // ✅ CORRECTO - Workspace-scoped para UX simplificada\n * const reports = await BaserowClient.create({\n * credentials: {\n * email: process.env.SERVICE_EMAIL,\n * password: process.env.SERVICE_PASSWORD\n * },\n * workspace: 'Empresa X'\n * })\n * // API simplificada: admin.databases.list() en vez de admin.workspace('X').databases.list()\n * const databases = await reports.databases.list()\n * ```\n *\n * **Caso: Webhook Processor (Vercel Edge Function)**\n * ```typescript\n * // ✅ CORRECTO - Token para stateless execution\n * export default async function handler(req: Request) {\n * const client = await BaserowClient.create({\n * token: process.env.BASEROW_TOKEN\n * })\n *\n * // Verificar evento sin mantener sesión\n * const row = await client.database(DB_ID).table(TBL_ID).rows.get(rowId)\n * return Response.json({ verified: true })\n * }\n * ```\n *\n * **Caso: CLI Tool de Migración**\n * ```typescript\n * // ✅ CORRECTO - Credenciales globales para setup completo\n * const migrator = await BaserowClient.create({\n * credentials: {\n * email: await promptEmail(),\n * password: await promptPassword()\n * }\n * })\n *\n * // Crear estructura completa: workspace → database → tables → fields → data\n * const ws = await migrator.workspaces.create({ name: 'Production' })\n * const db = await migrator.workspace('Production').databases.create('Main DB')\n * const table = await migrator.workspace('Production').database('Main DB').tables.create({ name: 'Users' })\n * ```\n *\n * @aiWarning\n * ## ⚠️ Errores Comunes de Selección\n *\n * **❌ Error 1: Usar Credentials en Frontend**\n * ```typescript\n * // ❌ PELIGROSO - Credenciales expuestas en bundle\n * const client = await BaserowClient.create({\n * credentials: {\n * email: 'admin@company.com', // ❌ Visible en DevTools\n * password: 'secret123' // ❌ Comprometido\n * }\n * })\n * ```\n * **✅ Solución: Usar Database Token**\n * ```typescript\n * const client = await BaserowClient.create({\n * token: import.meta.env.VITE_BASEROW_TOKEN // ✅ Scope limitado\n * })\n * ```\n *\n * **❌ Error 2: Token para Operaciones Admin**\n * ```typescript\n * // ❌ IMPOSIBLE - Database Token no puede crear campos\n * const client = await BaserowClient.create({ token: 'db_xxx' })\n * await client.database(1).table(2).field.createText('name') // ❌ 403 Forbidden\n * ```\n * **✅ Solución: Usar Credentials**\n * ```typescript\n * const admin = await BaserowClient.create({ credentials: {...} })\n * await admin.workspace('X').database('Y').table('Z').field.createText('name') // ✅\n * ```\n *\n * **❌ Error 3: Global Credentials para Single Workspace**\n * ```typescript\n * // ❌ VERBOSE - API innecesariamente compleja\n * const admin = await BaserowClient.create({ credentials: {...} })\n * await admin.workspace('My Company').databases.list()\n * await admin.workspace('My Company').database('DB').tables.list()\n * // Repetir 'My Company' en cada llamada\n * ```\n * **✅ Solución: Usar Workspace-Scoped**\n * ```typescript\n * const admin = await BaserowClient.create({\n * credentials: {...},\n * workspace: 'My Company' // ✅ Workspace automático\n * })\n * await admin.databases.list() // ✅ Simplificado\n * await admin.database('DB').tables.list() // ✅ Sin prefijo workspace\n * ```\n *\n * @example Uso básico con auto-detección\n * ```typescript\n * import { BaserowClient, PrismaBaserowMapper } from '@gzl10/baserow'\n *\n * // === ARQUITECTURA DE 3 CLIENTES ===\n *\n * // 🎯 Token-based (auto-detectado) - para operaciones de datos\n * const client = await BaserowClient.create({\n * url: 'https://baserow.com',\n * token: 'db_token_123'\n * })\n * // Tipo inferido automáticamente: BaserowWithToken\n * const rows = await client.database(123).table(456).rows.list()\n *\n * // Operaciones bulk optimizadas\n * const bulkRows = await client.database(123).table(456).rows.createBulk([\n * { name: 'Alice', email: 'alice@example.com' },\n * { name: 'Bob', email: 'bob@example.com' }\n * ], { batchSize: 100 })\n *\n * // 🎯 Credentials global (auto-detectado) - para administración multi-workspace\n * const admin = await BaserowClient.create({\n * url: 'https://baserow.com',\n * credentials: { email: 'admin@co.com', password: 'pass' }\n * })\n * // Tipo inferido automáticamente: BaserowWithCredentialsGlobal\n * const workspaces = await admin.workspaces.list()\n * const newWS = await admin.workspaces.create({ name: 'Nueva Empresa' })\n * const updatedWS = await admin.workspace('My Workspace').update({ name: 'New Name' })\n * const database = await admin.workspace('My Workspace').databases.create('CRM')\n * await admin.workspace('Old Workspace').delete()\n *\n * // 🎯 Credentials workspace (auto-detectado) - para workspace específico\n * const adminWS = await BaserowClient.create({\n * url: 'https://baserow.com',\n * credentials: { email: 'admin@co.com', password: 'pass' },\n * workspace: 'My Workspace' // Esto activa modo workspace\n * })\n * // Tipo inferido automáticamente: BaserowWithCredentialsWorkspace\n * const databases = await adminWS.databases.list()\n * const table = await adminWS.database('My DB').tables.create({ name: 'Customers' })\n *\n * // === PRISMA MAPPER ===\n * // Sintaxis familiar Prisma para consultas complejas\n * const prismaQuery = {\n * where: {\n * AND: [\n * { status: 'active' },\n * { age: { gte: 18, lt: 65 } },\n * { email: { contains: '@company.com' } }\n * ]\n * },\n * orderBy: [{ created_at: 'desc' }, { name: 'asc' }],\n * take: 20,\n * skip: 40,\n * select: { id: true, name: true, email: true }\n * }\n *\n * const baserowOptions = PrismaBaserowMapper.transformPrismaToBaserow(prismaQuery)\n * const filteredRows = await client.database(123).table(456).rows.list(baserowOptions)\n *\n * // === COBERTURA COMPLETA DE CAMPOS ===\n * // 21/22 tipos de campos soportados incluyendo campos avanzados v1.1.0+\n * const textField = await table.field.createText('full_name')\n * const emailField = await table.field.createEmail('contact_email')\n * const fileField = await table.field.createFile('documents') // ¡Nuevo!\n * const autoField = await table.field.createAutonumber('ticket_id') // ¡Nuevo!\n * const countField = await table.field.createCount('items_count', targetTableId) // ¡Nuevo!\n * const rollupField = await table.field.createRollup('total_amount', tableId, fieldId, 'sum') // ¡Nuevo!\n * const lookupField = await table.field.createLookup('item_names', tableId, fieldId) // ¡Nuevo!\n * ```\n *\n * @example Uso con presets de performance\n * ```typescript\n * import { BaserowClient, PERFORMANCE_PRESETS } from '@gzl10/baserow'\n *\n * // Cliente con preset de producción (balanceado)\n * const prodClient = await BaserowClient.create({\n * url: 'https://api.baserow.com',\n * token: process.env.BASEROW_TOKEN,\n * performance: PERFORMANCE_PRESETS.production\n * })\n *\n * // Cliente para desarrollo (más relajado)\n * const devClient = await BaserowClient.create({\n * url: 'http://localhost:3000',\n * credentials: { email: 'dev@example.com', password: 'dev123' },\n * performance: PERFORMANCE_PRESETS.development\n * })\n *\n * // Cliente para tests (conservador)\n * const testClient = await BaserowClient.create({\n * url: 'https://test.baserow.com',\n * token: process.env.TEST_TOKEN,\n * performance: PERFORMANCE_PRESETS.testing\n * })\n * ```\n *\n */\n\nimport { ClientWithToken } from './ClientWithToken'\nimport { ClientWithCreds } from './ClientWithCreds'\nimport { ClientWithCredsWs } from './ClientWithCredsWs'\nimport { PerformanceManager } from './utils/performance'\nimport { verifyLogin, verifyBaserowHealth } from './utils/auth'\nimport {\n BaserowTokenConfig,\n BaserowCredentialsConfig,\n BaserowCredentialsWorkspaceConfig,\n BaserowUnifiedConfig,\n BaserowCredentials\n} from './types'\n\n// Conditional types para inferencia automática de tipos\ntype InferClientType<T> = T extends { token: string }\n ? ClientWithToken\n : T extends { workspace: string }\n ? ClientWithCredsWs\n : T extends { credentials: any }\n ? ClientWithCreds\n : never\n\nexport class BaserowClient {\n /**\n * Crea un cliente con Database Token para operaciones de datos\n * @internal\n */\n private static withToken(config: BaserowTokenConfig): ClientWithToken {\n return new ClientWithToken(config)\n }\n\n /**\n * Crea un cliente admin con credenciales\n * @internal\n */\n private static async withCredentials(config: BaserowCredentialsConfig): Promise<ClientWithCreds>\n private static async withCredentials(config: BaserowCredentialsWorkspaceConfig): Promise<ClientWithCredsWs>\n private static async withCredentials(\n config: BaserowCredentialsConfig | BaserowCredentialsWorkspaceConfig\n ): Promise<ClientWithCreds | ClientWithCredsWs> {\n // Si tiene workspace, crear cliente de workspace específico\n if ('workspace' in config && config.workspace) {\n return await ClientWithCredsWs.create({\n url: config.url,\n credentials: config.credentials,\n workspace: config.workspace,\n logger: config.logger,\n performance: config.performance\n })\n }\n\n // Si no, crear cliente global\n return await ClientWithCreds.create({\n url: config.url,\n credentials: config.credentials,\n logger: config.logger,\n performance: config.performance\n })\n }\n\n /**\n * Auto-detecta el tipo de configuración y crea el cliente apropiado (overload para token)\n */\n static create(config: BaserowTokenConfig): Promise<ClientWithToken>\n\n /**\n * Auto-detecta el tipo de configuración y crea el cliente apropiado (overload para credentials)\n */\n static create(config: BaserowCredentialsConfig): Promise<ClientWithCreds>\n\n /**\n * Auto-detecta el tipo de configuración y crea el cliente apropiado (overload para credentials + workspace)\n */\n static create(config: BaserowCredentialsWorkspaceConfig): Promise<ClientWithCredsWs>\n\n /**\n * Auto-detecta el tipo de configuración y crea el cliente apropiado (generic)\n */\n static create<T extends BaserowUnifiedConfig>(config: T): Promise<InferClientType<T>>\n\n /**\n * Implementación de create con auto-detección\n */\n static async create(config: BaserowUnifiedConfig): Promise<ClientWithToken | ClientWithCreds | ClientWithCredsWs> {\n // Detectar tipo basado en propiedades\n if ('token' in config) {\n // Es configuración de token\n return this.withToken(config as BaserowTokenConfig)\n }\n\n if ('credentials' in config) {\n // Es configuración de credenciales\n if ('workspace' in config && config.workspace) {\n // Con workspace específico\n return await this.withCredentials(config as BaserowCredentialsWorkspaceConfig)\n } else {\n // Sin workspace (global)\n return await this.withCredentials(config as BaserowCredentialsConfig)\n }\n }\n\n throw new Error('Invalid configuration: must provide either \"token\" or \"credentials\"')\n }\n\n /**\n * Establece configuración global de rendimiento para todos los tipos de clientes\n *\n * @param defaults - Configuración parcial de performance para sobrescribir defaults\n *\n * @example\n * ```typescript\n * // Configurar defaults más agresivos para toda la aplicación\n * BaserowClient.setGlobalPerformanceDefaults({\n * timeout: 45000,\n * retries: 5,\n * maxRequestsPerSecond: 20\n * })\n * ```\n */\n static setGlobalPerformanceDefaults(defaults: Partial<import('./types.js').BaserowPerformanceOptions>): void {\n PerformanceManager.setGlobal(defaults)\n }\n\n /**\n * Obtiene la configuración global actual de rendimiento\n *\n * @returns Configuración global actual de performance\n *\n * @example\n * ```typescript\n * const currentDefaults = BaserowClient.getGlobalPerformanceDefaults()\n * console.log('Timeout global:', currentDefaults.timeout)\n * ```\n */\n static getGlobalPerformanceDefaults(): import('./types.js').BaserowPerformanceOptions {\n return PerformanceManager.getGlobal()\n }\n\n /**\n * Verificar si las credenciales de Baserow son válidas sin crear cliente\n *\n * Esta función es útil para:\n * - Validar credenciales en formularios de login\n * - Health checks de credenciales\n * - Verificación de configuración antes de inicializar la aplicación\n *\n * @param url URL base de Baserow (ej: 'https://baserow.com')\n * @param credentials Credenciales de usuario (email y password)\n * @param options Opciones adicionales (timeout, logger)\n * @returns Promise<boolean> - true si las credenciales son válidas\n *\n * @example\n * ```typescript\n * // Verificar credenciales antes de crear cliente\n * const isValid = await BaserowClient.verifyLogin('https://baserow.com', {\n * email: 'user@example.com',\n * password: 'password123'\n * })\n *\n * if (isValid) {\n * // Proceder a crear cliente admin\n * const admin = await BaserowClient.create({\n * url: 'https://baserow.com',\n * credentials: { email: 'user@example.com', password: 'password123' }\n * })\n * } else {\n * console.error('Credenciales inválidas')\n * }\n * ```\n */\n static async verifyLogin(\n url: string,\n credentials: BaserowCredentials,\n options?: import('./utils/auth.js').VerifyLoginOptions\n ): Promise<boolean> {\n return verifyLogin(url, credentials, options)\n }\n\n /**\n * Verificar si una URL de Baserow es accesible (health check básico)\n *\n * Útil para verificar conectividad del servidor antes de realizar operaciones.\n * No requiere autenticación.\n *\n * @param url URL base de Baserow (ej: 'https://baserow.com')\n * @param options Opciones de verificación (timeout, logger)\n * @returns Promise<boolean> - true si el servidor es accesible\n *\n * @example\n * ```typescript\n * // Verificar si Baserow está disponible\n * const isHealthy = await BaserowClient.health('https://baserow.com')\n *\n * if (!isHealthy) {\n * console.log('Servidor Baserow no disponible')\n * return\n * }\n *\n * // Proceder con operaciones normales\n * const client = await BaserowClient.create({ ... })\n * ```\n */\n static async health(\n url: string,\n options?: Pick<import('./utils/auth.js').VerifyLoginOptions, 'timeout' | 'logger'>\n ): Promise<boolean> {\n return verifyBaserowHealth(url, options)\n }\n}\n\n// Re-export de las clases internas para uso avanzado (pero marcadas como internas)\nexport { ClientWithToken } from './ClientWithToken'\nexport { ClientWithCreds } from './ClientWithCreds'\nexport { ClientWithCredsWs } from './ClientWithCredsWs'\n","import type { BaserowPerformanceOptions } from '../types'\n\n/**\n * Colección de configuraciones de performance predefinidas para diferentes entornos y casos de uso\n *\n * Facilita la selección de configuraciones apropiadas sin tener que conocer los valores específicos.\n * Cada preset está optimizado para escenarios específicos de uso.\n *\n * @example Uso básico con presets\n * ```typescript\n * import { BaserowClient, PERFORMANCE_PRESETS } from '@gzl10/baserow'\n *\n * // Para producción (balanceado)\n * const prodClient = await BaserowClient.create({\n * url: 'https://baserow.example.com',\n * token: process.env.TOKEN,\n * performance: PERFORMANCE_PRESETS.production\n * })\n *\n * // Para desarrollo local (más relajado)\n * const devClient = await BaserowClient.create({\n * url: 'http://localhost:3000',\n * credentials: { email: 'dev@example.com', password: 'password' },\n * performance: PERFORMANCE_PRESETS.development\n * })\n *\n * // Para tests (conservador)\n * const testClient = await BaserowClient.create({\n * url: 'https://test.baserow.com',\n * token: process.env.TEST_TOKEN,\n * performance: PERFORMANCE_PRESETS.testing\n * })\n * ```\n *\n * @example Selección dinámica de presets\n * ```typescript\n * import { getPerformancePreset } from '@gzl10/baserow'\n *\n * const envToPreset = {\n * production: 'production',\n * staging: 'testing',\n * development: 'development'\n * } as const\n *\n * const preset = getPerformancePreset(envToPreset[process.env.NODE_ENV as keyof typeof envToPreset])\n * ```\n *\n * @since 1.1.0\n * @public\n */\nexport const PERFORMANCE_PRESETS = {\n /**\n * Configuración balanceada para entornos de producción\n *\n * Optimizada para un equilibrio entre performance y estabilidad en producción.\n * Valores probados para cargas de trabajo estándar.\n *\n * @example\n * ```typescript\n * const client = await BaserowClient.create({\n * url: 'https://api.baserow.com',\n * token: process.env.BASEROW_TOKEN,\n * performance: PERFORMANCE_PRESETS.production\n * })\n * ```\n *\n * - **Timeout**: 30 segundos (estándar para APIs REST)\n * - **Reintentos**: 3 (recuperación robusta de errores temporales)\n * - **Rate limiting**: 10 req/s (equilibrio entre throughput y respeto al servidor)\n * - **Casos de uso**: APIs en producción, integraciones estables, servicios críticos\n *\n * @since 1.1.0\n */\n production: {\n timeout: 30000,\n retries: 3,\n maxRequestsPerSecond: 10,\n enableRateLimiting: true\n } as BaserowPerformanceOptions,\n\n /**\n * Configuración relajada para desarrollo local\n *\n * Diseñada para desarrollo cómodo con tiempos más generosos para debugging.\n * Menos agresiva para facilitar el desarrollo y troubleshooting.\n *\n * @example\n * ```typescript\n * const devClient = await BaserowClient.create({\n * url: 'http://localhost:3000',\n * credentials: { email: 'dev@example.com', password: 'dev123' },\n * performance: PERFORMANCE_PRESETS.development\n * })\n * ```\n *\n * - **Timeout**: 60 segundos (tiempo adicional para debugging y breakpoints)\n * - **Reintentos**: 2 (menos agresivo durante desarrollo)\n * - **Rate limiting**: 5 req/s (conservador para instancias locales)\n * - **Casos de uso**: Desarrollo local, debugging, instancias de desarrollo\n *\n * @since 1.1.0\n */\n development: {\n timeout: 60000,\n retries: 2,\n maxRequestsPerSecond: 5,\n enableRateLimiting: true\n } as BaserowPerformanceOptions,\n\n /**\n * Configuración conservadora para tests automatizados\n *\n * Optimizada para estabilidad en entornos de testing, especialmente CI/CD.\n * Balanceada para ser respetuosa con recursos compartidos.\n *\n * @example\n * ```typescript\n * const testClient = await BaserowClient.create({\n * url: 'https://test.baserow.com',\n * token: process.env.BASEROW_TEST_TOKEN,\n * performance: PERFORMANCE_PRESETS.testing\n * })\n * ```\n *\n * - **Timeout**: 45 segundos (tiempo adicional para CI/CD lentos)\n * - **Reintentos**: 2 (equilibrio entre estabilidad y velocidad de tests)\n * - **Rate limiting**: 5 req/s (respetuoso con servidores de testing compartidos)\n * - **Casos de uso**: Tests automatizados, CI/CD, entornos de QA\n *\n * @since 1.1.0\n */\n testing: {\n timeout: 45000,\n retries: 2,\n maxRequestsPerSecond: 5,\n enableRateLimiting: true\n } as BaserowPerformanceOptions,\n\n /**\n * Configuración de alta performance para servicios robustos\n *\n * Configuración agresiva diseñada para máximo throughput en infraestructura robusta.\n * Requiere servidores Baserow dedicados con recursos suficientes.\n *\n * @example\n * ```typescript\n * const fastClient = await BaserowClient.create({\n * url: 'https://dedicated.baserow.com',\n * token: process.env.BASEROW_FAST_TOKEN,\n * performance: PERFORMANCE_PRESETS.aggressive\n * })\n * ```\n *\n * - **Timeout**: 15 segundos (respuesta rápida esperada)\n * - **Reintentos**: 5 (agresivo en recuperación de errores)\n * - **Rate limiting**: 20 req/s (alta throughput)\n * - **Casos de uso**: Servicios de alta demanda, servidores dedicados, APIs críticas\n *\n * @warning Solo usar con servidores Baserow dedicados y robustos\n * @since 1.1.0\n */\n aggressive: {\n timeout: 15000,\n retries: 5,\n maxRequestsPerSecond: 20,\n enableRateLimiting: true\n } as BaserowPerformanceOptions,\n\n /**\n * Configuración muy conservadora para recursos limitados\n *\n * Configuración minimalista diseñada para minimizar la carga en servidores con recursos limitados.\n * Maximiza la compatibilidad y estabilidad en entornos restrictivos.\n *\n * @example\n * ```typescript\n * const conservativeClient = await BaserowClient.create({\n * url: 'https://shared.baserow.com',\n * token: process.env.BASEROW_LIMITED_TOKEN,\n * performance: PERFORMANCE_PRESETS.conservative\n * })\n * ```\n *\n * - **Timeout**: 60 segundos (paciencia con servidores lentos)\n * - **Reintentos**: 1 (mínimo impacto en servidor sobrecargado)\n * - **Rate limiting**: 2 req/s (muy respetuoso con recursos)\n * - **Casos de uso**: Servidores compartidos, recursos limitados, procesos batch\n *\n * @example Casos de uso ideales\n * ```typescript\n * // Para servidores Baserow compartidos con muchos usuarios\n * // Para instancias con recursos limitados (RAM/CPU)\n * // Para procesos batch que pueden esperar\n * // Para entornos con restricciones de red\n * ```\n *\n * @since 1.1.0\n */\n conservative: {\n timeout: 60000,\n retries: 1,\n maxRequestsPerSecond: 2,\n enableRateLimiting: true\n } as BaserowPerformanceOptions\n} as const\n\n/**\n * Tipo que representa las claves disponibles en PERFORMANCE_PRESETS\n *\n * Útil para crear funciones que acepten solo presets válidos con autocompletado TypeScript.\n *\n * @example\n * ```typescript\n * function createClientWithPreset(preset: PerformancePresetKey) {\n * return BaserowClient.create({\n * url: 'https://baserow.example.com',\n * token: process.env.TOKEN,\n * performance: PERFORMANCE_PRESETS[preset]\n * })\n * }\n * ```\n *\n * @since 1.1.0\n * @public\n */\nexport type PerformancePresetKey = keyof typeof PERFORMANCE_PRESETS\n\n/**\n * Función helper para obtener un preset por clave con validación TypeScript\n *\n * Proporciona una forma programática de acceder a presets con validación de tipos.\n * Especialmente útil cuando la selección de preset es dinámica.\n *\n * @param preset - Clave del preset deseado (con autocompletado)\n * @returns Configuración de performance del preset seleccionado\n *\n * @example Uso básico\n * ```typescript\n * const config = getPerformancePreset('testing')\n * // Equivalente a: PERFORMANCE_PRESETS.testing\n * ```\n *\n * @example Selección dinámica basada en entorno\n * ```typescript\n * const envToPreset: Record<string, PerformancePresetKey> = {\n * production: 'production',\n * staging: 'testing',\n * development: 'development',\n * test: 'testing'\n * }\n *\n * const currentPreset = getPerformancePreset(\n * envToPreset[process.env.NODE_ENV || 'development']\n * )\n * ```\n *\n * @since 1.1.0\n * @public\n */\nexport function getPerformancePreset(preset: PerformancePresetKey): BaserowPerformanceOptions {\n return PERFORMANCE_PRESETS[preset]\n}\n","/**\n * Utilidades Express para @gzl10/baserow\n *\n * Colección de middlewares, error handlers y serializadores opcionales\n * para facilitar la integración con Express sin crear dependencias directas.\n *\n * @example\n * ```typescript\n * import { BaserowClient, express } from '@gzl10/baserow'\n *\n * const client = new BaserowClient({ url, token })\n * const app = express()\n *\n * // Setup middlewares\n * app.use('/api', express.baserowContext(client))\n * app.use('/api', express.requireAuth())\n *\n * // Routes\n * app.get('/api/tables/:tableId/rows', async (req, res, next) => {\n * try {\n * const rows = await req.baserow!.database(123).table(456).rows.list()\n * res.json(express.serializePaginated(rows))\n * } catch (error) {\n * next(error)\n * }\n * })\n *\n * // Error handling\n * app.use(express.errorHandler())\n * ```\n *\n * @since 1.0.0\n */\n\n// Middlewares\nexport {\n baserowContext,\n baserowAuth,\n requireAuth,\n baserowCleanup,\n type ExpressRequest,\n type ExpressResponse,\n type ExpressNextFunction,\n type ExpressMiddleware\n} from './middleware'\n\n// Error handling\nexport {\n errorHandler,\n simpleErrorHandler,\n serializeError,\n serializeGenericError,\n getHttpStatusCode,\n type ErrorResponse\n} from './errors'\n\n// Serializadores\nexport {\n serializePaginated,\n serializeRowsWithMeta,\n serializeTable,\n serializeDatabase,\n serializeWorkspace,\n cleanField,\n createSuccessResponse,\n serializeQueryOptions,\n type PaginatedResponse,\n type RowsResponse\n} from './serializers'\n","/**\n * Middlewares Express opcionales para @gzl10/baserow\n *\n * Utilidades que facilitan la integración con Express sin crear dependencias directas.\n * Los tipos Express son inferidos dinámicamente para evitar dependency en @types/express.\n *\n * @example\n * ```typescript\n * import express from 'express'\n * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'\n *\n * const app = express()\n * const client = await BaserowClient.create({ url, token })\n *\n * // Attach client to request\n * app.use('/api', baserowExpress.baserowContext(client))\n *\n * // Auto-create client per request with auth from headers\n * app.use('/api', baserowExpress.baserowAuth({\n * url: process.env.BASEROW_URL!,\n * token: process.env.BASEROW_TOKEN!\n * }))\n *\n * // Routes now have access to req.baserow\n * app.get('/api/rows', async (req, res) => {\n * const rows = await req.baserow!.database(123).table(456).rows.list()\n * res.json(rows)\n * })\n * ```\n *\n * @since 1.0.0\n */\n\nimport type { BaserowConfig } from '../types'\nimport { ClientWithToken } from '../ClientWithToken'\n\n/**\n * Tipos Express mínimos sin dependencias\n */\nexport interface ExpressRequest {\n [key: string]: any\n headers: { [key: string]: string | string[] | undefined }\n baserow?: ClientWithToken\n}\n\nexport interface ExpressResponse {\n status(code: number): ExpressResponse\n json(body: any): ExpressResponse\n send(body?: any): ExpressResponse\n}\n\nexport interface ExpressNextFunction {\n (error?: any): void\n}\n\nexport type ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: ExpressNextFunction\n) => void | Promise<void>\n\n/**\n * Middleware para attachar cliente Baserow al request\n *\n * Agrega el cliente a `req.baserow` para uso en routes subsecuentes.\n * El cliente debe ser creado externamente y reutilizado.\n *\n * @param client - Cliente Baserow configurado\n * @returns Middleware Express\n *\n * @example\n * ```typescript\n * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'\n *\n * const client = await BaserowClient.create({ url, token })\n * app.use('/api', baserowExpress.baserowContext(client))\n *\n * // En las rutas\n * app.get('/api/tables', async (req, res) => {\n * const tables = await req.baserow!.database(123).tables.findMany()\n * res.json(tables)\n * })\n * ```\n */\nexport function baserowContext(client: ClientWithToken): ExpressMiddleware {\n return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {\n req.baserow = client\n next()\n }\n}\n\n/**\n * Middleware para auto-crear cliente Baserow por request\n *\n * Crea un nuevo cliente para cada request basado en la configuración.\n * Útil cuando cada request puede tener diferentes tokens o configuraciones.\n *\n * @param config - Configuración base de Baserow\n * @returns Middleware Express\n *\n * @example\n * ```typescript\n * import { express as baserowExpress } from '@gzl10/baserow'\n *\n * app.use('/api', baserowExpress.baserowAuth({\n * url: process.env.BASEROW_URL!,\n * token: process.env.BASEROW_TOKEN! // Puede ser sobrescrito por headers\n * }))\n *\n * // Cliente auto-creado por request disponible en req.baserow\n * app.get('/api/data', async (req, res) => {\n * const rows = await req.baserow!.database(123).table(456).rows.list()\n * res.json(rows)\n * })\n * ```\n */\nexport function baserowAuth(config: BaserowConfig): ExpressMiddleware {\n return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {\n try {\n // Permitir override del token vía headers\n const authHeader = req.headers.authorization as string\n const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : config.token\n\n const clientConfig: BaserowConfig = {\n ...config,\n token\n }\n\n req.baserow = new ClientWithToken(clientConfig)\n next()\n } catch (error) {\n next(error)\n }\n }\n}\n\n/**\n * Middleware para requerir autenticación Baserow\n *\n * Valida que existe un cliente Baserow válido en el request.\n * Debe usarse después de `baserowContext` o `baserowAuth`.\n *\n * @returns Middleware Express\n *\n * @example\n * ```typescript\n * import { express as baserowExpress } from '@gzl10/baserow'\n *\n * app.use('/api', baserowExpress.baserowAuth(config))\n * app.use('/api', baserowExpress.requireAuth())\n *\n * app.get('/api/protected', async (req, res) => {\n * // req.baserow está garantizado aquí\n * const data = await req.baserow!.database(123).table(456).rows.list()\n * res.json(data)\n * })\n * ```\n */\nexport function requireAuth(): ExpressMiddleware {\n return async (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {\n try {\n if (!req.baserow) {\n res.status(401).json({\n error: 'Baserow client not found. Use baserowAuth() middleware first.',\n code: 'BASEROW_CLIENT_MISSING'\n })\n return\n }\n\n // Opcional: verificar conectividad\n const isConnected = await req.baserow.health()\n if (!isConnected) {\n res.status(503).json({\n error: 'Baserow server not accessible',\n code: 'BASEROW_UNAVAILABLE'\n })\n return\n }\n\n next()\n } catch (error) {\n next(error)\n }\n }\n}\n\n/**\n * Middleware para cleanup automático de recursos Baserow\n *\n * Limpia el cliente Baserow al final del request para evitar memory leaks.\n * Especialmente útil cuando se usan clientes per-request.\n *\n * @returns Middleware Express\n *\n * @example\n * ```typescript\n * import { express as baserowExpress } from '@gzl10/baserow'\n *\n * app.use('/api', baserowExpress.baserowAuth(config))\n * // ... otras rutas ...\n * app.use('/api', baserowExpress.baserowCleanup()) // Debe ir al final\n * ```\n */\nexport function baserowCleanup(): ExpressMiddleware {\n return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {\n // Cleanup después de que la respuesta se envíe\n if (req.baserow) {\n const cleanup = () => {\n try {\n req.baserow?.destroy()\n delete req.baserow\n } catch (error) {\n // Log error pero no fallar el request\n // eslint-disable-next-line no-console\n console.warn('Error cleaning up Baserow client:', error)\n }\n }\n\n // Cleanup en el próximo tick después de enviar response\n process.nextTick(cleanup)\n }\n\n next()\n }\n}\n","/**\n * Manejo de errores Express para @gzl10/baserow\n *\n * Convertidores y middlewares para transformar BaserowError en respuestas HTTP apropiadas.\n * Proporciona mapeo automático de errores de Baserow a códigos de estado HTTP estándar.\n *\n * @example\n * ```typescript\n * import express from 'express'\n * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'\n *\n * const app = express()\n *\n * // Setup middlewares\n * app.use('/api', baserowExpress.baserowAuth(config))\n *\n * // Routes\n * app.get('/api/table/:id', async (req, res, next) => {\n * try {\n * const table = await req.baserow!.database(123).table(parseInt(req.params.id)).get()\n * res.json(baserowExpress.serializeTable(table))\n * } catch (error) {\n * next(error) // Será manejado por errorHandler()\n * }\n * })\n *\n * // Error handler global (debe ir al final)\n * app.use(baserowExpress.errorHandler())\n * ```\n *\n * @since 1.0.0\n */\n\nimport { BaserowError, BaserowNotFoundError, BaserowValidationError, BaserowRateLimitError } from '../types'\nimport type { ExpressRequest, ExpressResponse, ExpressNextFunction } from './middleware'\n\n/**\n * Formato estándar para respuestas de error\n */\nexport interface ErrorResponse {\n error: string\n code: string\n status: number\n details?: any\n timestamp?: string\n}\n\n/**\n * Convierte BaserowError a código de estado HTTP apropiado\n *\n * Mapea los tipos de error específicos de Baserow a códigos HTTP estándar:\n * - BaserowNotFoundError → 404\n * - BaserowValidationError → 400\n * - BaserowRateLimitError → 429\n * - BaserowError (generic auth) → 401\n * - Otros BaserowError → 500\n *\n * @param error - Error de Baserow a convertir\n * @returns Código de estado HTTP apropiado\n */\nexport function getHttpStatusCode(error: BaserowError): number {\n if (error instanceof BaserowNotFoundError) {\n return 404\n }\n\n if (error instanceof BaserowValidationError) {\n return 400\n }\n\n if (error instanceof BaserowRateLimitError) {\n return 429\n }\n\n // Detectar errores de auth por status code o mensaje\n if (error.status === 401 || error.message?.toLowerCase().includes('auth')) {\n return 401\n }\n\n // Usar status del error si está disponible\n if (error.status) {\n return error.status\n }\n\n // Default para errores desconocidos\n return 500\n}\n\n/**\n * Convierte BaserowError a formato de respuesta Express estándar\n *\n * Crea un objeto de error normalizado que incluye toda la información relevante\n * sin exponer detalles internos sensibles.\n *\n * @param error - Error de Baserow a serializar\n * @returns Objeto de error serializado para respuesta HTTP\n *\n * @example\n * ```typescript\n * try {\n * await someBaserowOperation()\n * } catch (error) {\n * const response = serializeError(error as BaserowError)\n * res.status(response.status).json(response)\n * }\n * ```\n */\nexport function serializeError(error: BaserowError): ErrorResponse {\n const status = getHttpStatusCode(error)\n\n const response: ErrorResponse = {\n error: error.message || 'Unknown Baserow error',\n code: error.constructor.name.replace('Error', '').toUpperCase(),\n status,\n timestamp: new Date().toISOString()\n }\n\n // Agregar detalles específicos para ciertos tipos de error\n if (error instanceof BaserowValidationError) {\n response.details = {\n fieldErrors: error.fieldErrors\n }\n }\n\n if (error instanceof BaserowRateLimitError) {\n response.details = {\n retryAfter: error.retryAfter\n }\n }\n\n if (error instanceof BaserowNotFoundError) {\n response.details = {\n message: error.message\n }\n }\n\n return response\n}\n\n/**\n * Convierte Error genérico a formato de respuesta Express\n *\n * Maneja errores que no son específicos de Baserow (por ejemplo, errores de red,\n * errores de validación de entrada, etc.).\n *\n * @param error - Error genérico a serializar\n * @returns Objeto de error serializado\n */\nexport function serializeGenericError(error: Error): ErrorResponse {\n return {\n error: error.message || 'Internal server error',\n code: 'INTERNAL_ERROR',\n status: 500,\n timestamp: new Date().toISOString()\n }\n}\n\n/**\n * Middleware Express para manejo automático de errores Baserow\n *\n * Convierte automáticamente errores de Baserow en respuestas HTTP apropiadas.\n * Debe ser usado como el último middleware de la aplicación.\n *\n * @param options - Opciones de configuración del error handler\n * @returns Middleware Express para manejo de errores\n *\n * @example\n * ```typescript\n * import { express as baserowExpress } from '@gzl10/baserow'\n *\n * // Al final de la configuración de middlewares\n * app.use(baserowExpress.errorHandler({\n * includeStack: process.env.NODE_ENV === 'development',\n * logger: console\n * }))\n *\n * // Ejemplo de error personalizado en ruta\n * app.get('/api/custom', async (req, res, next) => {\n * try {\n * // Operación que puede fallar\n * const data = await req.baserow!.database(999).table(123).rows.list()\n * res.json(data)\n * } catch (error) {\n * next(error) // Automáticamente convierte BaserowError a HTTP response\n * }\n * })\n * ```\n */\nexport function errorHandler(\n options: {\n includeStack?: boolean\n logger?: { error: (message: any) => void }\n } = {}\n) {\n return (error: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {\n // Si ya se envió la respuesta, pasar al error handler por defecto\n if ((res as any).headersSent) {\n return next(error)\n }\n\n let response: ErrorResponse\n\n if (error instanceof BaserowError) {\n response = serializeError(error)\n } else if (error instanceof Error) {\n response = serializeGenericError(error)\n } else {\n // Error desconocido\n response = {\n error: 'Unknown error occurred',\n code: 'UNKNOWN_ERROR',\n status: 500,\n timestamp: new Date().toISOString()\n }\n }\n\n // Agregar stack trace en desarrollo\n if (options.includeStack && error instanceof Error) {\n ;(response as any).stack = error.stack\n }\n\n // Log del error\n if (options.logger) {\n options.logger.error(\n 'Baserow API Error: ' +\n JSON.stringify({\n error: response,\n url: (req as any).url,\n method: (req as any).method,\n stack: error instanceof Error ? error.stack : undefined\n })\n )\n }\n\n res.status(response.status).json(response)\n }\n}\n\n/**\n * Handler de errores simplificado para casos básicos\n *\n * Versión simplificada del error handler que solo maneja errores de Baserow\n * sin opciones de configuración adicionales.\n *\n * @returns Middleware Express básico para errores\n */\nexport function simpleErrorHandler() {\n return (error: any, _req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {\n if ((res as any).headersSent) {\n return next(error)\n }\n\n if (error instanceof BaserowError) {\n const response = serializeError(error)\n return res.status(response.status).json(response)\n }\n\n // Para errores no-Baserow, pasar al siguiente handler\n next(error)\n }\n}\n","/**\n * Serializadores Express para @gzl10/baserow\n *\n * Utilidades para convertir objetos de Baserow en formatos apropiados para respuestas Express.\n * Proporciona serialización consistente y limpieza de datos para APIs REST.\n *\n * @example\n * ```typescript\n * import express from 'express'\n * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'\n *\n * app.get('/api/tables/:id/rows', async (req, res, next) => {\n * try {\n * const result = await req.baserow!.database(123).table(456).rows.list()\n * const serialized = baserowExpress.serializePaginated(result)\n * res.json(serialized)\n * } catch (error) {\n * next(error) // Will be handled by baserowExpress.errorHandler()\n * }\n * })\n * ```\n *\n * @since 1.0.0\n */\n\nimport type { Row, Table, Field, Database, Workspace, QueryOptions } from '../types'\n\n// Definir tipos locales para los serializers Express\nexport interface PaginatedResult<T> {\n results: T[]\n count: number\n next: string | null\n previous: string | null\n}\n\n/**\n * Formato estándar para respuestas paginadas\n */\nexport interface PaginatedResponse<T = any> {\n data: T[]\n pagination: {\n count: number\n next: string | null\n previous: string | null\n page: number\n totalPages: number\n }\n meta?: {\n total: number\n filters?: Record<string, any>\n ordering?: string\n }\n}\n\n/**\n * Formato para respuestas de filas con metadatos\n */\nexport interface RowsResponse {\n data: Row[]\n fields: Field[]\n table: {\n id: number\n name: string\n database_id: number\n }\n pagination?: PaginatedResponse<Row>['pagination']\n}\n\n/**\n * Serializa resultado paginado de Baserow para respuesta Express\n *\n * Convierte el formato interno de paginación de Baserow en un formato\n * estándar y consistente para APIs REST.\n *\n * @param result - Resultado paginado de Baserow\n * @param options - Opciones adicionales de serialización\n * @returns Respuesta paginada formateada\n *\n * @example\n * ```typescript\n * import { express as baserowExpress } from '@gzl10/baserow'\n *\n * app.get('/api/rows', async (req, res) => {\n * const result = await req.baserow!.database(123).table(456).rows.list()\n * const response = baserowExpress.serializePaginated(result, {\n * baseUrl: req.baseUrl,\n * includeTotal: true\n * })\n * res.json(response)\n * })\n * ```\n */\nexport function serializePaginated<T>(\n result: PaginatedResult<T>,\n options: {\n baseUrl?: string\n includeTotal?: boolean\n } = {}\n): PaginatedResponse<T> {\n const { baseUrl = '', includeTotal = true } = options\n\n // Calcular página actual y total de páginas\n const page = Math.floor((result.previous ? extractPageFromUrl(result.previous) : 0) + 1)\n const totalPages = result.count > 0 ? Math.ceil(result.count / result.results.length) : 1\n\n const response: PaginatedResponse<T> = {\n data: result.results,\n pagination: {\n count: result.results.length,\n next: result.next ? makeRelativeUrl(result.next, baseUrl) : null,\n previous: result.previous ? makeRelativeUrl(result.previous, baseUrl) : null,\n page,\n totalPages\n }\n }\n\n if (includeTotal) {\n response.meta = {\n total: result.count\n }\n }\n\n return response\n}\n\n/**\n * Serializa filas de tabla con metadatos completos\n *\n * Incluye datos de filas junto con información de campos y tabla\n * para proporcionar contexto completo en la respuesta.\n *\n * @param rows - Filas de la tabla\n * @param table - Información de la tabla\n * @param fields - Campos de la tabla\n * @returns Respuesta completa con filas y metadatos\n */\nexport function serializeRowsWithMeta(rows: Row[] | PaginatedResult<Row>, table: Table, fields: Field[]): RowsResponse {\n const isPaginated = 'results' in rows\n const rowData = isPaginated ? rows.results : rows\n\n const response: RowsResponse = {\n data: rowData,\n fields: fields,\n table: {\n id: table.id,\n name: table.name,\n database_id: table.database_id\n }\n }\n\n if (isPaginated) {\n response.pagination = serializePaginated(rows).pagination\n }\n\n return response\n}\n\n/**\n * Limpia y serializa un campo de Baserow\n *\n * Remueve propiedades internas y normaliza la estructura del campo\n * para respuestas de API.\n *\n * @param field - Campo de Baserow a limpiar\n * @returns Campo serializado\n */\nexport function cleanField(field: Field): Partial<Field> {\n // Propiedades core que siempre incluir\n const cleaned: Partial<Field> = {\n id: field.id,\n name: field.name,\n type: field.type,\n order: field.order,\n primary: field.primary\n }\n\n // Agregar propiedades específicas del tipo si existen\n if ('table_id' in field) cleaned.table_id = field.table_id\n if ('description' in field) cleaned.description = field.description\n if ('read_only' in field) cleaned.read_only = field.read_only\n\n // Propiedades específicas de tipos de campo\n if ('number_decimal_places' in field) cleaned.number_decimal_places = field.number_decimal_places\n if ('date_format' in field) cleaned.date_format = field.date_format\n if ('date_include_time' in field) cleaned.date_include_time = field.date_include_time\n if ('select_options' in field) cleaned.select_options = field.select_options\n if ('link_row_table_id' in field) cleaned.link_row_table_id = field.link_row_table_id\n\n return cleaned\n}\n\n/**\n * Serializa tabla de Baserow para respuesta Express\n *\n * @param table - Tabla de Baserow\n * @param includeFields - Si incluir campos en la respuesta\n * @returns Tabla serializada\n */\nexport function serializeTable(table: Table, includeFields = false): Partial<Table> {\n const serialized: Partial<Table> = {\n id: table.id,\n name: table.name,\n order: table.order,\n database_id: table.database_id\n }\n\n if (includeFields && 'fields' in table && table.fields) {\n ;(serialized as any).fields = table.fields\n }\n\n return serialized\n}\n\n/**\n * Serializa database de Baserow para respuesta Express\n *\n * @param database - Database de Baserow\n * @param includeTables - Si incluir tablas en la respuesta\n * @returns Database serializada\n */\nexport function serializeDatabase(database: Database, includeTables = false): Partial<Database> {\n const serialized: Partial<Database> = {\n id: database.id,\n name: database.name,\n order: database.order,\n workspace: database.workspace\n ? {\n id: database.workspace.id,\n name: database.workspace.name\n }\n : undefined\n }\n\n if (includeTables && database.tables) {\n serialized.tables = database.tables\n }\n\n return serialized\n}\n\n/**\n * Serializa workspace de Baserow para respuesta Express\n *\n * @param workspace - Workspace de Baserow\n * @returns Workspace serializado\n */\nexport function serializeWorkspace(workspace: Workspace): Partial<Workspace> {\n return {\n id: workspace.id,\n name: workspace.name,\n order: workspace.order\n }\n}\n\n/**\n * Crea respuesta de éxito estándar\n *\n * @param data - Datos a incluir en la respuesta\n * @param message - Mensaje opcional de éxito\n * @returns Respuesta estándar de éxito\n */\nexport function createSuccessResponse<T>(data: T, message?: string) {\n return {\n success: true,\n data,\n message,\n timestamp: new Date().toISOString()\n }\n}\n\n/**\n * Serializa opciones de query para logging o debugging\n *\n * @param options - Opciones de query de Baserow\n * @returns Opciones limpias para logging\n */\nexport function serializeQueryOptions(options: QueryOptions): Record<string, any> {\n const cleaned: Record<string, any> = {}\n\n if (options.page !== undefined) cleaned.page = options.page\n if (options.size !== undefined) cleaned.size = options.size\n if (options.search) cleaned.search = options.search\n if (options.order_by) cleaned.order_by = options.order_by\n if (options.user_field_names !== undefined) cleaned.user_field_names = options.user_field_names\n if (options.filters) cleaned.filters = options.filters\n\n return cleaned\n}\n\n// Utilidades internas\n\n/**\n * Extrae número de página de URL de paginación\n */\nfunction extractPageFromUrl(url: string): number {\n const match = url.match(/[?&]page=(\\d+)/)\n return match ? parseInt(match[1], 10) : 1\n}\n\n/**\n * Convierte URL absoluta a relativa\n */\nfunction makeRelativeUrl(absoluteUrl: string, baseUrl: string): string {\n if (!baseUrl || !absoluteUrl.startsWith('http')) {\n return absoluteUrl\n }\n\n try {\n const url = new URL(absoluteUrl)\n return url.pathname + url.search\n } catch {\n return absoluteUrl\n }\n}\n"],"mappings":"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","names":[]}
|