@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +435 -0
  2. package/README.md +847 -0
  3. package/dist/index.d.ts +8749 -0
  4. package/dist/index.js +11167 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +91 -0
  7. package/src/BaserowClient.ts +501 -0
  8. package/src/ClientWithCreds.ts +545 -0
  9. package/src/ClientWithCredsWs.ts +852 -0
  10. package/src/ClientWithToken.ts +171 -0
  11. package/src/contexts/DatabaseClientContext.ts +114 -0
  12. package/src/contexts/DatabaseContext.ts +870 -0
  13. package/src/contexts/DatabaseTokenContext.ts +331 -0
  14. package/src/contexts/FieldContext.ts +399 -0
  15. package/src/contexts/RowContext.ts +99 -0
  16. package/src/contexts/TableClientContext.ts +291 -0
  17. package/src/contexts/TableContext.ts +1247 -0
  18. package/src/contexts/TableOnlyContext.ts +74 -0
  19. package/src/contexts/WorkspaceContext.ts +490 -0
  20. package/src/express/errors.ts +260 -0
  21. package/src/express/index.ts +69 -0
  22. package/src/express/middleware.ts +225 -0
  23. package/src/express/serializers.ts +314 -0
  24. package/src/index.ts +247 -0
  25. package/src/presets/performance.ts +262 -0
  26. package/src/services/AuthService.ts +472 -0
  27. package/src/services/DatabaseService.ts +246 -0
  28. package/src/services/DatabaseTokenService.ts +186 -0
  29. package/src/services/FieldService.ts +1543 -0
  30. package/src/services/RowService.ts +982 -0
  31. package/src/services/SchemaControlService.ts +420 -0
  32. package/src/services/TableService.ts +781 -0
  33. package/src/services/WorkspaceService.ts +113 -0
  34. package/src/services/core/BaseAuthClient.ts +111 -0
  35. package/src/services/core/BaseClient.ts +107 -0
  36. package/src/services/core/BaseService.ts +71 -0
  37. package/src/services/core/HttpService.ts +115 -0
  38. package/src/services/core/ValidationService.ts +149 -0
  39. package/src/types/auth.ts +177 -0
  40. package/src/types/core.ts +91 -0
  41. package/src/types/errors.ts +105 -0
  42. package/src/types/fields.ts +456 -0
  43. package/src/types/index.ts +222 -0
  44. package/src/types/requests.ts +333 -0
  45. package/src/types/responses.ts +50 -0
  46. package/src/types/schema.ts +446 -0
  47. package/src/types/tokens.ts +36 -0
  48. package/src/types.ts +11 -0
  49. package/src/utils/auth.ts +174 -0
  50. package/src/utils/axios.ts +647 -0
  51. package/src/utils/field-cache.ts +164 -0
  52. package/src/utils/httpFactory.ts +66 -0
  53. package/src/utils/jwt-decoder.ts +188 -0
  54. package/src/utils/jwtTokens.ts +50 -0
  55. package/src/utils/performance.ts +105 -0
  56. package/src/utils/prisma-mapper.ts +961 -0
  57. package/src/utils/validation.ts +463 -0
  58. package/src/validators/schema.ts +419 -0
