@gzl10/nexus-sdk 0.12.3 → 0.12.6

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 (3) hide show
  1. package/dist/index.d.ts +1654 -413
  2. package/dist/index.js +26 -12
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -5,37 +5,37 @@ export { CookieOptions, NextFunction, Request, RequestHandler, Response, Router
5
5
  import { Logger } from 'pino';
6
6
 
7
7
  /**
8
- * Type guards y utilidades para EntityDefinitions
8
+ * Type guards and utilities for EntityDefinitions
9
9
  */
10
10
 
11
11
  /**
12
- * Entidades que persisten en BD local con tabla propia
13
- * Excluye: action, external, virtual, computed, single (usa tabla compartida)
12
+ * Entities that persist in the local DB with their own table.
13
+ * Excludes: action, external, virtual, computed, single (uses shared table).
14
14
  */
15
15
  type PersistentEntityDefinition = CollectionEntityDefinition | ReferenceEntityDefinition | EventEntityDefinition | ConfigEntityDefinition | TempEntityDefinition | ViewEntityDefinition;
16
16
  /**
17
- * Entidades sin persistencia local (read-only o sin BD)
17
+ * Entities without local persistence (read-only or without DB)
18
18
  */
19
19
  type NonPersistentEntityDefinition = ActionEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition;
20
20
  /**
21
- * Type guard para verificar si una entidad persiste en BD local con tabla propia
21
+ * Type guard to check whether an entity persists in the local DB with its own table
22
22
  */
23
23
  declare function isPersistentEntity(entity: EntityDefinition): entity is PersistentEntityDefinition;
24
24
  /**
25
- * Type guard para verificar si es un singleton (usa tabla compartida sys_settings)
25
+ * Type guard to check whether it is a singleton (uses shared sys_settings table)
26
26
  */
27
27
  declare function isSingletonEntity(entity: EntityDefinition): entity is SingleEntityDefinition;
28
28
  /**
29
- * Type guard para verificar si una entidad tiene tabla propia (para migraciones)
29
+ * Type guard to check whether an entity has its own table (for migrations)
30
30
  */
31
31
  declare function hasTable(entity: EntityDefinition): entity is PersistentEntityDefinition;
32
32
  /**
33
- * Obtiene el nombre de una entidad en PascalCase singular
33
+ * Gets an entity name in singular PascalCase.
34
34
  * 'cms_posts' → 'Post', 'rol_role_permissions' → 'RolePermission'
35
35
  */
36
36
  declare function getEntityName(entity: EntityDefinition): string;
37
37
  /**
38
- * Obtiene el subject CASL de una entidad
38
+ * Gets the CASL subject for an entity
39
39
  */
40
40
  declare function getEntitySubject(entity: PersistentEntityDefinition): string;
41
41
 
@@ -47,57 +47,59 @@ declare function getEntitySubject(entity: PersistentEntityDefinition): string;
47
47
  * depending on the full @gzl10/nexus-backend package.
48
48
  */
49
49
  /**
50
- * Unión discriminada de todos los tipos de entidad
50
+ * Discriminated union of all entity types
51
51
  *
52
- * | Type | Persistencia | CRUD | Uso principal |
53
- * |------------|--------------|-----------------|----------------------------------|
54
- * | collection | (BD) | Completo | Datos de negocio (users, posts) |
55
- * | single | (BD) | Update/Read | Config global (site_config) |
56
- * | external | No | Read | Datos externos (stripe_customers)|
57
- * | virtual | No | Read | Orquestación (unified_customers) |
58
- * | computed | No/opcional | Read | KPIs, estadísticas |
59
- * | view | Sí/virtual | Read | Lectura optimizada (projections) |
60
- * | reference | | Read (admin) | Catálogos (countries, currencies)|
61
- * | config | | Update/Read | Config por módulo |
62
- * | event | | Append | Auditoría (audit_logs) |
63
- * | temp | No (TTL) | Read/Write | Cache, sesiones (otp_codes) |
64
- * | action | No | Execute | Operaciones, workflows |
52
+ * | Type | Persistence | CRUD | Primary use |
53
+ * |------------|-------------|-----------------|----------------------------------|
54
+ * | collection | Yes (DB) | Full | Business data (users, posts) |
55
+ * | single | Yes (DB) | Update/Read | Global config (site_config) |
56
+ * | external | No | Read | External data (stripe_customers) |
57
+ * | virtual | No | Read | Orchestration (unified_customers)|
58
+ * | computed | No/optional | Read | KPIs, stats |
59
+ * | view | Yes/virtual | Read | Optimized reads (projections) |
60
+ * | reference | Yes | Read (admin) | Catalogs (countries, currencies) |
61
+ * | config | Yes | Update/Read | Per-module config |
62
+ * | event | Yes | Append | Audit logs (audit_logs) |
63
+ * | temp | No (TTL) | Read/Write | Cache, sessions (otp_codes) |
64
+ * | action | No | Execute | Operations, workflows |
65
65
  */
66
66
 
67
67
  type KnexCreateTableBuilder = Knex.CreateTableBuilder;
68
68
  type KnexAlterTableBuilder = Knex.AlterTableBuilder;
69
69
  type KnexTransaction = Knex.Transaction;
70
70
  /**
71
- * Request autenticado con usuario y abilities CASL
72
- * El backend especializa TUser y TAbility con tipos concretos
71
+ * Base authenticated request with user and CASL abilities (generic).
72
+ * Use AuthRequest for the pre-typed version.
73
73
  */
74
- interface AuthRequest<TUser = unknown, TAbility = unknown> extends Request {
74
+ interface BaseAuthRequest<TUser = unknown, TAbility = unknown> extends Request {
75
75
  user: TUser;
76
76
  ability: TAbility;
77
77
  }
78
78
  /**
79
- * Usuario base sin contraseña para uso en plugins
80
- * Usado para tipar relaciones con usuarios (ej: author, created_by)
79
+ * Base user without a password for plugin use.
80
+ * Used to type relations with users (e.g., author, created_by).
81
+ * Note: Multi-role support - roles are loaded via user_roles pivot table.
81
82
  */
82
83
  interface BaseUser {
83
84
  id: string;
84
85
  name: string;
85
86
  email: string;
86
- role_id: string;
87
87
  created_at: Date;
88
88
  updated_at: Date;
89
89
  created_by: string | null;
90
90
  updated_by: string | null;
91
+ /** Extensibility: allows custom user properties */
92
+ [key: string]: unknown;
91
93
  }
92
94
  /**
93
- * Parámetros de paginación para queries
95
+ * Pagination parameters for queries
94
96
  */
95
97
  interface PaginationParams {
96
98
  page: number;
97
99
  limit: number;
98
100
  }
99
101
  /**
100
- * Resultado paginado genérico
102
+ * Generic paginated result
101
103
  */
102
104
  interface PaginatedResult<T> {
103
105
  items: T[];
@@ -108,183 +110,603 @@ interface PaginatedResult<T> {
108
110
  hasNext: boolean;
109
111
  }
110
112
  /**
111
- * Tipos de columna en base de datos
113
+ * Standard validation error detail for API responses
114
+ */
115
+ interface ValidationDetail {
116
+ path: string;
117
+ message: string;
118
+ }
119
+ /**
120
+ * Localized string supporting multiple languages.
121
+ * Can be a simple string (single language) or object with language codes.
122
+ *
123
+ * @example
124
+ * // Simple string (backward compatible)
125
+ * label: 'Users'
126
+ *
127
+ * // Multi-language object
128
+ * label: { en: 'Users', es: 'Usuarios' }
129
+ */
130
+ type LocalizedString = string | Record<string, string>;
131
+ /**
132
+ * Resolves a LocalizedString to the appropriate language.
133
+ * Falls back to 'en', then first available, then empty string.
134
+ *
135
+ * @param value - The localized string to resolve
136
+ * @param locale - The target locale (e.g., 'en', 'es')
137
+ * @returns The resolved string for the given locale
112
138
  */
113
- type DbType = 'string' | 'text' | 'integer' | 'decimal' | 'boolean' | 'date' | 'datetime' | 'json' | 'uuid';
139
+ declare function resolveLocalized(value: LocalizedString | undefined, locale: string): string;
114
140
  /**
115
- * Tipos de input en UI
141
+ * Gets the appropriate label based on count (pluralization).
142
+ * Returns labelPlural for count !== 1, label otherwise.
143
+ *
144
+ * @param label - Singular form
145
+ * @param labelPlural - Plural form (optional, defaults to label)
146
+ * @param count - The count to determine singular/plural
147
+ * @param locale - The target locale
148
+ * @returns The resolved string for the given count and locale
116
149
  */
117
- type InputType = 'text' | 'email' | 'password' | 'url' | 'tel' | 'number' | 'decimal' | 'textarea' | 'markdown' | 'json' | 'select' | 'multiselect' | 'tags' | 'checkbox' | 'switch' | 'date' | 'datetime' | 'file' | 'fileArray' | 'image' | 'imageArray' | 'hidden';
150
+ declare function getCountLabel(label: LocalizedString | undefined, labelPlural: LocalizedString | undefined, count: number, locale: string): string;
118
151
  /**
119
- * Configuración de base de datos para un campo
152
+ * Database column types for Knex migrations.
153
+ *
154
+ * Maps to SQL column types:
155
+ * - `string` → VARCHAR(size) - Use with `db.size` for length
156
+ * - `text` → TEXT - Unlimited length text
157
+ * - `integer` → INTEGER - Whole numbers
158
+ * - `decimal` → DECIMAL(p,s) - Use with `db.precision` for scale
159
+ * - `boolean` → BOOLEAN - True/false values
160
+ * - `date` → DATE - Date without time
161
+ * - `datetime` → DATETIME/TIMESTAMP - Date with time
162
+ * - `json` → JSON/JSONB - Structured data (use for LocalizedString)
163
+ * - `uuid` → UUID - Universally unique identifier
164
+ * - `array` → Virtual only - No database column generated
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * db: { type: 'string', size: 100 } // VARCHAR(100)
169
+ * db: { type: 'decimal', precision: [10, 2] } // DECIMAL(10,2)
170
+ * db: { type: 'json', nullable: false } // JSON NOT NULL
171
+ * ```
172
+ */
173
+ type DbType = 'string' | 'text' | 'integer' | 'decimal' | 'boolean' | 'date' | 'datetime' | 'json' | 'uuid' | 'array';
174
+ /**
175
+ * UI input types that determine which form component renders.
176
+ *
177
+ * **Text inputs:**
178
+ * - `text` → NInput - Single line text
179
+ * - `email` → NInput with email validation
180
+ * - `password` → NInput with password mask
181
+ * - `url` → NInput with URL validation
182
+ * - `tel` → NInput for phone numbers
183
+ *
184
+ * **Numeric inputs:**
185
+ * - `number` → NInputNumber - Integer values
186
+ * - `decimal` → NInputNumber with decimal precision
187
+ *
188
+ * **Rich text:**
189
+ * - `textarea` → NInput multiline
190
+ * - `markdown` → MarkdownInput with preview
191
+ * - `json` → JsonInput with syntax highlighting
192
+ *
193
+ * **Selection:**
194
+ * - `select` → NSelect - Single selection dropdown
195
+ * - `multiselect` → NSelect with multiple - Multiple selection
196
+ * - `transfer` → NTransfer - Dual list selection
197
+ * - `tags` → NSelect with tags - Free-form tags input
198
+ *
199
+ * **Boolean:**
200
+ * - `checkbox` → NCheckbox
201
+ * - `switch` → NSwitch - Toggle switch
202
+ *
203
+ * **Date/Time:**
204
+ * - `date` → NDatePicker - Date only
205
+ * - `datetime` → NDatePicker with time
206
+ *
207
+ * **Files:**
208
+ * - `file` → FileInput - Single file upload
209
+ * - `fileArray` → FileInput multiple - Multiple files
210
+ * - `image` → ImageInput - Single image with preview
211
+ * - `imageArray` → ImageInput multiple - Image gallery
212
+ *
213
+ * **Other:**
214
+ * - `icon` → IconPicker - Icon selection from library
215
+ *
216
+ * @see {@link FieldOptions} for select/multiselect configuration
217
+ * @see {@link FieldStorageConfig} for file/image configuration
218
+ */
219
+ type InputType = 'text' | 'email' | 'password' | 'url' | 'tel' | 'number' | 'decimal' | 'textarea' | 'markdown' | 'json' | 'select' | 'multiselect' | 'transfer' | 'tags' | 'checkbox' | 'switch' | 'date' | 'datetime' | 'file' | 'fileArray' | 'image' | 'imageArray' | 'icon';
220
+ /**
221
+ * Database column configuration for Knex migrations.
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // String with max length
226
+ * db: { type: 'string', size: 100, nullable: false }
227
+ *
228
+ * // Decimal with precision
229
+ * db: { type: 'decimal', precision: [10, 2] }
230
+ *
231
+ * // UUID with auto-generation
232
+ * db: { type: 'uuid', defaultFn: 'uuid' }
233
+ *
234
+ * // Virtual field (no column in DB)
235
+ * db: { type: 'array', virtual: true }
236
+ * ```
120
237
  */
121
238
  interface FieldDbConfig {
239
+ /** Column type for the database */
122
240
  type: DbType;
241
+ /** If true, no database column is created (for computed/virtual fields like role_ids) */
242
+ virtual?: boolean;
243
+ /** String length for VARCHAR columns */
123
244
  size?: number;
245
+ /** Precision and scale for DECIMAL: [precision, scale] e.g., [10, 2] for DECIMAL(10,2) */
124
246
  precision?: [number, number];
125
- nullable: boolean;
247
+ /** Allow NULL values. Inferred from `required` if not specified */
248
+ nullable?: boolean;
249
+ /** Create unique constraint on this column */
126
250
  unique?: boolean;
251
+ /** Static default value for the column */
127
252
  default?: unknown;
253
+ /** Database function for default value: 'now' for timestamps, 'uuid' for auto-generated UUIDs */
128
254
  defaultFn?: 'now' | 'uuid';
255
+ /** Create index on this column for faster queries */
129
256
  index?: boolean;
130
257
  }
131
258
  /**
132
- * Configuración de relación (Foreign Key)
259
+ * Foreign key relationship configuration.
260
+ *
261
+ * Defines how this field references another table in the database.
262
+ * Used for both migrations (FK constraint) and UI (dropdown options).
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * // Basic relation to users table
267
+ * relation: { table: 'users' }
268
+ *
269
+ * // With cascade delete
270
+ * relation: { table: 'categories', onDelete: 'CASCADE' }
271
+ *
272
+ * // Reference non-id column
273
+ * relation: { table: 'countries', column: 'code' }
274
+ * ```
133
275
  */
134
276
  interface FieldRelation {
277
+ /** Target table name */
135
278
  table: string;
279
+ /** Target column name (default: 'id') */
136
280
  column?: string;
281
+ /** Action when referenced row is deleted */
137
282
  onDelete?: 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'NO ACTION';
283
+ /** Action when referenced row's key is updated */
138
284
  onUpdate?: 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'NO ACTION';
139
285
  }
140
286
  /**
141
- * Configuración de validación para un campo
287
+ * Validation rules for runtime and form validation.
288
+ *
289
+ * Used to generate Zod schemas for API validation and
290
+ * form validation rules in the UI.
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * // String length constraints
295
+ * validation: { min: 1, max: 200 }
296
+ *
297
+ * // Email format
298
+ * validation: { format: 'email' }
299
+ *
300
+ * // Regex pattern
301
+ * validation: { pattern: '^[a-z0-9-]+$' }
302
+ *
303
+ * // Enum values
304
+ * validation: { enum: ['draft', 'published', 'archived'] }
305
+ * ```
142
306
  */
143
307
  interface FieldValidationConfig {
144
- required?: boolean;
308
+ /** Minimum value (numbers) or minimum length (strings) */
145
309
  min?: number;
310
+ /** Maximum value (numbers) or maximum length (strings) */
146
311
  max?: number;
312
+ /** Regular expression pattern for validation */
147
313
  pattern?: string;
314
+ /** Predefined format validation */
148
315
  format?: 'email' | 'url' | 'uuid' | 'slug';
316
+ /** Allowed values (creates enum constraint) */
149
317
  enum?: string[];
150
318
  }
151
319
  /**
152
- * Opciones para campos select/relaciones
320
+ * Configuration for select, multiselect, and dropdown fields.
321
+ *
322
+ * Options can be loaded from:
323
+ * - Static array defined in the field
324
+ * - Dynamic endpoint (API call)
325
+ * - Related entity via `relation` config
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * // Static options
330
+ * options: {
331
+ * static: [
332
+ * { value: 'draft', label: { en: 'Draft', es: 'Borrador' } },
333
+ * { value: 'published', label: { en: 'Published', es: 'Publicado' } }
334
+ * ]
335
+ * }
336
+ *
337
+ * // Dynamic from API
338
+ * options: {
339
+ * endpoint: '/api/categories',
340
+ * valueField: 'id',
341
+ * labelField: 'name'
342
+ * }
343
+ *
344
+ * // Allow creating new options (combobox)
345
+ * options: { allowCreate: true, endpoint: '/api/tags' }
346
+ * ```
153
347
  */
154
348
  interface FieldOptions {
349
+ /** API endpoint for dynamic options (GET request) */
155
350
  endpoint?: string;
351
+ /** Field to use as option value (default: 'id') */
156
352
  valueField?: string;
353
+ /** Field to use as option label (default: 'name') */
157
354
  labelField?: string;
355
+ /** Static list of options */
158
356
  static?: Array<{
159
357
  value: string;
160
- label: string;
358
+ label: LocalizedString;
161
359
  }>;
360
+ /** Allow creating new options on-the-fly (renders combobox) */
162
361
  allowCreate?: boolean;
163
362
  }
164
363
  /**
165
- * Restricciones de acceso CASL para un campo
166
- */
167
- interface FieldCaslAccess {
168
- /** Roles que pueden leer este campo (vacío = todos, null = nadie excepto ADMIN) */
169
- readRoles?: string[];
170
- /** Roles que pueden escribir este campo (vacío = todos, null = nadie excepto ADMIN) */
171
- writeRoles?: string[];
172
- }
173
- /**
174
- * Configuración de storage para campos file/image
364
+ * Storage configuration for file and image upload fields.
365
+ *
366
+ * Controls upload behavior, file validation, and image processing.
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * // Image with thumbnails
371
+ * storage: {
372
+ * accept: 'image/*',
373
+ * maxSize: 5 * 1024 * 1024, // 5MB
374
+ * folder: 'avatars',
375
+ * thumbnails: [{ width: 100, height: 100 }, { width: 300, height: 300 }]
376
+ * }
377
+ *
378
+ * // PDF documents with deduplication
379
+ * storage: {
380
+ * accept: 'application/pdf',
381
+ * maxSize: 20 * 1024 * 1024, // 20MB
382
+ * folder: 'documents',
383
+ * dedupe: true
384
+ * }
385
+ *
386
+ * // Multiple images
387
+ * storage: {
388
+ * accept: 'image/*',
389
+ * maxFiles: 10,
390
+ * folder: 'gallery'
391
+ * }
392
+ * ```
175
393
  */
176
394
  interface FieldStorageConfig {
177
- /** Tipos MIME aceptados (ej: 'image/*', 'application/pdf') */
395
+ /** Accepted MIME types (e.g., 'image/*', 'application/pdf', 'image/png,image/jpeg') */
178
396
  accept?: string;
179
- /** Tamaño máximo en bytes (default: 10MB) */
397
+ /** Maximum file size in bytes (default: 10MB = 10485760) */
180
398
  maxSize?: number;
181
- /** Número máximo de archivos para tipos array */
399
+ /** Maximum number of files for array types (fileArray, imageArray) */
182
400
  maxFiles?: number;
183
- /** Carpeta/prefijo en storage (ej: 'avatars', 'documents') */
401
+ /** Folder/prefix in storage bucket (e.g., 'avatars', 'documents/contracts') */
184
402
  folder?: string;
185
- /** Tamaños de thumbnails a generar para imágenes */
403
+ /** Thumbnail sizes to auto-generate for images. Creates resized versions on upload */
186
404
  thumbnails?: Array<{
187
405
  width: number;
188
406
  height: number;
189
407
  }>;
408
+ /** Enable SHA256 hash deduplication - if identical file exists, reuse it instead of uploading again */
409
+ dedupe?: boolean;
190
410
  }
191
411
  /**
192
- * Metadata del campo
412
+ * Field metadata for UI behavior and data operations.
413
+ *
414
+ * Controls how the field behaves in tables, forms, exports, and searches.
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * // Searchable and sortable title field
419
+ * meta: { searchable: true, sortable: true, exportable: true }
420
+ *
421
+ * // Audit field (auto-populated)
422
+ * meta: { auditField: 'created_by', showInForm: false }
423
+ *
424
+ * // Show only in edit form, not create
425
+ * meta: { showInForm: 'edit' }
426
+ *
427
+ * // Hidden from tables but visible in forms
428
+ * meta: { showInDisplay: false }
429
+ * ```
193
430
  */
194
431
  interface FieldMeta {
432
+ /** Enable sorting by this field in table columns */
195
433
  sortable?: boolean;
434
+ /** Include this field in search/filter operations */
196
435
  searchable?: boolean;
436
+ /** Include this field in CSV/Excel exports */
197
437
  exportable?: boolean;
438
+ /** Mark as audit field (auto-populated with user ID on create/update) */
198
439
  auditField?: 'created_by' | 'updated_by';
199
- /** Restricciones de acceso CASL para este campo */
200
- casl?: FieldCaslAccess;
440
+ /** Show in display components (Table, List, Masonry). Default: true */
441
+ showInDisplay?: boolean;
442
+ /** Show in forms. Use 'create'/'edit' for conditional display. Default: true */
443
+ showInForm?: boolean | 'create' | 'edit';
201
444
  }
202
445
  /**
203
- * Definición completa de un campo
446
+ * Conditional boolean - can be static or evaluated at runtime based on record
447
+ * Functions are evaluated in backend before serializing to DTO
448
+ */
449
+ type ConditionalBoolean = boolean | ((record: Record<string, unknown>) => boolean);
450
+ /**
451
+ * Complete field definition for entity fields.
452
+ *
453
+ * This is the core building block for defining entity schemas in Nexus.
454
+ * Contains all the information needed for:
455
+ * - **UI**: Form inputs, table columns, list displays
456
+ * - **Database**: Knex migrations, column types, constraints
457
+ * - **Validation**: Zod schemas, runtime validation rules
204
458
  *
205
- * Contiene toda la información necesaria para:
206
- * - UI: formularios, listas
207
- * - BD: migraciones Knex
208
- * - Validación: schemas Zod
459
+ * @example
460
+ * ```typescript
461
+ * const titleField: FieldDefinition = {
462
+ * name: 'title',
463
+ * label: { en: 'Title', es: 'Título' },
464
+ * input: 'text',
465
+ * required: true,
466
+ * db: { type: 'string', nullable: false },
467
+ * validation: { min: 1, max: 200 }
468
+ * }
469
+ * ```
470
+ *
471
+ * @see {@link FieldDefinitionDTO} for the serializable API response version
472
+ * @see {@link EntityDefinition} for the parent entity structure
209
473
  */
210
474
  interface FieldDefinition {
475
+ /**
476
+ * Field identifier used as the database column name.
477
+ * Should be snake_case for database compatibility.
478
+ * @example 'title', 'created_at', 'author_id'
479
+ */
211
480
  name: string;
212
- label: string;
481
+ /**
482
+ * Human-readable label displayed in forms, tables, and lists.
483
+ * Supports localization with `{ en: '...', es: '...' }` format.
484
+ */
485
+ label: LocalizedString;
486
+ /**
487
+ * Input type that determines which UI component renders this field.
488
+ * @see {@link InputType} for all available input types
489
+ */
213
490
  input: InputType;
214
- placeholder?: string;
215
- hint?: string;
216
- hidden?: boolean;
217
- disabled?: boolean;
491
+ /**
492
+ * Placeholder text shown in empty input fields.
493
+ * Supports localization.
494
+ */
495
+ placeholder?: LocalizedString;
496
+ /**
497
+ * Help text displayed below the input field.
498
+ * Use for additional guidance or format hints.
499
+ */
500
+ hint?: LocalizedString;
501
+ /**
502
+ * Controls field visibility in the UI.
503
+ * - `true`: Always hidden
504
+ * - `false`: Always visible
505
+ * - `(record) => boolean`: Dynamic based on record values
506
+ * @example hidden: (record) => record.type === 'draft'
507
+ */
508
+ hidden?: ConditionalBoolean;
509
+ /**
510
+ * Controls whether the field is read-only in forms.
511
+ * - `true`: Always disabled
512
+ * - `false`: Always editable
513
+ * - `(record) => boolean`: Dynamic based on record values
514
+ * @example disabled: (record) => record.status === 'published'
515
+ */
516
+ disabled?: ConditionalBoolean;
517
+ /**
518
+ * Marks the field as required for form validation.
519
+ * - `true`: Always required
520
+ * - `false`: Always optional
521
+ * - `(record) => boolean`: Dynamic based on record values
522
+ * @example required: (record) => record.is_featured === true
523
+ */
524
+ required?: ConditionalBoolean;
525
+ /**
526
+ * Default value used when creating new records.
527
+ * Type should match the field's expected data type.
528
+ */
529
+ defaultValue?: unknown;
530
+ /**
531
+ * Database column configuration for Knex migrations.
532
+ * Optional for virtual fields, actions, or external data sources.
533
+ * @see {@link FieldDbConfig}
534
+ */
218
535
  db?: FieldDbConfig;
536
+ /**
537
+ * Foreign key relationship configuration.
538
+ * Defines how this field relates to another entity's table.
539
+ * @see {@link FieldRelation}
540
+ */
219
541
  relation?: FieldRelation;
542
+ /**
543
+ * Validation rules applied during form submission and API requests.
544
+ * Used to generate Zod schemas for runtime validation.
545
+ * @see {@link FieldValidationConfig}
546
+ */
220
547
  validation?: FieldValidationConfig;
548
+ /**
549
+ * Configuration for select inputs and relationship dropdowns.
550
+ * Defines static options or dynamic data sources.
551
+ * @see {@link FieldOptions}
552
+ */
221
553
  options?: FieldOptions;
554
+ /**
555
+ * Storage configuration for file and image inputs.
556
+ * Defines upload constraints, folders, and thumbnail generation.
557
+ * @see {@link FieldStorageConfig}
558
+ */
222
559
  storage?: FieldStorageConfig;
560
+ /**
561
+ * Additional metadata for categorization and UI organization.
562
+ * @see {@link FieldMeta}
563
+ */
223
564
  meta?: FieldMeta;
565
+ /**
566
+ * Additional props passed directly to the form input component.
567
+ * Allows customization of Naive UI component behavior.
568
+ * @example inputProps: { rows: 5, showCount: true }
569
+ */
570
+ inputProps?: Record<string, unknown>;
571
+ /**
572
+ * Additional props passed to display components (table columns, list items).
573
+ * Use `order` prop to control field position in tables.
574
+ * @example displayProps: { order: 1, width: 200 }
575
+ */
576
+ displayProps?: Record<string, unknown>;
224
577
  }
225
578
  /**
226
- * Índice compuesto de tabla
579
+ * Composite table index definition for database migrations.
580
+ *
581
+ * Creates multi-column indexes for optimized queries.
582
+ *
583
+ * @example
584
+ * ```typescript
585
+ * // Composite index for frequent queries
586
+ * indexes: [{ columns: ['tenant_id', 'created_at'] }]
587
+ *
588
+ * // Unique constraint on multiple columns
589
+ * indexes: [{ columns: ['user_id', 'role_id'], unique: true }]
590
+ * ```
227
591
  */
228
592
  interface EntityIndex {
593
+ /** Column names to include in the composite index */
229
594
  columns: string[];
595
+ /** Create unique constraint on the column combination */
230
596
  unique?: boolean;
231
597
  }
232
598
  /**
233
- * Acciones CASL estándar
599
+ * Standard CASL authorization actions.
600
+ *
601
+ * - `manage` - Full access (superuser wildcard, includes all actions)
602
+ * - `create` - Create new records
603
+ * - `read` - Read/view records
604
+ * - `update` - Modify existing records
605
+ * - `delete` - Remove records
606
+ * - `execute` - Execute actions/operations on records
607
+ *
608
+ * @see {@link RolePermission} for role-based permission configuration
609
+ * @see https://casl.js.org/v6/en/guide/intro
234
610
  */
235
611
  type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'execute';
236
612
  /**
237
- * Condición de ownership para permisos
238
- * El campo de la entidad que referencia al usuario
613
+ * Ownership condition for CASL permissions.
614
+ *
615
+ * Automatically generates conditions that restrict access to records
616
+ * owned by the current user.
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * // Users can only edit their own posts
621
+ * ownership: { field: 'author_id' }
622
+ * // Generates: { author_id: user.id }
623
+ *
624
+ * // Match against user's email instead of id
625
+ * ownership: { field: 'owner_email', userField: 'email' }
626
+ * // Generates: { owner_email: user.email }
627
+ * ```
239
628
  */
240
629
  interface OwnershipCondition {
241
- /** Campo que contiene el ID del propietario (ej: 'author_id', 'created_by') */
630
+ /** Entity field that contains the owner reference (e.g., 'author_id', 'created_by') */
242
631
  field: string;
243
- /** Variable del usuario a comparar (default: 'id') */
632
+ /** User property to compare against (default: 'id'). Use 'email' for email-based ownership */
244
633
  userField?: string;
245
634
  }
246
635
  /**
247
- * Permiso CASL para un rol específico
636
+ * CASL permission definition for a specific role.
637
+ *
638
+ * Defines what actions a role can perform on an entity,
639
+ * optionally with conditions and field restrictions.
640
+ *
641
+ * @example
642
+ * ```typescript
643
+ * // Editor can read and update, but not delete
644
+ * EDITOR: {
645
+ * actions: ['read', 'update'],
646
+ * fields: ['title', 'content', 'status']
647
+ * }
648
+ *
649
+ * // Viewer can only read published items
650
+ * VIEWER: {
651
+ * actions: ['read'],
652
+ * conditions: { status: 'published' }
653
+ * }
654
+ *
655
+ * // Deny delete for everyone except admin
656
+ * EDITOR: {
657
+ * actions: ['delete'],
658
+ * inverted: true
659
+ * }
660
+ * ```
248
661
  */
249
662
  interface RolePermission {
250
- /** Acciones permitidas */
663
+ /** Actions this role can perform on the entity */
251
664
  actions: CaslAction[];
252
- /** Condiciones (ownership, status, etc.) */
665
+ /** CASL conditions to filter which records the permission applies to */
253
666
  conditions?: Record<string, unknown>;
254
- /** Campos permitidos (null = todos) */
667
+ /** Restrict permission to specific fields. null = all fields allowed */
255
668
  fields?: string[] | null;
256
- /** Invertir permiso (cannot en lugar de can) */
669
+ /** If true, this is a denial rule (cannot) instead of allow (can) */
257
670
  inverted?: boolean;
258
671
  }
259
672
  /**
260
- * Configuración de autorización CASL para una entidad
673
+ * CASL authorization configuration for an entity.
674
+ *
675
+ * Defines role-based access control (RBAC) using CASL.
676
+ * Permissions are evaluated at runtime for each request.
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * casl: {
681
+ * subject: 'BlogPost',
682
+ * ownership: { field: 'author_id' },
683
+ * permissions: {
684
+ * ADMIN: { actions: ['manage'] },
685
+ * EDITOR: { actions: ['read', 'create', 'update'] },
686
+ * AUTHOR: { actions: ['read', 'update', 'delete'] }, // with ownership
687
+ * VIEWER: { actions: ['read'], conditions: { status: 'published' } }
688
+ * },
689
+ * sensitiveFields: ['internal_notes', 'analytics']
690
+ * }
691
+ * ```
692
+ *
693
+ * @see https://casl.js.org/v6/en/guide/intro
261
694
  */
262
695
  interface EntityCaslConfig {
263
- /**
264
- * Subject CASL (default: inferido de table → 'cms_posts' → 'CmsPost')
265
- */
696
+ /** CASL subject name. Default: inferred from table (e.g., 'cms_posts' → 'CmsPost') */
266
697
  subject?: string;
267
- /**
268
- * Campo de ownership para conditions automáticas
269
- * Si se define, se genera condition { [field]: '${user.id}' }
270
- */
698
+ /** Ownership configuration for automatic user-based filtering */
271
699
  ownership?: OwnershipCondition;
272
- /**
273
- * Permisos por rol
274
- * Las keys son nombres de roles (ADMIN, EDITOR, VIEWER, etc.)
275
- */
700
+ /** Role-based permissions. Keys are role names (ADMIN, EDITOR, VIEWER, etc.) */
276
701
  permissions?: Record<string, RolePermission>;
277
- /**
278
- * Campos sensibles que requieren permiso especial
279
- * Se excluyen de 'read' para roles sin acceso explícito
280
- */
702
+ /** Fields excluded from 'read' permission unless explicitly granted. Use for PII or internal data */
281
703
  sensitiveFields?: string[];
282
704
  }
283
705
  /**
284
- * Action vinculada a un registro de la entidad
285
- * Define operaciones que actúan sobre registros existentes
706
+ * Action linked to an entity record.
707
+ * Defines operations that act on existing records.
286
708
  *
287
- * Ruta generada: GET|POST /{entityRoutePrefix}/{key}/:id
709
+ * Generated route: GET|POST /{entityRoutePrefix}/{key}/:id
288
710
  *
289
711
  * @example
290
712
  * ```typescript
@@ -296,493 +718,920 @@ interface EntityCaslConfig {
296
718
  * select: ['id', 'filename', 'mimetype'],
297
719
  * handler: async (ctx, input, _req, res) => {
298
720
  * const { _record: file, _authUserId } = input
299
- * // file y _authUserId vienen inyectados automáticamente
721
+ * // file and _authUserId are injected automatically
300
722
  * }
301
723
  * }]
302
724
  * ```
303
725
  */
304
726
  interface EntityAction {
305
- /** Key única para la ruta (ej: 'download' → /download/:id) */
727
+ /** Unique key for the route (e.g., 'download' → /download/:id) */
306
728
  key: string;
307
- /** Label para UI */
308
- label: string;
309
- /** Icono Iconify (default: 'mdi:play') */
729
+ /** Label for the UI */
730
+ label: LocalizedString;
731
+ /** Iconify icon (default: 'mdi:play') */
310
732
  icon?: string;
311
- /** Método HTTP (default: 'POST') */
312
- method?: 'GET' | 'POST';
313
- /** Campos a seleccionar del registro (default: todos) */
733
+ /** HTTP method (default: 'POST') */
734
+ method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
735
+ /** Fields to select from the record (default: all) */
314
736
  select?: string[];
315
- /** Schema de validación del body (opcional) */
737
+ /** Body validation schema (optional) */
316
738
  inputSchema?: ValidationSchema;
317
- /** Schema del output (documentación) */
739
+ /** Output schema (documentation) */
318
740
  outputSchema?: ValidationSchema;
319
- /** Middleware custom (ej: multer) */
741
+ /** Custom middleware (e.g., multer) */
320
742
  middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
321
743
  /**
322
- * Handler - recibe _record y _authUserId automáticamente
323
- * @param ctx - Contexto del módulo
324
- * @param input - Body validado + _record + _authUserId
744
+ * Handler - receives _record and _authUserId automatically.
745
+ * @param ctx - Module context
746
+ * @param input - Validated body + _record + _authUserId
325
747
  * @param req - Express Request
326
- * @param res - Express Response (para streams, headers custom)
748
+ * @param res - Express Response (for streams, custom headers)
327
749
  */
328
750
  handler: (ctx: ModuleContext, input: unknown, req?: Request, res?: Response) => Promise<unknown>;
329
751
  /**
330
- * Permisos CASL específicos para esta action
331
- * Si no se define, hereda los permisos de la entidad padre
752
+ * Skip authentication for this action.
753
+ * Use for public endpoints like thumbnails, previews, etc.
754
+ * @default false
755
+ */
756
+ skipAuth?: boolean;
757
+ /**
758
+ * CASL permissions specific to this action.
759
+ * If not defined, it inherits permissions from the parent entity.
760
+ * Ignored if skipAuth is true.
332
761
  */
333
762
  casl?: {
334
- /** Action CASL (default: 'execute') */
763
+ /** CASL action (default: 'execute') */
335
764
  action?: string;
336
- /** Conditions adicionales */
765
+ /** Additional conditions */
337
766
  conditions?: Record<string, unknown>;
338
767
  };
339
768
  }
340
769
  /**
341
- * Propiedades base compartidas por todas las EntityDefinition
770
+ * Base properties shared by all EntityDefinition types.
771
+ *
772
+ * @internal This interface is not exported directly. Use specific entity types instead.
773
+ * @see {@link CollectionEntityDefinition}
774
+ * @see {@link SingleEntityDefinition}
775
+ * @see {@link ReferenceEntityDefinition}
342
776
  */
343
777
  interface BaseEntityDefinition {
344
- /** Nombre de tabla en BD (con prefijo: 'cms_posts') */
778
+ /** Database table name with module prefix (e.g., 'cms_posts', 'auth_users') */
345
779
  table: string;
346
- /** Nombre para mostrar en UI */
347
- label: string;
348
- /** Definición de campos */
780
+ /** Display name for the UI (singular form) */
781
+ label: LocalizedString;
782
+ /** Display name for the UI (plural form, used in navigation menus and list headers) */
783
+ labelPlural?: LocalizedString;
784
+ /** Field definitions keyed by field name. See {@link FieldDefinition} */
349
785
  fields: Record<string, FieldDefinition>;
350
- /** Autorización CASL */
786
+ /** CASL authorization configuration for role-based access control */
351
787
  casl?: EntityCaslConfig;
352
- /** Prefijo de ruta para montar (default: inferido de table) */
788
+ /** API route prefix. Default: inferred from table name (e.g., 'cms_posts' '/cms-posts') */
353
789
  routePrefix?: string;
354
- /** Orden de visualización en UI (menor = primero, default: 999) */
790
+ /** UI sidebar ordering. Lower values appear first (default: 999) */
355
791
  order?: number;
792
+ /** UI display mode: 'table' for data grids, 'list' for cards, 'masonry' for image galleries */
793
+ displayMode?: DisplayMode;
794
+ /** If true, hides from UI sidebar but entity remains accessible via API */
795
+ hidden?: boolean;
356
796
  }
357
797
  /**
358
- * Entidad de colección - CRUD completo (users, posts, orders)
359
- * Default cuando no se especifica type
798
+ * Collection entity - the most common entity type with full CRUD operations.
799
+ *
800
+ * Use for business data like users, posts, orders, products, etc.
801
+ * This is the default type when `type` is not specified.
802
+ *
803
+ * @example
804
+ * ```typescript
805
+ * const posts: CollectionEntityDefinition = {
806
+ * type: 'collection', // optional, default
807
+ * table: 'cms_posts',
808
+ * label: { en: 'Post', es: 'Publicación' },
809
+ * labelField: 'title',
810
+ * timestamps: true,
811
+ * audit: true,
812
+ * softDelete: true,
813
+ * fields: { ... }
814
+ * }
815
+ * ```
360
816
  */
361
817
  interface CollectionEntityDefinition extends BaseEntityDefinition {
818
+ /** Entity type. Default: 'collection' */
362
819
  type?: 'collection';
363
- /** Campo para mostrar en selects/referencias */
820
+ /** Field used as display label in dropdowns, references, and breadcrumbs (e.g., 'name', 'title') */
364
821
  labelField: string;
365
- /** Añadir created_at, updated_at */
822
+ /** Auto-add created_at and updated_at timestamp columns */
366
823
  timestamps?: boolean;
367
- /** Añadir created_by, updated_by */
824
+ /** Auto-add created_by and updated_by user reference columns */
368
825
  audit?: boolean;
369
- /** Índices compuestos */
826
+ /** Composite database indexes for query optimization */
370
827
  indexes?: EntityIndex[];
371
- /** Usar deleted_at en vez de DELETE físico */
828
+ /** Use soft delete (deleted_at column) instead of physical DELETE */
372
829
  softDelete?: boolean;
373
- /** Actions vinculadas a registros de esta entidad */
830
+ /** Custom actions that operate on entity records (e.g., download, publish, archive) */
374
831
  actions?: EntityAction[];
832
+ /** Show "Create" button in UI. Set false for entities created only via API/actions */
833
+ allowCreate?: boolean;
834
+ /** Allow editing in UI. Set false for read-only display entities */
835
+ allowEdit?: boolean;
375
836
  }
376
837
  /**
377
- * Entidad singleton - Un solo registro en tabla compartida sys_settings
378
- * Usa key-value: key identifica el registro, value es JSON con la estructura de fields
838
+ * Singleton entity - stores a single record in the shared sys_settings table.
839
+ *
840
+ * Perfect for application-wide configuration like site settings, SMTP config,
841
+ * feature flags, etc. Data is stored as JSON in a key-value format.
379
842
  *
380
843
  * @example
381
- * // site_config singleton
382
- * { type: 'single', key: 'site_config', label: 'Site Config', fields: { siteName: {...}, logo: {...} } }
383
- * // Se guarda en sys_settings como: { key: 'site_config', value: { siteName: 'Mi App', logo: '...' } }
844
+ * ```typescript
845
+ * const siteConfig: SingleEntityDefinition = {
846
+ * type: 'single',
847
+ * key: 'site_config',
848
+ * label: { en: 'Site Configuration', es: 'Configuración del Sitio' },
849
+ * defaults: { siteName: 'My App', maintenanceMode: false },
850
+ * fields: {
851
+ * siteName: { name: 'siteName', label: 'Site Name', input: 'text', ... },
852
+ * logo: { name: 'logo', label: 'Logo', input: 'image', ... }
853
+ * }
854
+ * }
855
+ * // Stored in sys_settings: { key: 'site_config', value: '{"siteName":"My App","logo":"..."}' }
856
+ * ```
384
857
  */
385
858
  interface SingleEntityDefinition {
859
+ /** Entity type identifier */
386
860
  type: 'single';
387
- /** Clave única en tabla sys_settings (ej: 'site_config', 'smtp_settings') */
861
+ /** Unique key in sys_settings table (e.g., 'site_config', 'smtp_settings', 'feature_flags') */
388
862
  key: string;
389
- /** Nombre para mostrar en UI */
390
- label: string;
391
- /** Definición de campos (estructura del JSON value) */
863
+ /** Display name for the UI (singular form) */
864
+ label: LocalizedString;
865
+ /** Display name for the UI (plural form) */
866
+ labelPlural?: LocalizedString;
867
+ /** Field definitions describing the JSON structure */
392
868
  fields: Record<string, FieldDefinition>;
393
- /** Valores por defecto al crear */
869
+ /** Default values applied when the setting is first accessed */
394
870
  defaults?: Record<string, unknown>;
395
- /** Autorización CASL */
871
+ /** CASL authorization for read/update operations */
396
872
  casl?: EntityCaslConfig;
397
- /** Prefijo de ruta para montar (default: inferido de key) */
873
+ /** API route prefix. Default: inferred from key */
398
874
  routePrefix?: string;
399
- /** Actions vinculadas a esta entidad single */
875
+ /** Custom actions for this singleton (e.g., 'test-smtp', 'reset-defaults') */
400
876
  actions?: EntityAction[];
401
877
  }
402
878
  /**
403
- * Entidad de referencia - Catálogos con CRUD admin (countries, currencies)
879
+ * Reference entity - read-only catalogs for lookups and dropdowns.
880
+ *
881
+ * Use for relatively static data like countries, currencies, categories, statuses.
882
+ * Data can be seeded from code and optionally edited by admins.
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const countries: ReferenceEntityDefinition = {
887
+ * type: 'reference',
888
+ * table: 'ref_countries',
889
+ * label: { en: 'Country', es: 'País' },
890
+ * labelField: 'name',
891
+ * seed: [
892
+ * { code: 'US', name: 'United States' },
893
+ * { code: 'ES', name: 'Spain' }
894
+ * ],
895
+ * allowAdminEdit: true,
896
+ * fields: { ... }
897
+ * }
898
+ * ```
404
899
  */
405
900
  interface ReferenceEntityDefinition extends BaseEntityDefinition {
901
+ /** Entity type identifier */
406
902
  type: 'reference';
407
- /** Campo para mostrar en selects/referencias */
903
+ /** Field used as display label in dropdowns (e.g., 'name', 'title') */
408
904
  labelField: string;
409
- /** Añadir created_at, updated_at */
905
+ /** Auto-add created_at and updated_at timestamp columns */
410
906
  timestamps?: boolean;
411
- /** Índices compuestos */
907
+ /** Composite database indexes */
412
908
  indexes?: EntityIndex[];
413
- /** Datos iniciales para seed */
909
+ /** Initial seed data inserted on first migration/startup */
414
910
  seed?: Array<Record<string, unknown>>;
415
- /** Permitir CRUD para admin (default: false, solo lectura) */
911
+ /** Allow admin users to edit reference data. Default: false (read-only) */
416
912
  allowAdminEdit?: boolean;
417
913
  }
418
914
  /**
419
- * Entidad de eventos - Logs de auditoría, append-only
915
+ * Event entity - append-only logs for audit trails and analytics.
916
+ *
917
+ * Records can only be created (appended), never updated or deleted.
918
+ * Supports automatic retention policies to prevent unbounded growth.
919
+ *
920
+ * @example
921
+ * ```typescript
922
+ * const auditLogs: EventEntityDefinition = {
923
+ * type: 'event',
924
+ * table: 'sys_audit_logs',
925
+ * label: { en: 'Audit Log', es: 'Log de Auditoría' },
926
+ * labelField: 'action',
927
+ * timestamps: true,
928
+ * retention: { days: 90, maxRows: 100000 },
929
+ * fields: {
930
+ * action: { name: 'action', label: 'Action', input: 'text', ... },
931
+ * user_id: { name: 'user_id', label: 'User', input: 'select', ... },
932
+ * payload: { name: 'payload', label: 'Data', input: 'json', ... }
933
+ * }
934
+ * }
935
+ * ```
420
936
  */
421
937
  interface EventEntityDefinition extends BaseEntityDefinition {
938
+ /** Entity type identifier */
422
939
  type: 'event';
423
- /** Campo para mostrar en listas */
940
+ /** Field used as display label in event lists (e.g., 'action', 'event_type') */
424
941
  labelField: string;
425
- /** Si false, no añade created_at/updated_at automáticos (útil si defines created_at explícitamente) */
942
+ /** Auto-add created_at column. Set false if you define timestamp fields explicitly */
426
943
  timestamps?: boolean;
427
- /** Política de retención automática */
944
+ /** Automatic data retention to limit table growth */
428
945
  retention?: {
429
- /** Eliminar registros más antiguos que N días */
946
+ /** Delete records older than N days */
430
947
  days?: number;
431
- /** Mantener solo los últimos N registros */
948
+ /** Keep only the most recent N records (FIFO) */
432
949
  maxRows?: number;
433
950
  };
434
951
  }
435
952
  /**
436
- * Entidad de acción - Comandos/operaciones sin persistencia
437
- * Para actions que operan sobre registros existentes, usar EntityAction en la entidad padre
953
+ * Action entity - standalone commands/operations without data persistence.
954
+ *
955
+ * Use for operations like sending emails, generating reports, triggering workflows.
956
+ * For actions that operate on existing records, use {@link EntityAction} instead.
957
+ *
958
+ * @example
959
+ * ```typescript
960
+ * const sendEmail: ActionEntityDefinition = {
961
+ * type: 'action',
962
+ * label: { en: 'Send Email', es: 'Enviar Email' },
963
+ * icon: 'mdi:email-send',
964
+ * fields: {
965
+ * to: { name: 'to', label: 'To', input: 'email', required: true },
966
+ * subject: { name: 'subject', label: 'Subject', input: 'text', required: true },
967
+ * body: { name: 'body', label: 'Body', input: 'markdown' }
968
+ * },
969
+ * handler: async (ctx, input) => {
970
+ * await ctx.services.mail.send(input)
971
+ * return { success: true }
972
+ * }
973
+ * }
974
+ * ```
438
975
  */
439
976
  interface ActionEntityDefinition {
977
+ /** Entity type identifier */
440
978
  type: 'action';
441
- /** Nombre para mostrar en UI */
442
- label: string;
443
- /** Icono Iconify para UI (default: 'mdi:play') */
979
+ /** Display name for the action button/form */
980
+ label: LocalizedString;
981
+ /** Iconify icon identifier (default: 'mdi:play') */
444
982
  icon?: string;
445
- /** Solo campos de formulario para input, sin BD */
983
+ /** Form fields for action input. These don't create database columns */
446
984
  fields: Record<string, FieldDefinition>;
447
- /** Schema Zod de input (se valida antes de ejecutar) */
985
+ /** Zod schema for validating input before execution */
448
986
  inputSchema?: ValidationSchema;
449
- /** Schema Zod de output (documenta respuesta) */
987
+ /** Zod schema documenting the response structure */
450
988
  outputSchema?: ValidationSchema;
451
- /** Método HTTP (default: POST) */
452
- method?: 'GET' | 'POST';
453
- /**
454
- * Middleware a aplicar antes del handler (ej: multer para uploads)
455
- * Puede retornar un middleware o un array de middlewares
456
- */
989
+ /** HTTP method for the action endpoint (default: 'POST') */
990
+ method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
991
+ /** Custom middleware (e.g., multer for file uploads). Returns single or array of handlers */
457
992
  middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
458
- /**
459
- * Handler que ejecuta la acción
460
- * @param ctx - Contexto del módulo
461
- * @param input - Datos del body. Incluye _authUserId si hay usuario autenticado.
462
- * @param req - Request de Express (opcional, para acceder a req.file, etc.)
463
- * @param res - Response de Express (opcional, para streams, headers custom, etc.)
464
- */
993
+ /** Handler function that executes the action logic. Input includes _authUserId if authenticated */
465
994
  handler?: (ctx: ModuleContext, input: unknown, req?: Request, res?: Response) => Promise<unknown>;
466
- /** HTTP status code para respuesta exitosa (default: 200, usar 201 para creación) */
995
+ /** HTTP status code for success response. Default: 200, use 201 for resource creation */
467
996
  successStatus?: number;
468
- /** Autorización CASL */
997
+ /** CASL authorization configuration */
469
998
  casl?: EntityCaslConfig;
470
- /** Prefijo de ruta para montar */
999
+ /** API route prefix for the action endpoint */
471
1000
  routePrefix?: string;
472
- /** Orden de visualización en UI (menor = primero, default: 999) */
1001
+ /** UI sidebar ordering. Lower values appear first (default: 999) */
473
1002
  order?: number;
474
1003
  }
475
1004
  /**
476
- * Entidad externa - Datos de APIs externas (stripe_customers, github_repos)
477
- * Read-only, sin persistencia local
1005
+ * External entity - read-only data from external APIs.
1006
+ *
1007
+ * Use for integrating third-party services like Stripe customers, GitHub repos,
1008
+ * or any external REST API. Data is fetched on-demand through adapters.
1009
+ *
1010
+ * @example
1011
+ * ```typescript
1012
+ * const stripeCustomers: ExternalEntityDefinition = {
1013
+ * type: 'external',
1014
+ * label: { en: 'Stripe Customer', es: 'Cliente Stripe' },
1015
+ * labelField: 'email',
1016
+ * adapter: 'stripe',
1017
+ * source: { endpoint: 'customers' },
1018
+ * cache: { ttl: 300, invalidateOn: ['stripe.customer.*'] },
1019
+ * fields: {
1020
+ * id: { name: 'id', label: 'ID', input: 'text' },
1021
+ * email: { name: 'email', label: 'Email', input: 'email' },
1022
+ * name: { name: 'name', label: 'Name', input: 'text' }
1023
+ * }
1024
+ * }
1025
+ * ```
478
1026
  */
479
1027
  interface ExternalEntityDefinition {
1028
+ /** Entity type identifier */
480
1029
  type: 'external';
481
- /** Nombre para mostrar en UI */
482
- label: string;
483
- /** Campo para mostrar en selects/referencias */
1030
+ /** Display name for the UI (singular form) */
1031
+ label: LocalizedString;
1032
+ /** Display name for the UI (plural form) */
1033
+ labelPlural?: LocalizedString;
1034
+ /** Field used as display label in lists and references */
484
1035
  labelField: string;
485
- /** Definición de campos (estructura esperada del API externo) */
1036
+ /** Field definitions mapping to the external API response structure */
486
1037
  fields: Record<string, FieldDefinition>;
487
- /** Nombre del adapter registrado (ej: 'stripe', 'github') */
1038
+ /** Registered adapter name that handles API communication (e.g., 'stripe', 'github', 'salesforce') */
488
1039
  adapter: string;
489
- /** Configuración del origen externo (pasada al adapter) */
1040
+ /** Configuration passed to the adapter */
490
1041
  source?: {
491
- /** Endpoint o recurso específico */
1042
+ /** API endpoint or resource path */
492
1043
  endpoint?: string;
493
- /** Config adicional */
1044
+ /** Additional adapter-specific configuration */
494
1045
  [key: string]: unknown;
495
1046
  };
496
- /** Configuración de cache */
1047
+ /** Response caching to reduce API calls */
497
1048
  cache?: {
498
- /** TTL en segundos */
1049
+ /** Cache time-to-live in seconds */
499
1050
  ttl: number;
500
- /** Campo para generar cache key */
1051
+ /** Field used to generate unique cache keys */
501
1052
  key?: string;
502
- /** Eventos que invalidan la cache (ej: ['db.users.*']) */
1053
+ /** Event patterns that invalidate cached data (e.g., ['stripe.customer.*']) */
503
1054
  invalidateOn?: string[];
504
1055
  };
505
- /** Autorización CASL */
1056
+ /** CASL authorization for read operations */
506
1057
  casl?: EntityCaslConfig;
507
- /** Prefijo de ruta para montar */
1058
+ /** API route prefix */
508
1059
  routePrefix?: string;
509
- /** Orden de visualización en UI (menor = primero, default: 999) */
1060
+ /** UI sidebar ordering (default: 999) */
510
1061
  order?: number;
1062
+ /** UI display mode: 'table', 'list', or 'masonry' */
1063
+ displayMode?: DisplayMode;
511
1064
  }
512
1065
  /**
513
- * Entidad virtual - Orquestación de múltiples fuentes (unified_customers)
514
- * Read-only, combina datos de varias entidades
1066
+ * Virtual entity - data aggregation across multiple sources.
1067
+ *
1068
+ * Use to create unified views combining local entities, external APIs, or computed data.
1069
+ * Read-only, no persistence - data is resolved on each request.
1070
+ *
1071
+ * @example
1072
+ * ```typescript
1073
+ * const unifiedContacts: VirtualEntityDefinition = {
1074
+ * type: 'virtual',
1075
+ * label: { en: 'Contact', es: 'Contacto' },
1076
+ * labelField: 'name',
1077
+ * sources: ['crm_contacts', 'stripe_customers'],
1078
+ * resolver: (sources, ctx) => {
1079
+ * const contacts = sources.crm_contacts.map(c => ({ ...c, source: 'crm' }))
1080
+ * const customers = sources.stripe_customers.map(c => ({ ...c, source: 'stripe' }))
1081
+ * return [...contacts, ...customers]
1082
+ * },
1083
+ * fields: { ... }
1084
+ * }
1085
+ * ```
515
1086
  */
516
1087
  interface VirtualEntityDefinition {
1088
+ /** Entity type identifier */
517
1089
  type: 'virtual';
518
- /** Nombre para mostrar en UI */
519
- label: string;
520
- /** Campo para mostrar en selects/referencias */
1090
+ /** Display name for the UI (singular form) */
1091
+ label: LocalizedString;
1092
+ /** Display name for the UI (plural form) */
1093
+ labelPlural?: LocalizedString;
1094
+ /** Field used as display label in lists and references */
521
1095
  labelField: string;
522
- /** Definición de campos (esquema unificado) */
1096
+ /** Unified field definitions for the combined schema */
523
1097
  fields: Record<string, FieldDefinition>;
524
- /** Fuentes de datos que se combinan (nombres de entidades/tables) */
1098
+ /** Entity or table names to fetch data from */
525
1099
  sources: string[];
526
- /** Función que combina los datos de las fuentes */
1100
+ /** Custom function to combine and transform data from all sources */
527
1101
  resolver?: (sources: Record<string, unknown[]>, ctx: ModuleContext) => unknown[] | Promise<unknown[]>;
528
- /** Autorización CASL */
1102
+ /** CASL authorization for read operations */
529
1103
  casl?: EntityCaslConfig;
530
- /** Prefijo de ruta para montar */
1104
+ /** API route prefix */
531
1105
  routePrefix?: string;
532
- /** Orden de visualización en UI (menor = primero, default: 999) */
1106
+ /** UI sidebar ordering (default: 999) */
533
1107
  order?: number;
1108
+ /** UI display mode: 'table', 'list', or 'masonry' */
1109
+ displayMode?: DisplayMode;
534
1110
  }
535
1111
  /**
536
- * Entidad computed - KPIs, estadísticas, métricas calculadas
537
- * Read-only, puede cachear opcionalmente
1112
+ * Computed entity - dynamically calculated data like KPIs, statistics, and metrics.
1113
+ *
1114
+ * Use for dashboards, reports, and analytics that derive from other data sources.
1115
+ * Read-only, computed on-demand with optional caching.
1116
+ *
1117
+ * @example
1118
+ * ```typescript
1119
+ * const salesMetrics: ComputedEntityDefinition = {
1120
+ * type: 'computed',
1121
+ * label: { en: 'Sales Metrics', es: 'Métricas de Ventas' },
1122
+ * isSingle: true, // returns single record with all metrics
1123
+ * cache: { ttl: 60, invalidateOn: ['db.orders.*'] },
1124
+ * compute: async (ctx) => [{
1125
+ * totalSales: await ctx.db('orders').sum('total'),
1126
+ * orderCount: await ctx.db('orders').count(),
1127
+ * avgOrderValue: await ctx.db('orders').avg('total')
1128
+ * }],
1129
+ * fields: {
1130
+ * totalSales: { name: 'totalSales', label: 'Total Sales', input: 'number' },
1131
+ * orderCount: { name: 'orderCount', label: 'Orders', input: 'number' },
1132
+ * avgOrderValue: { name: 'avgOrderValue', label: 'Avg Order', input: 'decimal' }
1133
+ * }
1134
+ * }
1135
+ * ```
538
1136
  */
539
1137
  interface ComputedEntityDefinition {
1138
+ /** Entity type identifier */
540
1139
  type: 'computed';
541
- /** Nombre para mostrar en UI */
542
- label: string;
543
- /** Campo para mostrar en selects/referencias */
1140
+ /** Display name for the UI (singular form) */
1141
+ label: LocalizedString;
1142
+ /** Display name for the UI (plural form) */
1143
+ labelPlural?: LocalizedString;
1144
+ /** Field used as display label (optional for computed entities) */
544
1145
  labelField?: string;
545
- /** Definición de campos (estructura del resultado) */
1146
+ /** Field definitions for the computed result structure */
546
1147
  fields: Record<string, FieldDefinition>;
547
- /** Función que calcula los datos */
1148
+ /** Function that computes and returns the data. Receives query params from URL */
548
1149
  compute?: (ctx: ModuleContext, params?: Record<string, unknown>) => Promise<unknown[]>;
549
- /** Configuración de cache */
1150
+ /** Caching to avoid recomputation on every request */
550
1151
  cache?: {
551
- /** TTL en segundos (0 = sin cache) */
1152
+ /** Cache time-to-live in seconds. 0 = no caching */
552
1153
  ttl: number;
553
- /** Eventos que invalidan la cache (ej: ['db.users.*']) */
1154
+ /** Event patterns that trigger cache invalidation */
554
1155
  invalidateOn?: string[];
555
1156
  };
556
- /** Autorización CASL */
1157
+ /** If true, returns single object instead of array (for dashboards, system info) */
1158
+ isSingle?: boolean;
1159
+ /** CASL authorization for read operations */
557
1160
  casl?: EntityCaslConfig;
558
- /** Prefijo de ruta para montar */
1161
+ /** API route prefix */
559
1162
  routePrefix?: string;
560
- /** Orden de visualización en UI (menor = primero, default: 999) */
1163
+ /** UI sidebar ordering (default: 999) */
561
1164
  order?: number;
1165
+ /** UI display mode: 'table', 'list', or 'masonry' */
1166
+ displayMode?: DisplayMode;
562
1167
  }
563
1168
  /**
564
- * Entidad view - Vista optimizada para lectura (projections, denormalizaciones)
565
- * Read-only, puede ser vista de BD o virtual
1169
+ * View entity - read-only database view or projection.
1170
+ *
1171
+ * Use for optimized read patterns, denormalized data, or SQL VIEWs.
1172
+ * Can reference actual database views or define virtual queries.
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * const orderSummary: ViewEntityDefinition = {
1177
+ * type: 'view',
1178
+ * table: 'v_order_summary', // actual SQL VIEW
1179
+ * label: { en: 'Order Summary', es: 'Resumen de Pedidos' },
1180
+ * labelField: 'order_number',
1181
+ * sourceEntity: 'orders',
1182
+ * query: (db) => db('orders')
1183
+ * .select('orders.*', 'customers.name as customer_name')
1184
+ * .join('customers', 'orders.customer_id', 'customers.id'),
1185
+ * fields: { ... }
1186
+ * }
1187
+ * ```
566
1188
  */
567
1189
  interface ViewEntityDefinition {
1190
+ /** Entity type identifier */
568
1191
  type: 'view';
569
- /** Tabla o vista en BD (puede ser VIEW SQL) */
1192
+ /** Database table or SQL VIEW name */
570
1193
  table: string;
571
- /** Nombre para mostrar en UI */
572
- label: string;
573
- /** Campo para mostrar en selects/referencias */
1194
+ /** Display name for the UI (singular form) */
1195
+ label: LocalizedString;
1196
+ /** Display name for the UI (plural form) */
1197
+ labelPlural?: LocalizedString;
1198
+ /** Field used as display label in lists and references */
574
1199
  labelField: string;
575
- /** Definición de campos */
1200
+ /** Field definitions for the view columns */
576
1201
  fields: Record<string, FieldDefinition>;
577
- /** Entidad fuente de la que deriva */
1202
+ /** Source entity this view derives from (for documentation) */
578
1203
  sourceEntity?: string;
579
- /** Query SQL o builder para crear la vista */
1204
+ /** SQL query string or Knex query builder function to define the view */
580
1205
  query?: string | ((db: Knex) => Knex.QueryBuilder);
581
- /** Autorización CASL */
1206
+ /** CASL authorization for read operations */
582
1207
  casl?: EntityCaslConfig;
583
- /** Prefijo de ruta para montar */
1208
+ /** API route prefix */
584
1209
  routePrefix?: string;
585
- /** Orden de visualización en UI (menor = primero, default: 999) */
1210
+ /** UI sidebar ordering (default: 999) */
586
1211
  order?: number;
587
1212
  }
588
1213
  /**
589
- * Entidad config - Configuración por módulo/tenant
590
- * Similar a single pero con scope
1214
+ * Config entity - scoped configuration settings.
1215
+ *
1216
+ * Similar to single entity but with scoping support for multi-tenant,
1217
+ * per-module, or per-user configurations.
1218
+ *
1219
+ * @example
1220
+ * ```typescript
1221
+ * const moduleConfig: ConfigEntityDefinition = {
1222
+ * type: 'config',
1223
+ * table: 'module_configs',
1224
+ * label: { en: 'Module Config', es: 'Config de Módulo' },
1225
+ * scope: 'module',
1226
+ * scopeField: 'module_name',
1227
+ * defaults: { enableFeatureX: false, maxItems: 100 },
1228
+ * timestamps: true,
1229
+ * fields: {
1230
+ * module_name: { name: 'module_name', label: 'Module', input: 'text' },
1231
+ * enableFeatureX: { name: 'enableFeatureX', label: 'Feature X', input: 'switch' },
1232
+ * maxItems: { name: 'maxItems', label: 'Max Items', input: 'number' }
1233
+ * }
1234
+ * }
1235
+ * ```
591
1236
  */
592
1237
  interface ConfigEntityDefinition extends BaseEntityDefinition {
1238
+ /** Entity type identifier */
593
1239
  type: 'config';
594
- /** Campo que identifica el scope (ej: 'module_name', 'tenant_id') */
1240
+ /** Field that identifies the configuration scope (e.g., 'module_name', 'tenant_id', 'user_id') */
595
1241
  scopeField?: string;
596
- /** Scope de la configuración */
1242
+ /** Configuration scope level: 'global', 'module', 'tenant', or 'user' */
597
1243
  scope?: 'global' | 'module' | 'tenant' | 'user';
598
- /** Valores por defecto */
1244
+ /** Default values applied when config is first accessed */
599
1245
  defaults?: Record<string, unknown>;
600
- /** Añadir updated_at */
1246
+ /** Auto-add updated_at timestamp column */
601
1247
  timestamps?: boolean;
602
- /** Añadir updated_by */
1248
+ /** Auto-add updated_by user reference column */
603
1249
  audit?: boolean;
604
1250
  }
605
1251
  /**
606
- * Entidad temporal - Cache, sesiones, OTP codes
607
- * Con TTL automático, sin auditoría
1252
+ * Temporary entity - short-lived data with automatic expiration.
1253
+ *
1254
+ * Use for OTP codes, session tokens, password reset links, cache entries.
1255
+ * Records are automatically deleted after TTL expires.
1256
+ *
1257
+ * @example
1258
+ * ```typescript
1259
+ * const otpCodes: TempEntityDefinition = {
1260
+ * type: 'temp',
1261
+ * table: 'auth_otp_codes',
1262
+ * label: { en: 'OTP Code', es: 'Código OTP' },
1263
+ * ttl: 300, // 5 minutes
1264
+ * indexes: [{ columns: ['user_id', 'code'] }],
1265
+ * fields: {
1266
+ * user_id: { name: 'user_id', label: 'User', input: 'select', ... },
1267
+ * code: { name: 'code', label: 'Code', input: 'text', ... },
1268
+ * expires_at: { name: 'expires_at', label: 'Expires', input: 'datetime', ... }
1269
+ * }
1270
+ * }
1271
+ * ```
608
1272
  */
609
1273
  interface TempEntityDefinition extends BaseEntityDefinition {
1274
+ /** Entity type identifier */
610
1275
  type: 'temp';
611
- /** Tiempo de vida en segundos */
1276
+ /** Time-to-live in seconds. Records are deleted after this duration */
612
1277
  ttl: number;
613
- /** Campo que almacena la fecha de expiración (default: 'expires_at') */
1278
+ /** Field that stores the expiration timestamp (default: 'expires_at') */
614
1279
  ttlField?: string;
615
- /** Campo para mostrar en listas (opcional) */
1280
+ /** Field used as display label in lists (optional for temp entities) */
616
1281
  labelField?: string;
617
- /** Índices para búsqueda rápida */
1282
+ /** Database indexes for efficient lookups */
618
1283
  indexes?: EntityIndex[];
619
1284
  }
620
1285
  /**
621
- * Todos los tipos de entidad disponibles
1286
+ * All available entity type identifiers.
1287
+ *
1288
+ * @see {@link EntityDefinition} for the full discriminated union with documentation
622
1289
  */
623
1290
  type EntityType = 'collection' | 'single' | 'external' | 'virtual' | 'computed' | 'view' | 'reference' | 'config' | 'event' | 'temp' | 'action';
624
1291
  /**
625
- * Union de todas las definiciones de entidad
626
- *
627
- * | Type | Persistencia | CRUD | Uso principal |
628
- * |------------|--------------|-----------------|----------------------------------|
629
- * | collection | (BD) | Completo | Datos de negocio (users, posts) |
630
- * | single | Sí (BD) | Update/Read | Config global (site_config) |
631
- * | external | No | Read | Datos externos (stripe_customers)|
632
- * | virtual | No | Read | Orquestación (unified_customers) |
633
- * | computed | No/opcional | Read | KPIs, estadísticas |
634
- * | view | Sí/virtual | Read | Lectura optimizada (projections) |
635
- * | reference | | Read (admin) | Catálogos (countries, currencies)|
636
- * | config | Sí | Update/Read | Config por módulo |
637
- * | event | | Append | Auditoría (audit_logs) |
638
- * | temp | No (TTL) | Read/Write | Cache, sesiones (otp_codes) |
639
- * | action | No | Execute | Operaciones, workflows |
1292
+ * Display mode for entity list views in the UI.
1293
+ *
1294
+ * - `table` - Data grid with columns, sorting, filtering (default)
1295
+ * - `list` - Card-based vertical list
1296
+ * - `masonry` - Pinterest-style image gallery grid
1297
+ */
1298
+ type DisplayMode = 'table' | 'list' | 'masonry';
1299
+ /**
1300
+ * Union of all entity definitions
1301
+ *
1302
+ * | Type | Persistence | CRUD | Primary use |
1303
+ * |------------|-------------|-----------------|----------------------------------|
1304
+ * | collection | Yes (DB) | Full | Business data (users, posts) |
1305
+ * | single | Yes (DB) | Update/Read | Global config (site_config) |
1306
+ * | external | No | Read | External data (stripe_customers) |
1307
+ * | virtual | No | Read | Orchestration (unified_customers)|
1308
+ * | computed | No/optional | Read | KPIs, stats |
1309
+ * | view | Yes/virtual | Read | Optimized reads (projections) |
1310
+ * | reference | Yes | Read (admin) | Catalogs (countries, currencies) |
1311
+ * | config | Yes | Update/Read | Per-module config |
1312
+ * | event | Yes | Append | Audit logs (audit_logs) |
1313
+ * | temp | No (TTL) | Read/Write | Cache, sessions (otp_codes) |
1314
+ * | action | No | Execute | Operations, workflows |
640
1315
  */
641
1316
  type EntityDefinition = CollectionEntityDefinition | SingleEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition | ViewEntityDefinition | ReferenceEntityDefinition | ConfigEntityDefinition | EventEntityDefinition | TempEntityDefinition | ActionEntityDefinition;
642
1317
  /**
643
- * Requisitos para activar un módulo
1318
+ * Requirements that must be met to activate a module.
1319
+ *
1320
+ * Modules with unmet requirements are skipped during initialization.
1321
+ *
1322
+ * @example
1323
+ * ```typescript
1324
+ * requirements: {
1325
+ * env: ['STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET'],
1326
+ * modules: ['auth', 'storage']
1327
+ * }
1328
+ * ```
644
1329
  */
645
1330
  interface ModuleRequirements {
1331
+ /** Environment variables that must be defined */
646
1332
  env?: string[];
1333
+ /** Other modules that must be loaded first */
647
1334
  modules?: string[];
648
1335
  }
649
1336
  /**
650
- * Interfaz genérica para ability CASL
651
- * Permite verificar permisos sin depender directamente de @casl/ability
1337
+ * Generic interface for CASL ability.
1338
+ *
1339
+ * Provides permission checking without directly depending on @casl/ability package.
1340
+ * Used in request objects for authorization checks.
1341
+ *
1342
+ * @example
1343
+ * ```typescript
1344
+ * if (req.ability.can('update', post)) {
1345
+ * // User can update this post
1346
+ * }
1347
+ * if (req.ability.cannot('delete', post)) {
1348
+ * throw new ForbiddenError('Cannot delete post')
1349
+ * }
1350
+ * ```
652
1351
  */
653
1352
  interface AbilityLike {
1353
+ /** Check if action is allowed on subject */
654
1354
  can: (action: string, subject: unknown) => boolean;
1355
+ /** Check if action is denied on subject */
655
1356
  cannot: (action: string, subject: unknown) => boolean;
656
1357
  }
657
1358
  /**
658
- * Error de forbidden con método throwUnlessCan
1359
+ * CASL ForbiddenError instance for throwing permission errors.
659
1360
  */
660
1361
  interface ForbiddenErrorInstance {
1362
+ /** Throws ForbiddenError if action is not allowed on subject */
661
1363
  throwUnlessCan: (action: string, subject: unknown) => void;
662
1364
  }
663
1365
  /**
664
- * Constructor de ForbiddenError con método from()
1366
+ * CASL ForbiddenError constructor for creating error instances.
665
1367
  */
666
1368
  interface ForbiddenErrorConstructor {
1369
+ /** Creates a ForbiddenErrorInstance bound to an ability */
667
1370
  from: (ability: any) => ForbiddenErrorInstance;
668
1371
  }
669
1372
  /**
670
- * Abilities CASL disponibles en el contexto
671
- * Permite usar CASL en plugins sin importar @casl/ability directamente
1373
+ * CASL abilities API available in module context.
1374
+ *
1375
+ * Allows modules to use CASL for authorization without importing @casl/ability directly.
1376
+ *
1377
+ * @example
1378
+ * ```typescript
1379
+ * // In module handler
1380
+ * const ability = ctx.abilities.defineAbilityFor(user)
1381
+ * ctx.abilities.ForbiddenError.from(ability).throwUnlessCan('update', post)
1382
+ * ```
672
1383
  */
673
1384
  interface ModuleAbilities {
674
- /** Crea una ability CASL para un usuario */
675
- defineAbilityFor: (user: BaseUser | null, ...args: unknown[]) => unknown;
676
- /** Empaqueta reglas CASL para enviar al cliente */
1385
+ /** Creates CASL ability instance for a user based on their roles */
1386
+ defineAbilityFor: (user: {
1387
+ id: string;
1388
+ } | null, ...args: unknown[]) => unknown;
1389
+ /** Serializes CASL rules for client-side authorization (frontend) */
677
1390
  packRules: (ability: unknown) => unknown[];
678
- /** Wrapper para verificar permisos contra instancias */
1391
+ /** Wraps an object as CASL subject for instance-level permission checks */
679
1392
  subject: (type: string, object: Record<string, unknown>) => unknown;
680
- /** Error de CASL para throwUnlessCan */
1393
+ /** ForbiddenError constructor for throwUnlessCan pattern */
681
1394
  ForbiddenError: ForbiddenErrorConstructor;
682
1395
  }
683
1396
  /**
684
- * AuthRequest pre-tipado para uso en plugins
685
- * Usa BaseUser y AbilityLike para tipado básico sin depender del backend
1397
+ * Pre-typed authenticated request with BaseUser and AbilityLike.
1398
+ *
1399
+ * Use this type for route handlers that require authentication.
1400
+ *
1401
+ * @example
1402
+ * ```typescript
1403
+ * const handler = async (req: AuthRequest, res: Response) => {
1404
+ * const userId = req.user.id
1405
+ * if (req.ability.can('read', 'Post')) { ... }
1406
+ * }
1407
+ * ```
686
1408
  */
687
- type PluginAuthRequest = AuthRequest<BaseUser, AbilityLike>;
1409
+ type AuthRequest = BaseAuthRequest<BaseUser, AbilityLike>;
688
1410
  /**
689
- * Helpers del contexto (migraciones, utilidades)
1411
+ * Helper utilities available in module context.
1412
+ *
1413
+ * Provides common operations for migrations, ID generation, and path resolution.
690
1414
  */
691
1415
  interface ContextHelpers {
1416
+ /** Generates a unique UUID v4 identifier */
692
1417
  generateId: () => string;
1418
+ /** Adds created_at and updated_at columns to a table during migration */
693
1419
  addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void;
1420
+ /** Adds created_by and updated_by columns if they don't exist */
694
1421
  addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise<void>;
695
- /** Añade campo is_default a tablas de tipo config */
1422
+ /** Adds is_default boolean field for config tables */
696
1423
  addConfigDefaultField: (db: Knex, tableName: string) => Promise<void>;
1424
+ /** Adds a column only if it doesn't exist. Returns true if column was added */
697
1425
  addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise<boolean>;
1426
+ /** Gets the path to the nexus-backend library directory */
698
1427
  getLibPath: () => string;
1428
+ /** Gets the path to the user's project root directory */
699
1429
  getProjectPath: () => string;
700
- /** Timestamp formateado para el driver de BD actual (MySQL: 'YYYY-MM-DD HH:MM:SS', otros: ISO 8601) */
1430
+ /** Returns current timestamp formatted for the DB driver (MySQL: 'YYYY-MM-DD HH:MM:SS', others: ISO 8601) */
701
1431
  nowTimestamp: (db: Knex) => string;
702
- /** Formatea una fecha para el driver de BD actual */
1432
+ /** Formats a Date object for the current DB driver */
703
1433
  formatTimestamp: (db: Knex, date?: Date) => string;
704
1434
  }
705
1435
  /**
706
- * Utilidades criptográficas del contexto
1436
+ * Cryptographic utilities for password handling.
1437
+ *
1438
+ * Uses bcrypt with cost factor 12 for secure password hashing.
707
1439
  */
708
1440
  interface ContextCrypto {
709
- /** Hash de password usando bcrypt (cost factor 12) */
1441
+ /** Hashes a password using bcrypt (cost factor 12) */
710
1442
  hashPassword: (password: string) => Promise<string>;
711
- /** Verifica password contra hash bcrypt (timing-safe) */
1443
+ /** Verifies password against bcrypt hash (timing-safe comparison) */
712
1444
  verifyPassword: (password: string, hash: string) => Promise<boolean>;
713
- /** Hash dummy para timing-safe comparison cuando usuario no existe */
1445
+ /** Pre-computed dummy hash for timing-safe comparison when user doesn't exist */
714
1446
  DUMMY_HASH: string;
715
1447
  }
716
1448
  /**
717
- * API de Socket.IO del contexto
718
- * @note Solo disponible si Socket.IO está inicializado
1449
+ * Socket.IO real-time communication API.
1450
+ *
1451
+ * Provides access to WebSocket connections for real-time features.
1452
+ * Only available if Socket.IO is initialized in the backend configuration.
719
1453
  */
720
1454
  interface ContextSocket {
721
- /** Obtiene la instancia de Socket.IO (throws si no inicializado) */
1455
+ /** Gets the Socket.IO server instance. Throws if not initialized */
722
1456
  getIO: () => unknown;
723
- /** Verifica si Socket.IO está inicializado */
1457
+ /** Checks if Socket.IO server is running */
724
1458
  isInitialized: () => boolean;
725
- /** Verifica si un usuario específico está conectado */
1459
+ /** Checks if a specific user has active WebSocket connections */
726
1460
  isUserConnected: (userId: string) => boolean;
727
- /** Obtiene el número de sockets conectados para un usuario */
1461
+ /** Gets the number of active sockets for a user (multiple tabs/devices) */
728
1462
  getUserSocketCount: (userId: string) => number;
729
- /** Obtiene los IDs de usuarios conectados */
1463
+ /** Gets array of all connected user IDs */
730
1464
  getConnectedUsers: () => string[];
731
1465
  }
732
1466
  /**
733
- * API del Engine para introspección de módulos y plugins
734
- * Permite acceder al registro de módulos y plugins cargados
1467
+ * Application manifest with module and plugin registry.
1468
+ *
1469
+ * Represents either the core nexus-backend manifest or a user project manifest.
1470
+ */
1471
+ interface AppManifest {
1472
+ /** Package name from package.json */
1473
+ name: string;
1474
+ /** Package version from package.json */
1475
+ version: string;
1476
+ /** Registered modules in this application */
1477
+ modules: ModuleManifest[];
1478
+ /** Registered plugins (only present in user manifest) */
1479
+ plugins?: PluginManifest[];
1480
+ }
1481
+ /**
1482
+ * Engine API for runtime introspection of modules and plugins.
1483
+ *
1484
+ * Provides access to the complete registry of loaded components.
735
1485
  */
736
1486
  interface ContextEngine {
737
- /** Obtiene todos los módulos registrados */
1487
+ /** Gets all registered modules (core + user) */
738
1488
  getModules: () => ModuleManifest[];
739
- /** Obtiene todos los plugins registrados */
1489
+ /** Gets all registered plugins */
740
1490
  getPlugins: () => PluginManifest[];
741
- /** Obtiene los subjects CASL de un módulo específico */
1491
+ /** Gets CASL subjects defined by a specific module */
742
1492
  getModuleSubjects: (mod: ModuleManifest) => string[];
743
- /** Obtiene todos los subjects CASL registrados en el sistema */
1493
+ /** Gets all CASL subjects registered across all modules */
744
1494
  getRegisteredSubjects: () => string[];
745
- }
746
- /**
747
- * Schema de validación genérico (compatible con Zod sin depender de él)
748
- * Cualquier objeto con método parse() es válido
1495
+ /** Gets modules from nexus-backend core (auth, users, storage, etc.) */
1496
+ getCoreModules: () => ModuleManifest[];
1497
+ /** Gets modules from user project and plugins */
1498
+ getUserModules: () => ModuleManifest[];
1499
+ /** Gets the nexus-backend library manifest */
1500
+ getCoreManifest: () => AppManifest;
1501
+ /** Gets the user project manifest. Returns null in standalone development mode */
1502
+ getUserManifest: () => AppManifest | null;
1503
+ /** Returns true if running as a library inside a user project */
1504
+ hasUserApp: () => boolean;
1505
+ }
1506
+ /**
1507
+ * Generic validation schema interface compatible with Zod.
1508
+ *
1509
+ * Any object with a parse() method that throws on invalid data works.
749
1510
  */
750
1511
  interface ValidationSchema {
1512
+ /** Parses and validates data. Throws on validation failure */
751
1513
  parse: (data: unknown) => unknown;
752
1514
  }
753
1515
  /**
754
- * Opciones para el middleware de validación
1516
+ * Schemas for the validation middleware.
1517
+ *
1518
+ * Validates request body, query parameters, and URL parameters.
755
1519
  */
756
1520
  interface ValidateSchemas {
1521
+ /** Schema for request body (req.body) */
757
1522
  body?: ValidationSchema;
1523
+ /** Schema for query parameters (req.query) */
758
1524
  query?: ValidationSchema;
1525
+ /** Schema for URL parameters (req.params) */
759
1526
  params?: ValidationSchema;
760
1527
  }
761
1528
  /**
762
- * Middlewares disponibles en el contexto
1529
+ * Configuration options for rate limiting middleware.
1530
+ */
1531
+ interface RateLimitOptions {
1532
+ /** Time window duration in milliseconds (default: 60000 = 1 minute) */
1533
+ windowMs?: number;
1534
+ /** Maximum requests allowed per window (default: 100) */
1535
+ max?: number;
1536
+ /** Error message when rate limit is exceeded */
1537
+ message?: string;
1538
+ }
1539
+ /**
1540
+ * Middleware factories available in module context.
1541
+ *
1542
+ * Provides common Express middlewares for validation, rate limiting, etc.
763
1543
  */
764
1544
  interface ModuleMiddlewares {
1545
+ /** Creates validation middleware for body/query/params */
765
1546
  validate: (schemas: ValidateSchemas) => RequestHandler;
1547
+ /** Creates rate limiting middleware */
1548
+ rateLimit: (options?: RateLimitOptions) => RequestHandler;
1549
+ /** Extensible: additional middlewares can be registered */
766
1550
  [key: string]: RequestHandler | ((...args: any[]) => RequestHandler) | undefined;
767
1551
  }
768
1552
  /**
769
- * Interfaz mínima de EventEmitter compatible con EventEmitter2
1553
+ * Minimal EventEmitter interface compatible with EventEmitter2.
1554
+ *
1555
+ * Used for inter-module communication and event-driven architecture.
770
1556
  */
771
1557
  interface EventEmitterLike {
1558
+ /** Emits an event with arguments. Returns true if listeners exist */
772
1559
  emit: (event: string, ...args: unknown[]) => boolean;
1560
+ /** Registers an event listener */
773
1561
  on: (event: string, listener: (...args: unknown[]) => void) => this;
1562
+ /** Removes an event listener */
774
1563
  off: (event: string, listener: (...args: unknown[]) => void) => this;
1564
+ /** Registers a one-time event listener */
775
1565
  once: (event: string, listener: (...args: unknown[]) => void) => this;
1566
+ /** Registers a listener for all events (EventEmitter2 feature) */
776
1567
  onAny?: (listener: (event: string, ...args: unknown[]) => void) => this;
777
1568
  }
778
1569
  /**
779
- * Reporter mínimo para errores (Sentry/GlitchTip, etc.)
1570
+ * Error reporter interface for external monitoring services.
1571
+ *
1572
+ * Compatible with Sentry, GlitchTip, or any error tracking service.
780
1573
  */
781
1574
  interface LoggerReporter {
1575
+ /** Captures and reports an exception to the monitoring service */
782
1576
  captureException: (error: Error, context?: Record<string, unknown>) => void;
783
1577
  }
1578
+ /**
1579
+ * Filter operators for advanced filtering
1580
+ *
1581
+ * @example
1582
+ * // Simple equality (shorthand)
1583
+ * { status: 'active' }
1584
+ *
1585
+ * // With operators
1586
+ * { name: { $contains: 'john' } }
1587
+ * { age: { $gte: 18 } }
1588
+ * { email: { $startswith: 'admin' } }
1589
+ *
1590
+ * // Array (IN operator shorthand)
1591
+ * { status: ['active', 'pending'] }
1592
+ */
1593
+ interface FilterOperators {
1594
+ /** Equal (default if no operator) */
1595
+ $eq?: unknown;
1596
+ /** Not equal */
1597
+ $ne?: unknown;
1598
+ /** Greater than */
1599
+ $gt?: number | string | Date;
1600
+ /** Greater than or equal */
1601
+ $gte?: number | string | Date;
1602
+ /** Less than */
1603
+ $lt?: number | string | Date;
1604
+ /** Less than or equal */
1605
+ $lte?: number | string | Date;
1606
+ /** Contains (LIKE %value%) */
1607
+ $contains?: string;
1608
+ /** Starts with (LIKE value%) */
1609
+ $startswith?: string;
1610
+ /** Ends with (LIKE %value) */
1611
+ $endswith?: string;
1612
+ /** In array */
1613
+ $in?: unknown[];
1614
+ /** Not in array */
1615
+ $nin?: unknown[];
1616
+ /** Is null (true = IS NULL, false = IS NOT NULL) */
1617
+ $isnull?: boolean;
1618
+ }
1619
+ /**
1620
+ * Filter value can be:
1621
+ * - Primitive (shorthand for $eq)
1622
+ * - Array (shorthand for $in)
1623
+ * - Object with operators
1624
+ */
1625
+ type FilterValue = string | number | boolean | null | unknown[] | FilterOperators;
784
1626
  /**
785
1627
  * Query parameters for entity list operations
1628
+ *
1629
+ * @example
1630
+ * // Simple filters
1631
+ * { filters: { status: 'active' } }
1632
+ *
1633
+ * // With operators (see FilterOperators)
1634
+ * { filters: { name: { $contains: 'john' }, age: { $gte: 18 } } }
786
1635
  */
787
1636
  interface EntityQuery {
788
1637
  page?: number;
@@ -790,52 +1639,92 @@ interface EntityQuery {
790
1639
  sort?: string;
791
1640
  order?: 'asc' | 'desc';
792
1641
  search?: string;
1642
+ /** Filters with optional operators - use FilterValue type for type safety */
793
1643
  filters?: Record<string, unknown>;
794
1644
  }
795
1645
  /**
796
- * Interfaz base para sub-servicio de roles
797
- * El backend extiende esto con tipos concretos
1646
+ * Role management service interface.
1647
+ *
1648
+ * Provides CRUD operations for roles. Used internally by UsersService.
798
1649
  */
799
1650
  interface BaseRolesService {
1651
+ /** Gets paginated list of all roles */
800
1652
  findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
1653
+ /** Gets a role by its ID */
801
1654
  findById(id: string): Promise<unknown>;
1655
+ /** Gets a role by its name (e.g., 'ADMIN', 'EDITOR') */
802
1656
  findByName(name: string): Promise<unknown | undefined>;
803
- getPermissionsByRoleId(roleId: string): Promise<unknown[]>;
1657
+ /** Creates a new role */
804
1658
  create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
1659
+ /** Updates an existing role */
805
1660
  update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
1661
+ /** Deletes a role by ID */
806
1662
  delete(id: string): Promise<void>;
807
- updatePermissions(id: string, permissions: unknown[], userId?: string): Promise<unknown>;
808
1663
  }
809
1664
  /**
810
- * Interfaz base para servicio de usuarios
811
- * El backend extiende esto con tipos concretos (UserWithRole, etc.)
1665
+ * User management service interface.
1666
+ *
1667
+ * Provides user CRUD and role assignment operations.
1668
+ * The backend extends this with concrete types (UserWithRole, etc.).
812
1669
  */
813
1670
  interface BaseUsersService {
1671
+ /** Gets paginated list of users */
814
1672
  findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
1673
+ /** Gets a user by ID without role information */
815
1674
  findById(id: string): Promise<unknown>;
816
- findByIdWithRole(id: string): Promise<unknown | null>;
1675
+ /** Gets a user by ID including their assigned roles */
1676
+ findByIdWithRoles(id: string): Promise<unknown | null>;
1677
+ /** Creates a new user */
817
1678
  create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
1679
+ /** Updates an existing user */
818
1680
  update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
1681
+ /** Deletes a user by ID */
819
1682
  delete(id: string): Promise<void>;
1683
+ /** Gets array of role IDs for a user */
1684
+ getRoleIds(userId: string): Promise<string[]>;
1685
+ /** Gets array of role names for a user (e.g., ['ADMIN', 'EDITOR']) */
1686
+ getRoleNames(userId: string): Promise<string[]>;
1687
+ /** Gets full role objects for a user */
1688
+ getUserRoles(userId: string): Promise<unknown[]>;
1689
+ /** Assigns a single role to a user (adds to existing roles) */
1690
+ assignRole(userId: string, roleId: string): Promise<void>;
1691
+ /** Removes a single role from a user */
1692
+ removeRole(userId: string, roleId: string): Promise<void>;
1693
+ /** Replaces all user roles with the provided list */
1694
+ setRoles(userId: string, roleIds: string[]): Promise<void>;
1695
+ /** Nested roles service for direct role management */
820
1696
  roles: BaseRolesService;
821
1697
  }
822
1698
  /**
823
- * Opciones para envío de email
1699
+ * Email sending options.
1700
+ *
1701
+ * Supports both simple text emails and rich HTML templates with actions.
824
1702
  */
825
1703
  interface SendMailOptions {
1704
+ /** Recipient email address(es) */
826
1705
  to: string | string[];
1706
+ /** Email subject line */
827
1707
  subject: string;
1708
+ /** Title for email template (displayed prominently) */
828
1709
  title?: string;
1710
+ /** Plain text message for simple emails */
829
1711
  message?: string;
1712
+ /** Full HTML content (overrides template) */
830
1713
  html?: string;
1714
+ /** Plain text fallback (generated from HTML if not provided) */
831
1715
  text?: string;
1716
+ /** Sender email address (uses default if not provided) */
832
1717
  from?: string;
1718
+ /** Reply-to email address */
833
1719
  replyTo?: string;
1720
+ /** CTA buttons for email template */
834
1721
  actions?: Array<{
835
1722
  label: string;
836
1723
  url: string;
837
1724
  }>;
1725
+ /** Logo URL for email header */
838
1726
  logoUrl?: string;
1727
+ /** File attachments */
839
1728
  attachments?: Array<{
840
1729
  filename: string;
841
1730
  content: Buffer | string;
@@ -843,290 +1732,642 @@ interface SendMailOptions {
843
1732
  }>;
844
1733
  }
845
1734
  /**
846
- * Resultado del envío de email
1735
+ * Result from sending an email.
847
1736
  */
848
1737
  interface SendMailResult {
1738
+ /** Unique message ID from the mail server */
849
1739
  messageId: string;
1740
+ /** Email addresses that accepted the message */
850
1741
  accepted: string[];
1742
+ /** Email addresses that rejected the message */
851
1743
  rejected: string[];
852
1744
  }
853
1745
  /**
854
- * Interfaz base para servicio de email
1746
+ * Email service interface for sending emails.
855
1747
  */
856
1748
  interface BaseMailService {
1749
+ /** Sends an email. Returns null if mail service is not configured */
857
1750
  send(options: SendMailOptions): Promise<SendMailResult | null>;
1751
+ /** Verifies SMTP connection is working */
858
1752
  verify(): Promise<boolean>;
859
1753
  }
860
1754
  /**
861
- * Servicios core disponibles en ctx.services
862
- * Los plugins pueden extender con servicios adicionales
1755
+ * Core services available via ctx.services.
1756
+ *
1757
+ * Modules can extend this with additional services.
863
1758
  */
864
1759
  interface CoreServices {
1760
+ /** Error logging and reporting service */
865
1761
  logger?: LoggerReporter;
1762
+ /** User management service */
866
1763
  users?: BaseUsersService;
1764
+ /** Email sending service */
867
1765
  mail?: BaseMailService;
1766
+ /** Extensible: additional services registered by modules */
868
1767
  [key: string]: unknown;
869
1768
  }
870
1769
  /**
871
- * Interfaz base para todos los servicios de entidades
1770
+ * Base interface for all entity services.
1771
+ *
1772
+ * Provides read operations common to all entity types.
872
1773
  */
873
1774
  interface BaseEntityService<T = unknown> {
1775
+ /** Gets paginated list of records with optional filtering/sorting */
874
1776
  findAll(query?: EntityQuery): Promise<PaginatedResult<T>>;
1777
+ /** Gets a single record by ID. Returns null if not found */
875
1778
  findById(id: string): Promise<T | null>;
1779
+ /** The entity definition this service operates on */
876
1780
  readonly definition: EntityDefinition;
877
1781
  }
878
1782
  /**
879
- * Servicio para entidades Collection (CRUD completo)
1783
+ * Service for Collection entities with full CRUD operations.
1784
+ *
1785
+ * @see {@link CollectionEntityDefinition}
880
1786
  */
881
1787
  interface CollectionEntityService<T = unknown> extends BaseEntityService<T> {
1788
+ /** Creates a new record. userId is used for audit fields */
882
1789
  create(data: Partial<T>, userId?: string): Promise<T>;
1790
+ /** Updates an existing record by ID */
883
1791
  update(id: string, data: Partial<T>, userId?: string): Promise<T>;
1792
+ /** Deletes a record by ID (soft delete if configured) */
884
1793
  delete(id: string): Promise<void>;
885
1794
  }
886
1795
  /**
887
- * Servicio para entidades Temp (TTL auto-cleanup)
1796
+ * Service for Temp entities with automatic TTL expiration.
1797
+ *
1798
+ * @see {@link TempEntityDefinition}
888
1799
  */
889
1800
  interface TempEntityService<T = unknown> extends BaseEntityService<T> {
1801
+ /** Creates a temporary record with automatic expiration */
890
1802
  create(data: Partial<T>): Promise<T>;
1803
+ /** Updates a record. Optionally extends TTL from current time */
891
1804
  update(id: string, data: Partial<T>, extendTtl?: boolean): Promise<T>;
1805
+ /** Deletes a record immediately */
892
1806
  delete(id: string): Promise<void>;
1807
+ /** Extends the TTL of a record by additional seconds */
893
1808
  extendTtl(id: string, additionalSeconds?: number): Promise<T>;
1809
+ /** Removes all expired records. Returns count of deleted records */
894
1810
  cleanup(): Promise<number>;
1811
+ /** Gets remaining TTL in seconds. Returns null if expired or not found */
895
1812
  getRemainingTtl(id: string): Promise<number | null>;
1813
+ /** Checks if a record exists and hasn't expired */
896
1814
  isValid(id: string): Promise<boolean>;
897
1815
  }
898
1816
  /**
899
- * Servicio para entidades Event (append-only, immutable)
1817
+ * Service for Event entities (append-only audit logs).
1818
+ *
1819
+ * @see {@link EventEntityDefinition}
900
1820
  */
901
1821
  interface EventEntityService<T = unknown> extends BaseEntityService<T> {
1822
+ /** Creates a new event record (append-only) */
902
1823
  create(data: Partial<T>): Promise<T>;
1824
+ /** Alias for create - appends a new event */
903
1825
  append(data: Partial<T>): Promise<T>;
1826
+ /** Runs retention policy cleanup. Returns count of deleted records */
904
1827
  runRetentionCleanup(): Promise<number>;
1828
+ /** Queries events within a date range */
905
1829
  findByDateRange(start: Date, end: Date, query?: EntityQuery): Promise<PaginatedResult<T>>;
906
1830
  }
907
1831
  /**
908
- * Servicio para entidades Single (registro único, update/read)
1832
+ * Service for Single entities (global configuration).
1833
+ *
1834
+ * @see {@link SingleEntityDefinition}
909
1835
  */
910
1836
  interface SingleEntityService<T = unknown> extends BaseEntityService<T> {
1837
+ /** Gets the single record (creates with defaults if not exists) */
911
1838
  get(): Promise<T | null>;
1839
+ /** Updates the single record */
912
1840
  update(data: Partial<T>, userId?: string): Promise<T>;
913
1841
  }
914
1842
  /**
915
- * Servicio para entidades Config (configuración clave-valor)
1843
+ * Service for Config entities (scoped key-value configuration).
1844
+ *
1845
+ * @see {@link ConfigEntityDefinition}
916
1846
  */
917
1847
  interface ConfigEntityService<T = unknown> extends BaseEntityService<T> {
1848
+ /** Gets configuration value by key */
918
1849
  get(key: string): Promise<T | null>;
1850
+ /** Sets configuration value for a key */
919
1851
  set(key: string, value: unknown, userId?: string): Promise<T>;
1852
+ /** Gets all configuration as key-value object */
920
1853
  getAll(): Promise<Record<string, unknown>>;
921
1854
  }
922
- /** Servicio para entidades Reference (lectura con join automático) */
1855
+ /**
1856
+ * Service for Reference entities (read-only catalogs).
1857
+ * @see {@link ReferenceEntityDefinition}
1858
+ */
923
1859
  type ReferenceEntityService<T = unknown> = BaseEntityService<T>;
924
- /** Servicio para entidades View (vista SQL, solo lectura) */
1860
+ /**
1861
+ * Service for View entities (SQL views, read-only).
1862
+ * @see {@link ViewEntityDefinition}
1863
+ */
925
1864
  type ViewEntityService<T = unknown> = BaseEntityService<T>;
926
- /** Servicio para entidades Computed (campos calculados, solo lectura) */
1865
+ /**
1866
+ * Service for Computed entities (dynamically calculated data).
1867
+ * @see {@link ComputedEntityDefinition}
1868
+ */
927
1869
  type ComputedEntityService<T = unknown> = BaseEntityService<T>;
928
- /** Servicio para entidades External (datos de API externa) */
1870
+ /**
1871
+ * Service for External entities (external API data).
1872
+ * @see {@link ExternalEntityDefinition}
1873
+ */
929
1874
  type ExternalEntityService<T = unknown> = BaseEntityService<T>;
930
- /** Servicio para entidades Virtual (orquestación de múltiples fuentes) */
1875
+ /**
1876
+ * Service for Virtual entities (aggregated from multiple sources).
1877
+ * @see {@link VirtualEntityDefinition}
1878
+ */
931
1879
  type VirtualEntityService<T = unknown> = BaseEntityService<T>;
932
1880
  /**
933
- * Servicio para entidades Action (ejecutar acciones)
1881
+ * Service for Action entities (command execution).
1882
+ *
1883
+ * @see {@link ActionEntityDefinition}
934
1884
  */
935
1885
  interface ActionEntityService<T = unknown> {
1886
+ /** Executes the action with provided input data */
936
1887
  execute(data: Partial<T>): Promise<unknown>;
1888
+ /** The action definition this service implements */
937
1889
  readonly definition: EntityDefinition;
938
1890
  }
939
1891
  /**
940
- * Hooks para personalizar comportamiento de servicios de entidad
941
- * Permite a plugins añadir lógica sin necesitar herencia
1892
+ * Hooks to customize entity service behavior.
1893
+ * Allows plugins to add logic without inheritance.
942
1894
  */
943
1895
  interface EntityServiceHooks<T = unknown> {
944
1896
  /**
945
- * Ejecutado antes de crear un registro
946
- * @param data Datos a insertar
947
- * @returns Datos modificados
1897
+ * Runs before creating a record.
1898
+ * @param data Data to insert
1899
+ * @returns Modified data
948
1900
  */
949
1901
  beforeCreate?: (data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
950
1902
  /**
951
- * Ejecutado después de crear un registro
952
- * @param record Registro creado
1903
+ * Runs after creating a record.
1904
+ * @param record Created record
953
1905
  */
954
1906
  afterCreate?: (record: T) => Promise<void> | void;
955
1907
  /**
956
- * Ejecutado antes de actualizar un registro
957
- * @param id ID del registro
958
- * @param data Datos a actualizar
959
- * @returns Datos modificados
1908
+ * Runs before updating a record.
1909
+ * @param id Record ID
1910
+ * @param data Data to update
1911
+ * @returns Modified data
960
1912
  */
961
1913
  beforeUpdate?: (id: string, data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
962
1914
  /**
963
- * Ejecutado después de actualizar un registro
964
- * @param record Registro actualizado
1915
+ * Runs after updating a record.
1916
+ * @param record Updated record
965
1917
  */
966
1918
  afterUpdate?: (record: T) => Promise<void> | void;
967
1919
  /**
968
- * Ejecutado antes de eliminar un registro
969
- * @param id ID del registro
1920
+ * Runs before deleting a record.
1921
+ * @param id Record ID
970
1922
  */
971
1923
  beforeDelete?: (id: string) => Promise<void> | void;
972
1924
  /**
973
- * Ejecutado después de eliminar un registro
974
- * @param id ID eliminado
1925
+ * Runs after deleting a record.
1926
+ * @param id Deleted ID
975
1927
  */
976
1928
  afterDelete?: (id: string) => Promise<void> | void;
977
1929
  /**
978
- * Ejecutado después de obtener un registro por ID
979
- * @param record Registro obtenido (o null)
980
- * @returns Registro modificado
1930
+ * Runs after getting a record by ID.
1931
+ * @param record Retrieved record (or null)
1932
+ * @returns Modified record
981
1933
  */
982
1934
  afterFindById?: (record: T | null) => Promise<T | null> | T | null;
983
1935
  /**
984
- * Ejecutado después de obtener lista paginada
985
- * @param result Resultado paginado
986
- * @returns Resultado modificado
1936
+ * Runs after retrieving a paginated list.
1937
+ * @param result Paginated result
1938
+ * @returns Modified result
987
1939
  */
988
1940
  afterFindAll?: (result: PaginatedResult<T>) => Promise<PaginatedResult<T>> | PaginatedResult<T>;
989
1941
  }
990
1942
  /**
991
- * Opciones para createEntityService
1943
+ * Options for createEntityService
992
1944
  */
993
1945
  interface CreateEntityServiceOptions<T = unknown> {
994
- /** Hooks para personalizar comportamiento */
1946
+ /** Hooks to customize behavior */
995
1947
  hooks?: EntityServiceHooks<T>;
996
1948
  }
997
1949
  /**
998
- * Handler de entidad para Express
1950
+ * Express route handler type for entity operations.
999
1951
  */
1000
1952
  type EntityHandler = (req: Request, res: Response) => Promise<void>;
1001
1953
  /**
1002
- * Controller de entidad con handlers CRUD
1954
+ * Entity controller with CRUD operation handlers.
1955
+ *
1956
+ * Generated by `createEntityController` with built-in CASL authorization.
1003
1957
  */
1004
1958
  interface EntityController {
1959
+ /** GET / - List all records with pagination */
1005
1960
  list: EntityHandler;
1961
+ /** GET /:id - Get single record by ID */
1006
1962
  get: EntityHandler;
1963
+ /** POST / - Create new record (collection entities) */
1007
1964
  create?: EntityHandler;
1965
+ /** PUT /:id - Update existing record */
1008
1966
  update?: EntityHandler;
1967
+ /** DELETE /:id - Delete record */
1009
1968
  delete?: EntityHandler;
1969
+ /** POST /:id/:action - Execute entity action */
1010
1970
  execute?: EntityHandler;
1011
1971
  }
1012
1972
  /**
1013
- * Contexto inyectado a los módulos
1973
+ * Module context providing access to all Nexus services and utilities.
1974
+ *
1975
+ * Injected into module lifecycle functions (init, routes, migrate, seed).
1976
+ *
1977
+ * @example
1978
+ * ```typescript
1979
+ * const myModule: ModuleManifest = {
1980
+ * name: 'my-module',
1981
+ * routes: (ctx) => {
1982
+ * const router = ctx.createRouter()
1983
+ * router.get('/hello', (req, res) => res.json({ message: 'Hello!' }))
1984
+ * return router
1985
+ * }
1986
+ * }
1987
+ * ```
1014
1988
  */
1015
1989
  interface ModuleContext {
1990
+ /** Knex database connection for queries and transactions */
1016
1991
  db: Knex;
1992
+ /** Pino logger instance for structured logging */
1017
1993
  logger: Logger;
1994
+ /** Helper utilities for migrations and common operations */
1018
1995
  helpers: ContextHelpers;
1019
- /** Utilidades criptográficas (hashPassword, verifyPassword) */
1996
+ /** Cryptographic utilities (password hashing) */
1020
1997
  crypto: ContextCrypto;
1021
- /** API de Socket.IO para comunicación en tiempo real */
1998
+ /** Socket.IO API for real-time communication */
1022
1999
  socket: ContextSocket;
1023
- /** API del Engine para introspección de módulos y plugins */
2000
+ /** Engine API for module/plugin introspection */
1024
2001
  engine: ContextEngine;
2002
+ /** Factory for creating Express routers */
1025
2003
  createRouter: () => Router;
2004
+ /** Middleware factories (validate, rateLimit, etc.) */
1026
2005
  middleware: ModuleMiddlewares;
1027
- /** Configuración resuelta de la aplicación */
2006
+ /** Resolved application configuration from env and config files */
1028
2007
  config: Record<string, unknown>;
2008
+ /** HTTP error constructors for consistent error responses */
1029
2009
  errors: {
2010
+ /** Generic application error with status code */
1030
2011
  AppError: new (message: string, statusCode?: number, details?: unknown) => Error & {
1031
2012
  details?: unknown;
1032
2013
  };
2014
+ /** 404 Not Found error */
1033
2015
  NotFoundError: new (message?: string) => Error;
2016
+ /** 401 Unauthorized error */
1034
2017
  UnauthorizedError: new (message?: string) => Error;
2018
+ /** 403 Forbidden error */
1035
2019
  ForbiddenError: new (message?: string) => Error;
2020
+ /** 409 Conflict error */
1036
2021
  ConflictError: new (message?: string) => Error;
1037
- ValidationError: new (message?: string, errors?: unknown[]) => Error & {
1038
- errors: unknown[];
2022
+ /** 400 Validation error with field details */
2023
+ ValidationError: new (message?: string, details?: ValidationDetail[]) => Error & {
2024
+ details: ValidationDetail[];
1039
2025
  };
1040
2026
  };
2027
+ /** CASL abilities API for authorization */
1041
2028
  abilities: ModuleAbilities;
1042
- /** Sistema de eventos compatible con EventEmitter2 */
2029
+ /** Event emitter for inter-module communication */
1043
2030
  events: EventEmitterLike;
1044
- /** Servicios de módulos (inyectados por backend) */
2031
+ /** Core services (users, mail, logger) */
1045
2032
  services: CoreServices;
1046
- /** Factory para crear servicios de entidades (detecta tipo automáticamente) */
2033
+ /** Creates entity service based on definition type (auto-detects) */
1047
2034
  createEntityService<T = unknown>(definition: EntityDefinition, options?: CreateEntityServiceOptions<T>): BaseEntityService<T>;
1048
- /** Factory para crear controller de entidad con CASL y validación */
2035
+ /** Creates entity controller with CASL authorization and validation */
1049
2036
  createEntityController(service: BaseEntityService, definition: EntityDefinition): EntityController;
1050
- /** Factory para crear router de entidad con rutas CRUD */
2037
+ /** Creates Express router with standard CRUD routes */
1051
2038
  createEntityRouter(controller: EntityController, definition: EntityDefinition): Router;
1052
2039
  }
1053
2040
  /**
1054
- * Manifest de un módulo Nexus
2041
+ * Module manifest defining a Nexus module.
2042
+ *
2043
+ * Modules are the core building blocks of Nexus applications.
2044
+ * Each module can define entities, routes, migrations, and services.
2045
+ *
2046
+ * @example
2047
+ * ```typescript
2048
+ * const postsModule: ModuleManifest = {
2049
+ * name: 'posts',
2050
+ * label: { en: 'Posts', es: 'Publicaciones' },
2051
+ * icon: 'mdi:post',
2052
+ * category: 'content',
2053
+ * definitions: [postsEntity, categoriesEntity],
2054
+ * routes: (ctx) => {
2055
+ * const router = ctx.createRouter()
2056
+ * // Custom routes here
2057
+ * return router
2058
+ * }
2059
+ * }
2060
+ * ```
1055
2061
  */
1056
2062
  interface ModuleManifest {
1057
- /** Identificador único del módulo (ej: 'users', 'posts') */
2063
+ /** Unique module identifier (e.g., 'users', 'posts', 'cms') */
1058
2064
  name: string;
1059
- /** Nombre para mostrar en UI. Opcional si pertenece a un plugin */
1060
- label?: string;
1061
- /** Icono del módulo (Iconify MDI: 'mdi:account-group') */
2065
+ /** Display name for UI (singular). Optional if module belongs to a plugin */
2066
+ label?: LocalizedString;
2067
+ /** Display name for UI (plural, used in menus and lists) */
2068
+ labelPlural?: LocalizedString;
2069
+ /** Iconify MDI icon identifier (e.g., 'mdi:account-group') */
1062
2070
  icon?: string;
1063
- /** Descripción del módulo */
1064
- description?: string;
1065
- /** Tipo de módulo */
2071
+ /** Module description for documentation */
2072
+ description?: LocalizedString;
2073
+ /** Module type: 'core' (built-in), 'plugin' (from plugin), 'auth-plugin', or 'custom' */
1066
2074
  type?: 'core' | 'plugin' | 'auth-plugin' | 'custom';
1067
- /** Categoría del módulo para agrupar en sidebar */
2075
+ /** Category for sidebar grouping */
1068
2076
  category: Category;
1069
- /** Dependencias de otros módulos */
2077
+ /** Other module names that must be loaded before this one */
1070
2078
  dependencies?: string[];
1071
- /** Requisitos para activar el módulo */
2079
+ /** Requirements that must be met to enable this module */
1072
2080
  required?: ModuleRequirements;
1073
- /** Función de migración de base de datos. Normalmente se genera desde definitions (EntityDefinition) */
2081
+ /** Database migration function. Usually auto-generated from entity definitions */
1074
2082
  migrate?: (ctx: ModuleContext) => Promise<void>;
1075
- /** Función de seed de datos iniciales */
2083
+ /** Seed function for initial/reference data */
1076
2084
  seed?: (ctx: ModuleContext) => Promise<void>;
1077
- /** Inicialización del módulo */
2085
+ /** Initialization function called on app startup */
1078
2086
  init?: (ctx: ModuleContext) => void;
1079
- /** Factory de rutas del módulo */
2087
+ /** Factory function returning Express router with custom routes */
1080
2088
  routes?: (ctx: ModuleContext) => Router;
1081
- /** Prefijo de rutas (default: /{name}) */
2089
+ /** API route prefix. Default: `/{name}` */
1082
2090
  routePrefix?: string;
1083
- /**
1084
- * Definiciones de entidades (nuevo sistema unificado).
1085
- * Single source of truth para BD, validación, UI y CASL.
1086
- * Sus subjects se registran automáticamente.
1087
- */
2091
+ /** Entity definitions - single source of truth for DB, validation, UI, and CASL */
1088
2092
  definitions?: EntityDefinition[];
1089
2093
  }
1090
2094
  /**
1091
- * Categorías disponibles para plugins y módulos
2095
+ * Available categories for organizing modules in the UI sidebar.
2096
+ *
2097
+ * @see {@link CATEGORIES} for metadata (labels, icons, order)
1092
2098
  */
1093
- type Category = 'content' | 'data' | 'storage' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'security';
2099
+ type Category = 'content' | 'data' | 'assets' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'security' | 'legal' | 'settings';
1094
2100
  /**
1095
- * Metadata de una categoría
2101
+ * Metadata for a category including display information.
1096
2102
  */
1097
2103
  interface CategoryMeta {
1098
- label: string;
2104
+ /** Localized display label */
2105
+ label: LocalizedString;
2106
+ /** Iconify MDI icon identifier */
1099
2107
  icon: string;
2108
+ /** Sort order in sidebar (lower = first) */
1100
2109
  order: number;
1101
2110
  }
1102
2111
  /**
1103
- * Registro centralizado de categorías con su metadata
2112
+ * Registry of all categories with their metadata.
2113
+ *
2114
+ * Used by the UI to render the sidebar navigation.
1104
2115
  */
1105
2116
  declare const CATEGORIES: Record<Category, CategoryMeta>;
1106
2117
  /**
1107
- * Orden de categorías para sidebar (derivado de CATEGORIES)
2118
+ * Categories sorted by their display order for sidebar rendering.
1108
2119
  */
1109
2120
  declare const CATEGORY_ORDER: Category[];
1110
2121
  /**
1111
- * Manifest de un plugin Nexus
2122
+ * Plugin manifest defining a distributable Nexus plugin package.
2123
+ *
2124
+ * Plugins bundle one or more modules into a reusable package.
2125
+ * Distributed via npm (e.g., @gzl10/nexus-plugin-cms).
2126
+ *
2127
+ * @example
2128
+ * ```typescript
2129
+ * const cmsPlugin: PluginManifest = {
2130
+ * name: '@gzl10/nexus-plugin-cms',
2131
+ * code: 'CMS',
2132
+ * label: { en: 'CMS', es: 'CMS' },
2133
+ * category: 'content',
2134
+ * version: '1.0.0',
2135
+ * description: { en: 'Content Management System', es: 'Sistema de Gestión de Contenidos' },
2136
+ * modules: [postsModule, categoriesModule, tagsModule]
2137
+ * }
2138
+ * ```
1112
2139
  */
1113
2140
  interface PluginManifest {
1114
- /** Nombre del plugin (normalmente el nombre del paquete npm) */
2141
+ /** Plugin npm package name (e.g., '@gzl10/nexus-plugin-cms') */
1115
2142
  name: string;
1116
- /** Código del plugin (3-4 chars UPPERCASE). Se hereda a todos los módulos */
2143
+ /** Short code prefix for tables (3-4 uppercase chars, e.g., 'CMS'). Applied to all modules */
1117
2144
  code: string;
1118
- /** Etiqueta para mostrar en UI. Se hereda a los módulos si no la definen */
1119
- label: string;
1120
- /** Icono del plugin (Iconify MDI). Se hereda a los módulos si no lo definen */
2145
+ /** Display label (singular). Inherited by modules without explicit label */
2146
+ label: LocalizedString;
2147
+ /** Display label (plural, for menus) */
2148
+ labelPlural?: LocalizedString;
2149
+ /** Iconify MDI icon. Inherited by modules without explicit icon */
1121
2150
  icon?: string;
1122
- /** Categoría del plugin para agrupar en sidebar */
2151
+ /** Category for sidebar grouping. Inherited by modules without explicit category */
1123
2152
  category: Category;
1124
- /** Versión del plugin (semver) */
2153
+ /** Plugin version following SemVer (e.g., '1.0.0') */
1125
2154
  version: string;
1126
- /** Descripción del plugin */
1127
- description: string;
1128
- /** Módulos incluidos en el plugin */
2155
+ /** Plugin description for documentation */
2156
+ description: LocalizedString;
2157
+ /** Modules bundled in this plugin */
1129
2158
  modules: ModuleManifest[];
1130
2159
  }
2160
+ /**
2161
+ * Serializable field definition for API responses.
2162
+ *
2163
+ * Unlike {@link FieldDefinition}, this contains no functions (ConditionalBoolean
2164
+ * resolved to static boolean). Some fields may be omitted for security.
2165
+ *
2166
+ * @see {@link FieldDefinition} for the full server-side definition
2167
+ */
2168
+ interface FieldDefinitionDTO {
2169
+ /** Field identifier */
2170
+ name: string;
2171
+ /** Display label */
2172
+ label: LocalizedString;
2173
+ /** Input type for form rendering */
2174
+ input: InputType;
2175
+ /** Placeholder text */
2176
+ placeholder?: LocalizedString;
2177
+ /** Help text */
2178
+ hint?: LocalizedString;
2179
+ /** Whether field is hidden (resolved from ConditionalBoolean) */
2180
+ hidden?: boolean;
2181
+ /** Whether field is disabled (resolved from ConditionalBoolean) */
2182
+ disabled?: boolean;
2183
+ /** Whether field is required (resolved from ConditionalBoolean) */
2184
+ required?: boolean;
2185
+ /** Default value for form creation */
2186
+ defaultValue?: unknown;
2187
+ /** Database config (may be partially omitted for security) */
2188
+ db?: {
2189
+ type: string;
2190
+ nullable?: boolean;
2191
+ default?: unknown;
2192
+ unique?: boolean;
2193
+ index?: boolean;
2194
+ };
2195
+ /** Relationship config (table name may be omitted for security) */
2196
+ relation?: {
2197
+ table?: string;
2198
+ column?: string;
2199
+ labelField?: string;
2200
+ onDelete?: string;
2201
+ };
2202
+ /** Validation rules */
2203
+ validation?: {
2204
+ min?: number;
2205
+ max?: number;
2206
+ pattern?: string;
2207
+ format?: string;
2208
+ enum?: string[];
2209
+ };
2210
+ /** Select/dropdown options configuration */
2211
+ options?: {
2212
+ endpoint?: string;
2213
+ valueField?: string;
2214
+ labelField?: string;
2215
+ static?: Array<{
2216
+ value: string;
2217
+ label: LocalizedString;
2218
+ }>;
2219
+ allowCreate?: boolean;
2220
+ };
2221
+ /** Storage configuration for file uploads */
2222
+ storage?: {
2223
+ accept?: string;
2224
+ maxSize?: number;
2225
+ maxFiles?: number;
2226
+ folder?: string;
2227
+ thumbnails?: Array<{
2228
+ width: number;
2229
+ height: number;
2230
+ }>;
2231
+ dedupe?: boolean;
2232
+ };
2233
+ /** Field metadata */
2234
+ meta?: FieldMeta;
2235
+ /** Props for input components */
2236
+ inputProps?: Record<string, unknown>;
2237
+ /** Props for display components */
2238
+ displayProps?: Record<string, unknown>;
2239
+ }
2240
+ /**
2241
+ * Serializable entity definition for API responses.
2242
+ *
2243
+ * Flattened structure without discriminated union.
2244
+ * Sensitive information may be omitted for security.
2245
+ *
2246
+ * @see {@link EntityDefinition} for the full server-side definition
2247
+ */
2248
+ interface EntityDefinitionDTO {
2249
+ /** Unique entity identifier (auto-generated at registration) */
2250
+ id: string;
2251
+ /** Table name (may be omitted for security) */
2252
+ table?: string;
2253
+ /** Key for single entities */
2254
+ key?: string;
2255
+ /** Entity type */
2256
+ type: EntityType;
2257
+ /** Display label (singular) */
2258
+ label: LocalizedString;
2259
+ /** Display label (plural) */
2260
+ labelPlural?: LocalizedString;
2261
+ /** Field used as display label */
2262
+ labelField?: string;
2263
+ /** Iconify icon */
2264
+ icon?: string;
2265
+ /** API route prefix */
2266
+ routePrefix?: string;
2267
+ /** UI sort order */
2268
+ order?: number;
2269
+ /** Field definitions */
2270
+ fields: Record<string, FieldDefinitionDTO>;
2271
+ /** Total field count */
2272
+ fieldsCount: number;
2273
+ /** Has created_at/updated_at columns */
2274
+ hasTimestamps: boolean;
2275
+ /** Has created_by/updated_by columns */
2276
+ hasAudit: boolean;
2277
+ /** CASL subject name */
2278
+ caslSubject?: string;
2279
+ /** Returns single record instead of array */
2280
+ isSingle?: boolean;
2281
+ /** UI display mode */
2282
+ displayMode?: DisplayMode;
2283
+ /** Show create button in UI */
2284
+ allowCreate?: boolean;
2285
+ /** Allow editing in UI */
2286
+ allowEdit?: boolean;
2287
+ /** Hidden from UI sidebar */
2288
+ hidden?: boolean;
2289
+ }
2290
+ /**
2291
+ * Serializable module for API responses.
2292
+ */
2293
+ interface ModuleDTO {
2294
+ /** Module identifier */
2295
+ name: string;
2296
+ /** Display label */
2297
+ label: LocalizedString;
2298
+ /** Display label (plural) */
2299
+ labelPlural?: LocalizedString;
2300
+ /** Iconify icon */
2301
+ icon?: string;
2302
+ /** Module description */
2303
+ description?: LocalizedString;
2304
+ /** Module type */
2305
+ type: string;
2306
+ /** Sidebar category */
2307
+ category?: Category;
2308
+ /** Module dependencies */
2309
+ dependencies: string[];
2310
+ /** API route prefix */
2311
+ routePrefix: string;
2312
+ /** CASL subjects defined by this module */
2313
+ subjects: string[];
2314
+ /** Entity definitions */
2315
+ definitions: EntityDefinitionDTO[];
2316
+ /** Has custom routes */
2317
+ hasRoutes: boolean;
2318
+ /** Has migrations */
2319
+ hasMigrate: boolean;
2320
+ /** Has seed data */
2321
+ hasSeed: boolean;
2322
+ /** Has init function */
2323
+ hasInit: boolean;
2324
+ }
2325
+ /**
2326
+ * Serializable plugin for API responses.
2327
+ */
2328
+ interface PluginDTO {
2329
+ /** Plugin package name */
2330
+ name: string;
2331
+ /** Plugin code prefix */
2332
+ code: string;
2333
+ /** Display label */
2334
+ label: LocalizedString;
2335
+ /** Display label (plural) */
2336
+ labelPlural?: LocalizedString;
2337
+ /** Iconify icon */
2338
+ icon?: string;
2339
+ /** Sidebar category */
2340
+ category?: Category;
2341
+ /** Plugin version */
2342
+ version: string;
2343
+ /** Plugin description */
2344
+ description: LocalizedString;
2345
+ /** Modules in this plugin */
2346
+ modules: ModuleDTO[];
2347
+ }
2348
+ /**
2349
+ * Application manifest DTO (core or user app).
2350
+ */
2351
+ interface ManifestDTO {
2352
+ /** Application name */
2353
+ name: string;
2354
+ /** Application version */
2355
+ version: string;
2356
+ /** Registered modules */
2357
+ modules: ModuleDTO[];
2358
+ /** Registered plugins */
2359
+ plugins?: PluginDTO[];
2360
+ }
2361
+ /**
2362
+ * Combined manifest DTO with both core and user manifests.
2363
+ */
2364
+ interface CombinedManifestDTO {
2365
+ /** Core nexus-backend manifest */
2366
+ core: ManifestDTO;
2367
+ /** User project manifest (null in standalone mode) */
2368
+ user: ManifestDTO | null;
2369
+ /** Whether running with a user project */
2370
+ hasUserApp: boolean;
2371
+ }
1131
2372
 
1132
- export { type AbilityLike, type ActionEntityDefinition, type ActionEntityService, type AuthRequest, type BaseEntityService, type BaseMailService, type BaseRolesService, type BaseUser, type BaseUsersService, CATEGORIES, CATEGORY_ORDER, type CaslAction, type Category, type CategoryMeta, type CollectionEntityDefinition, type CollectionEntityService, type ComputedEntityDefinition, type ComputedEntityService, type ConfigEntityDefinition, type ConfigEntityService, type ContextCrypto, type ContextEngine, type ContextHelpers, type ContextSocket, type CoreServices, type CreateEntityServiceOptions, type DbType, type EntityAction, type EntityCaslConfig, type EntityController, type EntityDefinition, type EntityHandler, type EntityIndex, type EntityQuery, type EntityServiceHooks, type EntityType, type EventEmitterLike, type EventEntityDefinition, type EventEntityService, type ExternalEntityDefinition, type ExternalEntityService, type FieldCaslAccess, type FieldDbConfig, type FieldDefinition, type FieldMeta, type FieldOptions, type FieldRelation, type FieldStorageConfig, type FieldValidationConfig, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type LoggerReporter, type ModuleAbilities, type ModuleContext, type ModuleManifest, type ModuleMiddlewares, type ModuleRequirements, type NonPersistentEntityDefinition, type OwnershipCondition, type PaginatedResult, type PaginationParams, type PersistentEntityDefinition, type PluginAuthRequest, type PluginManifest, type ReferenceEntityDefinition, type ReferenceEntityService, type RolePermission, type SendMailOptions, type SendMailResult, type SingleEntityDefinition, type SingleEntityService, type TempEntityDefinition, type TempEntityService, type ValidateSchemas, type ValidationSchema, type ViewEntityDefinition, type ViewEntityService, type VirtualEntityDefinition, type VirtualEntityService, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity };
2373
+ export { type AbilityLike, type ActionEntityDefinition, type ActionEntityService, type AppManifest, type AuthRequest, type BaseAuthRequest, type BaseEntityService, type BaseMailService, type BaseRolesService, type BaseUser, type BaseUsersService, CATEGORIES, CATEGORY_ORDER, type CaslAction, type Category, type CategoryMeta, type CollectionEntityDefinition, type CollectionEntityService, type CombinedManifestDTO, type ComputedEntityDefinition, type ComputedEntityService, type ConditionalBoolean, type ConfigEntityDefinition, type ConfigEntityService, type ContextCrypto, type ContextEngine, type ContextHelpers, type ContextSocket, type CoreServices, type CreateEntityServiceOptions, type DbType, type DisplayMode, type EntityAction, type EntityCaslConfig, type EntityController, type EntityDefinition, type EntityDefinitionDTO, type EntityHandler, type EntityIndex, type EntityQuery, type EntityServiceHooks, type EntityType, type EventEmitterLike, type EventEntityDefinition, type EventEntityService, type ExternalEntityDefinition, type ExternalEntityService, type FieldDbConfig, type FieldDefinition, type FieldDefinitionDTO, type FieldMeta, type FieldOptions, type FieldRelation, type FieldStorageConfig, type FieldValidationConfig, type FilterOperators, type FilterValue, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type LocalizedString, type LoggerReporter, type ManifestDTO, type ModuleAbilities, type ModuleContext, type ModuleDTO, type ModuleManifest, type ModuleMiddlewares, type ModuleRequirements, type NonPersistentEntityDefinition, type OwnershipCondition, type PaginatedResult, type PaginationParams, type PersistentEntityDefinition, type PluginDTO, type PluginManifest, type RateLimitOptions, type ReferenceEntityDefinition, type ReferenceEntityService, type RolePermission, type SendMailOptions, type SendMailResult, type SingleEntityDefinition, type SingleEntityService, type TempEntityDefinition, type TempEntityService, type ValidateSchemas, type ValidationDetail, type ValidationSchema, type ViewEntityDefinition, type ViewEntityService, type VirtualEntityDefinition, type VirtualEntityService, getCountLabel, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity, resolveLocalized };