@gzl10/baserow 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +435 -0
- package/README.md +847 -0
- package/dist/index.d.ts +8749 -0
- package/dist/index.js +11167 -0
- package/dist/index.js.map +1 -0
- package/package.json +91 -0
- package/src/BaserowClient.ts +501 -0
- package/src/ClientWithCreds.ts +545 -0
- package/src/ClientWithCredsWs.ts +852 -0
- package/src/ClientWithToken.ts +171 -0
- package/src/contexts/DatabaseClientContext.ts +114 -0
- package/src/contexts/DatabaseContext.ts +870 -0
- package/src/contexts/DatabaseTokenContext.ts +331 -0
- package/src/contexts/FieldContext.ts +399 -0
- package/src/contexts/RowContext.ts +99 -0
- package/src/contexts/TableClientContext.ts +291 -0
- package/src/contexts/TableContext.ts +1247 -0
- package/src/contexts/TableOnlyContext.ts +74 -0
- package/src/contexts/WorkspaceContext.ts +490 -0
- package/src/express/errors.ts +260 -0
- package/src/express/index.ts +69 -0
- package/src/express/middleware.ts +225 -0
- package/src/express/serializers.ts +314 -0
- package/src/index.ts +247 -0
- package/src/presets/performance.ts +262 -0
- package/src/services/AuthService.ts +472 -0
- package/src/services/DatabaseService.ts +246 -0
- package/src/services/DatabaseTokenService.ts +186 -0
- package/src/services/FieldService.ts +1543 -0
- package/src/services/RowService.ts +982 -0
- package/src/services/SchemaControlService.ts +420 -0
- package/src/services/TableService.ts +781 -0
- package/src/services/WorkspaceService.ts +113 -0
- package/src/services/core/BaseAuthClient.ts +111 -0
- package/src/services/core/BaseClient.ts +107 -0
- package/src/services/core/BaseService.ts +71 -0
- package/src/services/core/HttpService.ts +115 -0
- package/src/services/core/ValidationService.ts +149 -0
- package/src/types/auth.ts +177 -0
- package/src/types/core.ts +91 -0
- package/src/types/errors.ts +105 -0
- package/src/types/fields.ts +456 -0
- package/src/types/index.ts +222 -0
- package/src/types/requests.ts +333 -0
- package/src/types/responses.ts +50 -0
- package/src/types/schema.ts +446 -0
- package/src/types/tokens.ts +36 -0
- package/src/types.ts +11 -0
- package/src/utils/auth.ts +174 -0
- package/src/utils/axios.ts +647 -0
- package/src/utils/field-cache.ts +164 -0
- package/src/utils/httpFactory.ts +66 -0
- package/src/utils/jwt-decoder.ts +188 -0
- package/src/utils/jwtTokens.ts +50 -0
- package/src/utils/performance.ts +105 -0
- package/src/utils/prisma-mapper.ts +961 -0
- package/src/utils/validation.ts +463 -0
- package/src/validators/schema.ts +419 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { FieldService } from '../services/FieldService'
|
|
2
|
+
import { Field, Logger } from '../types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Metadata de campos con tipos y opciones select
|
|
6
|
+
*/
|
|
7
|
+
export interface FieldMetadata {
|
|
8
|
+
/** Mapa de nombre de campo → tipo de campo */
|
|
9
|
+
types: Record<string, string>
|
|
10
|
+
/** Mapa de nombre de campo → {valor: id} para campos select */
|
|
11
|
+
selectOptions: Record<string, Record<string, number>>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Cache centralizado para metadata de campos de tablas
|
|
16
|
+
*
|
|
17
|
+
* Proporciona un sistema de cache eficiente para tipos de campo y opciones
|
|
18
|
+
* de campos select/multiselect, permitiendo resolver automáticamente
|
|
19
|
+
* valores de texto a IDs de opción para filtros de Baserow.
|
|
20
|
+
*
|
|
21
|
+
* **Características:**
|
|
22
|
+
* - Cache por tabla (tableId) para evitar conflictos
|
|
23
|
+
* - Mapeo automático value → ID para campos select
|
|
24
|
+
* - Invalidación granular por tabla o completa
|
|
25
|
+
* - Logging opcional para debugging
|
|
26
|
+
* - Performance optimizada con Map interna
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const cache = new FieldCache(logger)
|
|
31
|
+
* const metadata = await cache.getFieldMetadata(fieldService, 123)
|
|
32
|
+
*
|
|
33
|
+
* // Tipos de campo: metadata.types['categoria'] // 'single_select'
|
|
34
|
+
* // Opciones select: metadata.selectOptions['categoria']['Premium'] // 45
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @since 1.2.0
|
|
38
|
+
*/
|
|
39
|
+
export class FieldCache {
|
|
40
|
+
private cache = new Map<number, FieldMetadata>()
|
|
41
|
+
private logger?: Logger
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Crear nueva instancia de FieldCache
|
|
45
|
+
*
|
|
46
|
+
* @param logger - Logger opcional para debugging y trazabilidad
|
|
47
|
+
*/
|
|
48
|
+
constructor(logger?: Logger) {
|
|
49
|
+
this.logger = logger
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Obtener metadata completa de campos para una tabla
|
|
54
|
+
*
|
|
55
|
+
* Retorna tipos de campo y opciones de select con cache automático.
|
|
56
|
+
* Si los datos no están en cache, hace fetch automático y los almacena.
|
|
57
|
+
*
|
|
58
|
+
* @param fieldService - Servicio de campos para hacer fetch si necesario
|
|
59
|
+
* @param tableId - ID numérico de la tabla
|
|
60
|
+
* @returns Promise con metadata completa de campos
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const metadata = await cache.getFieldMetadata(fieldService, 123)
|
|
65
|
+
*
|
|
66
|
+
* // Usar tipos
|
|
67
|
+
* const fieldType = metadata.types['categoria'] // 'single_select'
|
|
68
|
+
*
|
|
69
|
+
* // Resolver valor a ID
|
|
70
|
+
* const optionId = metadata.selectOptions['categoria']['Premium'] // 45
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
async getFieldMetadata(fieldService: FieldService, tableId: number): Promise<FieldMetadata> {
|
|
74
|
+
if (!this.cache.has(tableId)) {
|
|
75
|
+
const fields = await fieldService.findMany(tableId)
|
|
76
|
+
const metadata = this.buildMetadata(fields, tableId)
|
|
77
|
+
this.cache.set(tableId, metadata)
|
|
78
|
+
}
|
|
79
|
+
return this.cache.get(tableId)!
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Construir metadata a partir de array de campos
|
|
84
|
+
*
|
|
85
|
+
* @private
|
|
86
|
+
* @param fields - Array de campos de la tabla
|
|
87
|
+
* @param tableId - ID de la tabla (para logging)
|
|
88
|
+
* @returns Metadata construida
|
|
89
|
+
*/
|
|
90
|
+
private buildMetadata(fields: Field[], tableId: number): FieldMetadata {
|
|
91
|
+
const types: Record<string, string> = {}
|
|
92
|
+
const selectOptions: Record<string, Record<string, number>> = {}
|
|
93
|
+
|
|
94
|
+
for (const field of fields) {
|
|
95
|
+
// Mapear nombre → tipo
|
|
96
|
+
types[field.name] = field.type
|
|
97
|
+
|
|
98
|
+
// Para campos select, construir mapeo value → id
|
|
99
|
+
if (['single_select', 'multiple_select'].includes(field.type) && field.select_options) {
|
|
100
|
+
selectOptions[field.name] = Object.fromEntries(
|
|
101
|
+
field.select_options.map(option => [option.value, option.id]).filter(([, id]) => id !== undefined)
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Logging detallado para debugging
|
|
107
|
+
this.logger?.debug?.(`Field metadata cached for table ${tableId}`, {
|
|
108
|
+
fieldCount: fields.length,
|
|
109
|
+
selectFieldCount: Object.keys(selectOptions).length,
|
|
110
|
+
typeDistribution: Object.values(types).reduce(
|
|
111
|
+
(acc, type) => {
|
|
112
|
+
acc[type] = (acc[type] || 0) + 1
|
|
113
|
+
return acc
|
|
114
|
+
},
|
|
115
|
+
{} as Record<string, number>
|
|
116
|
+
),
|
|
117
|
+
selectFields: Object.keys(selectOptions)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
return { types, selectOptions }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Invalidar cache para una tabla específica o completo
|
|
125
|
+
*
|
|
126
|
+
* Útil cuando se sabe que los campos de una tabla han cambiado
|
|
127
|
+
* (campos agregados, eliminados, o opciones select modificadas).
|
|
128
|
+
*
|
|
129
|
+
* @param tableId - ID de tabla específica a invalidar. Si no se proporciona, limpia todo el cache
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* // Invalidar tabla específica
|
|
134
|
+
* cache.clearCache(123)
|
|
135
|
+
*
|
|
136
|
+
* // Limpiar todo el cache
|
|
137
|
+
* cache.clearCache()
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
clearCache(tableId?: number): void {
|
|
141
|
+
if (tableId !== undefined) {
|
|
142
|
+
const wasDeleted = this.cache.delete(tableId)
|
|
143
|
+
this.logger?.debug?.(`Cache cleared for table ${tableId}`, { wasDeleted })
|
|
144
|
+
} else {
|
|
145
|
+
const cacheSize = this.cache.size
|
|
146
|
+
this.cache.clear()
|
|
147
|
+
this.logger?.debug?.(`Full cache cleared`, { clearedTables: cacheSize })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Obtener estadísticas del cache
|
|
153
|
+
*
|
|
154
|
+
* Útil para debugging y monitoreo de performance.
|
|
155
|
+
*
|
|
156
|
+
* @returns Estadísticas del cache
|
|
157
|
+
*/
|
|
158
|
+
getCacheStats(): { cachedTables: number; tableIds: number[] } {
|
|
159
|
+
return {
|
|
160
|
+
cachedTables: this.cache.size,
|
|
161
|
+
tableIds: Array.from(this.cache.keys())
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory centralizado para creación de HttpClient
|
|
3
|
+
*
|
|
4
|
+
* Elimina duplicación de código en la inicialización de HttpClient
|
|
5
|
+
* proporcionando una fuente única de verdad para la configuración.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { HttpClient } from './axios'
|
|
12
|
+
import { PerformanceManager } from './performance'
|
|
13
|
+
import type { Logger, BaserowPerformanceOptions } from '../types'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Configuración para crear HttpClient
|
|
17
|
+
*/
|
|
18
|
+
export interface HttpClientFactoryConfig {
|
|
19
|
+
/** URL base de Baserow (ej: 'https://baserow.com') */
|
|
20
|
+
url: string
|
|
21
|
+
/** Token de autenticación (Database Token o JWT) */
|
|
22
|
+
token: string
|
|
23
|
+
/** Logger opcional para debug */
|
|
24
|
+
logger?: Logger
|
|
25
|
+
/** Configuración de performance opcional */
|
|
26
|
+
performance?: Partial<BaserowPerformanceOptions>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Crea una instancia de HttpClient con configuración estandarizada
|
|
31
|
+
*
|
|
32
|
+
* Centraliza la lógica de:
|
|
33
|
+
* - Sanitización de URL base
|
|
34
|
+
* - Configuración de performance (merge con defaults globales)
|
|
35
|
+
* - Configuración de token y logger
|
|
36
|
+
*
|
|
37
|
+
* @param config - Configuración del cliente HTTP
|
|
38
|
+
* @returns Instancia configurada de HttpClient
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Para Database Token
|
|
43
|
+
* const client = createHttpClient({
|
|
44
|
+
* url: 'https://baserow.com',
|
|
45
|
+
* token: 'db_token_123',
|
|
46
|
+
* logger: console
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* // Para JWT (durante login)
|
|
50
|
+
* const tempClient = createHttpClient({
|
|
51
|
+
* url: 'https://baserow.com',
|
|
52
|
+
* token: '', // Sin token inicial
|
|
53
|
+
* logger: console,
|
|
54
|
+
* performance: { timeout: 15000 }
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function createHttpClient(config: HttpClientFactoryConfig): HttpClient {
|
|
59
|
+
return new HttpClient({
|
|
60
|
+
baseURL: `${config.url.replace(/\/$/, '')}/api`,
|
|
61
|
+
token: config.token,
|
|
62
|
+
logger: config.logger,
|
|
63
|
+
// Combinar defaults globales con configuración específica de instancia
|
|
64
|
+
...PerformanceManager.merge(config.performance)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades para decodificar JWT tokens sin dependencias externas
|
|
3
|
+
*
|
|
4
|
+
* Proporciona funciones para extraer información de JWT tokens usando
|
|
5
|
+
* solo decodificación Base64 nativa de Node.js/Browser.
|
|
6
|
+
*
|
|
7
|
+
* **Características:**
|
|
8
|
+
* - Sin dependencias externas
|
|
9
|
+
* - Manejo robusto de errores
|
|
10
|
+
* - Compatible con Node.js y navegadores
|
|
11
|
+
* - Extrae claim `exp` (expiration) del payload
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const token = 'eyJhbGc...'
|
|
16
|
+
* const expiry = decodeJwtExpiry(token)
|
|
17
|
+
* if (expiry) {
|
|
18
|
+
* console.log('Token expira en:', expiry)
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { Logger } from '../types'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Decodifica el claim `exp` (expiration) de un JWT token
|
|
29
|
+
*
|
|
30
|
+
* Lee el payload del JWT token y extrae el timestamp de expiración
|
|
31
|
+
* sin validar la firma (solo para leer metadatos del token).
|
|
32
|
+
*
|
|
33
|
+
* **JWT Structure:**
|
|
34
|
+
* - JWT = `header.payload.signature` (3 partes separadas por `.`)
|
|
35
|
+
* - `payload` está en Base64URL encoding
|
|
36
|
+
* - `exp` claim es Unix timestamp en **segundos** (no milisegundos)
|
|
37
|
+
*
|
|
38
|
+
* **Implementación:**
|
|
39
|
+
* 1. Split token por `.`
|
|
40
|
+
* 2. Decodificar parte [1] (payload) desde Base64
|
|
41
|
+
* 3. Parsear JSON del payload
|
|
42
|
+
* 4. Extraer `exp` y convertir a Date (multiplicar por 1000 para ms)
|
|
43
|
+
*
|
|
44
|
+
* @param token - JWT token completo en formato string
|
|
45
|
+
* @param logger - Logger opcional para debugging
|
|
46
|
+
* @returns Date de expiración o undefined si no se puede decodificar
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // Token válido
|
|
51
|
+
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDkzOTY4MDB9.sig'
|
|
52
|
+
* const expiry = decodeJwtExpiry(token)
|
|
53
|
+
* // expiry = Date(2024-03-02T12:00:00.000Z)
|
|
54
|
+
*
|
|
55
|
+
* // Token sin exp claim
|
|
56
|
+
* const tokenNoExp = 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.sig'
|
|
57
|
+
* const expiry2 = decodeJwtExpiry(tokenNoExp)
|
|
58
|
+
* // expiry2 = undefined
|
|
59
|
+
*
|
|
60
|
+
* // Token malformado
|
|
61
|
+
* const badToken = 'invalid-token'
|
|
62
|
+
* const expiry3 = decodeJwtExpiry(badToken)
|
|
63
|
+
* // expiry3 = undefined
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @since 1.0.0
|
|
67
|
+
*/
|
|
68
|
+
export function decodeJwtExpiry(token: string, logger?: Logger): Date | undefined {
|
|
69
|
+
try {
|
|
70
|
+
// Validar que el token tenga formato JWT (3 partes separadas por puntos)
|
|
71
|
+
const parts = token.split('.')
|
|
72
|
+
if (parts.length !== 3) {
|
|
73
|
+
logger?.warn?.(`❌ JWT malformado: esperaba 3 partes, recibió ${parts.length}`)
|
|
74
|
+
return undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Decodificar payload (parte [1]) desde Base64
|
|
78
|
+
// atob() es global en navegadores y Node.js 16+
|
|
79
|
+
// Para Node.js < 16, usar Buffer.from(parts[1], 'base64').toString()
|
|
80
|
+
const payload = JSON.parse(
|
|
81
|
+
typeof atob !== 'undefined'
|
|
82
|
+
? atob(parts[1]) // Browser / Node.js 16+
|
|
83
|
+
: Buffer.from(parts[1], 'base64').toString() // Node.js < 16
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Extraer claim 'exp' (expiration time)
|
|
87
|
+
// exp está en formato Unix timestamp (segundos desde epoch)
|
|
88
|
+
if (payload.exp === undefined || payload.exp === null || typeof payload.exp !== 'number') {
|
|
89
|
+
logger?.warn?.('❌ JWT sin claim exp válido')
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Convertir Unix timestamp (segundos) a Date (milisegundos)
|
|
94
|
+
const expiryDate = new Date(payload.exp * 1000)
|
|
95
|
+
|
|
96
|
+
// Validar que la fecha sea válida
|
|
97
|
+
if (isNaN(expiryDate.getTime())) {
|
|
98
|
+
logger?.warn?.(`❌ Claim exp inválido: ${payload.exp}`)
|
|
99
|
+
return undefined
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
logger?.debug?.(`✅ JWT exp decodificado: ${expiryDate.toISOString()}`)
|
|
103
|
+
return expiryDate
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger?.warn?.('❌ Error decodificando JWT exp:', error)
|
|
106
|
+
return undefined
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Decodifica el payload completo de un JWT token
|
|
112
|
+
*
|
|
113
|
+
* Útil para debugging o para acceder a otros claims del token
|
|
114
|
+
* además de `exp`.
|
|
115
|
+
*
|
|
116
|
+
* @param token - JWT token completo en formato string
|
|
117
|
+
* @param logger - Logger opcional para debugging
|
|
118
|
+
* @returns Payload del JWT o undefined si no se puede decodificar
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const token = 'eyJhbGc...'
|
|
123
|
+
* const payload = decodeJwtPayload(token)
|
|
124
|
+
* console.log(payload)
|
|
125
|
+
* // { user_id: 123, exp: 1709396800, iat: 1709393200 }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @since 1.0.0
|
|
129
|
+
*/
|
|
130
|
+
export function decodeJwtPayload(token: string, logger?: Logger): Record<string, any> | undefined {
|
|
131
|
+
try {
|
|
132
|
+
const parts = token.split('.')
|
|
133
|
+
if (parts.length !== 3) {
|
|
134
|
+
logger?.warn?.(`❌ JWT malformado: esperaba 3 partes, recibió ${parts.length}`)
|
|
135
|
+
return undefined
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const payload = JSON.parse(
|
|
139
|
+
typeof atob !== 'undefined' ? atob(parts[1]) : Buffer.from(parts[1], 'base64').toString()
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return payload
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logger?.warn?.('❌ Error decodificando JWT payload:', error)
|
|
145
|
+
return undefined
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Verifica si un JWT token está expirado
|
|
151
|
+
*
|
|
152
|
+
* Compara el claim `exp` del token con el tiempo actual.
|
|
153
|
+
* Útil para validaciones rápidas sin decodificar manualmente.
|
|
154
|
+
*
|
|
155
|
+
* @param token - JWT token completo en formato string
|
|
156
|
+
* @param bufferMs - Buffer de tiempo en ms para considerar "casi expirado" (default: 0)
|
|
157
|
+
* @param logger - Logger opcional para debugging
|
|
158
|
+
* @returns true si está expirado, false si es válido, undefined si no se puede decodificar
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const token = 'eyJhbGc...'
|
|
163
|
+
*
|
|
164
|
+
* // Verificar si está expirado ahora
|
|
165
|
+
* if (isJwtExpired(token)) {
|
|
166
|
+
* console.log('Token expirado')
|
|
167
|
+
* }
|
|
168
|
+
*
|
|
169
|
+
* // Verificar si expira en los próximos 5 minutos
|
|
170
|
+
* const fiveMinutes = 5 * 60 * 1000
|
|
171
|
+
* if (isJwtExpired(token, fiveMinutes)) {
|
|
172
|
+
* console.log('Token expira pronto, renovar')
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @since 1.0.0
|
|
177
|
+
*/
|
|
178
|
+
export function isJwtExpired(token: string, bufferMs: number = 0, logger?: Logger): boolean | undefined {
|
|
179
|
+
const expiry = decodeJwtExpiry(token, logger)
|
|
180
|
+
if (!expiry) {
|
|
181
|
+
return undefined // No se pudo decodificar
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const now = Date.now()
|
|
185
|
+
const expiryWithBuffer = expiry.getTime() - bufferMs
|
|
186
|
+
|
|
187
|
+
return now >= expiryWithBuffer
|
|
188
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades centralizadas para manejo de JWT tokens
|
|
3
|
+
*
|
|
4
|
+
* Elimina duplicación de lógica de configuración de tokens JWT
|
|
5
|
+
* entre ClientWithCreds y ClientWithCredsWs.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { LoginResponse } from '../types'
|
|
12
|
+
import type { AuthService } from '../services/AuthService'
|
|
13
|
+
import { decodeJwtExpiry } from './jwt-decoder'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Configura los tokens JWT en un AuthService existente
|
|
17
|
+
*
|
|
18
|
+
* Centraliza la lógica de transferencia de tokens desde LoginResponse
|
|
19
|
+
* al AuthService interno, eliminando duplicación entre clientes.
|
|
20
|
+
*
|
|
21
|
+
* **Importante:** La expiración se decodifica del claim `exp` del JWT,
|
|
22
|
+
* no se asume un TTL hardcoded.
|
|
23
|
+
*
|
|
24
|
+
* @param authService - Instancia de AuthService donde configurar tokens
|
|
25
|
+
* @param loginResponse - Respuesta del login con tokens JWT
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // En ClientWithCreds.create()
|
|
30
|
+
* const admin = new ClientWithCreds(config, loginResponse)
|
|
31
|
+
* setupJwtTokens(admin.auth, loginResponse)
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function setupJwtTokens(authService: AuthService, loginResponse: LoginResponse): void {
|
|
35
|
+
// Acceder a propiedades privadas del AuthService
|
|
36
|
+
// Esto es necesario para transferir tokens del login temporal al AuthService final
|
|
37
|
+
const authServiceAny = authService as any
|
|
38
|
+
|
|
39
|
+
authServiceAny['accessToken'] = loginResponse.access_token
|
|
40
|
+
authServiceAny['refreshToken'] = loginResponse.refresh_token
|
|
41
|
+
|
|
42
|
+
// Decodificar expiración desde el claim 'exp' del JWT (estándar)
|
|
43
|
+
const expiry = decodeJwtExpiry(loginResponse.access_token)
|
|
44
|
+
if (!expiry) {
|
|
45
|
+
// Fallback conservador si no se puede decodificar
|
|
46
|
+
authServiceAny['tokenExpiry'] = new Date(Date.now() + 10 * 60 * 1000) // 10 min
|
|
47
|
+
} else {
|
|
48
|
+
authServiceAny['tokenExpiry'] = expiry
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { BaserowPerformanceOptions } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defaults centralizados de performance para toda la librería
|
|
5
|
+
*
|
|
6
|
+
* Fuente única de verdad para valores por defecto de configuración de performance.
|
|
7
|
+
* Usado por PerformanceManager y HttpClient para evitar duplicación.
|
|
8
|
+
*/
|
|
9
|
+
export const PERFORMANCE_DEFAULTS: BaserowPerformanceOptions = {
|
|
10
|
+
timeout: 30000,
|
|
11
|
+
retries: 3,
|
|
12
|
+
maxRequestsPerSecond: 10,
|
|
13
|
+
enableRateLimiting: true
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gestor centralizado de configuración de performance para toda la librería Baserow
|
|
18
|
+
*
|
|
19
|
+
* Proporciona una API unificada para gestionar la configuración global de performance
|
|
20
|
+
* que se aplicará a todas las instancias de clientes Baserow.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
*/
|
|
25
|
+
export class PerformanceManager {
|
|
26
|
+
/**
|
|
27
|
+
* Configuración global por defecto de performance
|
|
28
|
+
*/
|
|
29
|
+
private static globalDefaults: BaserowPerformanceOptions = { ...PERFORMANCE_DEFAULTS }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Establece la configuración global de performance
|
|
33
|
+
*
|
|
34
|
+
* @param defaults - Configuración parcial para sobrescribir defaults
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* PerformanceManager.setGlobal({
|
|
39
|
+
* timeout: 60000,
|
|
40
|
+
* retries: 5
|
|
41
|
+
* })
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
static setGlobal(defaults: Partial<BaserowPerformanceOptions>): void {
|
|
45
|
+
PerformanceManager.globalDefaults = {
|
|
46
|
+
...PerformanceManager.globalDefaults,
|
|
47
|
+
...defaults
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Obtiene la configuración global actual de performance
|
|
53
|
+
*
|
|
54
|
+
* @returns Copia de la configuración global actual
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const current = PerformanceManager.getGlobal()
|
|
59
|
+
* console.log('Timeout global:', current.timeout)
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
static getGlobal(): BaserowPerformanceOptions {
|
|
63
|
+
return { ...PerformanceManager.globalDefaults }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resetea la configuración global a los valores por defecto de la librería
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* PerformanceManager.reset()
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
static reset(): void {
|
|
75
|
+
PerformanceManager.globalDefaults = { ...PERFORMANCE_DEFAULTS }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Combina la configuración global con la configuración específica de instancia
|
|
80
|
+
*
|
|
81
|
+
* La configuración de instancia tiene prioridad sobre la global.
|
|
82
|
+
*
|
|
83
|
+
* @param instanceConfig - Configuración específica de la instancia (opcional)
|
|
84
|
+
* @returns Configuración final combinada
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // Solo configuración global
|
|
89
|
+
* const config1 = PerformanceManager.merge()
|
|
90
|
+
*
|
|
91
|
+
* // Configuración global + override de instancia
|
|
92
|
+
* const config2 = PerformanceManager.merge({
|
|
93
|
+
* retries: 1, // Override
|
|
94
|
+
* timeout: 5000 // Override
|
|
95
|
+
* // maxRequestsPerSecond y enableRateLimiting vienen de global
|
|
96
|
+
* })
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
static merge(instanceConfig?: Partial<BaserowPerformanceOptions>): BaserowPerformanceOptions {
|
|
100
|
+
return {
|
|
101
|
+
...PerformanceManager.globalDefaults,
|
|
102
|
+
...instanceConfig
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|