@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,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validadores Zod para el sistema de schemas
|
|
3
|
+
*
|
|
4
|
+
* Proporciona validación robusta de schemas antes de aplicarlos,
|
|
5
|
+
* previniendo errores y asegurando consistencia en los datos.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from 'zod'
|
|
10
|
+
import type {
|
|
11
|
+
DatabaseSchema,
|
|
12
|
+
TableSchema,
|
|
13
|
+
FieldSchema,
|
|
14
|
+
RelationshipSchema,
|
|
15
|
+
LoadSchemaOptions,
|
|
16
|
+
SelectOption
|
|
17
|
+
} from '../types/schema'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Colores válidos para opciones de select
|
|
21
|
+
*/
|
|
22
|
+
const selectOptionColors = [
|
|
23
|
+
'blue',
|
|
24
|
+
'green',
|
|
25
|
+
'red',
|
|
26
|
+
'yellow',
|
|
27
|
+
'orange',
|
|
28
|
+
'purple',
|
|
29
|
+
'pink',
|
|
30
|
+
'brown',
|
|
31
|
+
'gray',
|
|
32
|
+
'dark_blue',
|
|
33
|
+
'dark_green',
|
|
34
|
+
'dark_red',
|
|
35
|
+
'dark_yellow',
|
|
36
|
+
'dark_orange',
|
|
37
|
+
'dark_purple',
|
|
38
|
+
'dark_pink',
|
|
39
|
+
'dark_brown',
|
|
40
|
+
'dark_gray'
|
|
41
|
+
] as const
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Tipos de campos válidos de Baserow
|
|
45
|
+
*/
|
|
46
|
+
const fieldTypes = [
|
|
47
|
+
// Texto
|
|
48
|
+
'text',
|
|
49
|
+
'long_text',
|
|
50
|
+
'url',
|
|
51
|
+
'email',
|
|
52
|
+
'phone_number',
|
|
53
|
+
// Numéricos
|
|
54
|
+
'number',
|
|
55
|
+
'rating',
|
|
56
|
+
// Fecha
|
|
57
|
+
'date',
|
|
58
|
+
'last_modified',
|
|
59
|
+
'created_on',
|
|
60
|
+
'last_modified_by',
|
|
61
|
+
'created_by',
|
|
62
|
+
// Selección
|
|
63
|
+
'single_select',
|
|
64
|
+
'multiple_select',
|
|
65
|
+
// Boolean
|
|
66
|
+
'boolean',
|
|
67
|
+
// Relación
|
|
68
|
+
'link_row',
|
|
69
|
+
'formula',
|
|
70
|
+
// Avanzados
|
|
71
|
+
'file',
|
|
72
|
+
'autonumber',
|
|
73
|
+
'count',
|
|
74
|
+
'rollup',
|
|
75
|
+
'lookup'
|
|
76
|
+
] as const
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Validador para opciones de select
|
|
80
|
+
*/
|
|
81
|
+
export const SelectOptionValidator = z.object({
|
|
82
|
+
value: z.string().min(1, 'El valor de la opción no puede estar vacío'),
|
|
83
|
+
color: z.enum(selectOptionColors, {
|
|
84
|
+
errorMap: () => ({ message: 'Color de opción no válido' })
|
|
85
|
+
})
|
|
86
|
+
}) satisfies z.ZodType<SelectOption>
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validadores para configuraciones específicas de campos
|
|
90
|
+
*/
|
|
91
|
+
export const FieldConfigValidators = {
|
|
92
|
+
text: z
|
|
93
|
+
.object({
|
|
94
|
+
default: z.string().optional(),
|
|
95
|
+
placeholder: z.string().optional()
|
|
96
|
+
})
|
|
97
|
+
.optional(),
|
|
98
|
+
|
|
99
|
+
number: z
|
|
100
|
+
.object({
|
|
101
|
+
decimals: z.number().int().min(0).max(10).optional(),
|
|
102
|
+
min: z.number().optional(),
|
|
103
|
+
max: z.number().optional(),
|
|
104
|
+
default: z.number().optional(),
|
|
105
|
+
prefix: z.string().optional(),
|
|
106
|
+
suffix: z.string().optional(),
|
|
107
|
+
separators: z.enum(['COMMA_PERIOD', 'PERIOD_COMMA']).optional()
|
|
108
|
+
})
|
|
109
|
+
.optional(),
|
|
110
|
+
|
|
111
|
+
date: z
|
|
112
|
+
.object({
|
|
113
|
+
includeTime: z.boolean().optional(),
|
|
114
|
+
format: z.enum(['ISO', 'US', 'EU']).optional(),
|
|
115
|
+
timeFormat: z.enum(['24', '12']).optional(),
|
|
116
|
+
default: z.string().optional(),
|
|
117
|
+
timezone: z.string().optional()
|
|
118
|
+
})
|
|
119
|
+
.optional(),
|
|
120
|
+
|
|
121
|
+
single_select: z.object({
|
|
122
|
+
options: z.array(SelectOptionValidator).min(1, 'Debe haber al menos una opción')
|
|
123
|
+
}),
|
|
124
|
+
|
|
125
|
+
multiple_select: z.object({
|
|
126
|
+
options: z.array(SelectOptionValidator).min(1, 'Debe haber al menos una opción')
|
|
127
|
+
}),
|
|
128
|
+
|
|
129
|
+
boolean: z
|
|
130
|
+
.object({
|
|
131
|
+
default: z.boolean().optional()
|
|
132
|
+
})
|
|
133
|
+
.optional(),
|
|
134
|
+
|
|
135
|
+
link_row: z
|
|
136
|
+
.object({
|
|
137
|
+
targetTableId: z.number().int().positive().optional(),
|
|
138
|
+
targetTableName: z.string().min(1).optional()
|
|
139
|
+
})
|
|
140
|
+
.refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName'),
|
|
141
|
+
|
|
142
|
+
formula: z.object({
|
|
143
|
+
formula: z.string().min(1, 'La fórmula no puede estar vacía')
|
|
144
|
+
}),
|
|
145
|
+
|
|
146
|
+
file: z
|
|
147
|
+
.object({
|
|
148
|
+
allowedFileTypes: z.array(z.string()).optional(),
|
|
149
|
+
maxFileSize: z.number().int().positive().optional()
|
|
150
|
+
})
|
|
151
|
+
.optional(),
|
|
152
|
+
|
|
153
|
+
count: z
|
|
154
|
+
.object({
|
|
155
|
+
targetTableId: z.number().int().positive().optional(),
|
|
156
|
+
targetTableName: z.string().min(1).optional()
|
|
157
|
+
})
|
|
158
|
+
.refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName'),
|
|
159
|
+
|
|
160
|
+
rollup: z
|
|
161
|
+
.object({
|
|
162
|
+
targetTableId: z.number().int().positive().optional(),
|
|
163
|
+
targetTableName: z.string().min(1).optional(),
|
|
164
|
+
targetFieldId: z.number().int().positive().optional(),
|
|
165
|
+
targetFieldName: z.string().min(1).optional(),
|
|
166
|
+
aggregation: z.enum(['sum', 'avg', 'min', 'max', 'count'])
|
|
167
|
+
})
|
|
168
|
+
.refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName')
|
|
169
|
+
.refine(data => data.targetFieldId || data.targetFieldName, 'Debe especificar targetFieldId o targetFieldName'),
|
|
170
|
+
|
|
171
|
+
lookup: z
|
|
172
|
+
.object({
|
|
173
|
+
targetTableId: z.number().int().positive().optional(),
|
|
174
|
+
targetTableName: z.string().min(1).optional(),
|
|
175
|
+
targetFieldId: z.number().int().positive().optional(),
|
|
176
|
+
targetFieldName: z.string().min(1).optional()
|
|
177
|
+
})
|
|
178
|
+
.refine(data => data.targetTableId || data.targetTableName, 'Debe especificar targetTableId o targetTableName')
|
|
179
|
+
.refine(data => data.targetFieldId || data.targetFieldName, 'Debe especificar targetFieldId o targetFieldName')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validador dinámico para configuración de campos
|
|
184
|
+
* Valida la configuración específica según el tipo de campo
|
|
185
|
+
*/
|
|
186
|
+
function createFieldConfigValidator(fieldType: string) {
|
|
187
|
+
const validator = FieldConfigValidators[fieldType as keyof typeof FieldConfigValidators]
|
|
188
|
+
return validator || z.any().optional()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// FieldConfigValidator removido - ahora se usa createFieldConfigValidator para validación tipada
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Validador para schema de campo
|
|
195
|
+
*/
|
|
196
|
+
export const FieldSchemaValidator = z
|
|
197
|
+
.object({
|
|
198
|
+
name: z
|
|
199
|
+
.string()
|
|
200
|
+
.min(1, 'El nombre del campo no puede estar vacío')
|
|
201
|
+
.max(255, 'El nombre del campo no puede exceder 255 caracteres')
|
|
202
|
+
.regex(
|
|
203
|
+
/^[a-zA-Z][a-zA-Z0-9_]*$/,
|
|
204
|
+
'El nombre del campo debe empezar con letra y contener solo letras, números y guiones bajos'
|
|
205
|
+
),
|
|
206
|
+
|
|
207
|
+
type: z.enum(fieldTypes, {
|
|
208
|
+
errorMap: () => ({ message: 'Tipo de campo no válido' })
|
|
209
|
+
}),
|
|
210
|
+
|
|
211
|
+
description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),
|
|
212
|
+
|
|
213
|
+
config: z.any().optional()
|
|
214
|
+
})
|
|
215
|
+
.refine(
|
|
216
|
+
field => {
|
|
217
|
+
// Validar que la configuración sea apropiada para el tipo de campo usando validadores específicos
|
|
218
|
+
try {
|
|
219
|
+
if (field.config !== undefined) {
|
|
220
|
+
const specificValidator = createFieldConfigValidator(field.type)
|
|
221
|
+
specificValidator.parse(field.config)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Verificar campos que requieren configuración obligatoria
|
|
225
|
+
const requiredConfigTypes = [
|
|
226
|
+
'single_select',
|
|
227
|
+
'multiple_select',
|
|
228
|
+
'link_row',
|
|
229
|
+
'formula',
|
|
230
|
+
'count',
|
|
231
|
+
'rollup',
|
|
232
|
+
'lookup'
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
if (requiredConfigTypes.includes(field.type) && !field.config) {
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return true
|
|
240
|
+
} catch {
|
|
241
|
+
// Error de validación específica del tipo de campo
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
message: 'La configuración no es válida para este tipo de campo'
|
|
247
|
+
}
|
|
248
|
+
) satisfies z.ZodType<FieldSchema>
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validador para schema de tabla
|
|
252
|
+
*/
|
|
253
|
+
export const TableSchemaValidator = z
|
|
254
|
+
.object({
|
|
255
|
+
name: z
|
|
256
|
+
.string()
|
|
257
|
+
.min(1, 'El nombre de la tabla no puede estar vacío')
|
|
258
|
+
.max(255, 'El nombre de la tabla no puede exceder 255 caracteres')
|
|
259
|
+
.regex(
|
|
260
|
+
/^[a-zA-Z][a-zA-Z0-9_]*$/,
|
|
261
|
+
'El nombre de la tabla debe empezar con letra y contener solo letras, números y guiones bajos'
|
|
262
|
+
),
|
|
263
|
+
|
|
264
|
+
description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),
|
|
265
|
+
|
|
266
|
+
fields: z
|
|
267
|
+
.array(FieldSchemaValidator)
|
|
268
|
+
.min(1, 'La tabla debe tener al menos un campo')
|
|
269
|
+
.max(200, 'La tabla no puede tener más de 200 campos')
|
|
270
|
+
})
|
|
271
|
+
.refine(
|
|
272
|
+
table => {
|
|
273
|
+
// Validar que no haya nombres de campos duplicados
|
|
274
|
+
const fieldNames = table.fields.map(f => f.name.toLowerCase())
|
|
275
|
+
const uniqueNames = new Set(fieldNames)
|
|
276
|
+
return fieldNames.length === uniqueNames.size
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
message: 'Los nombres de campos deben ser únicos dentro de la tabla'
|
|
280
|
+
}
|
|
281
|
+
) satisfies z.ZodType<TableSchema>
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Validador para schema de relación
|
|
285
|
+
*/
|
|
286
|
+
export const RelationshipSchemaValidator = z
|
|
287
|
+
.object({
|
|
288
|
+
name: z
|
|
289
|
+
.string()
|
|
290
|
+
.min(1, 'El nombre de la relación no puede estar vacío')
|
|
291
|
+
.max(255, 'El nombre de la relación no puede exceder 255 caracteres')
|
|
292
|
+
.regex(
|
|
293
|
+
/^[a-zA-Z][a-zA-Z0-9_]*$/,
|
|
294
|
+
'El nombre de la relación debe empezar con letra y contener solo letras, números y guiones bajos'
|
|
295
|
+
),
|
|
296
|
+
|
|
297
|
+
sourceTable: z.string().min(1, 'El nombre de la tabla origen no puede estar vacío'),
|
|
298
|
+
|
|
299
|
+
targetTable: z.string().min(1, 'El nombre de la tabla destino no puede estar vacío'),
|
|
300
|
+
|
|
301
|
+
description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional()
|
|
302
|
+
})
|
|
303
|
+
.refine(
|
|
304
|
+
rel => {
|
|
305
|
+
// No permitir auto-relaciones por simplicidad
|
|
306
|
+
return rel.sourceTable !== rel.targetTable
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
message: 'Las auto-relaciones no están soportadas'
|
|
310
|
+
}
|
|
311
|
+
) satisfies z.ZodType<RelationshipSchema>
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Validador para schema de base de datos completo
|
|
315
|
+
*/
|
|
316
|
+
export const DatabaseSchemaValidator = z
|
|
317
|
+
.object({
|
|
318
|
+
version: z.string().optional(),
|
|
319
|
+
|
|
320
|
+
description: z.string().max(1000, 'La descripción no puede exceder 1000 caracteres').optional(),
|
|
321
|
+
|
|
322
|
+
tables: z
|
|
323
|
+
.array(TableSchemaValidator)
|
|
324
|
+
.min(1, 'El schema debe contener al menos una tabla')
|
|
325
|
+
.max(100, 'El schema no puede contener más de 100 tablas'),
|
|
326
|
+
|
|
327
|
+
relationships: z.array(RelationshipSchemaValidator).optional()
|
|
328
|
+
})
|
|
329
|
+
.refine(
|
|
330
|
+
schema => {
|
|
331
|
+
// Validar que no haya nombres de tablas duplicados
|
|
332
|
+
const tableNames = schema.tables.map(t => t.name.toLowerCase())
|
|
333
|
+
const uniqueNames = new Set(tableNames)
|
|
334
|
+
return tableNames.length === uniqueNames.size
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
message: 'Los nombres de tablas deben ser únicos'
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
.refine(
|
|
341
|
+
schema => {
|
|
342
|
+
// Validar que las relaciones referencien tablas existentes
|
|
343
|
+
if (!schema.relationships) return true
|
|
344
|
+
|
|
345
|
+
const tableNames = new Set(schema.tables.map(t => t.name.toLowerCase()))
|
|
346
|
+
|
|
347
|
+
for (const rel of schema.relationships) {
|
|
348
|
+
if (!tableNames.has(rel.sourceTable.toLowerCase()) || !tableNames.has(rel.targetTable.toLowerCase())) {
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return true
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
message: 'Las relaciones deben referenciar tablas existentes en el schema'
|
|
357
|
+
}
|
|
358
|
+
) satisfies z.ZodType<DatabaseSchema>
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Validador para opciones de carga de schema
|
|
362
|
+
*/
|
|
363
|
+
export const LoadSchemaOptionsValidator = z.object({
|
|
364
|
+
mode: z.enum(['create-only', 'update', 'recreate']).default('create-only'),
|
|
365
|
+
version: z.string().optional(),
|
|
366
|
+
force: z.boolean().default(false),
|
|
367
|
+
cleanup: z.boolean().default(false)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Función helper para validar un schema completo
|
|
372
|
+
*/
|
|
373
|
+
export function validateDatabaseSchema(schema: unknown): DatabaseSchema {
|
|
374
|
+
try {
|
|
375
|
+
return DatabaseSchemaValidator.parse(schema)
|
|
376
|
+
} catch (error) {
|
|
377
|
+
if (error instanceof z.ZodError) {
|
|
378
|
+
const errorMessages = error.errors.map(err => `${err.path.join('.')}: ${err.message}`).join('\n')
|
|
379
|
+
|
|
380
|
+
throw new Error(`Schema validation failed:\n${errorMessages}`)
|
|
381
|
+
}
|
|
382
|
+
throw error
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Función helper para validar opciones de carga
|
|
388
|
+
*/
|
|
389
|
+
export function validateLoadSchemaOptions(options: unknown): LoadSchemaOptions {
|
|
390
|
+
return LoadSchemaOptionsValidator.parse(options || {})
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Función helper para obtener validador específico de configuración por tipo de campo
|
|
395
|
+
*/
|
|
396
|
+
export function getFieldConfigValidator(fieldType: string) {
|
|
397
|
+
return createFieldConfigValidator(fieldType)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Validador para nombres de campos y tablas seguros
|
|
402
|
+
*/
|
|
403
|
+
export const SafeNameValidator = z
|
|
404
|
+
.string()
|
|
405
|
+
.min(1, 'El nombre no puede estar vacío')
|
|
406
|
+
.max(255, 'El nombre no puede exceder 255 caracteres')
|
|
407
|
+
.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, 'El nombre debe empezar con letra y contener solo letras, números y guiones bajos')
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Validar si un nombre es seguro para usar en Baserow
|
|
411
|
+
*/
|
|
412
|
+
export function validateSafeName(name: string): boolean {
|
|
413
|
+
try {
|
|
414
|
+
SafeNameValidator.parse(name)
|
|
415
|
+
return true
|
|
416
|
+
} catch {
|
|
417
|
+
return false
|
|
418
|
+
}
|
|
419
|
+
}
|