@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
@@ -0,0 +1,463 @@
1
+ /**
2
+ * Utilidades de validación para la librería @gzl10/baserow
3
+ *
4
+ * Proporciona funciones de validación reutilizables para parámetros,
5
+ * configuraciones y datos de entrada. Todas las funciones lanzan
6
+ * BaserowValidationError con mensajes estructurados para facilitar
7
+ * el debugging y manejo de errores.
8
+ *
9
+ * **Funciones disponibles:**
10
+ * - Validación de campos requeridos y tipos básicos
11
+ * - Validación de URLs y tokens
12
+ * - Sanitización de nombres de campos
13
+ * - Validación de tipos de campos de Baserow
14
+ * - Utilidades para procesamiento bulk (chunking)
15
+ *
16
+ * @since 1.0.0
17
+ */
18
+
19
+ import { BaserowValidationError } from '../types'
20
+
21
+ /**
22
+ * Valida que un valor no sea null, undefined o string vacío
23
+ *
24
+ * Función genérica que garantiza que un valor requerido esté presente.
25
+ * Utilizada en toda la librería para validar parámetros obligatorios.
26
+ *
27
+ * @template T - Tipo del valor a validar
28
+ * @param value - Valor a validar
29
+ * @param fieldName - Nombre del campo para mensajes de error
30
+ * @returns El valor validado (mismo tipo)
31
+ *
32
+ * @throws {BaserowValidationError} Si el valor es null, undefined o string vacío
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // Validar parámetros requeridos
37
+ * const name = validateRequired(userInput.name, 'name')
38
+ * const id = validateRequired(params.id, 'table ID')
39
+ *
40
+ * // Usada internamente en servicios
41
+ * validateRequired(tableId, 'table ID') // Lanza error si tableId es null/undefined
42
+ * ```
43
+ *
44
+ * @since 1.0.0
45
+ */
46
+ export function validateRequired<T>(value: T, fieldName: string): T {
47
+ if (value === undefined || value === null || value === '') {
48
+ throw new BaserowValidationError(`${fieldName} is required`, {
49
+ [fieldName]: [`${fieldName} is required`]
50
+ })
51
+ }
52
+ return value
53
+ }
54
+
55
+ /**
56
+ * Valida que un número sea positivo (mayor que 0)
57
+ *
58
+ * Utilizada para validar IDs, cantidades, tamaños de página y otros
59
+ * valores numéricos que deben ser positivos en la API de Baserow.
60
+ *
61
+ * @param value - Número a validar
62
+ * @param fieldName - Nombre del campo para mensajes de error
63
+ * @returns El número validado
64
+ *
65
+ * @throws {BaserowValidationError} Si no es un número o es menor/igual que 0
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // Validar IDs
70
+ * const tableId = validatePositiveNumber(params.tableId, 'table ID')
71
+ * const fieldId = validatePositiveNumber(data.fieldId, 'field ID')
72
+ *
73
+ * // Validar opciones de paginación
74
+ * const pageSize = validatePositiveNumber(options.size, 'page size')
75
+ * ```
76
+ *
77
+ * @since 1.0.0
78
+ */
79
+ export function validatePositiveNumber(value: number, fieldName: string): number {
80
+ if (typeof value !== 'number' || value <= 0) {
81
+ throw new BaserowValidationError(`${fieldName} must be a positive number`, {
82
+ [fieldName]: [`${fieldName} must be a positive number`]
83
+ })
84
+ }
85
+ return value
86
+ }
87
+
88
+ /**
89
+ * Valida que un valor sea un string con longitud mínima
90
+ *
91
+ * Verifica tipo string y longitud mínima configurable.
92
+ * Utilizada para validar nombres, descripciones y otros campos de texto.
93
+ *
94
+ * @param value - Valor a validar
95
+ * @param fieldName - Nombre del campo para mensajes de error
96
+ * @param minLength - Longitud mínima requerida (default: 1)
97
+ * @returns El string validado
98
+ *
99
+ * @throws {BaserowValidationError} Si no es string o no cumple longitud mínima
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * // Validar nombres de recursos
104
+ * const tableName = validateString(data.name, 'table name')
105
+ * const fieldName = validateString(field.name, 'field name')
106
+ *
107
+ * // Validar con longitud mínima específica
108
+ * const description = validateString(data.desc, 'description', 10)
109
+ * ```
110
+ *
111
+ * @since 1.0.0
112
+ */
113
+ export function validateString(value: any, fieldName: string, minLength = 1): string {
114
+ if (typeof value !== 'string' || value.length < minLength) {
115
+ throw new BaserowValidationError(`${fieldName} must be a string with at least ${minLength} character(s)`, {
116
+ [fieldName]: [`${fieldName} must be a string with at least ${minLength} character(s)`]
117
+ })
118
+ }
119
+ return value
120
+ }
121
+
122
+ /**
123
+ * Valida que un string sea una URL válida
124
+ *
125
+ * Utiliza el constructor URL nativo de JavaScript para validar formato.
126
+ * Soporta todos los protocolos válidos (http, https, ftp, etc.).
127
+ *
128
+ * @param url - String URL a validar
129
+ * @param fieldName - Nombre del campo para mensajes de error (default: 'url')
130
+ * @returns La URL validada
131
+ *
132
+ * @throws {BaserowValidationError} Si el formato de URL es inválido
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * // Validar URLs de configuración
137
+ * const baseUrl = validateUrl(config.url, 'Baserow URL')
138
+ * const webhookUrl = validateUrl(data.webhook_url, 'webhook URL')
139
+ *
140
+ * // Validar campos URL de Baserow
141
+ * const siteUrl = validateUrl(fieldData.url, 'website URL')
142
+ * ```
143
+ *
144
+ * @since 1.0.0
145
+ */
146
+ export function validateUrl(url: string, fieldName: string = 'url'): string {
147
+ try {
148
+ new URL(url)
149
+ return url
150
+ } catch {
151
+ throw new BaserowValidationError(`${fieldName} must be a valid URL`, {
152
+ [fieldName]: [`${fieldName} must be a valid URL`]
153
+ })
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Verifica si un string es una URL válida sin lanzar errores
159
+ *
160
+ * Versión no-throw de validateUrl() que retorna boolean.
161
+ * Útil para validaciones condicionales y filtros.
162
+ *
163
+ * @param url - String URL a verificar
164
+ * @returns true si es URL válida, false si no
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * // Verificación condicional
169
+ * if (isValidUrl(userInput)) {
170
+ * // Procesar como URL
171
+ * } else {
172
+ * // Manejar como texto plano
173
+ * }
174
+ *
175
+ * // Filtrar URLs válidas
176
+ * const validUrls = urls.filter(isValidUrl)
177
+ * ```
178
+ *
179
+ * @since 1.0.0
180
+ */
181
+ export function isValidUrl(url: string): boolean {
182
+ try {
183
+ new URL(url)
184
+ return true
185
+ } catch {
186
+ return false
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Valida un token de autenticación (Database Token o JWT)
192
+ *
193
+ * Verifica que el token no esté vacío y remueve espacios extra.
194
+ * Soporta tanto Database Tokens como JWT tokens de Baserow.
195
+ *
196
+ * @param token - Token a validar
197
+ * @returns El token validado y sanitizado (sin espacios)
198
+ *
199
+ * @throws {BaserowValidationError} Si el token está vacío o no es string
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * // Validar tokens de configuración
204
+ * const dbToken = validateToken(config.token)
205
+ * const jwtToken = validateToken(process.env.BASEROW_JWT)
206
+ *
207
+ * // Se auto-sanitiza
208
+ * const clean = validateToken(' token123 ') // returns 'token123'
209
+ * ```
210
+ *
211
+ * @since 1.0.0
212
+ */
213
+ export function validateToken(token: string): string {
214
+ if (!token || typeof token !== 'string' || token.trim().length === 0) {
215
+ throw new BaserowValidationError('Token is required and cannot be empty', {
216
+ token: ['Token is required and cannot be empty']
217
+ })
218
+ }
219
+ return token.trim()
220
+ }
221
+
222
+ /**
223
+ * Valida la configuración completa de un cliente Baserow
224
+ *
225
+ * Valida que la configuración tenga estructura correcta con URL y token válidos.
226
+ * Utilizada por todos los clientes (BaserowClient, BaserowAdmin*) durante la inicialización.
227
+ *
228
+ * @param config - Configuración a validar
229
+ * @returns void (lanza error si es inválida)
230
+ *
231
+ * @throws {BaserowValidationError} Si faltan campos requeridos o son inválidos
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * // Validación en constructores de clientes
236
+ * validateConfig({
237
+ * url: 'https://baserow.example.com',
238
+ * token: 'valid-token-123'
239
+ * }) // OK
240
+ *
241
+ * validateConfig({
242
+ * url: 'invalid-url',
243
+ * token: ''
244
+ * }) // Lanza BaserowValidationError
245
+ * ```
246
+ *
247
+ * @since 1.0.0
248
+ */
249
+ export function validateConfig(config: any): void {
250
+ if (!config || typeof config !== 'object') {
251
+ throw new BaserowValidationError('Config must be an object', {
252
+ config: ['Config must be an object']
253
+ })
254
+ }
255
+
256
+ validateRequired(config.url, 'url')
257
+ validateRequired(config.token, 'token')
258
+
259
+ if (!isValidUrl(config.url)) {
260
+ throw new BaserowValidationError('URL must be a valid URL', {
261
+ url: ['URL must be a valid URL']
262
+ })
263
+ }
264
+
265
+ validateToken(config.token)
266
+ }
267
+
268
+ /**
269
+ * Sanitiza y valida nombres de campos de Baserow
270
+ *
271
+ * Aplica las reglas de nombres de campos de Baserow:
272
+ * - Debe ser string no vacío
273
+ * - Máximo 255 caracteres
274
+ * - Remueve espacios al inicio y final
275
+ *
276
+ * @param name - Nombre de campo a sanitizar
277
+ * @returns El nombre sanitizado y validado
278
+ *
279
+ * @throws {BaserowValidationError} Si el nombre es inválido
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * // Sanitizar nombres de entrada del usuario
284
+ * const fieldName = sanitizeFieldName(' Nombre del Campo ') // 'Nombre del Campo'
285
+ * const cleanName = sanitizeFieldName(userInput.fieldName)
286
+ *
287
+ * // Validar antes de crear campos
288
+ * const safeName = sanitizeFieldName(data.name)
289
+ * await fieldService.createTextField(tableId, safeName)
290
+ * ```
291
+ *
292
+ * @since 1.0.0
293
+ */
294
+ export function sanitizeFieldName(name: string): string {
295
+ if (typeof name !== 'string' || name == null) {
296
+ throw new BaserowValidationError('Field name must be a string', {
297
+ name: ['Field name must be a string']
298
+ })
299
+ }
300
+
301
+ // Eliminar espacios al inicio y final
302
+ name = name.trim()
303
+
304
+ if (name.length === 0) {
305
+ throw new BaserowValidationError('Field name cannot be empty', {
306
+ name: ['Field name cannot be empty']
307
+ })
308
+ }
309
+
310
+ if (name.length > 255) {
311
+ throw new BaserowValidationError('Field name cannot exceed 255 characters', {
312
+ name: ['Field name cannot exceed 255 characters']
313
+ })
314
+ }
315
+
316
+ return name
317
+ }
318
+
319
+ /**
320
+ * Divide un array en chunks (trozos) de tamaño específico
321
+ *
322
+ * Utilizada para operaciones bulk optimizadas que procesan datos en lotes
323
+ * para evitar timeouts y limitar el uso de memoria. Esencial para
324
+ * createBulk(), updateBulk() y deleteBulk().
325
+ *
326
+ * @template T - Tipo de elementos del array
327
+ * @param array - Array a dividir en chunks
328
+ * @param chunkSize - Tamaño de cada chunk (debe ser positivo)
329
+ * @returns Array de arrays (chunks) con el tamaño especificado
330
+ *
331
+ * @throws {BaserowValidationError} Si chunkSize es menor o igual que 0
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * // Procesar rows en lotes de 50
336
+ * const rows = [...] // 1000 rows
337
+ * const chunks = chunkArray(rows, 50) // [[50 rows], [50 rows], ...]
338
+ *
339
+ * for (const chunk of chunks) {
340
+ * await rowService.createBulk(tableId, chunk)
341
+ * }
342
+ *
343
+ * // Procesar IDs para delete bulk
344
+ * const ids = [1, 2, 3, ..., 100]
345
+ * const idChunks = chunkArray(ids, 25) // [[25 IDs], [25 IDs], ...]
346
+ * ```
347
+ *
348
+ * @since 1.0.0
349
+ */
350
+ export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
351
+ if (chunkSize <= 0) {
352
+ throw new BaserowValidationError('Chunk size must be positive', {
353
+ chunkSize: ['Chunk size must be positive']
354
+ })
355
+ }
356
+
357
+ const chunks: T[][] = []
358
+ for (let i = 0; i < array.length; i += chunkSize) {
359
+ chunks.push(array.slice(i, i + chunkSize))
360
+ }
361
+ return chunks
362
+ }
363
+
364
+ /**
365
+ * Crea una pausa (delay) asíncrona por un tiempo especificado
366
+ *
367
+ * Utilizada para retry logic, rate limiting manual y esperas
368
+ * entre operaciones. Especialmente útil en tests y operaciones
369
+ * que requieren delays entre requests.
370
+ *
371
+ * @param ms - Millisegundos a esperar
372
+ * @returns Promise que se resuelve después del delay
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * // Esperar entre operaciones
377
+ * await createRow(data1)
378
+ * await delay(1000) // Esperar 1 segundo
379
+ * await createRow(data2)
380
+ *
381
+ * // Retry logic con delay
382
+ * for (let i = 0; i < 3; i++) {
383
+ * try {
384
+ * return await operation()
385
+ * } catch (error) {
386
+ * if (i < 2) await delay(1000 * (i + 1)) // Backoff: 1s, 2s
387
+ * }
388
+ * }
389
+ * ```
390
+ *
391
+ * @since 1.0.0
392
+ */
393
+ export function delay(ms: number): Promise<void> {
394
+ return new Promise(resolve => setTimeout(resolve, ms))
395
+ }
396
+
397
+ const VALID_FIELD_TYPES = [
398
+ 'text',
399
+ 'long_text',
400
+ 'url',
401
+ 'email',
402
+ 'phone_number',
403
+ 'number',
404
+ 'rating',
405
+ 'boolean',
406
+ 'date',
407
+ 'last_modified',
408
+ 'last_modified_by',
409
+ 'created_on',
410
+ 'created_by',
411
+ 'single_select',
412
+ 'multiple_select',
413
+ 'file',
414
+ 'multiple_collaborators',
415
+ 'link_row',
416
+ 'count',
417
+ 'rollup',
418
+ 'lookup',
419
+ 'formula',
420
+ 'autonumber'
421
+ ] as const
422
+
423
+ /**
424
+ * Valida que un tipo de campo sea soportado por Baserow
425
+ *
426
+ * Verifica que el tipo esté en la lista de 21 tipos soportados por la librería.
427
+ * Lista actualizada con los nuevos tipos avanzados (file, autonumber, count, rollup, lookup).
428
+ *
429
+ * **Tipos soportados (21/22):**
430
+ * - **Texto**: text, long_text, url, email, phone_number
431
+ * - **Numéricos**: number, rating, boolean
432
+ * - **Fecha/Auditoría**: date, last_modified, last_modified_by, created_on, created_by
433
+ * - **Selección**: single_select, multiple_select
434
+ * - **Relacionales**: link_row, formula
435
+ * - **Avanzados**: file, autonumber, count, rollup, lookup
436
+ * - **Otros**: multiple_collaborators
437
+ *
438
+ * @param type - Tipo de campo a validar
439
+ * @param fieldName - Nombre del campo para mensajes de error (default: 'type')
440
+ * @returns El tipo validado
441
+ *
442
+ * @throws {BaserowValidationError} Si el tipo no está soportado
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * // Validar antes de crear campos
447
+ * const fieldType = validateFieldType('text', 'field type')
448
+ * const advancedType = validateFieldType('rollup', 'rollup field type')
449
+ *
450
+ * // Usar en FieldService
451
+ * validateFieldType(data.type) // Lanza error si no es soportado
452
+ * ```
453
+ *
454
+ * @since 1.0.0
455
+ */
456
+ export function validateFieldType(type: string, fieldName: string = 'type'): string {
457
+ if (!VALID_FIELD_TYPES.includes(type as any)) {
458
+ throw new BaserowValidationError(`Invalid field type: ${type}`, {
459
+ [fieldName]: [`Field type must be one of: ${VALID_FIELD_TYPES.join(', ')}`]
460
+ })
461
+ }
462
+ return type
463
+ }