@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,260 @@
1
+ /**
2
+ * Manejo de errores Express para @gzl10/baserow
3
+ *
4
+ * Convertidores y middlewares para transformar BaserowError en respuestas HTTP apropiadas.
5
+ * Proporciona mapeo automático de errores de Baserow a códigos de estado HTTP estándar.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import express from 'express'
10
+ * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'
11
+ *
12
+ * const app = express()
13
+ *
14
+ * // Setup middlewares
15
+ * app.use('/api', baserowExpress.baserowAuth(config))
16
+ *
17
+ * // Routes
18
+ * app.get('/api/table/:id', async (req, res, next) => {
19
+ * try {
20
+ * const table = await req.baserow!.database(123).table(parseInt(req.params.id)).get()
21
+ * res.json(baserowExpress.serializeTable(table))
22
+ * } catch (error) {
23
+ * next(error) // Será manejado por errorHandler()
24
+ * }
25
+ * })
26
+ *
27
+ * // Error handler global (debe ir al final)
28
+ * app.use(baserowExpress.errorHandler())
29
+ * ```
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+
34
+ import { BaserowError, BaserowNotFoundError, BaserowValidationError, BaserowRateLimitError } from '../types'
35
+ import type { ExpressRequest, ExpressResponse, ExpressNextFunction } from './middleware'
36
+
37
+ /**
38
+ * Formato estándar para respuestas de error
39
+ */
40
+ export interface ErrorResponse {
41
+ error: string
42
+ code: string
43
+ status: number
44
+ details?: any
45
+ timestamp?: string
46
+ }
47
+
48
+ /**
49
+ * Convierte BaserowError a código de estado HTTP apropiado
50
+ *
51
+ * Mapea los tipos de error específicos de Baserow a códigos HTTP estándar:
52
+ * - BaserowNotFoundError → 404
53
+ * - BaserowValidationError → 400
54
+ * - BaserowRateLimitError → 429
55
+ * - BaserowError (generic auth) → 401
56
+ * - Otros BaserowError → 500
57
+ *
58
+ * @param error - Error de Baserow a convertir
59
+ * @returns Código de estado HTTP apropiado
60
+ */
61
+ export function getHttpStatusCode(error: BaserowError): number {
62
+ if (error instanceof BaserowNotFoundError) {
63
+ return 404
64
+ }
65
+
66
+ if (error instanceof BaserowValidationError) {
67
+ return 400
68
+ }
69
+
70
+ if (error instanceof BaserowRateLimitError) {
71
+ return 429
72
+ }
73
+
74
+ // Detectar errores de auth por status code o mensaje
75
+ if (error.status === 401 || error.message?.toLowerCase().includes('auth')) {
76
+ return 401
77
+ }
78
+
79
+ // Usar status del error si está disponible
80
+ if (error.status) {
81
+ return error.status
82
+ }
83
+
84
+ // Default para errores desconocidos
85
+ return 500
86
+ }
87
+
88
+ /**
89
+ * Convierte BaserowError a formato de respuesta Express estándar
90
+ *
91
+ * Crea un objeto de error normalizado que incluye toda la información relevante
92
+ * sin exponer detalles internos sensibles.
93
+ *
94
+ * @param error - Error de Baserow a serializar
95
+ * @returns Objeto de error serializado para respuesta HTTP
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * try {
100
+ * await someBaserowOperation()
101
+ * } catch (error) {
102
+ * const response = serializeError(error as BaserowError)
103
+ * res.status(response.status).json(response)
104
+ * }
105
+ * ```
106
+ */
107
+ export function serializeError(error: BaserowError): ErrorResponse {
108
+ const status = getHttpStatusCode(error)
109
+
110
+ const response: ErrorResponse = {
111
+ error: error.message || 'Unknown Baserow error',
112
+ code: error.constructor.name.replace('Error', '').toUpperCase(),
113
+ status,
114
+ timestamp: new Date().toISOString()
115
+ }
116
+
117
+ // Agregar detalles específicos para ciertos tipos de error
118
+ if (error instanceof BaserowValidationError) {
119
+ response.details = {
120
+ fieldErrors: error.fieldErrors
121
+ }
122
+ }
123
+
124
+ if (error instanceof BaserowRateLimitError) {
125
+ response.details = {
126
+ retryAfter: error.retryAfter
127
+ }
128
+ }
129
+
130
+ if (error instanceof BaserowNotFoundError) {
131
+ response.details = {
132
+ message: error.message
133
+ }
134
+ }
135
+
136
+ return response
137
+ }
138
+
139
+ /**
140
+ * Convierte Error genérico a formato de respuesta Express
141
+ *
142
+ * Maneja errores que no son específicos de Baserow (por ejemplo, errores de red,
143
+ * errores de validación de entrada, etc.).
144
+ *
145
+ * @param error - Error genérico a serializar
146
+ * @returns Objeto de error serializado
147
+ */
148
+ export function serializeGenericError(error: Error): ErrorResponse {
149
+ return {
150
+ error: error.message || 'Internal server error',
151
+ code: 'INTERNAL_ERROR',
152
+ status: 500,
153
+ timestamp: new Date().toISOString()
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Middleware Express para manejo automático de errores Baserow
159
+ *
160
+ * Convierte automáticamente errores de Baserow en respuestas HTTP apropiadas.
161
+ * Debe ser usado como el último middleware de la aplicación.
162
+ *
163
+ * @param options - Opciones de configuración del error handler
164
+ * @returns Middleware Express para manejo de errores
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * import { express as baserowExpress } from '@gzl10/baserow'
169
+ *
170
+ * // Al final de la configuración de middlewares
171
+ * app.use(baserowExpress.errorHandler({
172
+ * includeStack: process.env.NODE_ENV === 'development',
173
+ * logger: console
174
+ * }))
175
+ *
176
+ * // Ejemplo de error personalizado en ruta
177
+ * app.get('/api/custom', async (req, res, next) => {
178
+ * try {
179
+ * // Operación que puede fallar
180
+ * const data = await req.baserow!.database(999).table(123).rows.list()
181
+ * res.json(data)
182
+ * } catch (error) {
183
+ * next(error) // Automáticamente convierte BaserowError a HTTP response
184
+ * }
185
+ * })
186
+ * ```
187
+ */
188
+ export function errorHandler(
189
+ options: {
190
+ includeStack?: boolean
191
+ logger?: { error: (message: any) => void }
192
+ } = {}
193
+ ) {
194
+ return (error: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {
195
+ // Si ya se envió la respuesta, pasar al error handler por defecto
196
+ if ((res as any).headersSent) {
197
+ return next(error)
198
+ }
199
+
200
+ let response: ErrorResponse
201
+
202
+ if (error instanceof BaserowError) {
203
+ response = serializeError(error)
204
+ } else if (error instanceof Error) {
205
+ response = serializeGenericError(error)
206
+ } else {
207
+ // Error desconocido
208
+ response = {
209
+ error: 'Unknown error occurred',
210
+ code: 'UNKNOWN_ERROR',
211
+ status: 500,
212
+ timestamp: new Date().toISOString()
213
+ }
214
+ }
215
+
216
+ // Agregar stack trace en desarrollo
217
+ if (options.includeStack && error instanceof Error) {
218
+ ;(response as any).stack = error.stack
219
+ }
220
+
221
+ // Log del error
222
+ if (options.logger) {
223
+ options.logger.error(
224
+ 'Baserow API Error: ' +
225
+ JSON.stringify({
226
+ error: response,
227
+ url: (req as any).url,
228
+ method: (req as any).method,
229
+ stack: error instanceof Error ? error.stack : undefined
230
+ })
231
+ )
232
+ }
233
+
234
+ res.status(response.status).json(response)
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Handler de errores simplificado para casos básicos
240
+ *
241
+ * Versión simplificada del error handler que solo maneja errores de Baserow
242
+ * sin opciones de configuración adicionales.
243
+ *
244
+ * @returns Middleware Express básico para errores
245
+ */
246
+ export function simpleErrorHandler() {
247
+ return (error: any, _req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {
248
+ if ((res as any).headersSent) {
249
+ return next(error)
250
+ }
251
+
252
+ if (error instanceof BaserowError) {
253
+ const response = serializeError(error)
254
+ return res.status(response.status).json(response)
255
+ }
256
+
257
+ // Para errores no-Baserow, pasar al siguiente handler
258
+ next(error)
259
+ }
260
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Utilidades Express para @gzl10/baserow
3
+ *
4
+ * Colección de middlewares, error handlers y serializadores opcionales
5
+ * para facilitar la integración con Express sin crear dependencias directas.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { BaserowClient, express } from '@gzl10/baserow'
10
+ *
11
+ * const client = new BaserowClient({ url, token })
12
+ * const app = express()
13
+ *
14
+ * // Setup middlewares
15
+ * app.use('/api', express.baserowContext(client))
16
+ * app.use('/api', express.requireAuth())
17
+ *
18
+ * // Routes
19
+ * app.get('/api/tables/:tableId/rows', async (req, res, next) => {
20
+ * try {
21
+ * const rows = await req.baserow!.database(123).table(456).rows.list()
22
+ * res.json(express.serializePaginated(rows))
23
+ * } catch (error) {
24
+ * next(error)
25
+ * }
26
+ * })
27
+ *
28
+ * // Error handling
29
+ * app.use(express.errorHandler())
30
+ * ```
31
+ *
32
+ * @since 1.0.0
33
+ */
34
+
35
+ // Middlewares
36
+ export {
37
+ baserowContext,
38
+ baserowAuth,
39
+ requireAuth,
40
+ baserowCleanup,
41
+ type ExpressRequest,
42
+ type ExpressResponse,
43
+ type ExpressNextFunction,
44
+ type ExpressMiddleware
45
+ } from './middleware'
46
+
47
+ // Error handling
48
+ export {
49
+ errorHandler,
50
+ simpleErrorHandler,
51
+ serializeError,
52
+ serializeGenericError,
53
+ getHttpStatusCode,
54
+ type ErrorResponse
55
+ } from './errors'
56
+
57
+ // Serializadores
58
+ export {
59
+ serializePaginated,
60
+ serializeRowsWithMeta,
61
+ serializeTable,
62
+ serializeDatabase,
63
+ serializeWorkspace,
64
+ cleanField,
65
+ createSuccessResponse,
66
+ serializeQueryOptions,
67
+ type PaginatedResponse,
68
+ type RowsResponse
69
+ } from './serializers'
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Middlewares Express opcionales para @gzl10/baserow
3
+ *
4
+ * Utilidades que facilitan la integración con Express sin crear dependencias directas.
5
+ * Los tipos Express son inferidos dinámicamente para evitar dependency en @types/express.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import express from 'express'
10
+ * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'
11
+ *
12
+ * const app = express()
13
+ * const client = await BaserowClient.create({ url, token })
14
+ *
15
+ * // Attach client to request
16
+ * app.use('/api', baserowExpress.baserowContext(client))
17
+ *
18
+ * // Auto-create client per request with auth from headers
19
+ * app.use('/api', baserowExpress.baserowAuth({
20
+ * url: process.env.BASEROW_URL!,
21
+ * token: process.env.BASEROW_TOKEN!
22
+ * }))
23
+ *
24
+ * // Routes now have access to req.baserow
25
+ * app.get('/api/rows', async (req, res) => {
26
+ * const rows = await req.baserow!.database(123).table(456).rows.list()
27
+ * res.json(rows)
28
+ * })
29
+ * ```
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+
34
+ import type { BaserowConfig } from '../types'
35
+ import { ClientWithToken } from '../ClientWithToken'
36
+
37
+ /**
38
+ * Tipos Express mínimos sin dependencias
39
+ */
40
+ export interface ExpressRequest {
41
+ [key: string]: any
42
+ headers: { [key: string]: string | string[] | undefined }
43
+ baserow?: ClientWithToken
44
+ }
45
+
46
+ export interface ExpressResponse {
47
+ status(code: number): ExpressResponse
48
+ json(body: any): ExpressResponse
49
+ send(body?: any): ExpressResponse
50
+ }
51
+
52
+ export interface ExpressNextFunction {
53
+ (error?: any): void
54
+ }
55
+
56
+ export type ExpressMiddleware = (
57
+ req: ExpressRequest,
58
+ res: ExpressResponse,
59
+ next: ExpressNextFunction
60
+ ) => void | Promise<void>
61
+
62
+ /**
63
+ * Middleware para attachar cliente Baserow al request
64
+ *
65
+ * Agrega el cliente a `req.baserow` para uso en routes subsecuentes.
66
+ * El cliente debe ser creado externamente y reutilizado.
67
+ *
68
+ * @param client - Cliente Baserow configurado
69
+ * @returns Middleware Express
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * import { BaserowClient, express as baserowExpress } from '@gzl10/baserow'
74
+ *
75
+ * const client = await BaserowClient.create({ url, token })
76
+ * app.use('/api', baserowExpress.baserowContext(client))
77
+ *
78
+ * // En las rutas
79
+ * app.get('/api/tables', async (req, res) => {
80
+ * const tables = await req.baserow!.database(123).tables.findMany()
81
+ * res.json(tables)
82
+ * })
83
+ * ```
84
+ */
85
+ export function baserowContext(client: ClientWithToken): ExpressMiddleware {
86
+ return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {
87
+ req.baserow = client
88
+ next()
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Middleware para auto-crear cliente Baserow por request
94
+ *
95
+ * Crea un nuevo cliente para cada request basado en la configuración.
96
+ * Útil cuando cada request puede tener diferentes tokens o configuraciones.
97
+ *
98
+ * @param config - Configuración base de Baserow
99
+ * @returns Middleware Express
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { express as baserowExpress } from '@gzl10/baserow'
104
+ *
105
+ * app.use('/api', baserowExpress.baserowAuth({
106
+ * url: process.env.BASEROW_URL!,
107
+ * token: process.env.BASEROW_TOKEN! // Puede ser sobrescrito por headers
108
+ * }))
109
+ *
110
+ * // Cliente auto-creado por request disponible en req.baserow
111
+ * app.get('/api/data', async (req, res) => {
112
+ * const rows = await req.baserow!.database(123).table(456).rows.list()
113
+ * res.json(rows)
114
+ * })
115
+ * ```
116
+ */
117
+ export function baserowAuth(config: BaserowConfig): ExpressMiddleware {
118
+ return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {
119
+ try {
120
+ // Permitir override del token vía headers
121
+ const authHeader = req.headers.authorization as string
122
+ const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : config.token
123
+
124
+ const clientConfig: BaserowConfig = {
125
+ ...config,
126
+ token
127
+ }
128
+
129
+ req.baserow = new ClientWithToken(clientConfig)
130
+ next()
131
+ } catch (error) {
132
+ next(error)
133
+ }
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Middleware para requerir autenticación Baserow
139
+ *
140
+ * Valida que existe un cliente Baserow válido en el request.
141
+ * Debe usarse después de `baserowContext` o `baserowAuth`.
142
+ *
143
+ * @returns Middleware Express
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * import { express as baserowExpress } from '@gzl10/baserow'
148
+ *
149
+ * app.use('/api', baserowExpress.baserowAuth(config))
150
+ * app.use('/api', baserowExpress.requireAuth())
151
+ *
152
+ * app.get('/api/protected', async (req, res) => {
153
+ * // req.baserow está garantizado aquí
154
+ * const data = await req.baserow!.database(123).table(456).rows.list()
155
+ * res.json(data)
156
+ * })
157
+ * ```
158
+ */
159
+ export function requireAuth(): ExpressMiddleware {
160
+ return async (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {
161
+ try {
162
+ if (!req.baserow) {
163
+ res.status(401).json({
164
+ error: 'Baserow client not found. Use baserowAuth() middleware first.',
165
+ code: 'BASEROW_CLIENT_MISSING'
166
+ })
167
+ return
168
+ }
169
+
170
+ // Opcional: verificar conectividad
171
+ const isConnected = await req.baserow.health()
172
+ if (!isConnected) {
173
+ res.status(503).json({
174
+ error: 'Baserow server not accessible',
175
+ code: 'BASEROW_UNAVAILABLE'
176
+ })
177
+ return
178
+ }
179
+
180
+ next()
181
+ } catch (error) {
182
+ next(error)
183
+ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Middleware para cleanup automático de recursos Baserow
189
+ *
190
+ * Limpia el cliente Baserow al final del request para evitar memory leaks.
191
+ * Especialmente útil cuando se usan clientes per-request.
192
+ *
193
+ * @returns Middleware Express
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * import { express as baserowExpress } from '@gzl10/baserow'
198
+ *
199
+ * app.use('/api', baserowExpress.baserowAuth(config))
200
+ * // ... otras rutas ...
201
+ * app.use('/api', baserowExpress.baserowCleanup()) // Debe ir al final
202
+ * ```
203
+ */
204
+ export function baserowCleanup(): ExpressMiddleware {
205
+ return (req: ExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => {
206
+ // Cleanup después de que la respuesta se envíe
207
+ if (req.baserow) {
208
+ const cleanup = () => {
209
+ try {
210
+ req.baserow?.destroy()
211
+ delete req.baserow
212
+ } catch (error) {
213
+ // Log error pero no fallar el request
214
+ // eslint-disable-next-line no-console
215
+ console.warn('Error cleaning up Baserow client:', error)
216
+ }
217
+ }
218
+
219
+ // Cleanup en el próximo tick después de enviar response
220
+ process.nextTick(cleanup)
221
+ }
222
+
223
+ next()
224
+ }
225
+ }