package/README.md ADDED
@@ -0,0 +1,847 @@
1
+ # @gzl10/baserow
2
+
3
+ Cliente TypeScript moderno para Baserow API v1.35+ (2025) con **arquitectura de tres clientes**, **PrismaBaserowMapper**, **21/22 tipos de campos** y **gestión automática de JWT con refresh tokens**.
4
+
5
+ ## ✨ Características Principales
6
+
7
+ - 🎯 **Arquitectura de 3 Clientes**: Token, Admin Global y Admin Workspace
8
+ - 🔐 **Auto-Refresh JWT**: Gestión automática de access y refresh tokens con renovación inteligente
9
+ - 🗺️ **PrismaBaserowMapper**: Sintaxis familiar estilo Prisma para consultas (35+ tests)
10
+ - 📋 **21/22 Tipos de Campos**: Cobertura completa incluyendo file, autonumber, count, rollup, lookup
11
+ - 🚀 **HTTP Optimizado**: axios-retry + axios-rate-limit oficiales (36% menos código)
12
+ - 🔄 **API Jerárquica**: Navegación intuitiva workspace → database → table → fields/rows
13
+ - ⚡ **Operaciones Bulk**: Procesamiento batch optimizado con retry logic
14
+ - 🧪 **Testing Robusto**: 60+ tests (35+ integración + 34 error handling), 42% cobertura error handling
15
+
16
+ ## 📚 Documentación
17
+
18
+ Esta es una guía de inicio rápido. **[Ver índice completo de documentación →](docs/README.md)**
19
+
20
+ ### 📖 Guías de Usuario
21
+
22
+ - **[Filtros y Ordenaciones](docs/FILTERS_AND_SORTING.md)** - Guía completa de búsquedas, filtros y ordenaciones
23
+ - **[API Reference](docs/BASEROW_API_REFERENCE.md)** - Referencia completa de la API
24
+ - **[Keep-Alive](docs/KEEP_ALIVE.md)** - Configuración de keep-alive para backends 24/7
25
+ - **[Migración](docs/MIGRATION.md)** - Guía de migración desde versiones anteriores
26
+
27
+ ### 🔧 Documentación Técnica
28
+
29
+ - **[Testing](docs/TESTING.md)** - Guía de tests y cobertura
30
+ - **[Debugging](docs/DEBUGGING.md)** - Solución de problemas comunes
31
+ - **[Roadmap](docs/ROADMAP.md)** - Plan de evolución de la librería
32
+
33
+ ### 📊 Análisis y Referencia
34
+
35
+ - **[Baserow 1.35.3 Impact](docs/BASEROW_1.35.3_IMPACT_ANALYSIS.md)** - Análisis de compatibilidad con Baserow 1.35.3
36
+ - **[Endpoint Validation](docs/ENDPOINT_VALIDATION_RESULTS.md)** - Validación de endpoints de API
37
+
38
+ ---
39
+
40
+ ## 🎯 Arquitectura
41
+
42
+ ### Cliente Principal Unificado
43
+
44
+ ```typescript
45
+ import { BaserowClient } from '@gzl10/baserow'
46
+
47
+ // 1. Cliente con Database Token (solo lectura de datos)
48
+ const client = await BaserowClient.create({
49
+ url: 'https://baserow.example.com',
50
+ token: 'db_token_123'
51
+ })
52
+
53
+ // 2. Cliente Admin Global (acceso multi-workspace)
54
+ const adminGlobal = await BaserowClient.create({
55
+ url: 'https://baserow.example.com',
56
+ credentials: { email: 'admin@example.com', password: 'password' }
57
+ })
58
+
59
+ // 3. Cliente Admin Workspace (scope específico)
60
+ const adminWorkspace = await BaserowClient.create({
61
+ url: 'https://baserow.example.com',
62
+ credentials: { email: 'admin@example.com', password: 'password' },
63
+ workspace: 'Mi Workspace'
64
+ })
65
+ ```
66
+
67
+ ### Tres Arquitecturas Especializadas
68
+
69
+ | Cliente | Autenticación | Capacidades | Casos de Uso |
70
+ | ------------------ | ------------------- | ------------------------------------------------- | ------------------------------------------------- |
71
+ | **BaserowClient** | Database Token | CRUD de datos, operaciones bulk | Apps cliente, integración de datos, APIs públicas |
72
+ | **AdminGlobal** | JWT Multi-workspace | Gestión completa de workspaces, databases, tables | Scripts administrativos, herramientas DevOps |
73
+ | **AdminWorkspace** | JWT Scope limitado | Operaciones dentro de un workspace específico | Apps empresariales, dashboards especializados |
74
+
75
+ ## 🚀 Instalación
76
+
77
+ ```bash
78
+ pnpm install @gzl10/baserow
79
+ ```
80
+
81
+ ## 🔍 PrismaBaserowMapper - Sintaxis Familiar
82
+
83
+ Sintaxis estilo Prisma.js para consultas Baserow con **35+ tests** de validación:
84
+
85
+ ```typescript
86
+ import { BaserowClient } from '@gzl10/baserow'
87
+
88
+ const client = await BaserowClient.create({ url, token })
89
+
90
+ // Sintaxis Prisma familiar
91
+ const employees = await client
92
+ .database(123)
93
+ .table(456)
94
+ .rows.findMany({
95
+ where: {
96
+ AND: [{ status: 'active' }, { age: { gte: 18, lt: 65 } }, { email: { contains: '@company.com' } }]
97
+ },
98
+ orderBy: [{ created_at: 'desc' }, { name: 'asc' }],
99
+ take: 20,
100
+ skip: 40,
101
+ select: { id: true, name: true, email: true }
102
+ })
103
+ ```
104
+
105
+ **Operadores soportados:**
106
+
107
+ - **Comparación**: `equals`, `not`, `contains`, `startsWith`, `endsWith`, `gt`, `gte`, `lt`, `lte`
108
+ - **Lógicos**: `AND`, `OR` (flat), `NOT`
109
+ - **Estado**: `isEmpty`, `isNotEmpty`
110
+ - **Fechas**: Conversión automática a ISO strings
111
+
112
+ **📖 Ver guía completa:** [Filtros y Ordenaciones](docs/FILTERS_AND_SORTING.md)
113
+
114
+ ## 📖 Uso Básico
115
+
116
+ ### Tipos de Configuración (Aliases DX-friendly)
117
+
118
+ Para facilitar el desarrollo, la librería exporta aliases descriptivos:
119
+
120
+ ```typescript
121
+ // Tipos de configuración (aliases cortos)
122
+ import type { TokenConfig, CredentialsConfig, CredentialsWorkspaceConfig } from '@gzl10/baserow'
123
+
124
+ // Clases de cliente (para tipado avanzado)
125
+ import type { ClientWithToken, ClientWithCreds, ClientWithCredsWs } from '@gzl10/baserow'
126
+
127
+ // Equivalencias:
128
+ // - TokenConfig = BaserowTokenConfig
129
+ // - CredentialsConfig = BaserowCredentialsConfig
130
+ // - CredentialsWorkspaceConfig = BaserowCredentialsWorkspaceConfig
131
+ ```
132
+
133
+ ### 1. Cliente con Database Token (Solo datos)
134
+
135
+ ```typescript
136
+ import { BaserowClient } from '@gzl10/baserow'
137
+ import type { TokenConfig } from '@gzl10/baserow'
138
+
139
+ const config: TokenConfig = {
140
+ url: 'https://baserow.example.com',
141
+ token: 'your_database_token'
142
+ }
143
+
144
+ const client = await BaserowClient.create(config)
145
+
146
+ // API jerárquica simple
147
+ const rows = await client.database(123).table(456).rows.list()
148
+ const newRow = await client.database(123).table(456).rows.create({
149
+ name: 'John Doe',
150
+ email: 'john@example.com'
151
+ })
152
+
153
+ // Operaciones bulk optimizadas
154
+ const bulkRows = await client
155
+ .database(123)
156
+ .table(456)
157
+ .rows.createBulk(
158
+ [
159
+ { name: 'Alice', email: 'alice@example.com' },
160
+ { name: 'Bob', email: 'bob@example.com' }
161
+ ],
162
+ { batchSize: 100 }
163
+ )
164
+ ```
165
+
166
+ ### 2. Cliente Admin Global (Multi-workspace)
167
+
168
+ ```typescript
169
+ import type { CredentialsConfig } from '@gzl10/baserow'
170
+
171
+ const config: CredentialsConfig = {
172
+ url: 'https://baserow.example.com',
173
+ credentials: {
174
+ email: 'admin@example.com',
175
+ password: 'your_password'
176
+ }
177
+ }
178
+
179
+ const admin = await BaserowClient.create(config)
180
+
181
+ // Gestión completa de workspaces
182
+ const workspaces = await admin.workspaces.list()
183
+ const newWS = await admin.workspaces.create({ name: 'Nueva Empresa' })
184
+ const updatedWS = await admin.workspace('Company').update({ name: 'New Company' })
185
+ await admin.workspace('Old Company').delete()
186
+
187
+ // Creación jerárquica de recursos
188
+ const database = await admin.workspace('Company').databases.create('CRM')
189
+ const table = await admin
190
+ .workspace('Company')
191
+ .database('CRM')
192
+ .tables.create({
193
+ name: 'Customers',
194
+ data: [
195
+ ['Name', 'Email'],
196
+ ['Alice', 'alice@example.com']
197
+ ],
198
+ first_row_header: true
199
+ })
200
+ ```
201
+
202
+ ### 3. Cliente Admin Workspace (Scope limitado)
203
+
204
+ ```typescript
205
+ import type { CredentialsWorkspaceConfig } from '@gzl10/baserow'
206
+
207
+ const config: CredentialsWorkspaceConfig = {
208
+ url: 'https://baserow.example.com',
209
+ credentials: {
210
+ email: 'admin@example.com',
211
+ password: 'your_password'
212
+ },
213
+ workspace: 'Company'
214
+ }
215
+
216
+ const adminWS = await BaserowClient.create(config)
217
+
218
+ // API simplificada sin prefijo workspace
219
+ const databases = await adminWS.databases.list()
220
+ const table = await adminWS.database('CRM').tables.create({ name: 'Products' })
221
+ ```
222
+
223
+ ## 📋 Cobertura Completa de Campos (21/22 tipos)
224
+
225
+ La librería soporta **prácticamente todos los tipos de campos** de Baserow v1.35+ incluyendo los nuevos campos avanzados:
226
+
227
+ ### ✅ Campos Implementados por Categoría
228
+
229
+ #### Texto y Comunicación (5 tipos)
230
+
231
+ ```typescript
232
+ const nameField = await table.field.createText('full_name')
233
+ const descField = await table.field.createLongText('description')
234
+ const websiteField = await table.field.createUrl('website')
235
+ const emailField = await table.field.createEmail('contact_email')
236
+ const phoneField = await table.field.createPhoneNumber('phone')
237
+ ```
238
+
239
+ #### Numéricos y Lógicos (3 tipos)
240
+
241
+ ```typescript
242
+ const priceField = await table.field.createNumber('price', 2) // 2 decimales
243
+ const activeField = await table.field.createBoolean('is_active')
244
+ const ratingField = await table.field.createRating('rating', 5) // máx 5 estrellas
245
+ ```
246
+
247
+ #### Fecha y Auditoría (5 tipos)
248
+
249
+ ```typescript
250
+ const birthdateField = await table.field.createDate('birthdate')
251
+ const lastModField = await table.field.createLastModified('updated_at')
252
+ const lastUserField = await table.field.createLastModifiedBy('updated_by')
253
+ const createdField = await table.field.createCreatedOn('created_at')
254
+ const creatorField = await table.field.createCreatedBy('created_by')
255
+ ```
256
+
257
+ #### Selección y Opciones (2 tipos)
258
+
259
+ ```typescript
260
+ const statusField = await table.field.createSingleSelect('status', [
261
+ { value: 'active', color: 'green' },
262
+ { value: 'inactive', color: 'red' }
263
+ ])
264
+ const tagsField = await table.field.createMultipleSelect('tags', [
265
+ { value: 'urgent', color: 'red' },
266
+ { value: 'important', color: 'orange' }
267
+ ])
268
+ ```
269
+
270
+ #### Relacionales (2 tipos)
271
+
272
+ ```typescript
273
+ const linkField = await table.field.createLinkRow('related_items', targetTableId)
274
+ const formulaField = await table.field.createFormula('total_price', 'field("quantity") * field("price")')
275
+ ```
276
+
277
+ #### 🆕 Campos Avanzados (5 tipos) - ¡Nuevos en v1.1.0+!
278
+
279
+ ```typescript
280
+ // Archivos adjuntos
281
+ const docsField = await table.field.createFile('documents')
282
+
283
+ // Numeración automática
284
+ const ticketField = await table.field.createAutonumber('ticket_id')
285
+
286
+ // Conteo de relaciones
287
+ const itemsCountField = await table.field.createCount('items_count', targetTableId)
288
+
289
+ // Agregación desde tabla relacionada
290
+ const totalField = await table.field.createRollup('total_amount', tableId, fieldId, 'sum')
291
+
292
+ // Búsqueda desde tabla relacionada
293
+ const namesField = await table.field.createLookup('item_names', tableId, fieldId)
294
+ ```
295
+
296
+ ### API Fluida Consistente
297
+
298
+ Todos los tipos de campos siguen el mismo patrón de API para máxima consistencia:
299
+
300
+ ```typescript
301
+ // Patrón: table.field.create[TipoCampo](nombre, ...parametrosEspecificos)
302
+ const field = await table.field.createText('campo_nombre')
303
+
304
+ // Operaciones CRUD disponibles para todos los tipos
305
+ const updatedField = await table.field.update(fieldId, { name: 'nuevo_nombre' })
306
+ await table.field.delete(fieldId)
307
+ const allFields = await table.field.list()
308
+ ```
309
+
310
+ ## 🔧 Utilidades Express (Opcionales)
311
+
312
+ La librería incluye utilidades opcionales para integración con Express **sin crear dependencias**.
313
+
314
+ ### Configuración Básica
315
+
316
+ ```typescript
317
+ import express from 'express'
318
+ import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'
319
+
320
+ const app = express()
321
+ const client = await BaserowClient.create({ url, token })
322
+
323
+ // Middlewares
324
+ app.use('/api', baserowExpress.baserowContext(client))
325
+ app.use('/api', baserowExpress.requireAuth())
326
+
327
+ // Routes con cliente automático
328
+ app.get('/api/tables/:tableId/rows', async (req, res, next) => {
329
+ try {
330
+ const rows = await req.baserow!.database(123).table(parseInt(req.params.tableId)).rows.list()
331
+ res.json(baserowExpress.serializePaginated(rows))
332
+ } catch (error) {
333
+ next(error)
334
+ }
335
+ })
336
+
337
+ // Error handler
338
+ app.use(baserowExpress.errorHandler())
339
+ ```
340
+
341
+ ### Middlewares Disponibles
342
+
343
+ ```typescript
344
+ // Auto-crear cliente por request
345
+ app.use(
346
+ '/api',
347
+ baserowExpress.baserowAuth({
348
+ url: process.env.BASEROW_URL!,
349
+ token: process.env.BASEROW_TOKEN!
350
+ })
351
+ )
352
+
353
+ // Validar autenticación
354
+ app.use('/api', baserowExpress.requireAuth())
355
+
356
+ // Cleanup automático de recursos
357
+ app.use('/api', baserowExpress.baserowCleanup())
358
+ ```
359
+
360
+ ### Serializadores
361
+
362
+ ```typescript
363
+ // Paginated responses
364
+ const paginatedRows = baserowExpress.serializePaginated(result)
365
+
366
+ // Response con metadatos
367
+ const rowsWithMeta = baserowExpress.serializeRowsWithMeta(rows, table, fields)
368
+
369
+ // Clean entities
370
+ const cleanTable = baserowExpress.serializeTable(table, true)
371
+ ```
372
+
373
+ ### Error Handling
374
+
375
+ ```typescript
376
+ // Error handler con logging
377
+ app.use(
378
+ baserowExpress.errorHandler({
379
+ includeStack: process.env.NODE_ENV === 'development',
380
+ logger: console
381
+ })
382
+ )
383
+
384
+ // Handler simplificado
385
+ app.use(baserowExpress.simpleErrorHandler())
386
+ ```
387
+
388
+ ## ⚡ Características Técnicas
389
+
390
+ - ✅ **TypeScript**: Tipado fuerte con inferencia automática de tipos
391
+ - ✅ **ESM Moderno**: Compilación optimizada con tsup (5-10x más rápido que tsc)
392
+ - ✅ **HTTP Simplificado**: 36% menos código usando axios-retry + axios-rate-limit oficiales
393
+ - ✅ **Connection Pooling**: Node.js 20+ keepAlive nativo (eliminado agentkeepalive)
394
+ - ✅ **PrismaMapper**: Sintaxis familiar Prisma con 35+ tests de transformaciones
395
+ - ✅ **21/22 Campos**: Cobertura prácticamente completa incluyendo campos avanzados
396
+ - ✅ **API Jerárquica**: workspace → database → table → fields/rows navegación intuitiva
397
+ - ✅ **Retry Logic**: Manejo robusto de rate limiting y timeouts configurable
398
+ - ✅ **Error Handling**: Errores tipados (BaserowError, ValidationError, etc.) + 34 tests de validación (v1.0.2)
399
+ - ✅ **Express Ready**: Utilidades opcionales sin dependencias obligatorias
400
+ - ✅ **Testing Robusto**: 60+ tests (35+ integración + 34 error handling), 42% cobertura
401
+
402
+ ## 🔧 Configuración Avanzada
403
+
404
+ ### 🔐 Autenticación y Refresh Tokens
405
+
406
+ La librería gestiona automáticamente **JWT access tokens** y **refresh tokens** con renovación automática:
407
+
408
+ #### Persistencia de Tokens (Por Instancia)
409
+
410
+ ```typescript
411
+ const adminClient = await BaserowClient.create({
412
+ url: 'https://baserow.example.com',
413
+ credentials: { email: 'admin@example.com', password: 'password' }
414
+ })
415
+
416
+ // ✅ Tokens almacenados en memoria de la instancia
417
+ // ✅ TTL leído automáticamente del claim 'exp' del JWT (~10 min en Baserow)
418
+ // ✅ Auto-refresh cuando el access token expira
419
+ // ✅ Thread-safe: cada instancia tiene sus propios tokens
420
+ // ❌ No persistidos en disco: re-login necesario después de reinicio
421
+
422
+ // Verificar estado de autenticación
423
+ console.log(adminClient.isAuthenticated()) // true
424
+ console.log(adminClient.getCurrentToken()) // 'eyJhbGc...'
425
+
426
+ // Renovar manualmente si es necesario
427
+ const newToken = await adminClient.refreshToken()
428
+ ```
429
+
430
+ **Nota sobre expiración de tokens:**
431
+ La librería usa **decodificación estándar JWT** para leer el claim `exp` del token, no asume ningún TTL hardcoded. Esto garantiza:
432
+
433
+ - ✅ **Precisión perfecta**: usa el tiempo real del servidor Baserow
434
+ - ✅ **Adaptabilidad**: funciona si Baserow cambia la configuración de TTL
435
+ - ✅ **Estándar JWT**: sigue las mejores prácticas de la industria
436
+
437
+ #### Auto-Refresh en Errores 401
438
+
439
+ ```typescript
440
+ // El cliente configura automáticamente un interceptor
441
+ // que renueva el token en caso de 401 Unauthorized
442
+ adminClient.setupAutoRefresh() // Opcional, ya configurado internamente
443
+
444
+ // Cualquier request que falle con 401 intentará:
445
+ // 1. Renovar el access token con el refresh token
446
+ // 2. Reintentar la request original con el nuevo token
447
+ // 3. Si el refresh falla, limpiar tokens y lanzar error
448
+ ```
449
+
450
+ #### Manejo de Errores de Autenticación
451
+
452
+ ```typescript
453
+ import { BaserowAuthTokenExpiredError, BaserowAuthTokenInvalidError } from '@gzl10/baserow'
454
+
455
+ try {
456
+ await adminClient.refreshToken()
457
+ } catch (error) {
458
+ if (error instanceof BaserowAuthTokenExpiredError) {
459
+ // Refresh token expirado - re-login necesario
460
+ console.error('❌ Refresh token expired - please login again')
461
+ // Redirigir a login o solicitar nuevas credenciales
462
+ } else if (error instanceof BaserowAuthTokenInvalidError) {
463
+ // Refresh token inválido o corrupto
464
+ console.error('❌ Invalid refresh token - check credentials')
465
+ }
466
+ }
467
+ ```
468
+
469
+ #### Códigos de Error Específicos
470
+
471
+ | Error | Código | Descripción | Acción Recomendada |
472
+ | ------------------------------ | --------------------- | ------------------------------- | -------------------------------------- |
473
+ | `BaserowAuthTokenExpiredError` | `AUTH_TOKEN_EXPIRED` | Access o refresh token expirado | Re-login con credenciales |
474
+ | `BaserowAuthTokenInvalidError` | `AUTH_TOKEN_INVALID` | Token inválido o corrupto | Verificar credenciales y configuración |
475
+ | `BaserowError` | `NO_REFRESH_TOKEN` | No hay refresh token disponible | Hacer login primero |
476
+ | `BaserowError` | `INVALID_CREDENTIALS` | Credenciales incorrectas | Verificar email y password |
477
+
478
+ #### Keep-Alive para Backends 24/7 (v1.3.0+)
479
+
480
+ ⚠️ **Limitación de Baserow**: El refresh token expira **7 días después del login** (fijo, NO se renueva al hacer refresh).
481
+
482
+ **Solución**: Keep-alive ejecuta **re-login automático** cada 5 días para obtener tokens frescos, previniendo la expiración.
483
+
484
+ ```typescript
485
+ // Backend de larga duración con keep-alive
486
+ const backendClient = await BaserowClient.create({
487
+ url: 'https://baserow.example.com',
488
+ credentials: { email: 'backend@example.com', password: 'password' },
489
+ keepAlive: {
490
+ enabled: true,
491
+ intervalMinutes: 7200 // Default: 7200 (5 días)
492
+ }
493
+ })
494
+
495
+ // ✅ Re-login automático cada 5 días → tokens siempre frescos
496
+ // ✅ Backend funciona indefinidamente sin intervención manual
497
+ // ✅ Se detiene automáticamente en logout() o destroy()
498
+ // ⚠️ Credenciales almacenadas en memoria (solo backends confiables)
499
+ ```
500
+
501
+ **Recomendaciones de intervalo:**
502
+
503
+ | Tipo de Backend | Intervalo | Motivo |
504
+ | ------------------- | ----------------- | ---------------------------------------------------- |
505
+ | **Producción 24/7** | 7200 min (5 días) | Balance óptimo: margen de 2 días antes de expiración |
506
+ | **Crítico** | 4320 min (3 días) | Extra conservador: margen de 4 días |
507
+ | **Desarrollo** | 1440 min (1 día) | Testing frecuente, detección rápida de problemas |
508
+
509
+ **Ejemplo completo backend Express:**
510
+
511
+ ```typescript
512
+ import { BaserowClient } from '@gzl10/baserow'
513
+
514
+ // Inicializar cliente con keep-alive al arrancar servidor
515
+ const client = await BaserowClient.create({
516
+ url: process.env.BASEROW_URL!,
517
+ credentials: {
518
+ email: process.env.BASEROW_EMAIL!,
519
+ password: process.env.BASEROW_PASSWORD!
520
+ },
521
+ keepAlive: { enabled: true }, // Default: 5 días
522
+ workspace: 'Production'
523
+ })
524
+
525
+ // Usar cliente en rutas Express
526
+ app.get('/api/data', async (req, res) => {
527
+ const databases = await client.databases.findMany()
528
+ res.json(databases)
529
+ })
530
+
531
+ // Limpiar al cerrar servidor
532
+ process.on('SIGTERM', () => {
533
+ client.destroy() // Detiene keep-alive y limpia credenciales
534
+ process.exit(0)
535
+ })
536
+ ```
537
+
538
+ **⚠️ Seguridad**:
539
+
540
+ - Credenciales se almacenan en memoria del proceso
541
+ - Solo usar en backends propios/confiables
542
+ - NO usar en frontends o aplicaciones compartidas
543
+ - Se limpian automáticamente en `logout()`/`destroy()`
544
+
545
+ ### Performance con Presets Predefinidos
546
+
547
+ La librería incluye **presets de performance optimizados** para diferentes entornos:
548
+
549
+ ```typescript
550
+ import { BaserowClient, PERFORMANCE_PRESETS } from '@gzl10/baserow'
551
+
552
+ // Producción (balanceado): 30s timeout, 3 retries, 10 req/s
553
+ const prodClient = await BaserowClient.create({
554
+ url: 'https://baserow.example.com',
555
+ token: 'your_token',
556
+ performance: PERFORMANCE_PRESETS.production
557
+ })
558
+
559
+ // Desarrollo (relajado): 60s timeout, 2 retries, 5 req/s
560
+ const devClient = await BaserowClient.create({
561
+ url: 'http://localhost:3000',
562
+ credentials: { email: 'dev@example.com', password: 'password' },
563
+ performance: PERFORMANCE_PRESETS.development
564
+ })
565
+
566
+ // Testing (conservador): 45s timeout, 2 retries, 5 req/s
567
+ const testClient = await BaserowClient.create({
568
+ url: 'https://test.baserow.com',
569
+ token: 'test_token',
570
+ performance: PERFORMANCE_PRESETS.testing
571
+ })
572
+ ```
573
+
574
+ #### Presets Disponibles
575
+
576
+ | Preset | Timeout | Retries | Rate (req/s) | Uso Ideal |
577
+ | -------------- | ------- | ------- | ------------ | --------------------- |
578
+ | `production` | 30s | 3 | 10 | Producción balanceada |
579
+ | `development` | 60s | 2 | 5 | Desarrollo local |
580
+ | `testing` | 45s | 2 | 5 | Tests automatizados |
581
+ | `aggressive` | 15s | 5 | 20 | Servidores robustos |
582
+ | `conservative` | 60s | 1 | 2 | Recursos limitados |
583
+
584
+ ```typescript
585
+ // Helper function para obtener presets dinámicamente
586
+ import { getPerformancePreset } from '@gzl10/baserow'
587
+
588
+ const preset = getPerformancePreset('production')
589
+ // Equivalente a: PERFORMANCE_PRESETS.production
590
+ ```
591
+
592
+ ### Performance Global
593
+
594
+ ```typescript
595
+ import { PerformanceManager } from '@gzl10/baserow'
596
+
597
+ // Configurar defaults globales
598
+ PerformanceManager.setGlobal({
599
+ timeout: 45000,
600
+ retries: 5,
601
+ maxRequestsPerSecond: 20,
602
+ enableRateLimiting: true
603
+ })
604
+
605
+ // Ver configuración actual
606
+ const currentDefaults = PerformanceManager.getGlobal()
607
+ ```
608
+
609
+ ### Por Instancia (Custom)
610
+
611
+ ```typescript
612
+ const client = await BaserowClient.create({
613
+ url: 'https://baserow.example.com',
614
+ token: 'your_token',
615
+ performance: {
616
+ timeout: 60000,
617
+ retries: 3,
618
+ maxRequestsPerSecond: 10
619
+ }
620
+ })
621
+ ```
622
+
623
+ ## 🗂️ Operaciones Avanzadas
624
+
625
+ ### API Jerárquica CRUD Completa
626
+
627
+ La librería proporciona una API jerárquica consistente para operaciones CRUD en todos los niveles:
628
+
629
+ ```typescript
630
+ // ✅ Gestión de Workspaces
631
+ const workspaces = await admin.workspaces.list()
632
+ const newWS = await admin.workspaces.create({ name: 'Nueva Empresa' })
633
+ const updatedWS = await admin.workspace('Mi Empresa').update({ name: 'Empresa Actualizada' })
634
+ await admin.workspace('Empresa Vieja').delete()
635
+
636
+ // ✅ Gestión de Databases (dentro de workspace)
637
+ const databases = await admin.workspace('Mi Empresa').databases.list()
638
+ const newDB = await admin.workspace('Mi Empresa').databases.create('Nueva Database')
639
+ const updatedDB = await admin.workspace('Mi Empresa').database('CRM').update({ name: 'CRM v2' })
640
+ await admin.workspace('Mi Empresa').database('CRM Viejo').delete()
641
+
642
+ // ✅ Gestión de Tables (dentro de database)
643
+ const tables = await admin.workspace('Mi Empresa').database('CRM').tables.list()
644
+ const newTable = await admin
645
+ .workspace('Mi Empresa')
646
+ .database('CRM')
647
+ .tables.create({
648
+ name: 'Customers',
649
+ data: [
650
+ ['Name', 'Email'],
651
+ ['Alice', 'alice@example.com']
652
+ ],
653
+ first_row_header: true
654
+ })
655
+ const updatedTable = await admin.workspace('Mi Empresa').database('CRM').table('Customers').update({ name: 'Clients' })
656
+ await admin.workspace('Mi Empresa').database('CRM').table('Old Table').delete()
657
+ ```
658
+
659
+ ### Operaciones Bulk
660
+
661
+ ```typescript
662
+ // Crear múltiples filas
663
+ const newRows = await client
664
+ .database(123)
665
+ .table(456)
666
+ .rows.createBulk(
667
+ [
668
+ { name: 'Alice', email: 'alice@example.com' },
669
+ { name: 'Bob', email: 'bob@example.com' }
670
+ ],
671
+ { batchSize: 100 }
672
+ )
673
+
674
+ // Descargar tabla completa
675
+ const allRows = await client.database(123).table(456).rows.listAll()
676
+ ```
677
+
678
+ ### Gestión de Campos
679
+
680
+ ```typescript
681
+ // Crear campos de todos los tipos
682
+ const textField = await admin.workspace('Company').database('CRM').table('Customers').field.createText('company_name')
683
+
684
+ const emailField = await admin
685
+ .workspace('Company')
686
+ .database('CRM')
687
+ .table('Customers')
688
+ .field.createEmail('contact_email')
689
+
690
+ const selectField = await admin
691
+ .workspace('Company')
692
+ .database('CRM')
693
+ .table('Customers')
694
+ .field.createSingleSelect('status', [
695
+ { value: 'active', color: 'green' },
696
+ { value: 'inactive', color: 'red' }
697
+ ])
698
+ ```
699
+
700
+ ### Database Tokens
701
+
702
+ ```typescript
703
+ // Crear tokens con permisos específicos
704
+ const token = await admin
705
+ .workspace('Company')
706
+ .database('CRM')
707
+ .tokens.create({
708
+ name: 'API Integration Token',
709
+ permissions: {
710
+ create: true,
711
+ read: true,
712
+ update: true,
713
+ delete: false
714
+ }
715
+ })
716
+ ```
717
+
718
+ ## 🧪 Testing Comprehensivo
719
+
720
+ La librería incluye **60+ tests** de integración con **cobertura de error handling del 42%**:
721
+
722
+ ### Suite de Tests Disponibles
723
+
724
+ ```bash
725
+ # Todos los tests (rápido)
726
+ pnpm test
727
+
728
+ # Tests en modo watch
729
+ pnpm test:watch
730
+
731
+ # Solo tests de smoke (CI/CD)
732
+ pnpm test:smoke
733
+
734
+ # Tests específicos de error handling
735
+ pnpm test admin-database.test.ts admin-table.test.ts admin-field-basic.test.ts admin-field-advanced.test.ts
736
+ ```
737
+
738
+ ### 📊 Cobertura de Testing
739
+
740
+ - **✅ 60+ Tests Totales**: 35+ integración + 34 error handling (v1.0.2)
741
+ - **✅ Tests de Campos**: 8 archivos especializados por tipo de campo
742
+ - **✅ PrismaMapper**: 35+ tests de transformaciones complejas
743
+ - **✅ Error Handling**: 42% archivos con cobertura completa (+17% en v1.0.2)
744
+ - **✅ Retry Logic**: Tests de manejo de errores y rate limiting
745
+ - **✅ Operaciones Bulk**: Validación de procesamiento optimizado
746
+ - **✅ API Jerárquica**: Tests de navegación workspace → database → table
747
+
748
+ ### 🆕 Error Handling Tests (v1.0.2)
749
+
750
+ **Helpers Reutilizables**:
751
+
752
+ - `tests/helpers/error-assertions.ts` - Funciones para validar errores tipados
753
+ - `tests/fixtures/invalid-data.ts` - Datos inválidos para testing consistente
754
+
755
+ **Servicios con Error Handling Completo**:
756
+
757
+ - ✅ `admin-database.test.ts` - 8 tests de error (404, 400/422, validaciones)
758
+ - ✅ `admin-table.test.ts` - 9 tests de error (404, validaciones, edge cases)
759
+ - ✅ `admin-field-basic.test.ts` - 10 tests de error (validaciones por tipo)
760
+ - ✅ `admin-field-advanced.test.ts` - 7 tests de error (campos complejos)
761
+
762
+ **Códigos HTTP Validados**:
763
+
764
+ - 404 (Not Found) - BaserowNotFoundError con contexto
765
+ - 400/422 (Validation) - Nombres vacíos, longitud, parámetros inválidos
766
+ - Comportamiento Baserow - Documentado lo que acepta vs rechaza
767
+
768
+ ### Categorías de Tests
769
+
770
+ #### Tests de Campos por Tipo
771
+
772
+ - `admin-field-basic.test.ts` - Campos básicos (text, number, boolean) + **10 error tests**
773
+ - `admin-field-text.test.ts` - Campos de texto (url, email, phone)
774
+ - `admin-field-numeric.test.ts` - Campos numéricos (number, rating)
775
+ - `admin-field-datetime.test.ts` - Campos de fecha y auditoría
776
+ - `admin-field-select.test.ts` - Campos de selección
777
+ - `admin-field-relations.test.ts` - Campos relacionales con retry logic
778
+ - `admin-field-advanced.test.ts` - Campos avanzados (file, autonumber, count, rollup, lookup) + **7 error tests**
779
+ - `admin-field-audit.test.ts` - Campos de auditoría automática
780
+
781
+ #### Tests CRUD Core
782
+
783
+ - `admin-database.test.ts` - CRUD databases + **8 error tests**
784
+ - `admin-table.test.ts` - CRUD tables + **9 error tests**
785
+ - `admin-row.test.ts` - CRUD rows (próximo: error handling)
786
+ - `admin-workspace.test.ts` - Gestión workspaces
787
+
788
+ #### Tests del PrismaMapper
789
+
790
+ - `prisma-mapper.test.ts` - **35+ tests** de transformaciones Prisma → Baserow
791
+
792
+ ### 📝 Variables de Entorno para Tests
793
+
794
+ ```env
795
+ BASEROW_URL=https://baserow.example.com
796
+ BASEROW_ADMIN_MAIL=admin@example.com
797
+ BASEROW_ADMIN_PASSWORD=your_password
798
+ BASEROW_DB_TOKEN=your_database_token
799
+ ```
800
+
801
+ ### 🔧 Configuración de Tests
802
+
803
+ Los tests están configurados para:
804
+
805
+ - **Cleanup automático**: Destrucción de recursos tras cada test
806
+ - **Retry logic**: Manejo de campos que se crean de forma asíncrona
807
+ - **Timeouts configurables**: Adaptados para diferentes entornos
808
+ - **Mock y real API**: Soporte para tests unitarios e integración
809
+
810
+ ## 🔗 Compatibilidad y Versiones
811
+
812
+ ### Baserow v1.35.3 - Totalmente Compatible
813
+
814
+ ✅ **Sin Breaking Changes** - Upgrade seguro desde v1.35.0+
815
+
816
+ **Mejoras Automáticas (sin código requerido):**
817
+
818
+ - 🚀 **Búsquedas optimizadas** (v1.35.2) - Queries con `search` más rápidas
819
+ - 🔍 **Filtros robustos** (v1.35.2) - Mejor confiabilidad en operadores complejos (`AND`, `OR`, `NOT`)
820
+ - 🔐 **Permisos expandidos** (v1.35.3) - Exportaciones disponibles para roles visualizador
821
+ - 🪝 **Webhooks retroactivos** (v1.35.2) - Compatible con webhooks para registros pasados
822
+
823
+ **Funcionalidades Nuevas de Baserow:**
824
+
825
+ - **Date Dependency** (v1.35.2) - Conexión automática entre fechas inicio/fin/duración
826
+ - ⚠️ Configuración via UI de Baserow (API no documentada públicamente aún)
827
+ - ✅ Nuestra librería puede leer/escribir campos relacionados normalmente
828
+ - **PostgreSQL Sync bidireccional** (v1.35.0) - Feature de servidor
829
+ - Usar @gzl10/baserow para sincronizar datos post-sync
830
+
831
+ **Análisis Completo:** Ver [docs/BASEROW_1.35.3_IMPACT_ANALYSIS.md](docs/BASEROW_1.35.3_IMPACT_ANALYSIS.md)
832
+
833
+ ---
834
+
835
+ ## 🔗 Enlaces
836
+
837
+ - [Baserow API Documentation](https://baserow.io/docs)
838
+ - [TypeScript Documentation](https://www.typescriptlang.org/docs)
839
+ - [Express.js](https://expressjs.com)
840
+
841
+ ## ☕ Support
842
+
843
+ <a href="https://www.buymeacoffee.com/gzl10g" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
844
+
845
+ ## 📄 Licencia
846
+
847
+ MIT License - Ver archivo [LICENSE](LICENSE)