@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
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)
|