@gzl10/nexus-sdk 0.12.7 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1504 @@
1
+ import { Knex } from 'knex';
2
+ import { Request, Router, RequestHandler, Response } from 'express';
3
+ import { L as LocalizedString, i as FieldDefinition, E as EntityIndex } from './field-CngHXora.js';
4
+ import { Logger } from 'pino';
5
+
6
+ /**
7
+ * Server-Sent Events (SSE) types
8
+ * @module sse
9
+ */
10
+ /**
11
+ * SSE helper for streaming responses
12
+ */
13
+ interface SSEHelper {
14
+ /**
15
+ * Start an SSE stream
16
+ * @param res - Express response object
17
+ * @param handler - Async function that sends events
18
+ * @param options - Stream options
19
+ */
20
+ stream(res: unknown, // Response type from Express, kept generic to avoid dependency
21
+ handler: (send: SSESender) => Promise<void>, options?: SSEOptions): Promise<void>;
22
+ }
23
+ /**
24
+ * Sender object for emitting SSE events
25
+ */
26
+ interface SSESender {
27
+ /**
28
+ * Send a data event
29
+ * @param data - Data to send (will be JSON stringified)
30
+ * @param event - Optional event name (client listens with addEventListener)
31
+ * @param id - Optional event ID (for reconnection with Last-Event-ID)
32
+ */
33
+ send(data: unknown, event?: string, id?: string): void;
34
+ /**
35
+ * Send a comment (useful for keep-alive)
36
+ * @param text - Comment text
37
+ */
38
+ comment(text: string): void;
39
+ /**
40
+ * Close the stream
41
+ */
42
+ close(): void;
43
+ }
44
+ /**
45
+ * Options for SSE stream
46
+ */
47
+ interface SSEOptions {
48
+ /**
49
+ * Retry interval in ms for client reconnection (default: 3000)
50
+ */
51
+ retry?: number;
52
+ /**
53
+ * Keep-alive interval in ms (default: 30000, 0 to disable)
54
+ */
55
+ keepAlive?: number;
56
+ /**
57
+ * Callback when client disconnects
58
+ */
59
+ onClose?: () => void;
60
+ }
61
+ /**
62
+ * Configuration for batch/long-running actions.
63
+ * When set on an ActionDefinition, the action uses SSE for progress reporting.
64
+ */
65
+ interface BatchConfig {
66
+ /** Mark this action as a batch/long-running process */
67
+ batch: true;
68
+ /** Timeout in ms (default: 300000 = 5min). UI shows countdown. */
69
+ timeout?: number;
70
+ }
71
+ /**
72
+ * Progress event sent via SSE during batch action execution.
73
+ * Events are sent with the named event 'batch'.
74
+ */
75
+ interface BatchProgressEvent {
76
+ /** Current status of the batch process */
77
+ status: 'started' | 'progress' | 'completed' | 'error';
78
+ /** Progress percentage 0-100 */
79
+ percent?: number;
80
+ /** Current step number */
81
+ step?: number;
82
+ /** Total number of steps */
83
+ totalSteps?: number;
84
+ /** Human-readable message for the current operation */
85
+ message?: string;
86
+ /** Log entries (warnings, info, partial results) */
87
+ logs?: BatchLogEntry[];
88
+ /** Final result data (only on 'completed') */
89
+ result?: unknown;
90
+ /** Error details (only on 'error') */
91
+ error?: {
92
+ code: string;
93
+ message: string;
94
+ };
95
+ }
96
+ /**
97
+ * Individual log entry emitted during batch processing
98
+ */
99
+ interface BatchLogEntry {
100
+ level: 'info' | 'warn' | 'error';
101
+ message: string;
102
+ timestamp?: string;
103
+ }
104
+
105
+ /**
106
+ * Base types shared across the SDK
107
+ */
108
+
109
+ /**
110
+ * Base authenticated request with user and CASL abilities (generic).
111
+ * Use AuthRequest for the pre-typed version.
112
+ */
113
+ interface BaseAuthRequest<TUser = unknown, TAbility = unknown> extends Request {
114
+ user: TUser;
115
+ ability: TAbility;
116
+ }
117
+ /**
118
+ * Base user without a password for plugin use.
119
+ * Used to type relations with users (e.g., author, created_by).
120
+ * Note: Multi-role support - roles are loaded via user_roles pivot table.
121
+ */
122
+ interface BaseUser {
123
+ id: string;
124
+ name: string;
125
+ email: string;
126
+ created_at: Date;
127
+ updated_at: Date;
128
+ created_by: string | null;
129
+ updated_by: string | null;
130
+ /** Extensibility: allows custom user properties */
131
+ [key: string]: unknown;
132
+ }
133
+ /**
134
+ * Pagination parameters for queries
135
+ */
136
+ interface PaginationParams {
137
+ page: number;
138
+ limit: number;
139
+ }
140
+ /**
141
+ * Extended pagination parameters with offset (used internally)
142
+ */
143
+ interface PaginationParamsWithOffset extends PaginationParams {
144
+ /** Offset for database query */
145
+ offset: number;
146
+ }
147
+ /**
148
+ * Generic paginated result
149
+ */
150
+ interface PaginatedResult<T> {
151
+ items: T[];
152
+ total: number;
153
+ page: number;
154
+ limit: number;
155
+ totalPages: number;
156
+ hasNext: boolean;
157
+ }
158
+ /**
159
+ * Standard validation error detail for API responses
160
+ */
161
+ interface ValidationDetail {
162
+ path: string;
163
+ message: string;
164
+ }
165
+
166
+ /**
167
+ * CASL Authorization types
168
+ */
169
+
170
+ /**
171
+ * Standard CASL authorization actions.
172
+ *
173
+ * - `manage` - Full access (superuser wildcard, includes all actions)
174
+ * - `create` - Create new records
175
+ * - `read` - Read/view records
176
+ * - `update` - Modify existing records
177
+ * - `delete` - Remove records
178
+ * - `execute` - Execute actions/operations on records
179
+ */
180
+ type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'execute' | (string & {});
181
+ /**
182
+ * CASL permission definition for a specific role.
183
+ *
184
+ * Defines what actions a role can perform on an entity,
185
+ * optionally with conditions and field restrictions.
186
+ */
187
+ interface RolePermission {
188
+ /** Actions this role can perform on the entity */
189
+ actions: CaslAction[];
190
+ /** CASL conditions to filter which records the permission applies to */
191
+ conditions?: Record<string, unknown>;
192
+ /** Restrict permission to specific fields. null = all fields allowed */
193
+ fields?: string[] | null;
194
+ /** If true, this is a denial rule (cannot) instead of allow (can) */
195
+ inverted?: boolean;
196
+ }
197
+ /**
198
+ * CASL authorization configuration for an entity.
199
+ *
200
+ * Defines role-based access control (RBAC) using CASL.
201
+ * Permissions are evaluated at runtime for each request.
202
+ */
203
+ interface EntityCaslConfig {
204
+ /** CASL subject name. Default: inferred from table */
205
+ subject?: string;
206
+ /** Role-based permissions. Keys are role names or '*' wildcard */
207
+ permissions?: Record<string, RolePermission | RolePermission[]>;
208
+ /** Fields excluded from 'read' permission unless explicitly granted */
209
+ sensitiveFields?: string[];
210
+ }
211
+ /**
212
+ * Generic interface for CASL ability.
213
+ *
214
+ * Provides permission checking without directly depending on @casl/ability package.
215
+ */
216
+ interface AbilityLike {
217
+ /** Check if action is allowed on subject */
218
+ can: (action: string, subject: unknown) => boolean;
219
+ /** Check if action is denied on subject */
220
+ cannot: (action: string, subject: unknown) => boolean;
221
+ }
222
+ /**
223
+ * CASL ForbiddenError instance for throwing permission errors.
224
+ */
225
+ interface ForbiddenErrorInstance {
226
+ /** Throws ForbiddenError if action is not allowed on subject */
227
+ throwUnlessCan: (action: string, subject: unknown) => void;
228
+ }
229
+ /**
230
+ * CASL ForbiddenError constructor for creating error instances.
231
+ */
232
+ interface ForbiddenErrorConstructor {
233
+ /** Creates a ForbiddenErrorInstance bound to an ability */
234
+ from: (ability: any) => ForbiddenErrorInstance;
235
+ }
236
+ /**
237
+ * CASL abilities API available in module context.
238
+ *
239
+ * Allows modules to use CASL for authorization without importing @casl/ability directly.
240
+ */
241
+ interface ModuleAbilities {
242
+ /** Creates CASL ability instance for a user based on their roles */
243
+ defineAbilityFor: (user: {
244
+ id: string;
245
+ } | null, ...args: unknown[]) => unknown;
246
+ /** Serializes CASL rules for client-side authorization (frontend) */
247
+ packRules: (ability: unknown) => unknown[];
248
+ /** Wraps an object as CASL subject for instance-level permission checks */
249
+ subject: (type: string, object: Record<string, unknown>) => unknown;
250
+ /** ForbiddenError constructor for throwUnlessCan pattern */
251
+ ForbiddenError: ForbiddenErrorConstructor;
252
+ }
253
+ /**
254
+ * Pre-typed authenticated request with BaseUser and AbilityLike.
255
+ *
256
+ * Use this type for route handlers that require authentication.
257
+ */
258
+ type AuthRequest = BaseAuthRequest<BaseUser, AbilityLike>;
259
+
260
+ /**
261
+ * Query and filter types for data access
262
+ */
263
+
264
+ /**
265
+ * Filter operators for advanced filtering
266
+ *
267
+ * @example
268
+ * // Simple equality (shorthand)
269
+ * { status: 'active' }
270
+ *
271
+ * // With operators
272
+ * { name: { $contains: 'john' } }
273
+ * { age: { $gte: 18 } }
274
+ */
275
+ interface FilterOperators {
276
+ /** Equal (default if no operator) */
277
+ $eq?: unknown;
278
+ /** Not equal */
279
+ $ne?: unknown;
280
+ /** Greater than */
281
+ $gt?: number | string | Date;
282
+ /** Greater than or equal */
283
+ $gte?: number | string | Date;
284
+ /** Less than */
285
+ $lt?: number | string | Date;
286
+ /** Less than or equal */
287
+ $lte?: number | string | Date;
288
+ /** Contains (LIKE %value%) */
289
+ $contains?: string;
290
+ /** Starts with (LIKE value%) */
291
+ $startswith?: string;
292
+ /** Ends with (LIKE %value) */
293
+ $endswith?: string;
294
+ /** In array */
295
+ $in?: unknown[];
296
+ /** Not in array */
297
+ $nin?: unknown[];
298
+ /** Is null (true = IS NULL, false = IS NOT NULL) */
299
+ $isnull?: boolean;
300
+ }
301
+ /**
302
+ * Filter value can be:
303
+ * - Primitive (shorthand for $eq)
304
+ * - Array (shorthand for $in)
305
+ * - Object with operators
306
+ */
307
+ type FilterValue = string | number | boolean | null | unknown[] | FilterOperators;
308
+ /**
309
+ * Query parameters for entity list operations
310
+ */
311
+ interface EntityQuery {
312
+ page?: number;
313
+ limit?: number;
314
+ sort?: string;
315
+ order?: 'asc' | 'desc';
316
+ search?: string;
317
+ /** Filters with optional operators */
318
+ filters?: Record<string, unknown>;
319
+ }
320
+ /**
321
+ * Database adapter interface for normalized data access.
322
+ *
323
+ * Abstracts database operations using standardized types.
324
+ */
325
+ interface DatabaseAdapter {
326
+ /**
327
+ * Find multiple records with optional filtering, sorting, and pagination.
328
+ */
329
+ findMany<T = unknown>(table: string, query?: EntityQuery): Promise<PaginatedResult<T>>;
330
+ /**
331
+ * Find a single record by filters.
332
+ */
333
+ findOne<T = unknown>(table: string, filters: Record<string, unknown>): Promise<T | null>;
334
+ /**
335
+ * Find a single record by ID.
336
+ */
337
+ findById<T = unknown>(table: string, id: string): Promise<T | null>;
338
+ /**
339
+ * Count records matching filters.
340
+ */
341
+ count(table: string, filters?: Record<string, unknown>): Promise<number>;
342
+ /**
343
+ * Insert a new record.
344
+ */
345
+ insert<T = unknown>(table: string, data: Record<string, unknown>): Promise<T>;
346
+ /**
347
+ * Update a record by ID.
348
+ */
349
+ update<T = unknown>(table: string, id: string, data: Record<string, unknown>): Promise<T>;
350
+ /**
351
+ * Delete a record by ID.
352
+ */
353
+ delete(table: string, id: string): Promise<boolean>;
354
+ /**
355
+ * Execute operations within a transaction.
356
+ */
357
+ transaction<T>(fn: (trx: DatabaseAdapter) => Promise<T>): Promise<T>;
358
+ /**
359
+ * Raw access to the underlying database driver.
360
+ */
361
+ raw: unknown;
362
+ insertWithTtl?<T = unknown>(table: string, data: Record<string, unknown>, ttlSeconds: number): Promise<T>;
363
+ setTtl?(table: string, id: string, ttlSeconds: number): Promise<boolean>;
364
+ getTtl?(table: string, id: string): Promise<number | null>;
365
+ cleanupExpiredIds?(table: string): Promise<number>;
366
+ }
367
+
368
+ /**
369
+ * Schema adapter types for DDL operations
370
+ */
371
+ /**
372
+ * Column information returned by schema introspection.
373
+ */
374
+ interface SchemaColumnInfo {
375
+ /** Column name */
376
+ name: string;
377
+ /** Database type (varchar, integer, text, etc.) */
378
+ type: string;
379
+ /** Whether the column allows NULL values */
380
+ nullable: boolean;
381
+ /** Default value if any */
382
+ defaultValue: unknown;
383
+ }
384
+ /**
385
+ * Column builder for defining column properties.
386
+ * Methods return `this` for chaining.
387
+ */
388
+ interface SchemaColumnBuilder {
389
+ /** Mark as primary key */
390
+ primary(): SchemaColumnBuilder;
391
+ /** Add unique constraint */
392
+ unique(): SchemaColumnBuilder;
393
+ /** Add index */
394
+ index(): SchemaColumnBuilder;
395
+ /** Allow NULL values */
396
+ nullable(): SchemaColumnBuilder;
397
+ /** Disallow NULL values */
398
+ notNullable(): SchemaColumnBuilder;
399
+ /** Set default value */
400
+ defaultTo(value: unknown): SchemaColumnBuilder;
401
+ /** Add foreign key reference */
402
+ references(column: string): SchemaColumnBuilder;
403
+ /** Target table for foreign key */
404
+ inTable(table: string): SchemaColumnBuilder;
405
+ /** On delete behavior for foreign key */
406
+ onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaColumnBuilder;
407
+ }
408
+ /**
409
+ * Table builder for creating tables.
410
+ */
411
+ interface SchemaTableBuilder {
412
+ string(name: string, size?: number): SchemaColumnBuilder;
413
+ text(name: string): SchemaColumnBuilder;
414
+ integer(name: string): SchemaColumnBuilder;
415
+ bigInteger(name: string): SchemaColumnBuilder;
416
+ decimal(name: string, precision?: number, scale?: number): SchemaColumnBuilder;
417
+ boolean(name: string): SchemaColumnBuilder;
418
+ date(name: string): SchemaColumnBuilder;
419
+ datetime(name: string): SchemaColumnBuilder;
420
+ timestamp(name: string): SchemaColumnBuilder;
421
+ json(name: string): SchemaColumnBuilder;
422
+ jsonb(name: string): SchemaColumnBuilder;
423
+ uuid(name: string): SchemaColumnBuilder;
424
+ enum(name: string, values: readonly string[]): SchemaColumnBuilder;
425
+ binary(name: string, length?: number): SchemaColumnBuilder;
426
+ float(name: string, precision?: number, scale?: number): SchemaColumnBuilder;
427
+ specificType(name: string, type: string): SchemaColumnBuilder;
428
+ timestamps(): void;
429
+ index(columns: string | string[], indexName?: string): void;
430
+ unique(columns: string | string[], constraintName?: string): void;
431
+ primary(columns: string | string[], constraintName?: string): void;
432
+ foreign(column: string): SchemaForeignKeyBuilder;
433
+ increments(name?: string): SchemaColumnBuilder;
434
+ bigIncrements(name?: string): SchemaColumnBuilder;
435
+ }
436
+ /**
437
+ * Foreign key builder for defining references.
438
+ */
439
+ interface SchemaForeignKeyBuilder {
440
+ references(column: string): SchemaForeignKeyBuilder;
441
+ inTable(table: string): SchemaForeignKeyBuilder;
442
+ onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaForeignKeyBuilder;
443
+ onUpdate(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaForeignKeyBuilder;
444
+ }
445
+ /**
446
+ * Alter table builder extends table builder with modification operations.
447
+ */
448
+ interface SchemaAlterTableBuilder extends SchemaTableBuilder {
449
+ dropColumn(name: string): void;
450
+ renameColumn(from: string, to: string): void;
451
+ }
452
+ /**
453
+ * Schema adapter interface for normalized DDL operations.
454
+ */
455
+ interface SchemaAdapter {
456
+ hasTable(tableName: string): Promise<boolean>;
457
+ hasColumn(tableName: string, columnName: string): Promise<boolean>;
458
+ getColumns(tableName: string): Promise<SchemaColumnInfo[]>;
459
+ createTable(tableName: string, builder: (t: SchemaTableBuilder) => void): Promise<void>;
460
+ alterTable(tableName: string, builder: (t: SchemaAlterTableBuilder) => void): Promise<void>;
461
+ dropTable(tableName: string): Promise<void>;
462
+ addTimestamps(tableName: string): Promise<void>;
463
+ addAuditFields(tableName: string): Promise<void>;
464
+ addColumnIfMissing(tableName: string, columnName: string, type: string): Promise<boolean>;
465
+ /** Raw access to the underlying schema builder */
466
+ raw: unknown;
467
+ }
468
+
469
+ /**
470
+ * Entity service types and hooks
471
+ */
472
+
473
+ /**
474
+ * Base interface for all entity services.
475
+ */
476
+ interface BaseEntityService<T = unknown> {
477
+ /** Gets paginated list of records */
478
+ findAll(query?: EntityQuery): Promise<PaginatedResult<T>>;
479
+ /** Gets a single record by ID */
480
+ findById(id: string): Promise<T | null>;
481
+ /** The entity definition this service operates on */
482
+ readonly definition: EntityDefinition;
483
+ }
484
+ /**
485
+ * Service for Collection entities with full CRUD.
486
+ */
487
+ interface CollectionEntityService<T = unknown> extends BaseEntityService<T> {
488
+ create(data: Partial<T>, userId?: string): Promise<T>;
489
+ update(id: string, data: Partial<T>, userId?: string): Promise<T>;
490
+ delete(id: string): Promise<void>;
491
+ }
492
+ /**
493
+ * Service for Temp entities with TTL expiration.
494
+ */
495
+ interface TempEntityService<T = unknown> extends BaseEntityService<T> {
496
+ create(data: Partial<T>): Promise<T>;
497
+ update(id: string, data: Partial<T>, extendTtl?: boolean): Promise<T>;
498
+ delete(id: string): Promise<void>;
499
+ extendTtl(id: string, additionalSeconds?: number): Promise<T>;
500
+ cleanup(): Promise<number>;
501
+ getRemainingTtl(id: string): Promise<number | null>;
502
+ isValid(id: string): Promise<boolean>;
503
+ }
504
+ /**
505
+ * Service for Event entities (append-only).
506
+ */
507
+ interface EventEntityService<T = unknown> extends BaseEntityService<T> {
508
+ create(data: Partial<T>): Promise<T>;
509
+ append(data: Partial<T>): Promise<T>;
510
+ runRetentionCleanup(): Promise<number>;
511
+ findByDateRange(start: Date, end: Date, query?: EntityQuery): Promise<PaginatedResult<T>>;
512
+ }
513
+ /**
514
+ * Service for Single entities (global configuration).
515
+ */
516
+ interface SingleEntityService<T = unknown> extends BaseEntityService<T> {
517
+ get(): Promise<T | null>;
518
+ update(data: Partial<T>, userId?: string): Promise<T>;
519
+ }
520
+ /**
521
+ * Service for Config entities (scoped key-value).
522
+ */
523
+ interface ConfigEntityService<T = unknown> extends BaseEntityService<T> {
524
+ get(key: string): Promise<T | null>;
525
+ set(key: string, value: unknown, userId?: string): Promise<T>;
526
+ getAll(): Promise<Record<string, unknown>>;
527
+ }
528
+ /** Service for Reference entities (read-only) */
529
+ type ReferenceEntityService<T = unknown> = BaseEntityService<T>;
530
+ /** Service for View entities (SQL views, read-only) */
531
+ type ViewEntityService<T = unknown> = BaseEntityService<T>;
532
+ /** Service for Computed entities */
533
+ type ComputedEntityService<T = unknown> = BaseEntityService<T>;
534
+ /** Service for External entities */
535
+ type ExternalEntityService<T = unknown> = BaseEntityService<T>;
536
+ /** Service for Virtual entities */
537
+ type VirtualEntityService<T = unknown> = BaseEntityService<T>;
538
+ /**
539
+ * Service for Action entities.
540
+ */
541
+ interface ActionEntityService<T = unknown> {
542
+ execute(data: Partial<T>): Promise<unknown>;
543
+ readonly definition: EntityDefinition;
544
+ }
545
+ /**
546
+ * Hooks to customize entity service behavior.
547
+ */
548
+ interface EntityServiceHooks<T = unknown> {
549
+ beforeCreate?: (data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
550
+ afterCreate?: (record: T) => Promise<void> | void;
551
+ beforeUpdate?: (id: string, data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
552
+ afterUpdate?: (record: T) => Promise<void> | void;
553
+ beforeDelete?: (id: string) => Promise<void> | void;
554
+ afterDelete?: (id: string) => Promise<void> | void;
555
+ afterFindById?: (record: T | null) => Promise<T | null> | T | null;
556
+ afterFindAll?: (result: PaginatedResult<T>) => Promise<PaginatedResult<T>> | PaginatedResult<T>;
557
+ }
558
+ /**
559
+ * Options for createEntityService
560
+ */
561
+ interface CreateEntityServiceOptions<T = unknown> {
562
+ hooks?: EntityServiceHooks<T>;
563
+ }
564
+ /**
565
+ * Role management service interface.
566
+ */
567
+ interface BaseRolesService {
568
+ findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
569
+ findById(id: string): Promise<unknown>;
570
+ findByName(name: string): Promise<unknown | undefined>;
571
+ create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
572
+ update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
573
+ delete(id: string): Promise<void>;
574
+ }
575
+ /**
576
+ * User management service interface.
577
+ */
578
+ interface BaseUsersService {
579
+ findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
580
+ findById(id: string): Promise<unknown>;
581
+ findByIdWithRoles(id: string): Promise<unknown | null>;
582
+ create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
583
+ update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
584
+ delete(id: string): Promise<void>;
585
+ getRoleIds(userId: string): Promise<string[]>;
586
+ getRoleNames(userId: string): Promise<string[]>;
587
+ getUserRoles(userId: string): Promise<unknown[]>;
588
+ assignRole(userId: string, roleId: string): Promise<void>;
589
+ removeRole(userId: string, roleId: string): Promise<void>;
590
+ setRoles(userId: string, roleIds: string[]): Promise<void>;
591
+ roles: BaseRolesService;
592
+ }
593
+ /**
594
+ * Email sending options.
595
+ */
596
+ interface SendMailOptions {
597
+ to: string | string[];
598
+ subject: string;
599
+ title?: string;
600
+ message?: string;
601
+ html?: string;
602
+ text?: string;
603
+ from?: string;
604
+ replyTo?: string;
605
+ actions?: Array<{
606
+ label: string;
607
+ url: string;
608
+ }>;
609
+ logoUrl?: string;
610
+ attachments?: Array<{
611
+ filename: string;
612
+ content: Buffer | string;
613
+ contentType?: string;
614
+ }>;
615
+ }
616
+ /**
617
+ * Result from sending an email.
618
+ */
619
+ interface SendMailResult {
620
+ messageId: string;
621
+ accepted: string[];
622
+ rejected: string[];
623
+ }
624
+ /**
625
+ * Email service interface.
626
+ */
627
+ interface BaseMailService {
628
+ send(options: SendMailOptions): Promise<SendMailResult | null>;
629
+ verify(): Promise<boolean>;
630
+ }
631
+ /**
632
+ * Error reporter interface for external monitoring.
633
+ */
634
+ interface LoggerReporter {
635
+ captureException: (error: Error, context?: Record<string, unknown>) => void;
636
+ }
637
+ /**
638
+ * Core services available via ctx.services.
639
+ */
640
+ interface CoreServices {
641
+ logger?: LoggerReporter;
642
+ users?: BaseUsersService;
643
+ mail?: BaseMailService;
644
+ [key: string]: unknown;
645
+ }
646
+
647
+ /**
648
+ * Module and Plugin manifest types
649
+ */
650
+
651
+ /**
652
+ * Available categories for organizing modules in the UI sidebar.
653
+ */
654
+ type Category = 'content' | 'data' | 'assets' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'security' | 'legal' | 'settings';
655
+ /**
656
+ * Metadata for a category including display information.
657
+ */
658
+ interface CategoryMeta {
659
+ label: LocalizedString;
660
+ icon: string;
661
+ order: number;
662
+ }
663
+ /**
664
+ * Registry of all categories with their metadata.
665
+ */
666
+ declare const CATEGORIES: Record<Category, CategoryMeta>;
667
+ /**
668
+ * Categories sorted by their display order for sidebar rendering.
669
+ */
670
+ declare const CATEGORY_ORDER: Category[];
671
+ /**
672
+ * Cross-module action injection descriptor.
673
+ * Allows a module to inject actions into entities owned by other modules.
674
+ */
675
+ interface ActionInjection {
676
+ /** Target module and entity to inject the action into */
677
+ target: {
678
+ module: string;
679
+ entity: string;
680
+ };
681
+ /** The action definition to inject into the target entity's actions[] */
682
+ action: ActionDefinition;
683
+ }
684
+ /**
685
+ * Module manifest defining a Nexus module.
686
+ */
687
+ interface ModuleManifest {
688
+ /** Unique module identifier */
689
+ name: string;
690
+ /** Display name for UI (singular) */
691
+ label?: LocalizedString;
692
+ /** Display name for UI (plural) */
693
+ labelPlural?: LocalizedString;
694
+ /** Iconify MDI icon identifier */
695
+ icon?: string;
696
+ /** Module description for documentation */
697
+ description?: LocalizedString;
698
+ /** Module type */
699
+ type?: 'core' | 'plugin' | 'auth-plugin' | 'custom';
700
+ /** Category for sidebar grouping */
701
+ category: Category;
702
+ /** Other module names that must be loaded before this one */
703
+ dependencies?: string[];
704
+ /** Requirements that must be met to enable this module */
705
+ required?: ModuleRequirements;
706
+ /** Database migration function */
707
+ migrate?: (ctx: ModuleContext) => Promise<void>;
708
+ /** Seed function for initial/reference data */
709
+ seed?: (ctx: ModuleContext) => Promise<void>;
710
+ /** Initialization function called on app startup */
711
+ init?: (ctx: ModuleContext) => void;
712
+ /** Factory function returning Express router with custom routes */
713
+ routes?: (ctx: ModuleContext) => Router;
714
+ /** API route prefix. Default: `/{name}` */
715
+ routePrefix?: string;
716
+ /** Entity definitions */
717
+ definitions?: EntityDefinition[];
718
+ /** Custom route definitions for OpenAPI documentation */
719
+ customRoutes?: CustomRouteDefinition[];
720
+ /** Actions to inject into entities of other modules */
721
+ injectActions?: ActionInjection[];
722
+ }
723
+ /**
724
+ * Application manifest with module and plugin registry.
725
+ */
726
+ interface AppManifest {
727
+ name: string;
728
+ version: string;
729
+ modules: ModuleManifest[];
730
+ plugins?: PluginManifest[];
731
+ }
732
+ /**
733
+ * Environment variable definition for plugin installation.
734
+ */
735
+ interface PluginEnvVar {
736
+ name: string;
737
+ description: LocalizedString;
738
+ required: boolean;
739
+ default?: string;
740
+ example?: string;
741
+ pattern?: string;
742
+ sensitive?: boolean;
743
+ }
744
+ /**
745
+ * Configuration field definition for plugin settings.
746
+ */
747
+ interface PluginConfigField {
748
+ key: string;
749
+ label: LocalizedString;
750
+ description?: LocalizedString;
751
+ type: 'string' | 'number' | 'boolean' | 'select' | 'json';
752
+ default?: unknown;
753
+ required?: boolean;
754
+ options?: Array<{
755
+ value: string;
756
+ label: LocalizedString;
757
+ icon?: string;
758
+ }>;
759
+ }
760
+ /**
761
+ * Plugin configuration schema.
762
+ */
763
+ interface PluginConfigSchema {
764
+ fields: PluginConfigField[];
765
+ category?: LocalizedString;
766
+ }
767
+ /**
768
+ * Plugin setup/installation information.
769
+ */
770
+ interface PluginSetupInfo {
771
+ steps?: LocalizedString[];
772
+ docsUrl?: string;
773
+ postInstall?: (ctx: ModuleContext) => Promise<void>;
774
+ prerequisites?: LocalizedString[];
775
+ caveats?: LocalizedString[];
776
+ }
777
+ /**
778
+ * Plugin manifest defining a distributable Nexus plugin package.
779
+ */
780
+ interface PluginManifest {
781
+ /** Plugin npm package name */
782
+ name: string;
783
+ /** Short code prefix for tables (3-4 uppercase chars) */
784
+ code: string;
785
+ /** Display label (singular) */
786
+ label: LocalizedString;
787
+ /** Display label (plural) */
788
+ labelPlural?: LocalizedString;
789
+ /** Iconify MDI icon */
790
+ icon?: string;
791
+ /** Category for sidebar grouping */
792
+ category: Category;
793
+ /** Plugin version following SemVer */
794
+ version: string;
795
+ /** Plugin description */
796
+ description: LocalizedString;
797
+ /** Modules bundled in this plugin */
798
+ modules: ModuleManifest[];
799
+ /** Other plugin package names that must be loaded before this one */
800
+ dependencies?: string[];
801
+ /** Environment variables required or used by this plugin */
802
+ envVars?: PluginEnvVar[];
803
+ /** Configuration schema for plugin settings */
804
+ config?: PluginConfigSchema;
805
+ /** Peer dependencies required */
806
+ peerDependencies?: Record<string, string>;
807
+ /** Installation and setup information */
808
+ setup?: PluginSetupInfo;
809
+ /** Absolute path to plugin's migrations directory. Use import.meta.dirname to resolve. */
810
+ migrationsDir?: string;
811
+ }
812
+ /**
813
+ * Options for registerPlugin()
814
+ */
815
+ interface RegisterPluginOptions {
816
+ /** If true, no HTTP routes will be mounted for this plugin */
817
+ disableEndpoints?: boolean;
818
+ }
819
+
820
+ /**
821
+ * Module context types
822
+ */
823
+
824
+ /**
825
+ * Generic validation schema interface compatible with Zod.
826
+ */
827
+ interface ValidationSchema {
828
+ parse: (data: unknown) => unknown;
829
+ }
830
+ /**
831
+ * Schemas for the validation middleware.
832
+ */
833
+ interface ValidateSchemas {
834
+ body?: ValidationSchema;
835
+ query?: ValidationSchema;
836
+ params?: ValidationSchema;
837
+ }
838
+ /**
839
+ * Configuration options for rate limiting middleware.
840
+ */
841
+ interface RateLimitOptions {
842
+ windowMs?: number;
843
+ max?: number;
844
+ message?: string;
845
+ }
846
+ /**
847
+ * Middleware factories available in module context.
848
+ */
849
+ interface ModuleMiddlewares {
850
+ validate: (schemas: ValidateSchemas) => RequestHandler;
851
+ rateLimit: (options?: RateLimitOptions) => RequestHandler;
852
+ [key: string]: RequestHandler | ((...args: any[]) => RequestHandler) | undefined;
853
+ }
854
+ /**
855
+ * Minimal EventEmitter interface.
856
+ */
857
+ interface EventEmitterLike {
858
+ emit: (event: string, ...args: unknown[]) => boolean;
859
+ on: (event: string, listener: (...args: unknown[]) => void) => this;
860
+ off: (event: string, listener: (...args: unknown[]) => void) => this;
861
+ once: (event: string, listener: (...args: unknown[]) => void) => this;
862
+ onAny?: (listener: (event: string, ...args: unknown[]) => void) => this;
863
+ }
864
+ /**
865
+ * Cryptographic utilities for password handling.
866
+ */
867
+ interface ContextCrypto {
868
+ hashPassword: (password: string) => Promise<string>;
869
+ verifyPassword: (password: string, hash: string) => Promise<boolean>;
870
+ DUMMY_HASH: string;
871
+ }
872
+ /**
873
+ * LRU Cache options.
874
+ */
875
+ interface ContextLRUCacheOptions {
876
+ maxEntries?: number;
877
+ defaultTTL?: number;
878
+ }
879
+ /**
880
+ * LRU Cache instance interface.
881
+ */
882
+ interface ContextLRUCache<T = unknown> {
883
+ get(key: string): T | null;
884
+ set(key: string, value: T, ttl?: number): void;
885
+ delete(key: string): boolean;
886
+ has(key: string): boolean;
887
+ clear(): void;
888
+ }
889
+ /**
890
+ * Cache utilities available in module context.
891
+ */
892
+ interface ContextCache {
893
+ createLRUCache: <T = unknown>(options?: ContextLRUCacheOptions) => ContextLRUCache<T>;
894
+ }
895
+ /**
896
+ * Socket.IO Server interface (minimal)
897
+ */
898
+ interface SocketIOServer {
899
+ to(room: string): SocketIOBroadcastOperator;
900
+ in(room: string): SocketIOBroadcastOperator;
901
+ emit(event: string, ...args: unknown[]): boolean;
902
+ sockets: {
903
+ sockets: Map<string, unknown>;
904
+ };
905
+ }
906
+ /**
907
+ * Socket.IO Broadcast Operator interface
908
+ */
909
+ interface SocketIOBroadcastOperator {
910
+ emit(event: string, ...args: unknown[]): void;
911
+ fetchSockets(): Promise<unknown[]>;
912
+ }
913
+ /**
914
+ * Socket.IO API for real-time communication.
915
+ */
916
+ interface ContextSocket {
917
+ getIO: () => SocketIOServer;
918
+ isInitialized: () => boolean;
919
+ isUserConnected: (userId: string) => boolean;
920
+ getUserSocketCount: (userId: string) => number;
921
+ getConnectedUsers: () => string[];
922
+ joinUserToRoom: (userId: string, room: string) => boolean;
923
+ removeUserFromRoom: (userId: string, room: string) => boolean;
924
+ joinSocketToRoom: (socketId: string, room: string) => boolean;
925
+ removeSocketFromRoom: (socketId: string, room: string) => boolean;
926
+ getRoomMembers: (room: string) => Promise<string[]>;
927
+ getRoomSize: (room: string) => Promise<number>;
928
+ getUserRooms: (userId: string) => string[];
929
+ roomExists: (room: string) => Promise<boolean>;
930
+ emitToRoom: (room: string, event: string, data: unknown) => boolean;
931
+ emitToUser: (userId: string, event: string, data: unknown) => boolean;
932
+ emitToRole: (roleId: string, event: string, data: unknown) => boolean;
933
+ emitToAll: (event: string, data: unknown) => boolean;
934
+ emitToAuthenticated: (event: string, data: unknown) => boolean;
935
+ broadcastToRoom: (room: string, exceptUserIds: string | string[], event: string, data: unknown) => boolean;
936
+ }
937
+ /**
938
+ * Helper utilities available in module context.
939
+ */
940
+ interface ContextHelpers {
941
+ generateId: () => string;
942
+ addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void;
943
+ addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise<void>;
944
+ addSoftDeleteFieldIfMissing: (db: Knex, tableName: string) => Promise<void>;
945
+ addConfigDefaultField: (db: Knex, tableName: string) => Promise<void>;
946
+ addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise<boolean>;
947
+ getLibPath: () => string;
948
+ getProjectPath: () => string;
949
+ nowTimestamp: (db: Knex) => string;
950
+ formatTimestamp: (db: Knex, date?: Date) => string;
951
+ safeJsonParse: <T>(jsonString: string, fallback: T, context?: Record<string, unknown>) => T;
952
+ getPagination: (query?: EntityQuery) => PaginationParamsWithOffset;
953
+ applySearchFilter: (qb: Knex.QueryBuilder, definition: EntityDefinition, search: string) => Knex.QueryBuilder;
954
+ buildPaginatedResult: <T>(items: T[], total: number, pagination: PaginationParamsWithOffset) => PaginatedResult<T>;
955
+ }
956
+ /**
957
+ * Entity controller with CRUD operation handlers.
958
+ */
959
+ type EntityHandler = (req: Request, res: Response) => Promise<void>;
960
+ interface EntityController {
961
+ list: EntityHandler;
962
+ get: EntityHandler;
963
+ create?: EntityHandler;
964
+ update?: EntityHandler;
965
+ delete?: EntityHandler;
966
+ execute?: EntityHandler;
967
+ }
968
+ /**
969
+ * Core utilities namespace.
970
+ */
971
+ interface CoreContext {
972
+ logger: Logger;
973
+ errors: {
974
+ AppError: new (message: string, statusCode?: number, details?: unknown) => Error & {
975
+ details?: unknown;
976
+ };
977
+ NotFoundError: new (message?: string) => Error;
978
+ UnauthorizedError: new (codeOrMessage?: string, message?: string) => Error;
979
+ ForbiddenError: new (message?: string) => Error;
980
+ ConflictError: new (message?: string) => Error;
981
+ ValidationError: new (message?: string, details?: ValidationDetail[]) => Error & {
982
+ details: ValidationDetail[];
983
+ };
984
+ codes: Record<string, string>;
985
+ };
986
+ crypto: ContextCrypto;
987
+ cache: ContextCache;
988
+ socket: ContextSocket;
989
+ sse: SSEHelper;
990
+ abilities: ModuleAbilities;
991
+ events: EventEmitterLike;
992
+ middleware: ModuleMiddlewares;
993
+ createRouter: () => Router;
994
+ generateId: () => string;
995
+ generateIdByType: (type?: 'ulid' | 'uuid' | 'nanoid' | 'cuid2' | 'auto' | 'custom') => string | undefined;
996
+ getLibPath: () => string;
997
+ getProjectPath: () => string;
998
+ safeJsonParse: <T>(jsonString: string, fallback: T, context?: Record<string, unknown>) => T;
999
+ }
1000
+ /**
1001
+ * Database namespace.
1002
+ */
1003
+ interface DbContext {
1004
+ knex: Knex;
1005
+ adapter: DatabaseAdapter;
1006
+ schema: SchemaAdapter;
1007
+ addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void;
1008
+ addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise<void>;
1009
+ addSoftDeleteFieldIfMissing: (db: Knex, tableName: string) => Promise<void>;
1010
+ addConfigDefaultField: (db: Knex, tableName: string) => Promise<void>;
1011
+ addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise<boolean>;
1012
+ nowTimestamp: (db: Knex) => string;
1013
+ formatTimestamp: (db: Knex, date?: Date) => string;
1014
+ applySearchFilter: (qb: Knex.QueryBuilder, definition: EntityDefinition, search: string) => Knex.QueryBuilder;
1015
+ getPagination: (query?: EntityQuery) => PaginationParamsWithOffset;
1016
+ buildPaginatedResult: <T>(items: T[], total: number, pagination: PaginationParamsWithOffset) => PaginatedResult<T>;
1017
+ }
1018
+ /**
1019
+ * Runtime namespace.
1020
+ */
1021
+ interface RuntimeContext {
1022
+ createEntityService: <T = unknown>(definition: EntityDefinition, options?: CreateEntityServiceOptions<T>) => BaseEntityService<T>;
1023
+ createEntityServiceAsync: <T = unknown>(definition: EntityDefinition, options?: CreateEntityServiceOptions<T>) => Promise<BaseEntityService<T>>;
1024
+ createEntityController: (service: BaseEntityService, definition: EntityDefinition) => EntityController;
1025
+ createEntityRouter: (controller: EntityController, definition: EntityDefinition) => Router;
1026
+ }
1027
+ /**
1028
+ * Configuration namespace.
1029
+ */
1030
+ interface ConfigContext {
1031
+ env: {
1032
+ NODE_ENV: 'development' | 'production' | 'test';
1033
+ PORT: number;
1034
+ DATABASE_URL: string;
1035
+ CORS_ORIGIN: string;
1036
+ REDIS_URL?: string;
1037
+ REDIS_PREFIX: string;
1038
+ ADMIN_EMAIL?: string;
1039
+ ADMIN_PASSWORD?: string;
1040
+ BACKEND_URL?: string;
1041
+ COOKIE_DOMAIN?: string;
1042
+ TZ: string;
1043
+ TRUST_PROXY: boolean;
1044
+ [key: string]: unknown;
1045
+ };
1046
+ resolved: Record<string, unknown>;
1047
+ }
1048
+
1049
+ /**
1050
+ * Engine namespace.
1051
+ */
1052
+ interface EngineContext {
1053
+ getModules: () => ModuleManifest[];
1054
+ getPlugins: () => PluginManifest[];
1055
+ getModuleSubjects: (mod: ModuleManifest) => string[];
1056
+ getRegisteredSubjects: () => string[];
1057
+ getCoreModules: () => ModuleManifest[];
1058
+ getUserModules: () => ModuleManifest[];
1059
+ getCoreManifest: () => AppManifest;
1060
+ getUserManifest: () => AppManifest | null;
1061
+ hasUserApp: () => boolean;
1062
+ getSubjectForTable: (table: string) => string | undefined;
1063
+ }
1064
+ /**
1065
+ * Services registry namespace.
1066
+ */
1067
+ interface ServicesContext {
1068
+ register: (name: string, service: unknown) => void;
1069
+ get: <T = unknown>(name: string) => T;
1070
+ getOptional: <T = unknown>(name: string) => T | undefined;
1071
+ has: (name: string) => boolean;
1072
+ getBySuffix: <T = unknown>(suffix: string) => Array<{
1073
+ name: string;
1074
+ service: T;
1075
+ }>;
1076
+ [name: string]: unknown;
1077
+ }
1078
+ /**
1079
+ * Adapters registry namespace.
1080
+ */
1081
+ interface AdaptersContext {
1082
+ register: (name: string, dataAdapter: DatabaseAdapter, schemaAdapter?: SchemaAdapter) => void;
1083
+ get: <T extends DatabaseAdapter = DatabaseAdapter>(name?: string) => T;
1084
+ getSchema: <T extends SchemaAdapter = SchemaAdapter>(name?: string) => T | undefined;
1085
+ has: (name: string) => boolean;
1086
+ }
1087
+ /**
1088
+ * Module context providing access to all Nexus services and utilities.
1089
+ */
1090
+ interface ModuleContext {
1091
+ core: CoreContext;
1092
+ db: DbContext;
1093
+ runtime: RuntimeContext;
1094
+ config: ConfigContext;
1095
+ engine: EngineContext;
1096
+ services: ServicesContext;
1097
+ adapters: AdaptersContext;
1098
+ createRouter: () => Router;
1099
+ }
1100
+ /**
1101
+ * HTTP method for custom routes
1102
+ */
1103
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
1104
+ /**
1105
+ * Parameter definition for path or query parameters
1106
+ */
1107
+ interface RouteParameterDefinition {
1108
+ type: 'string' | 'integer' | 'boolean' | 'number';
1109
+ description?: LocalizedString;
1110
+ required?: boolean;
1111
+ default?: string | number | boolean;
1112
+ enum?: Array<string | number>;
1113
+ }
1114
+ /**
1115
+ * Response schema definition for custom routes
1116
+ */
1117
+ interface RouteResponseDefinition {
1118
+ type: 'object' | 'array' | 'string' | 'boolean' | 'number';
1119
+ properties?: Record<string, FieldDefinition>;
1120
+ items?: {
1121
+ $ref?: string;
1122
+ type?: 'object' | 'string' | 'number' | 'boolean';
1123
+ properties?: Record<string, FieldDefinition>;
1124
+ };
1125
+ description?: LocalizedString;
1126
+ }
1127
+ /**
1128
+ * Custom route definition for OpenAPI documentation.
1129
+ */
1130
+ interface CustomRouteDefinition {
1131
+ method: HttpMethod;
1132
+ path: string;
1133
+ summary: LocalizedString;
1134
+ description?: LocalizedString;
1135
+ input?: Record<string, FieldDefinition>;
1136
+ output?: RouteResponseDefinition;
1137
+ params?: Record<string, RouteParameterDefinition>;
1138
+ query?: Record<string, RouteParameterDefinition>;
1139
+ auth?: boolean;
1140
+ operationId?: string;
1141
+ tags?: string[];
1142
+ }
1143
+
1144
+ /**
1145
+ * Entity definition types - discriminated union for all entity types
1146
+ */
1147
+
1148
+ /**
1149
+ * Display mode for entity list views in the UI.
1150
+ */
1151
+ type DisplayMode = 'table' | 'list' | 'masonry' | 'tree';
1152
+ /**
1153
+ * All available entity type identifiers.
1154
+ */
1155
+ type EntityType = 'collection' | 'single' | 'external' | 'virtual' | 'computed' | 'view' | 'reference' | 'config' | 'event' | 'temp' | 'action' | 'tree' | 'dag';
1156
+ /**
1157
+ * Requirements that must be met to activate a module.
1158
+ */
1159
+ interface ModuleRequirements {
1160
+ /** Environment variables that must be defined */
1161
+ env?: string[];
1162
+ /** Other modules that must be loaded first */
1163
+ modules?: string[];
1164
+ }
1165
+ /**
1166
+ * Unified action definition for module, entity, and row-level actions.
1167
+ */
1168
+ interface ActionDefinition {
1169
+ /** Unique key for the action route */
1170
+ key: string;
1171
+ /** Display name for the action button/form */
1172
+ label: LocalizedString;
1173
+ /** Iconify icon identifier (default: 'mdi:play') */
1174
+ icon?: string;
1175
+ /** Entity type marker - required for standalone actions in module.definitions[] */
1176
+ type?: 'action';
1177
+ /** Action scope: module, entity, or row */
1178
+ scope?: 'module' | 'entity' | 'row';
1179
+ /** HTTP method for the action endpoint (default: 'POST') */
1180
+ method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
1181
+ /** Custom middleware (e.g., multer for file uploads, rate limiting) */
1182
+ middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
1183
+ /** Handler function that executes the action logic */
1184
+ handler?: (ctx: ModuleContext, input: unknown, req?: Request, res?: Response) => Promise<unknown>;
1185
+ /** Zod schema for validating input before execution */
1186
+ inputSchema?: ValidationSchema;
1187
+ /** Zod schema documenting the response structure */
1188
+ outputSchema?: ValidationSchema;
1189
+ /** Fields to select from the record (row-level only) */
1190
+ select?: string[];
1191
+ /** Form fields for UI generation */
1192
+ fields?: Record<string, FieldDefinition>;
1193
+ /** UI sidebar ordering (default: 999) */
1194
+ order?: number;
1195
+ /** HTTP status code for success response (default: 200) */
1196
+ successStatus?: number;
1197
+ /** Show action result in a modal (default: false — silent toast only) */
1198
+ showResult?: boolean;
1199
+ /** Skip authentication for this action */
1200
+ skipAuth?: boolean;
1201
+ /** CASL authorization configuration */
1202
+ casl?: EntityCaslConfig | {
1203
+ action?: string;
1204
+ conditions?: Record<string, unknown>;
1205
+ };
1206
+ /** Batch/long-running action configuration. When set, action uses SSE for progress. */
1207
+ batch?: BatchConfig;
1208
+ }
1209
+ /** @deprecated Use ActionDefinition instead */
1210
+ type EntityAction = ActionDefinition;
1211
+ /** Standalone action for module.definitions[] - requires type: 'action' */
1212
+ type StandaloneAction = ActionDefinition & {
1213
+ type: 'action';
1214
+ };
1215
+ /**
1216
+ * Base properties shared by all EntityDefinition types.
1217
+ * @internal
1218
+ */
1219
+ interface BaseEntityDefinition {
1220
+ /** Database table name with module prefix */
1221
+ table: string;
1222
+ /** Display name for the UI (singular form) */
1223
+ label: LocalizedString;
1224
+ /** Display name for the UI (plural form) */
1225
+ labelPlural?: LocalizedString;
1226
+ /** Iconify MDI icon identifier */
1227
+ icon?: string;
1228
+ /** Field definitions keyed by field name */
1229
+ fields: Record<string, FieldDefinition>;
1230
+ /** CASL authorization configuration */
1231
+ casl?: EntityCaslConfig;
1232
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1233
+ public?: boolean;
1234
+ /** API route prefix */
1235
+ routePrefix?: string;
1236
+ /** UI sidebar ordering (default: 999) */
1237
+ order?: number;
1238
+ /** UI display mode */
1239
+ displayMode?: DisplayMode;
1240
+ /** Default sort configuration */
1241
+ defaultSort?: {
1242
+ field: string;
1243
+ order: 'asc' | 'desc';
1244
+ };
1245
+ /** If true, hides from UI sidebar */
1246
+ hidden?: boolean;
1247
+ /** Middleware applied to all routes */
1248
+ middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
1249
+ /** External adapter name for this entity's data source */
1250
+ adapter?: string;
1251
+ }
1252
+ /**
1253
+ * Collection entity - the most common entity type with full CRUD operations.
1254
+ */
1255
+ interface CollectionEntityDefinition extends BaseEntityDefinition {
1256
+ type?: 'collection';
1257
+ /** Field used as display label in dropdowns */
1258
+ labelField: string;
1259
+ /** Auto-add created_at and updated_at timestamp columns */
1260
+ timestamps?: boolean;
1261
+ /** Auto-add created_by and updated_by user reference columns */
1262
+ audit?: boolean;
1263
+ /** Composite database indexes */
1264
+ indexes?: EntityIndex[];
1265
+ /** Use soft delete (deleted_at column) */
1266
+ softDelete?: boolean;
1267
+ /** Custom actions that operate on entity records */
1268
+ actions?: ActionDefinition[];
1269
+ /** Show "Create" button in UI */
1270
+ allowCreate?: boolean;
1271
+ /** Allow editing in UI */
1272
+ allowEdit?: boolean;
1273
+ }
1274
+ /**
1275
+ * Singleton entity - stores a single record in the shared sys_settings table.
1276
+ */
1277
+ interface SingleEntityDefinition {
1278
+ type: 'single';
1279
+ /** Unique key in sys_settings table */
1280
+ key: string;
1281
+ label: LocalizedString;
1282
+ labelPlural?: LocalizedString;
1283
+ icon?: string;
1284
+ fields: Record<string, FieldDefinition>;
1285
+ /** Default values applied when the setting is first accessed */
1286
+ defaults?: Record<string, unknown>;
1287
+ casl?: EntityCaslConfig;
1288
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1289
+ public?: boolean;
1290
+ routePrefix?: string;
1291
+ actions?: EntityAction[];
1292
+ }
1293
+ /**
1294
+ * Seed data configuration for reference/tree/dag entities.
1295
+ */
1296
+ interface SeedConfig {
1297
+ source: 'inline' | 'url' | 'file' | 'api';
1298
+ data?: Array<Record<string, unknown>>;
1299
+ url?: string;
1300
+ file?: string;
1301
+ uniqueKey?: string;
1302
+ syncSchedule?: string;
1303
+ headers?: Record<string, string>;
1304
+ dataPath?: string;
1305
+ transform?: string;
1306
+ }
1307
+ /**
1308
+ * Reference entity - catalogs for lookups and dropdowns.
1309
+ */
1310
+ interface ReferenceEntityDefinition extends BaseEntityDefinition {
1311
+ type: 'reference';
1312
+ labelField: string;
1313
+ timestamps?: boolean;
1314
+ indexes?: EntityIndex[];
1315
+ seed?: Array<Record<string, unknown>> | SeedConfig;
1316
+ allowCreate?: boolean;
1317
+ allowEdit?: boolean;
1318
+ allowDelete?: boolean;
1319
+ }
1320
+ /**
1321
+ * Tree entity - hierarchical data with parent-child relationships.
1322
+ */
1323
+ interface TreeEntityDefinition extends BaseEntityDefinition {
1324
+ type: 'tree';
1325
+ labelField?: string;
1326
+ timestamps?: boolean;
1327
+ audit?: boolean;
1328
+ indexes?: EntityIndex[];
1329
+ softDelete?: boolean;
1330
+ actions?: EntityAction[];
1331
+ seed: Array<Record<string, unknown>> | SeedConfig;
1332
+ allowCreate?: boolean;
1333
+ allowEdit?: boolean;
1334
+ allowDelete?: boolean;
1335
+ allowDragDrop?: boolean;
1336
+ }
1337
+ /**
1338
+ * DAG entity - Directed Acyclic Graph with multiple parents.
1339
+ */
1340
+ interface DagEntityDefinition extends BaseEntityDefinition {
1341
+ type: 'dag';
1342
+ labelField?: string;
1343
+ timestamps?: boolean;
1344
+ audit?: boolean;
1345
+ indexes?: EntityIndex[];
1346
+ softDelete?: boolean;
1347
+ actions?: EntityAction[];
1348
+ seed: Array<Record<string, unknown>> | SeedConfig;
1349
+ parentsTable?: string;
1350
+ allowCreate?: boolean;
1351
+ allowEdit?: boolean;
1352
+ allowDelete?: boolean;
1353
+ allowDragDrop?: boolean;
1354
+ }
1355
+ /**
1356
+ * Retention policy for automatic data cleanup.
1357
+ */
1358
+ interface RetentionPolicy {
1359
+ /** Delete records older than N seconds */
1360
+ seconds?: number;
1361
+ /** Delete records older than N days */
1362
+ days?: number;
1363
+ /** Keep only the most recent N records */
1364
+ maxRows?: number;
1365
+ }
1366
+ /**
1367
+ * Extended retention policy for temp entities.
1368
+ */
1369
+ interface TempRetentionPolicy extends RetentionPolicy {
1370
+ /** Field that stores the expiration timestamp */
1371
+ expiresField?: string;
1372
+ }
1373
+ /**
1374
+ * Event entity - append-only logs for audit trails.
1375
+ */
1376
+ interface EventEntityDefinition extends BaseEntityDefinition {
1377
+ type: 'event';
1378
+ labelField: string;
1379
+ timestamps?: boolean;
1380
+ retention?: RetentionPolicy;
1381
+ actions?: ActionDefinition[];
1382
+ }
1383
+ /** @deprecated Use ActionDefinition with `type: 'action'` instead */
1384
+ type ActionEntityDefinition = ActionDefinition & {
1385
+ type: 'action';
1386
+ };
1387
+ /**
1388
+ * External entity - read-only data from external APIs.
1389
+ */
1390
+ interface ExternalEntityDefinition {
1391
+ type: 'external';
1392
+ label: LocalizedString;
1393
+ labelPlural?: LocalizedString;
1394
+ icon?: string;
1395
+ labelField: string;
1396
+ fields: Record<string, FieldDefinition>;
1397
+ adapter: string;
1398
+ table: string;
1399
+ source?: Record<string, unknown>;
1400
+ cache?: {
1401
+ ttl: number;
1402
+ key?: string;
1403
+ invalidateOn?: string[];
1404
+ };
1405
+ casl?: EntityCaslConfig;
1406
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1407
+ public?: boolean;
1408
+ routePrefix?: string;
1409
+ order?: number;
1410
+ displayMode?: DisplayMode;
1411
+ }
1412
+ /**
1413
+ * Virtual entity - data aggregation across multiple sources.
1414
+ */
1415
+ interface VirtualEntityDefinition {
1416
+ type: 'virtual';
1417
+ label: LocalizedString;
1418
+ labelPlural?: LocalizedString;
1419
+ icon?: string;
1420
+ labelField: string;
1421
+ fields: Record<string, FieldDefinition>;
1422
+ sources: string[];
1423
+ resolver?: (sources: Record<string, unknown[]>, ctx: ModuleContext) => unknown[] | Promise<unknown[]>;
1424
+ casl?: EntityCaslConfig;
1425
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1426
+ public?: boolean;
1427
+ routePrefix?: string;
1428
+ order?: number;
1429
+ displayMode?: DisplayMode;
1430
+ }
1431
+ /**
1432
+ * Computed entity - dynamically calculated data like KPIs.
1433
+ */
1434
+ interface ComputedEntityDefinition {
1435
+ type: 'computed';
1436
+ label: LocalizedString;
1437
+ labelPlural?: LocalizedString;
1438
+ icon?: string;
1439
+ labelField?: string;
1440
+ fields: Record<string, FieldDefinition>;
1441
+ compute?: (ctx: ModuleContext, params?: Record<string, unknown>) => Promise<unknown[]>;
1442
+ cache?: {
1443
+ ttl: number;
1444
+ invalidateOn?: string[];
1445
+ };
1446
+ isSingle?: boolean;
1447
+ casl?: EntityCaslConfig;
1448
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1449
+ public?: boolean;
1450
+ routePrefix?: string;
1451
+ order?: number;
1452
+ displayMode?: DisplayMode;
1453
+ actions?: ActionDefinition[];
1454
+ defaultSort?: {
1455
+ field: string;
1456
+ order: 'asc' | 'desc';
1457
+ };
1458
+ hidden?: boolean;
1459
+ }
1460
+ /**
1461
+ * View entity - read-only database view or projection.
1462
+ */
1463
+ interface ViewEntityDefinition {
1464
+ type: 'view';
1465
+ table: string;
1466
+ label: LocalizedString;
1467
+ labelPlural?: LocalizedString;
1468
+ icon?: string;
1469
+ labelField: string;
1470
+ fields: Record<string, FieldDefinition>;
1471
+ sourceEntity?: string;
1472
+ query?: string | ((db: Knex) => Knex.QueryBuilder);
1473
+ casl?: EntityCaslConfig;
1474
+ /** If true, all endpoints are publicly accessible without authentication (default-deny) */
1475
+ public?: boolean;
1476
+ routePrefix?: string;
1477
+ order?: number;
1478
+ }
1479
+ /**
1480
+ * Config entity - scoped configuration settings.
1481
+ */
1482
+ interface ConfigEntityDefinition extends BaseEntityDefinition {
1483
+ type: 'config';
1484
+ scopeField?: string;
1485
+ scope?: 'global' | 'module' | 'tenant' | 'user';
1486
+ defaults?: Record<string, unknown>;
1487
+ timestamps?: boolean;
1488
+ audit?: boolean;
1489
+ }
1490
+ /**
1491
+ * Temporary entity - short-lived data with automatic expiration.
1492
+ */
1493
+ interface TempEntityDefinition extends BaseEntityDefinition {
1494
+ type: 'temp';
1495
+ retention: TempRetentionPolicy;
1496
+ labelField?: string;
1497
+ indexes?: EntityIndex[];
1498
+ }
1499
+ /**
1500
+ * Union of all entity definitions
1501
+ */
1502
+ type EntityDefinition = CollectionEntityDefinition | SingleEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition | ViewEntityDefinition | ReferenceEntityDefinition | TreeEntityDefinition | DagEntityDefinition | ConfigEntityDefinition | EventEntityDefinition | TempEntityDefinition | ActionEntityDefinition;
1503
+
1504
+ export { type ActionEntityDefinition as $, type ActionDefinition as A, type BatchConfig as B, type Category as C, type DisplayMode as D, type EntityType as E, type ForbiddenErrorInstance as F, type FilterValue as G, type EntityQuery as H, type DatabaseAdapter as I, type SchemaColumnInfo as J, type SchemaColumnBuilder as K, type SchemaTableBuilder as L, type ModuleAbilities as M, type SchemaForeignKeyBuilder as N, type SchemaAlterTableBuilder as O, type PaginationParams as P, type SchemaAdapter as Q, type ReferenceEntityDefinition as R, type SingleEntityDefinition as S, type TempEntityDefinition as T, type ModuleRequirements as U, type ViewEntityDefinition as V, type EntityAction as W, type StandaloneAction as X, type SeedConfig as Y, type RetentionPolicy as Z, type TempRetentionPolicy as _, type EntityDefinition as a, type BaseEntityService as a0, type CollectionEntityService as a1, type TempEntityService as a2, type EventEntityService as a3, type SingleEntityService as a4, type ConfigEntityService as a5, type ReferenceEntityService as a6, type ViewEntityService as a7, type ComputedEntityService as a8, type ExternalEntityService as a9, type CoreContext as aA, type DbContext as aB, type RuntimeContext as aC, type ConfigContext as aD, type EngineContext as aE, type ServicesContext as aF, type AdaptersContext as aG, type ModuleContext as aH, type HttpMethod as aI, type RouteParameterDefinition as aJ, type RouteResponseDefinition as aK, type CustomRouteDefinition as aL, type CategoryMeta as aM, type ActionInjection as aN, type ModuleManifest as aO, type AppManifest as aP, type PluginEnvVar as aQ, type PluginConfigField as aR, type PluginConfigSchema as aS, type PluginSetupInfo as aT, type PluginManifest as aU, type RegisterPluginOptions as aV, CATEGORIES as aW, CATEGORY_ORDER as aX, type VirtualEntityService as aa, type ActionEntityService as ab, type EntityServiceHooks as ac, type CreateEntityServiceOptions as ad, type BaseRolesService as ae, type BaseUsersService as af, type SendMailOptions as ag, type SendMailResult as ah, type BaseMailService as ai, type LoggerReporter as aj, type CoreServices as ak, type ValidationSchema as al, type ValidateSchemas as am, type RateLimitOptions as an, type ModuleMiddlewares as ao, type EventEmitterLike as ap, type ContextCrypto as aq, type ContextLRUCacheOptions as ar, type ContextLRUCache as as, type ContextCache as at, type SocketIOServer as au, type SocketIOBroadcastOperator as av, type ContextSocket as aw, type ContextHelpers as ax, type EntityHandler as ay, type EntityController as az, type CollectionEntityDefinition as b, type EventEntityDefinition as c, type ConfigEntityDefinition as d, type TreeEntityDefinition as e, type DagEntityDefinition as f, type ExternalEntityDefinition as g, type VirtualEntityDefinition as h, type ComputedEntityDefinition as i, type SSEHelper as j, type SSESender as k, type SSEOptions as l, type BatchProgressEvent as m, type BatchLogEntry as n, type BaseAuthRequest as o, type BaseUser as p, type PaginationParamsWithOffset as q, type PaginatedResult as r, type ValidationDetail as s, type CaslAction as t, type RolePermission as u, type EntityCaslConfig as v, type AbilityLike as w, type ForbiddenErrorConstructor as x, type AuthRequest as y, type FilterOperators as z };