import { Knex } from 'knex'; import { Hono, MiddlewareHandler, Context } from 'hono'; import { L as LocalizedString, c as FieldDefinition, k as LocaleConfig, E as EntityIndex, F as FieldCondition } from './field-DtWJTIA9.js'; import { Request, Response } from 'express'; import { Logger } from 'pino'; /** * Base types shared across the SDK */ /** * Base authenticated request with user and CASL abilities (generic). * Use AuthRequest for the pre-typed version. */ interface BaseAuthRequest extends Request { user: TUser; ability: TAbility; } /** * Base user without a password for plugin use. * Used to type relations with users (e.g., author, created_by). * Note: Multi-role support - roles are loaded via user_roles pivot table. */ interface BaseUser { id: string; name: string; email: string; created_at: Date; updated_at: Date; created_by: string | null; updated_by: string | null; /** Extensibility: allows custom user properties */ [key: string]: unknown; } /** * Pagination parameters for queries */ interface PaginationParams { page: number; limit: number; } /** * Extended pagination parameters with offset (used internally) */ interface PaginationParamsWithOffset extends PaginationParams { /** Offset for database query */ offset: number; } /** * Generic paginated result */ interface PaginatedResult { items: T[]; total: number; page: number; limit: number; totalPages: number; hasNext: boolean; } /** * Standard validation error detail for API responses */ interface ValidationDetail { path: string; message: string; } /** * CASL Authorization types */ /** * Standard CASL authorization actions. * * - `manage` - Full access (superuser wildcard, includes all actions) * - `create` - Create new records * - `read` - Read/view records * - `update` - Modify existing records * - `delete` - Remove records * - `execute` - Execute actions/operations on records */ type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'execute' | (string & {}); /** * CASL permission definition for a specific role. * * Defines what actions a role can perform on an entity, * optionally with conditions and field restrictions. */ interface RolePermission { /** Actions this role can perform on the entity */ actions: CaslAction[]; /** CASL conditions to filter which records the permission applies to */ conditions?: Record; /** Restrict permission to specific fields. null = all fields allowed */ fields?: string[] | null; /** If true, this is a denial rule (cannot) instead of allow (can) */ inverted?: boolean; } /** * CASL authorization configuration for an entity. * * Defines role-based access control (RBAC) using CASL. * Permissions are evaluated at runtime for each request. */ interface EntityCaslConfig { /** CASL subject name. Default: inferred from table */ subject?: string; /** * Role-based permissions. * Keys are role names or special wildcards: * - `'*'` — all users including anonymous (unauthenticated) * - `'@'` — all authenticated users (any role, but NOT anonymous) */ permissions?: Record; /** Fields excluded from 'read' permission unless explicitly granted */ sensitiveFields?: string[]; } /** * Generic interface for CASL ability. * * Provides permission checking without directly depending on @casl/ability package. */ interface AbilityLike { /** Check if action is allowed on subject */ can: (action: string, subject: unknown) => boolean; /** Check if action is denied on subject */ cannot: (action: string, subject: unknown) => boolean; } /** * CASL ForbiddenError instance for throwing permission errors. */ interface ForbiddenErrorInstance { /** Throws ForbiddenError if action is not allowed on subject */ throwUnlessCan: (action: string, subject: unknown) => void; } /** * CASL ForbiddenError constructor for creating error instances. */ interface ForbiddenErrorConstructor { /** Creates a ForbiddenErrorInstance bound to an ability */ from: (ability: any) => ForbiddenErrorInstance; } /** * CASL abilities API available in module context. * * Allows modules to use CASL for authorization without importing @casl/ability directly. */ interface ModuleAbilities { /** Creates CASL ability instance for a user based on their roles */ defineAbilityFor: (user: { id: string; } | null, ...args: unknown[]) => unknown; /** Serializes CASL rules for client-side authorization (frontend) */ packRules: (ability: unknown) => unknown[]; /** Wraps an object as CASL subject for instance-level permission checks */ subject: (type: string, object: Record) => unknown; /** ForbiddenError constructor for throwUnlessCan pattern */ ForbiddenError: ForbiddenErrorConstructor; } /** * Pre-typed authenticated request with BaseUser and AbilityLike. * * Use this type for route handlers that require authentication. */ type AuthRequest = BaseAuthRequest; /** * Query and filter types for data access */ /** * Filter operators for advanced filtering * * @example * // Simple equality (shorthand) * { status: 'active' } * * // With operators * { name: { $contains: 'john' } } * { age: { $gte: 18 } } */ interface FilterOperators { /** Equal (default if no operator) */ $eq?: unknown; /** Not equal */ $ne?: unknown; /** Greater than */ $gt?: number | string | Date; /** Greater than or equal */ $gte?: number | string | Date; /** Less than */ $lt?: number | string | Date; /** Less than or equal */ $lte?: number | string | Date; /** Contains (LIKE %value%) */ $contains?: string; /** Starts with (LIKE value%) */ $startswith?: string; /** Ends with (LIKE %value) */ $endswith?: string; /** In array */ $in?: unknown[]; /** Not in array */ $nin?: unknown[]; /** Is null (true = IS NULL, false = IS NOT NULL) */ $isnull?: boolean; /** Between (inclusive range) - value: [min, max] */ $between?: [number | string | Date, number | string | Date]; } /** * Filter value can be: * - Primitive (shorthand for $eq) * - Array (shorthand for $in) * - Object with operators */ type FilterValue = string | number | boolean | null | unknown[] | FilterOperators; /** * Query parameters for entity list operations */ interface EntityQuery { page?: number; limit?: number; sort?: string; order?: 'asc' | 'desc'; search?: string; /** Request localized content in specific locale (e.g., 'es', 'en') */ locale?: string; /** * Filters with optional operators. * All field filters are combined with AND logic. * Use `$or` key for OR logic between field groups: * `{ tenant: 'acme', $or: [{ status: 'active' }, { role: 'admin' }] }` * → tenant = 'acme' AND (status = 'active' OR role = 'admin') */ filters?: Record; } /** * Server-side query options extending EntityQuery with backend-only fields. * Not sent by the client — resolved from entity definition. */ interface ServerEntityQuery extends EntityQuery { /** Override default max limit (default: 100). Useful for trees, exports, or entities needing larger pages */ maxLimit?: number; } /** * Type-safe query with filters restricted to known entity fields. * Provides autocompletion for filter keys and FilterValue type checking. * * @example * interface User { id: string; name: string; age: number; active: boolean } * const query: TypedEntityQuery = { * filters: { name: { $contains: 'john' }, age: { $gte: 18 } } * } */ type TypedEntityQuery = Omit & { filters?: Partial>; }; /** * Database adapter interface for normalized data access. * * Abstracts database operations using standardized types. */ interface DatabaseAdapter { /** * Find multiple records with optional filtering, sorting, and pagination. */ findMany(table: string, query?: EntityQuery): Promise>; /** * Find a single record by filters. */ findOne(table: string, filters: Record): Promise; /** * Find a single record by ID. */ findById(table: string, id: string): Promise; /** * Count records matching filters. */ count(table: string, filters?: Record): Promise; /** * Insert a new record. */ insert(table: string, data: Record): Promise; /** * Update a record by ID. */ update(table: string, id: string, data: Record): Promise; /** * Delete a record by ID. */ delete(table: string, id: string): Promise; /** * Execute operations within a transaction. */ transaction(fn: (trx: DatabaseAdapter) => Promise): Promise; /** * Raw access to the underlying database driver. */ raw: unknown; insertWithTtl?(table: string, data: Record, ttlSeconds: number): Promise; setTtl?(table: string, id: string, ttlSeconds: number): Promise; getTtl?(table: string, id: string): Promise; cleanupExpiredIds?(table: string): Promise; } /** * Schema adapter types for DDL operations */ /** * Column information returned by schema introspection. */ interface SchemaColumnInfo { /** Column name */ name: string; /** Database type (varchar, integer, text, etc.) */ type: string; /** Whether the column allows NULL values */ nullable: boolean; /** Default value if any */ defaultValue: unknown; } /** * Column builder for defining column properties. * Methods return `this` for chaining. */ interface SchemaColumnBuilder { /** Mark as primary key */ primary(): SchemaColumnBuilder; /** Add unique constraint */ unique(): SchemaColumnBuilder; /** Add index */ index(): SchemaColumnBuilder; /** Allow NULL values */ nullable(): SchemaColumnBuilder; /** Disallow NULL values */ notNullable(): SchemaColumnBuilder; /** Set default value */ defaultTo(value: unknown): SchemaColumnBuilder; /** Add foreign key reference */ references(column: string): SchemaColumnBuilder; /** Target table for foreign key */ inTable(table: string): SchemaColumnBuilder; /** On delete behavior for foreign key */ onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaColumnBuilder; } /** * Table builder for creating tables. */ interface SchemaTableBuilder { string(name: string, size?: number): SchemaColumnBuilder; text(name: string): SchemaColumnBuilder; integer(name: string): SchemaColumnBuilder; bigInteger(name: string): SchemaColumnBuilder; decimal(name: string, precision?: number, scale?: number): SchemaColumnBuilder; boolean(name: string): SchemaColumnBuilder; date(name: string): SchemaColumnBuilder; datetime(name: string): SchemaColumnBuilder; timestamp(name: string): SchemaColumnBuilder; json(name: string): SchemaColumnBuilder; jsonb(name: string): SchemaColumnBuilder; uuid(name: string): SchemaColumnBuilder; enum(name: string, values: readonly string[]): SchemaColumnBuilder; binary(name: string, length?: number): SchemaColumnBuilder; float(name: string, precision?: number, scale?: number): SchemaColumnBuilder; specificType(name: string, type: string): SchemaColumnBuilder; timestamps(): void; index(columns: string | string[], indexName?: string): void; unique(columns: string | string[], constraintName?: string): void; primary(columns: string | string[], constraintName?: string): void; foreign(column: string): SchemaForeignKeyBuilder; increments(name?: string): SchemaColumnBuilder; bigIncrements(name?: string): SchemaColumnBuilder; } /** * Foreign key builder for defining references. */ interface SchemaForeignKeyBuilder { references(column: string): SchemaForeignKeyBuilder; inTable(table: string): SchemaForeignKeyBuilder; onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaForeignKeyBuilder; onUpdate(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): SchemaForeignKeyBuilder; } /** * Alter table builder extends table builder with modification operations. */ interface SchemaAlterTableBuilder extends SchemaTableBuilder { dropColumn(name: string): void; renameColumn(from: string, to: string): void; } /** * Schema adapter interface for normalized DDL operations. */ interface SchemaAdapter { hasTable(tableName: string): Promise; hasColumn(tableName: string, columnName: string): Promise; getColumns(tableName: string): Promise; createTable(tableName: string, builder: (t: SchemaTableBuilder) => void): Promise; alterTable(tableName: string, builder: (t: SchemaAlterTableBuilder) => void): Promise; dropTable(tableName: string): Promise; addTimestamps(tableName: string): Promise; addAuditFields(tableName: string): Promise; addColumnIfMissing(tableName: string, columnName: string, type: string): Promise; /** Raw access to the underlying schema builder */ raw: unknown; } /** * Entity service types and hooks */ /** * Base interface for all entity services. */ interface BaseEntityService { /** Gets paginated list of records */ findAll(query?: EntityQuery): Promise>; /** Gets a single record by ID */ findById(id: string): Promise; /** The entity definition this service operates on */ readonly definition: EntityDefinition; } /** * Service for Collection entities with full CRUD. */ interface CollectionEntityService extends BaseEntityService { create(data: Partial, userId?: string): Promise; update(id: string, data: Partial, userId?: string): Promise; delete(id: string): Promise; } /** * Service for Temp entities with TTL expiration. */ interface TempEntityService extends BaseEntityService { create(data: Partial): Promise; update(id: string, data: Partial, extendTtl?: boolean): Promise; delete(id: string): Promise; extendTtl(id: string, additionalSeconds?: number): Promise; cleanup(): Promise; getRemainingTtl(id: string): Promise; isValid(id: string): Promise; } /** * Service for Event entities (append-only). */ interface EventEntityService extends BaseEntityService { create(data: Partial): Promise; append(data: Partial): Promise; runRetentionCleanup(): Promise; findByDateRange(start: Date, end: Date, query?: EntityQuery): Promise>; } /** * Service for Single entities (global configuration). */ interface SingleEntityService extends BaseEntityService { get(): Promise; update(data: Partial, userId?: string): Promise; } /** * Service for Config entities (scoped key-value). */ interface ConfigEntityService extends BaseEntityService { get(key: string): Promise; set(key: string, value: unknown, userId?: string): Promise; getAll(): Promise>; } /** * Tree node with children for hierarchical responses. */ interface TreeNode { data: T; children: TreeNode[]; } /** * Tree entity service - hierarchical data with parent-child relationships. */ interface TreeEntityService extends CollectionEntityService { /** Get full tree hierarchy, optionally from a root node */ getTree(rootId?: string): Promise[]>; /** Get all root nodes (no parent) */ findRoots(): Promise; /** Get direct children of a node */ findChildren(parentId: string | null): Promise; /** Get ancestor chain from node to root */ getAncestors(id: string): Promise; /** Get all descendants recursively */ getDescendants(id: string): Promise; /** Move node to a new parent (null = root) */ move(id: string, newParentId: string | null): Promise; /** Get depth level of a node */ getDepth(id: string): Promise; /** Check if nodeA is an ancestor of nodeB */ isAncestorOf(ancestorId: string, descendantId: string): Promise; } /** * DAG entity service - Directed Acyclic Graph with multiple parents. */ interface DagEntityService extends TreeEntityService { /** Get all parents of a node */ getParents(nodeId: string): Promise; /** Replace all parents of a node */ setParents(nodeId: string, parentIds: string[]): Promise; /** Add a parent to a node */ addParent(nodeId: string, parentId: string): Promise; /** Remove a parent from a node */ removeParent(nodeId: string, parentId: string): Promise; } /** Service for Reference entities (read-only) */ type ReferenceEntityService = BaseEntityService; /** Service for View entities (SQL views, read-only) */ type ViewEntityService = BaseEntityService; /** Service for Computed entities */ type ComputedEntityService = BaseEntityService; /** Service for External entities */ type ExternalEntityService = BaseEntityService; /** Service for Virtual entities */ type VirtualEntityService = BaseEntityService; /** * Service for Action entities. */ interface ActionEntityService { execute(data: Partial): Promise; readonly definition: EntityDefinition; } /** * Hooks to customize entity service behavior. */ interface EntityServiceHooks { beforeCreate?: (data: Partial) => Promise> | Partial; afterCreate?: (record: T) => Promise | void; beforeUpdate?: (id: string, data: Partial) => Promise> | Partial; afterUpdate?: (record: T) => Promise | void; beforeDelete?: (id: string) => Promise | void; afterDelete?: (id: string) => Promise | void; afterFindById?: (record: T | null) => Promise | T | null; afterFindAll?: (result: PaginatedResult) => Promise> | PaginatedResult; } /** * Options for createEntityService */ interface CreateEntityServiceOptions { hooks?: EntityServiceHooks; } /** * Role management service interface. */ interface BaseRolesService { findAll(query?: EntityQuery): Promise>; findById(id: string): Promise; findByName(name: string): Promise; create(data: Record, userId?: string): Promise; update(id: string, data: Record, userId?: string): Promise; delete(id: string): Promise; } /** * User management service interface. */ interface BaseUsersService { findAll(query?: EntityQuery): Promise>; findById(id: string): Promise; findByIdWithRoles(id: string): Promise; create(data: Record, userId?: string): Promise; update(id: string, data: Record, userId?: string): Promise; delete(id: string): Promise; getRoleIds(userId: string): Promise; getRoleNames(userId: string): Promise; getUserRoles(userId: string): Promise; assignRole(userId: string, roleId: string): Promise; removeRole(userId: string, roleId: string): Promise; setRoles(userId: string, roleIds: string[]): Promise; roles: BaseRolesService; } /** * Email sending options. */ interface SendMailOptions { to: string | string[]; subject: string; title?: string; message?: string; html?: string; text?: string; from?: string; replyTo?: string; actions?: Array<{ label: string; url: string; }>; logoUrl?: string; attachments?: Array<{ filename: string; content: Buffer | string; contentType?: string; }>; } /** * Result from sending an email. */ interface SendMailResult { messageId: string; accepted: string[]; rejected: string[]; } /** * Email service interface. */ interface BaseMailService { send(options: SendMailOptions): Promise; verify(): Promise; } /** * Error reporter interface for external monitoring. */ interface LoggerReporter { captureException: (error: Error, context?: Record) => void; } /** * Core services available via ctx.services. */ interface CoreServices { logger?: LoggerReporter; users?: BaseUsersService; mail?: BaseMailService; [key: string]: unknown; } /** * Socket.IO and real-time event types shared across all packages. * * This is the canonical location for: * - Entity real-time events (server→client, client→server) * - Bridge rule types (nexusEvents → Socket.IO routing) * - Room naming conventions */ /** Default tenant ID for single-tenant deployments. */ declare const DEFAULT_TENANT_ID = "default"; /** * Builds a tenant-scoped entity room name for Socket.IO. * * @example entityRoom('default', 'users', 'users_accounts') // 'entity:default:users.users_accounts' */ declare function entityRoom(tenantId: string, module: string, entity: string): string; /** * Payload for entity change events pushed via Socket.IO. */ interface EntityChangePayload { /** Tenant that owns the data */ tenantId: string; /** Module that owns the entity */ module: string; /** Entity name within the module */ entity: string; /** CRUD action that occurred */ action: 'created' | 'updated' | 'deleted'; /** Record ID */ id: string; /** Record data (may be partial or omitted for deletes) */ data?: Record; /** User who performed the action */ userId?: string; /** ISO timestamp */ timestamp: string; } /** * Server→Client entity events received via Socket.IO. */ interface EntityRealtimeEvents { 'entity:created': (payload: EntityChangePayload) => void; 'entity:updated': (payload: EntityChangePayload) => void; 'entity:deleted': (payload: EntityChangePayload) => void; } /** * Client→Server events for real-time entity subscriptions. */ interface EntityRealtimeEmits { 'entity:subscribe': (entityKey: string) => void; 'entity:unsubscribe': (entityKey: string) => void; } /** * Target specification for bridge rules. * Determines which Socket.IO room(s) receive the event. */ type BridgeTarget = { type: 'room'; room: string | ((payload: any) => string); } | { type: 'user'; userId: string | ((payload: any) => string); } | { type: 'all'; } | { type: 'authenticated'; }; /** * Declarative rule that routes a nexusEvents event to Socket.IO. * * Modules register rules via `ctx.core.hub.registerRule()`. * The bridge listens to the nexusEvent and emits the socketEvent * to the specified target room(s). */ interface BridgeRule { /** nexusEvents event name to listen to (supports wildcards) */ nexusEvent: string; /** Socket.IO event name to emit to clients */ socketEvent: string; /** Target room/user specification */ target: BridgeTarget; /** Optional payload transformer */ transform?: (payload: unknown) => unknown; /** Optional debounce in ms (prevents flood for rapid db.* events) */ debounceMs?: number; } /** * Real-time mode for entity definitions. * - 'live': Server pushes changes to subscribed clients (read-only sync) * - 'sync': Bidirectional sync with conflict resolution (TASK-93) * - false: No real-time capabilities (default) */ type RealtimeMode = 'live' | 'sync' | false; /** * Cache system types — CacheManager with scoped managers */ interface CacheOptions { /** Maximum cache entries (default: 100) */ maxEntries?: number; /** Default TTL in seconds (default: 60) */ defaultTTL?: number; /** Event patterns that trigger cache invalidation (e.g. ['db.users.*']) */ invalidateOn?: string[]; } interface CacheStats { hits: number; misses: number; hitRate: number; size: number; maxEntries: number; } interface ManagedCache { readonly name: string; get(key: string): Promise; set(key: string, value: T, ttl?: number): Promise; delete(key: string): Promise; deleteByPrefix(prefix: string): Promise; has(key: string): Promise; clear(): Promise; prune(): Promise; getSize(): Promise; getStats(): CacheStats; resetStats(): void; /** Add extra invalidation rules after creation (for shared caches) */ addInvalidationRules(events: string[]): void; } interface ScopedCacheManager { /** Create a named cache. Throws if name already exists in this scope. */ create(name: string, options?: CacheOptions): ManagedCache; /** Get existing cache or create if not exists */ getOrCreate(name: string, options?: CacheOptions): ManagedCache; /** Get existing cache by local name */ get(name: string): ManagedCache | undefined; /** Clear all caches in this scope */ clearAll(): Promise; /** Stats for all caches in this scope */ stats(): Record; /** Destroy scope: clear caches, remove listeners, unregister */ destroy(): Promise; } interface CacheManagerContract { /** Create a scoped manager for a module */ scoped(scope: string, options?: { maxCaches?: number; }): ScopedCacheManager; /** Get any cache by full name (scope:name) */ get(fullName: string): ManagedCache | undefined; /** Global stats across all caches */ stats(): Record; /** Manually trigger invalidation for all caches listening to this event pattern */ invalidate(eventPattern: string): Promise; /** Unregister a cache by full name, removing listeners and rules */ unregister(fullName: string): Promise; /** Start prune interval (default 5min, incremental round-robin) */ startPruneScheduler(intervalMs?: number): void; /** Cleanup everything: listeners, intervals, caches */ destroy(): Promise; } /** * Page definition types for custom module pages. * Pages are a separate concept from entities — they represent * custom UI views that don't map to CRUD operations. */ /** * Page types supported by the UI page renderer. */ type PageType = 'dashboard' | 'wizard' | 'detail' | 'custom'; /** * Layout span for widget grid positioning (1-12 columns). */ interface WidgetLayout { /** Column span (1-12, default: 12) */ span?: number; /** Row order hint */ order?: number; } /** * Widget definition for dashboard pages. */ interface WidgetDefinition { /** Unique widget identifier within the page */ id: string; /** Widget type — determines the renderer */ type: 'stat' | 'table' | 'chart' | 'timeline' | 'form' | 'custom'; /** Display label */ label?: LocalizedString; /** Data source endpoint (relative to module routePrefix) */ endpoint: string; /** HTTP method (default: GET) */ method?: 'GET' | 'POST'; /** Grid layout */ layout?: WidgetLayout; /** Chart subtype (only for type: 'chart') */ chart?: 'bar' | 'line' | 'donut' | 'area'; /** Extra props passed to the widget component */ props?: Record; } /** * Page definition for custom module pages. */ interface PageDefinition { /** Unique page identifier within the module */ id: string; /** Display label */ label: LocalizedString; /** Iconify icon identifier */ icon?: string; /** Page type — determines the renderer */ type: PageType; /** CASL subject for permission check (optional) */ caslSubject?: string; /** Sort order in sidebar/tabs */ order?: number; /** Hide from sidebar/tabs */ hidden?: boolean; /** Data source for the page (single endpoint) */ dataSource?: { endpoint: string; method?: 'GET' | 'POST'; }; /** Widget definitions (for type: 'dashboard') */ widgets?: WidgetDefinition[]; /** Component name (for type: 'custom') — resolves to plugin/module UI asset */ component?: string; /** * Backend endpoint that returns `{ title: string, content: string }` HTML. * StandalonePage/PageTab fetch and render it in a generic layout. * Mutually exclusive with `component`. */ contentEndpoint?: string; /** Standalone pages render with full layout, not as tabs inside ModulePage */ standalone?: boolean; /** Route meta for the standalone page (auth, guest, permissions) */ meta?: Record; /** Layout for standalone pages: 'admin' (default, sidebar+header), 'public' (header+footer), 'centered' (minimal) */ layout?: 'admin' | 'public' | 'centered'; } /** * Serializable page definition for API responses. */ interface PageDefinitionDTO { id: string; label: LocalizedString; icon?: string; type: PageType; caslSubject?: string; order?: number; hidden?: boolean; dataSource?: { endpoint: string; method?: 'GET' | 'POST'; }; widgets?: WidgetDefinition[]; component?: string; contentEndpoint?: string; standalone?: boolean; meta?: Record; layout?: 'admin' | 'public' | 'centered'; } /** * Module and Plugin manifest types */ /** * Available categories for organizing modules in the UI sidebar. */ type Category = 'content' | 'data' | 'assets' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'identity' | 'compliance' | 'security' | 'settings'; /** * Metadata for a category including display information. */ interface CategoryMeta { label: LocalizedString; icon: string; order: number; } /** * Registry of all categories with their metadata. */ declare const CATEGORIES: Record; /** * Categories sorted by their display order for sidebar rendering. */ declare const CATEGORY_ORDER: Category[]; /** * Cross-module action injection descriptor. * Allows a module to inject actions into entities owned by other modules. */ interface ActionInjection { /** Target module and entity to inject the action into */ target: { module: string; entity: string; }; /** The action definition to inject into the target entity's actions[] */ action: ActionDefinition; } /** * Module manifest defining a Nexus module. */ interface ModuleManifest { /** Unique module identifier */ name: string; /** Display name for UI (singular) */ label?: LocalizedString; /** Display name for UI (plural) */ labelPlural?: LocalizedString; /** Iconify MDI icon identifier */ icon?: string; /** Module description for documentation */ description?: LocalizedString; /** Module type */ type?: 'core' | 'plugin' | 'auth-plugin' | 'custom'; /** Category for sidebar grouping */ category: Category; /** Other module names that must be loaded before this one */ dependencies?: string[]; /** Other module names that should be loaded before this one if present (soft dependency) */ optionalDependencies?: string[]; /** Requirements that must be met to enable this module */ required?: ModuleRequirements; /** Database migration function */ migrate?: (ctx: ModuleContext) => Promise; /** Seed function for initial/reference data */ seed?: (ctx: ModuleContext) => Promise; /** Initialization function called on app startup */ init?: (ctx: ModuleContext) => void | Promise; /** Factory function returning Hono router with custom routes */ routes?: (ctx: ModuleContext) => Hono; /** API route prefix. Default: `/{name}` */ routePrefix?: string; /** Entity definitions */ definitions?: EntityDefinition[]; /** Standalone action definitions (module-scope operations) */ actions?: ActionDefinition[]; /** Page definitions (custom views, dashboards, wizards) */ pages?: PageDefinition[]; /** Custom route definitions for OpenAPI documentation */ customRoutes?: CustomRouteDefinition[]; /** Actions to inject into entities of other modules */ injectActions?: ActionInjection[]; } /** * Application manifest with module and plugin registry. */ interface AppManifest { name: string; version: string; modules: ModuleManifest[]; plugins?: PluginManifest[]; } /** * Environment variable definition for plugin installation. */ interface PluginEnvVar { name: string; description: LocalizedString; required: boolean; default?: string; example?: string; pattern?: string; sensitive?: boolean; } /** * Configuration field definition for plugin settings. */ interface PluginConfigField { key: string; label: LocalizedString; description?: LocalizedString; type: 'string' | 'number' | 'boolean' | 'select' | 'json'; default?: unknown; required?: boolean; options?: Array<{ value: string; label: LocalizedString; icon?: string; }>; } /** * Plugin configuration schema. */ interface PluginConfigSchema { fields: PluginConfigField[]; category?: LocalizedString; } /** * Plugin setup/installation information. */ interface PluginSetupInfo { steps?: LocalizedString[]; docsUrl?: string; postInstall?: (ctx: ModuleContext) => Promise; prerequisites?: LocalizedString[]; caveats?: LocalizedString[]; } /** * Plugin manifest defining a distributable Nexus plugin package. */ interface PluginManifest { /** Plugin npm package name */ name: string; /** Unique 3-char lowercase plugin identifier (e.g. 'tag', 'lnk', 'ait') */ code: string; /** Display label (singular) */ label: LocalizedString; /** Display label (plural) */ labelPlural?: LocalizedString; /** Iconify MDI icon */ icon?: string; /** Absolute path to plugin image file. Auto-detected from image.png if present. */ image?: string; /** LLM-oriented documentation. Auto-detected from llms.txt if present. */ llms?: string; /** Category for sidebar grouping */ category: Category; /** Plugin version following SemVer */ version: string; /** Plugin description */ description: LocalizedString; /** Modules bundled in this plugin */ modules: ModuleManifest[]; /** Other plugin package names that must be loaded before this one */ dependencies?: string[]; /** Other plugin package names that should be loaded before this one if present */ optionalDependencies?: string[]; /** Environment variables required or used by this plugin */ envVars?: PluginEnvVar[]; /** Configuration schema for plugin settings */ config?: PluginConfigSchema; /** Peer dependencies required */ peerDependencies?: Record; /** Installation and setup information */ setup?: PluginSetupInfo; /** Absolute path to plugin's migrations directory. Use import.meta.dirname to resolve. */ migrationsDir?: string; } /** * Options for registerPlugin() */ interface RegisterPluginOptions { /** If true, no HTTP routes will be mounted for this plugin */ disableEndpoints?: boolean; } /** * Generic validation schema interface compatible with Zod. */ interface ValidationSchema { parse: (data: unknown) => unknown; } /** * Schemas for the validation middleware. */ interface ValidateSchemas { body?: ValidationSchema; query?: ValidationSchema; params?: ValidationSchema; } /** * Configuration options for rate limiting middleware. */ interface RateLimitOptions { windowMs?: number; max?: number; message?: string; skipSuccessfulRequests?: boolean; skipFailedRequests?: boolean; keyGenerator?: (req: any) => string; } /** * Middleware factories available in module context. */ interface ModuleMiddlewares { validate: (schemas: ValidateSchemas) => MiddlewareHandler; rateLimit: (options?: RateLimitOptions) => MiddlewareHandler; [key: string]: MiddlewareHandler | ((...args: any[]) => MiddlewareHandler) | undefined; } /** * Minimal EventEmitter interface. */ interface EventEmitterLike { emit: (event: string, ...args: unknown[]) => boolean; on: (event: string, listener: (...args: unknown[]) => void) => this; off: (event: string, listener: (...args: unknown[]) => void) => this; once: (event: string, listener: (...args: unknown[]) => void) => this; onAny?: (listener: (event: string, ...args: unknown[]) => void) => this; } /** * Semantic event API for cross-module communication. * Wraps the raw EventEmitter2 with explicit intent patterns. * * - notify: fire-and-forget, errors in listeners do not propagate * - query: request data from 0..N responders, returns flat array * - command: request action from 0..1 handler, returns first result */ interface ContextEvents { /** Fire-and-forget. Errors in listeners are caught and logged, never thrown. */ notify(event: string, payload?: unknown): void; /** Request data from 0..N listeners. Returns flat array of all responses. */ query(event: string, payload?: unknown): Promise; /** Request action from 0..1 handler. Returns first defined response or undefined. */ command(event: string, payload?: unknown): Promise; on(event: string, listener: (...args: unknown[]) => unknown): void; off(event: string, listener: (...args: unknown[]) => unknown): void; once(event: string, listener: (...args: unknown[]) => unknown): void; /** Subscribe to all events. Useful for cross-cutting concerns like webhooks and audit. */ onAny(listener: (event: string, ...args: unknown[]) => void): void; } /** * Cryptographic utilities for password handling and symmetric encryption. */ interface ContextCrypto { hashPassword: (password: string) => Promise; verifyPassword: (password: string, hash: string) => Promise; DUMMY_HASH: string; encrypt: (plaintext: string) => string; decrypt: (ciphertext: string) => string; } /** * Socket.IO Server interface (minimal) */ interface SocketIOServer { to(room: string): SocketIOBroadcastOperator; in(room: string): SocketIOBroadcastOperator; emit(event: string, ...args: unknown[]): boolean; sockets: { sockets: Map; }; } /** * Socket.IO Broadcast Operator interface */ interface SocketIOBroadcastOperator { emit(event: string, ...args: unknown[]): void; fetchSockets(): Promise; } /** * Socket.IO API for real-time communication. */ interface ContextSocket { getIO: () => SocketIOServer; isInitialized: () => boolean; isUserConnected: (userId: string) => boolean; getUserSocketCount: (userId: string) => number; getConnectedUsers: () => string[]; joinUserToRoom: (userId: string, room: string) => boolean; removeUserFromRoom: (userId: string, room: string) => boolean; joinSocketToRoom: (socketId: string, room: string) => boolean; removeSocketFromRoom: (socketId: string, room: string) => boolean; getRoomMembers: (room: string) => Promise; getRoomSize: (room: string) => Promise; getUserRooms: (userId: string) => string[]; roomExists: (room: string) => Promise; emitToRoom: (room: string, event: string, data: unknown) => boolean; emitToUser: (userId: string, event: string, data: unknown) => boolean; emitToRole: (roleId: string, event: string, data: unknown) => boolean; emitToAll: (event: string, data: unknown) => boolean; emitToAuthenticated: (event: string, data: unknown) => boolean; broadcastToRoom: (room: string, exceptUserIds: string | string[], event: string, data: unknown) => boolean; /** Executes callback when Socket.IO is ready. Runs immediately if already initialized. */ onReady: (callback: () => void) => void; } /** * Events hub API for declarative event→Socket.IO routing. * Modules register bridge rules via ctx.core.hub. */ interface ContextEventsHub { /** Register a bridge rule (nexusEvent → Socket.IO) */ registerRule: (rule: BridgeRule) => void; /** Remove all rules for a specific nexusEvent */ removeRule: (nexusEvent: string) => void; } /** * Helper utilities available in module context. */ interface ContextHelpers { generateId: () => string; addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void; addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise; addSoftDeleteFieldIfMissing: (db: Knex, tableName: string) => Promise; addConfigDefaultField: (db: Knex, tableName: string) => Promise; addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise; getLibPath: () => string; getProjectPath: () => string; nowTimestamp: (db: Knex) => string; formatTimestamp: (db: Knex, date?: Date) => string; safeJsonParse: (jsonString: string, fallback: T, context?: Record) => T; getPagination: (query?: EntityQuery) => PaginationParamsWithOffset; applySearchFilter: (qb: Knex.QueryBuilder, definition: EntityDefinition, search: string) => Knex.QueryBuilder; buildPaginatedResult: (items: T[], total: number, pagination: PaginationParamsWithOffset) => PaginatedResult; } /** * Entity controller with CRUD operation handlers. */ type EntityHandler = (req: Request, res: Response) => Promise; interface EntityController { list: EntityHandler; get: EntityHandler; create?: EntityHandler; update?: EntityHandler; delete?: EntityHandler; execute?: EntityHandler; } /** * Core utilities namespace. */ interface CoreContext { logger: Logger; errors: { AppError: new (message: string, statusCode?: number, details?: unknown) => Error & { details?: unknown; }; NotFoundError: new (message?: string) => Error; UnauthorizedError: new (codeOrMessage?: string, message?: string) => Error; ForbiddenError: new (codeOrMessage?: string, message?: string) => Error; ConflictError: new (codeOrMessage?: string, message?: string) => Error; ValidationError: new (message?: string, details?: ValidationDetail[]) => Error & { details: ValidationDetail[]; }; codes: Record; }; crypto: ContextCrypto; cache: ScopedCacheManager; socket: ContextSocket; abilities: ModuleAbilities; events: EventEmitterLike; hub: ContextEventsHub; middleware: ModuleMiddlewares; createRouter: () => Hono; generateId: () => string; generateIdByType: (type?: 'ulid' | 'uuid' | 'nanoid' | 'cuid2' | 'auto' | 'custom') => string | undefined; getLibPath: () => string; getProjectPath: () => string; safeJsonParse: (jsonString: string, fallback: T, context?: Record) => T; plugins: PluginOpsContext; } /** * Plugin operations namespace (file-based, no DB dependency). */ interface PluginOpsContext { /** Get plugin state from nexus.plugins.json */ getState: (name: string) => { enabled: boolean; } | undefined; /** Get all plugin states */ getAllStates: () => Record; /** Install plugin via pnpm and register in nexus.plugins.json */ install: (name: string, opts?: { version?: string; }) => Promise; /** Uninstall plugin via pnpm and remove from nexus.plugins.json */ uninstall: (name: string) => Promise; /** Enable a plugin */ enable: (name: string) => void; /** Disable a plugin */ disable: (name: string) => void; /** Normalize short name ("charts") to full npm name */ normalizeName: (name: string) => string; /** Extract short name from full npm name */ shortName: (name: string) => string; /** Discover all installed plugins from package.json (includes disabled) */ discover: () => Promise; } /** * Database namespace. */ interface DbContext { knex: Knex; /** Resolve table name with plugin prefix. Identity for core modules. */ t: (name: string) => string; adapter: DatabaseAdapter; schema: SchemaAdapter; addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void; addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise; addSoftDeleteFieldIfMissing: (db: Knex, tableName: string) => Promise; addConfigDefaultField: (db: Knex, tableName: string) => Promise; addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise; nowTimestamp: (db: Knex) => string; formatTimestamp: (db: Knex, date?: Date) => string; applySearchFilter: (qb: Knex.QueryBuilder, definition: EntityDefinition, search: string) => Knex.QueryBuilder; getPagination: (query?: EntityQuery) => PaginationParamsWithOffset; buildPaginatedResult: (items: T[], total: number, pagination: PaginationParamsWithOffset) => PaginatedResult; getKnex: (adapter?: string) => Knex; /** Run full seed for a module (manifest seed + entity auto-seed). Injected from db layer. */ seedModule: (mod: ModuleManifest) => Promise; } /** * Runtime namespace. */ interface RuntimeContext { createEntityService: (definition: EntityDefinition, options?: CreateEntityServiceOptions) => BaseEntityService; createEntityServiceAsync: (definition: EntityDefinition, options?: CreateEntityServiceOptions) => Promise>; createEntityController: (service: BaseEntityService, definition: EntityDefinition) => EntityController; createEntityRouter: (controller: EntityController, definition: EntityDefinition) => Hono; } /** * Configuration namespace. */ interface ConfigContext { env: { NODE_ENV: 'development' | 'production' | 'test'; PORT: number; DATABASE_URL: string; CORS_ORIGIN: string; REDIS_URL?: string; REDIS_PREFIX: string; ADMIN_EMAIL?: string; ADMIN_PASSWORD?: string; BACKEND_URL?: string; COOKIE_DOMAIN?: string; TZ: string; TRUST_PROXY: boolean; [key: string]: unknown; }; resolved: Record; } /** * Engine namespace. */ interface EngineContext { getModules: () => ModuleManifest[]; getPlugins: () => PluginManifest[]; getModuleSubjects: (mod: ModuleManifest) => string[]; getRegisteredSubjects: () => string[]; getCoreModules: () => ModuleManifest[]; getUserModules: () => ModuleManifest[]; getCoreManifest: () => AppManifest; getUserManifest: () => AppManifest | null; hasUserApp: () => boolean; getSubjectForTable: (table: string) => string | undefined; hasModule: (name: string) => boolean; hasPlugin: (name: string) => boolean; getPluginByCode: (code: string) => PluginManifest | undefined; hasPluginByCode: (code: string) => boolean; /** Get an entity definition by module name and entity identifier */ getEntityDefinition: (moduleName: string, entityName: string) => EntityDefinition | undefined; } /** * Services registry namespace. */ interface ServicesContext { register: (name: string, service: unknown) => void; get: (name: string) => T; getOptional: (name: string) => T | undefined; has: (name: string) => boolean; getBySuffix: (suffix: string) => Array<{ name: string; service: T; }>; [name: string]: unknown; } /** * Adapters registry namespace. */ interface AdaptersContext { register: (name: string, dataAdapter: DatabaseAdapter, schemaAdapter?: SchemaAdapter) => void; get: (name?: string) => T; getSchema: (name?: string) => T | undefined; has: (name: string) => boolean; } /** * Registry for extensible capabilities. * Plugins register resolvers via ctx.capabilities.register(). * GET /system/capabilities calls resolve() to aggregate all. */ interface CapabilitiesRegistry { /** Register a capability resolver. Sync or async. Warns if key already registered. */ register(key: string, resolver: () => unknown | Promise): void; /** Resolve all registered capabilities in parallel. Catches per-resolver errors. */ resolve(): Promise>; } /** * Module context providing access to all Nexus services and utilities. */ interface ModuleContext { /** Tenant ID for the current context. Always present — in single-tenant mode it's DEFAULT_TENANT_ID. */ tenantId: string; core: CoreContext; db: DbContext; runtime: RuntimeContext; config: ConfigContext; engine: EngineContext; services: ServicesContext; adapters: AdaptersContext; /** Extensible capabilities registry for public metadata. */ capabilities: CapabilitiesRegistry; /** Semantic event API for cross-module communication (notify/query/command). */ events: ContextEvents; createRouter: () => Hono; /** Supported locales for the platform */ locales: LocaleConfig[]; } /** * HTTP method for custom routes */ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; /** * Parameter definition for path or query parameters */ interface RouteParameterDefinition { type: 'string' | 'integer' | 'boolean' | 'number'; description?: LocalizedString; required?: boolean; default?: string | number | boolean; enum?: Array; } /** * Response schema definition for custom routes */ interface RouteResponseDefinition { type: 'object' | 'array' | 'string' | 'boolean' | 'number'; properties?: Record; items?: { $ref?: string; type?: 'object' | 'string' | 'number' | 'boolean'; properties?: Record; }; description?: LocalizedString; } /** * Custom route definition for OpenAPI documentation. */ interface CustomRouteDefinition { method: HttpMethod; path: string; summary: LocalizedString; description?: LocalizedString; input?: Record; output?: RouteResponseDefinition; params?: Record; query?: Record; auth?: boolean; operationId?: string; tags?: string[]; } /** * Seed context passed to onSeed hook. * Provides typed helpers for seeding data without direct DB access. * * Note: masters.register() is lazy (data flushed after onSeed completes). * All other methods (roles.add, users.add, plugin().entity().upsert, raw) are eager. */ interface SeedContext { /** Register master reference data. Lazy: flushed after onSeed completes. */ masters: { register(type: string, entries: Array<{ code: string; label: string | { en?: string; es?: string; [key: string]: string | undefined; }; order?: number; is_active?: boolean; metadata?: Record; }>, options?: { seed?: 'if-empty' | 'always'; }): void; }; /** * Add roles to the system. Eager: inserts immediately. Idempotent by name. * * Optional `permissions` map assigns CASL abilities to the role. * Keys are subject names (case-insensitive: 'masters' = 'Masters'). * Values are action arrays. * * Core subjects: Masters, Users, Roles, Auth, Mail, Storage, System. * Plugin subjects: documented in each plugin's llms.txt. * * @example * ```typescript * await seed.roles.add({ * name: 'SALES', * description: { en: 'Sales team' }, * permissions: { * 'masters': ['read'], * 'contacts': ['read', 'create', 'update'], * } * }) * ``` */ roles: { add(role: { name: string; description?: string | { en?: string; es?: string; [key: string]: string | undefined; }; /** CASL permissions. Keys: subject (case-insensitive). Values: action arrays. */ permissions?: Record>; }): Promise; }; /** Add users to the system. Eager: inserts immediately. Idempotent by email. */ users: { add(user: { email: string; password: string; name?: string; type?: 'human' | 'bot' | 'service'; roles?: string[]; }): Promise; }; /** Access plugin entities for seeding. Resolves table prefix automatically. */ plugin(name: string): { entity(name: string): { upsert(data: Record[], options?: { key?: string; }): Promise; }; }; /** Raw table insert (escape hatch). Returns number of rows inserted. */ raw(table: string, data: Record | Record[]): Promise; } /** * A workflow state with display metadata. */ interface WorkflowState { /** Unique state identifier (stored in DB) */ value: string; /** Human-readable label */ label: LocalizedString; /** Hex color for badges and board columns */ color?: string; /** Iconify icon name */ icon?: string; } /** * A valid transition between two workflow states. */ interface WorkflowTransition { /** Source state value */ from: string; /** Target state value */ to: string; /** Optional CASL action required for this transition (checked beyond base 'update' permission) */ casl?: { action: string; }; } /** * Workflow configuration for entity definitions. * Declares states, valid transitions, and optional per-transition CASL permissions. * Only applicable to mutable entity types: collection, tree, dag. */ interface WorkflowConfig { /** Field name that holds the workflow state (default: 'status') */ field?: string; /** Initial state value for new records (required, must exist in states) */ initial: string; /** Available states with display metadata */ states: WorkflowState[]; /** Valid state transitions */ transitions: WorkflowTransition[]; /** Label for the auto-injected field (default: { en: 'Status', es: 'Estado' }) */ fieldLabel?: LocalizedString; } /** * Serializable workflow state for frontend consumption. */ interface WorkflowStateDTO { value: string; label: LocalizedString; color?: string; icon?: string; } /** * Serializable workflow transition for frontend consumption. */ interface WorkflowTransitionDTO { from: string; to: string; /** CASL action name required for this transition (frontend checks ability.can(action, subject)) */ caslAction?: string; } /** * Serializable workflow config for EntityDefinitionDTO. */ interface WorkflowDTO { field: string; initial: string; states: WorkflowStateDTO[]; transitions: WorkflowTransitionDTO[]; } /** * Entity definition types - discriminated union for all entity types */ /** * Display mode for entity list views in the UI. */ type DisplayMode = 'table' | 'list' | 'masonry' | 'tree' | 'timeline' | 'board' | 'calendar'; /** * All available entity type identifiers. */ type EntityType = 'collection' | 'single' | 'external' | 'virtual' | 'computed' | 'view' | 'reference' | 'config' | 'event' | 'temp' | 'tree' | 'dag'; /** * Requirements that must be met to activate a module. */ interface ModuleRequirements { /** Environment variables that must be defined */ env?: string[]; /** Other modules that must be loaded first */ modules?: string[]; } /** * Confirmation type for actions. * - 'simple': Modal with Cancel/Confirm * - 'verify': User must type specific text to confirm * - 'double': Verify text + additional simple confirmation */ type ConfirmationType = 'simple' | 'verify' | 'double'; /** * Confirmation configuration for actions. * Displayed before executing the action. Pure UI metadata — backend passes through. */ interface ConfirmConfig { /** Confirmation type */ type: ConfirmationType; /** Title for the confirmation modal */ title?: LocalizedString; /** Message/description shown in the modal */ message: LocalizedString; /** Text the user must type to confirm (required for 'verify' and 'double') */ verifyText?: string; /** Label shown above the verify input (e.g., "Type DELETE to confirm") */ verifyLabel?: LocalizedString; /** Custom text for the confirm button */ confirmText?: LocalizedString; /** Custom text for the cancel button */ cancelText?: LocalizedString; /** Visual severity: controls button color and icon */ severity?: 'info' | 'warning' | 'error'; } /** * Individual post-action instruction. * Executed by the UI after a successful action. */ type PostAction = { type: 'refresh'; } | { type: 'refresh'; target: string; } | { type: 'refreshAll'; } | { type: 'navigate'; to: string; } | { type: 'redirect'; url: string; } | { type: 'download'; url?: string; } | { type: 'notification'; message?: string; status?: 'success' | 'warning' | 'info' | 'error'; } | { type: 'closeModal'; } | { type: 'emit'; event: string; } | { type: 'delay'; ms: number; }; /** * Pipeline of post-actions. Single action or sequential array. * When undefined, default behavior is: notification + refreshAll. */ type PostActionPipeline = PostAction | PostAction[]; /** * Unified action definition for module, entity, and row-level actions. */ interface ActionDefinition { /** Unique key for the action route */ key: string; /** Display name for the action button/form */ label: LocalizedString; /** Iconify icon identifier (default: 'mdi:play') */ icon?: string; /** Action scope: module, entity, or row */ scope?: 'module' | 'entity' | 'row'; /** Optional group label for visual grouping in UI */ group?: LocalizedString; /** Hide from UI — action is API-only, not serialized to DTO */ hidden?: boolean; /** Visual variant for the action button */ variant?: 'default' | 'primary' | 'danger'; /** HTTP method for the action endpoint (default: 'POST') */ method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'; /** Custom middleware (e.g., multer for file uploads, rate limiting) */ middleware?: (ctx: ModuleContext) => MiddlewareHandler | MiddlewareHandler[]; /** Handler function that executes the action logic */ handler?: (ctx: ModuleContext, input: unknown, c?: Context) => Promise; /** Zod schema for validating input before execution */ inputSchema?: ValidationSchema; /** Fields to select from the record (row-level only) */ select?: string[]; /** Input form fields for the action */ input?: Record; /** UI sidebar ordering (default: 999) */ order?: number; /** HTTP status code for success response (default: 200) */ successStatus?: number; /** Output field definitions for displaying action result in a read-only modal */ output?: Record; /** Reference to a PageDefinition id for rich result rendering (requires NEX-27) */ outputPage?: string; /** Confirmation dialog before executing the action */ confirm?: ConfirmConfig; /** Skip authentication for this action */ skipAuth?: boolean; /** CASL authorization configuration */ casl?: EntityCaslConfig | { action?: string; conditions?: Record; }; /** Enable SSE streaming for long-running actions */ batch?: boolean; /** Timeout in ms for action execution (default: 300000 = 5min) */ timeout?: number; /** Declarative post-execution behavior. Replaces default (notification + refreshAll). */ postAction?: PostActionPipeline; /** Condition for disabling. Function = server-side per-request. FieldCondition = client-side per-row. */ disabled?: FieldCondition | ((ctx: ModuleContext, c: Context) => boolean); /** Reason shown as tooltip when action is disabled */ disabledReason?: LocalizedString; /** Condition for visibility. FieldCondition = client-side per-row. When false, action is hidden (not just disabled). */ visible?: FieldCondition; } /** @deprecated Use ActionDefinition instead */ type EntityAction = ActionDefinition; /** Configuration for file attachments on an entity */ interface AttachableConfig { /** Accepted MIME types (e.g. 'image/*,application/pdf'). If omitted, all types allowed. */ accept?: string; /** Max number of attached files per entity instance. If omitted, unlimited. */ maxFiles?: number; } /** * Base properties shared by all EntityDefinition types. * @internal */ interface BaseEntityDefinition { /** Database table name with module prefix */ table: string; /** Display name for the UI (singular form) */ label: LocalizedString; /** Display name for the UI (plural form) */ labelPlural?: LocalizedString; /** Iconify MDI icon identifier */ icon?: string; /** Field definitions keyed by field name */ fields: Record; /** CASL authorization configuration */ casl?: EntityCaslConfig; /** API route prefix */ routePrefix?: string; /** UI sidebar ordering (default: 999) */ order?: number; /** UI display mode */ displayMode?: DisplayMode; /** Restrict available display modes. If omitted, auto-detected from entity config. */ availableDisplayModes?: DisplayMode[]; /** Field name to group by in board/list/masonry displays */ groupBy?: string; /** Field name for secondary grouping (collapsible) */ subgroupBy?: string; /** Explicit list of fields eligible for grouping. If omitted, auto-detected from field input types. */ groupableFields?: string[]; /** Subset of groupableFields where drag between board columns is allowed. Default: all groupable. */ columnDragFields?: string[]; /** Date field for calendar start (enables calendar display) */ calendarFrom?: string; /** Date field for calendar end (range display) */ calendarTo?: string; /** Default sort configuration */ defaultSort?: { field: string; order: 'asc' | 'desc'; }; /** If true, hides from UI sidebar */ hidden?: boolean; /** If false, HTTP routes are not mounted and entity is excluded from frontend DTO. Service, hooks, and CASL still work. Default: true */ expose?: boolean; /** Middleware applied to all routes */ middleware?: (ctx: ModuleContext) => MiddlewareHandler | MiddlewareHandler[]; /** External adapter name for this entity's data source */ adapter?: string; /** Lifecycle hooks factory. Called with ModuleContext at service creation time. */ hooks?: (ctx: ModuleContext) => EntityServiceHooks; /** Real-time mode: 'live' (push), 'sync' (bidirectional), or false (default) */ realtime?: RealtimeMode; /** * If true, entity data can be exported/imported via data/seeds/. * Use `nexus seed export` to snapshot DB data into versionable JSON files. * On startup, JSONs from data/seeds/ are imported with upsert semantics. */ seedable?: boolean; /** If true, this entity supports taxonomy tagging via tag_assignments pivot. UI renders tag selector. */ taggeable?: boolean; /** If true or config, this entity supports file attachments via file_attachments pivot. UI renders file picker. */ attachable?: boolean | AttachableConfig; } /** * Public type exposing the common properties shared by all entity definition types. * Use this when writing generic code that works with any entity definition. */ type SharedEntityProperties = Pick; /** * Collection entity - the most common entity type with full CRUD operations. */ interface CollectionEntityDefinition extends BaseEntityDefinition { type?: 'collection' | 'temp'; /** Field used as display label in dropdowns */ labelField: string; /** Auto-add created_at and updated_at timestamp columns */ timestamps?: boolean; /** Auto-add created_by and updated_by user reference columns */ audit?: boolean; /** Composite database indexes */ indexes?: EntityIndex[]; /** Seed data for initial population (catalogs, reference data) */ seed?: Array> | SeedConfig; /** Use soft delete (deleted_at column) */ softDelete?: boolean; /** If true, records cannot be updated or deleted (append-only). Absorbed from event entity type. */ immutable?: boolean; /** Retention policy for automatic data cleanup (absorbed from event entity type) */ retention?: RetentionPolicy; /** Cache config for adapter-backed entities (absorbed from external entity type) */ cache?: { ttl: number; key?: string; invalidateOn?: string[]; }; /** Metadata about external data source (when adapter is set) */ source?: Record; /** Custom actions that operate on entity records */ actions?: ActionDefinition[]; /** Show "Create" button in UI */ allowCreate?: boolean; /** Allow editing in UI */ allowEdit?: boolean; /** Allow deleting records. When false, DELETE endpoint is not mounted. Default: true */ allowDelete?: boolean; /** Workflow configuration: states, transitions, and per-transition CASL permissions */ workflow?: WorkflowConfig; /** * Auto-seed and env-lock configuration for a default record. * If any of the mapped env vars are defined, the default record is auto-created on startup * and those fields are marked as read-only (_envLocked in responses). */ envDefaults?: EnvDefaults; } /** * Mapping between a single entity field and an environment variable. * When the env var is defined, its value overrides the DB value and the field becomes read-only. */ interface EnvFieldMapping { /** Environment variable name */ env: string; /** How to parse the raw string value (default: string) */ parse?: 'number' | 'boolean' | 'json'; } /** Map of fieldName → EnvFieldMapping for a single entity */ type EnvMapping = Record; /** * Auto-seed and env-lock configuration for a collection entity's default record. * When env vars match `fields`, the record is auto-created on seed and those fields are locked. */ interface EnvDefaults { /** Field criteria that identify the default record (e.g. { scope: 'openai:default' }) */ match: Record; /** Per-field env var mappings — fields with a defined env var are locked */ fields: EnvMapping; } /** * Singleton entity - stores a single record in the shared single_records table. * When scopeField is set, operates in scoped mode (multiple records keyed by scope). */ interface SingleEntityDefinition extends Pick { type: 'single' | 'config'; /** Unique key in single_records table */ key: string; /** Default values applied when the setting is first accessed */ defaults?: Record; /** Scope field name for multi-record mode (absorbed from ConfigEntityDefinition) */ scopeField?: string; /** Enable timestamps (created_at, updated_at) */ timestamps?: boolean; /** Enable audit fields (created_by, updated_by) */ audit?: boolean; /** Custom actions */ actions?: ActionDefinition[]; /** * Per-field environment variable overrides. * When an env var is defined, its value overrides the DB value at runtime * and the field is marked as read-only (_envLocked in responses). */ envMapping?: EnvMapping; } /** * Seed data configuration for reference/tree/dag entities. */ interface SeedConfig { source: 'inline' | 'url' | 'file' | 'api'; data?: Array>; url?: string; file?: string; uniqueKey?: string; syncSchedule?: string; headers?: Record; dataPath?: string; transform?: string; } /** * @deprecated Use CollectionEntityDefinition with `seed` instead. * Reference entities are now collections with seed data. */ type ReferenceEntityDefinition = Omit & { type: 'reference' | 'collection'; }; /** * Tree entity - hierarchical data with parent-child relationships. */ interface TreeEntityDefinition extends BaseEntityDefinition { type: 'tree'; labelField?: string; timestamps?: boolean; audit?: boolean; indexes?: EntityIndex[]; softDelete?: boolean; actions?: EntityAction[]; seed: Array> | SeedConfig; allowCreate?: boolean; allowEdit?: boolean; allowDelete?: boolean; allowDragDrop?: boolean; /** Workflow configuration: states, transitions, and per-transition CASL permissions */ workflow?: WorkflowConfig; } /** * DAG entity - Directed Acyclic Graph with multiple parents. */ interface DagEntityDefinition extends BaseEntityDefinition { type: 'dag'; labelField?: string; timestamps?: boolean; audit?: boolean; indexes?: EntityIndex[]; softDelete?: boolean; actions?: EntityAction[]; seed: Array> | SeedConfig; parentsTable?: string; allowCreate?: boolean; allowEdit?: boolean; allowDelete?: boolean; allowDragDrop?: boolean; /** Workflow configuration: states, transitions, and per-transition CASL permissions */ workflow?: WorkflowConfig; } /** * Retention policy for automatic data cleanup. */ interface RetentionPolicy { /** Delete records older than N seconds */ seconds?: number; /** Delete records older than N days */ days?: number; /** Keep only the most recent N records */ maxRows?: number; /** Field that stores the expiration timestamp (absorbed from TempRetentionPolicy) */ expiresField?: string; } /** * @deprecated Use RetentionPolicy instead. expiresField has been absorbed into RetentionPolicy. */ type TempRetentionPolicy = RetentionPolicy; /** * @deprecated Use CollectionEntityDefinition with `immutable: true` instead. * Event entities are now collections with immutable flag and retention policy. */ type EventEntityDefinition = Omit & { type: 'event' | 'collection'; }; /** * @deprecated Use CollectionEntityDefinition with `adapter` instead. * External entities are now collections backed by a database adapter. */ type ExternalEntityDefinition = Omit & { type: 'external' | 'collection'; adapter: string; }; /** * @deprecated Use ComputedEntityDefinition with `sources` instead. * Virtual entities are now computed entities with source aggregation. */ type VirtualEntityDefinition = Omit & { type: 'virtual' | 'computed'; sources: string[]; }; /** * Computed entity - dynamically calculated data like KPIs. */ interface ComputedEntityDefinition { type: 'computed'; label: LocalizedString; labelPlural?: LocalizedString; icon?: string; labelField?: string; fields: Record; compute?: (ctx: ModuleContext, params?: Record) => Promise; /** Source service names for data aggregation (absorbed from virtual entity type) */ sources?: string[]; /** Custom resolver to combine source data. If omitted with sources, defaults to flat merge. */ resolver?: (sources: Record, ctx: ModuleContext) => unknown[] | Promise; cache?: { ttl: number; invalidateOn?: string[]; }; isSingle?: boolean; casl?: EntityCaslConfig; routePrefix?: string; order?: number; displayMode?: DisplayMode; /** Restrict available display modes. If omitted, auto-detected from entity config. */ availableDisplayModes?: DisplayMode[]; actions?: ActionDefinition[]; defaultSort?: { field: string; order: 'asc' | 'desc'; }; hidden?: boolean; /** If false, HTTP routes are not mounted and entity is excluded from frontend DTO. Service, hooks, and CASL still work. Default: true */ expose?: boolean; /** Real-time mode: 'live' pushes recomputed data to subscribed clients */ realtime?: RealtimeMode; /** Auto-refresh interval in ms. Frontend polls data at this rate when the entity tab is active. */ refreshInterval?: number; /** Pre-compute cache on startup. Useful for expensive computed entities. */ warmCache?: boolean; } /** * View entity - read-only database view or projection. */ interface ViewEntityDefinition { type: 'view'; table: string; label: LocalizedString; labelPlural?: LocalizedString; icon?: string; labelField: string; fields: Record; query?: string | ((db: Knex) => Knex.QueryBuilder); casl?: EntityCaslConfig; routePrefix?: string; order?: number; /** If false, HTTP routes are not mounted and entity is excluded from frontend DTO. Service, hooks, and CASL still work. Default: true */ expose?: boolean; /** Tables whose changes trigger live push to subscribed clients. Only used when realtime: 'live'. */ liveOn?: string[]; /** Real-time mode: 'live' pushes data to subscribed clients when liveOn tables change */ realtime?: RealtimeMode; } /** * @deprecated Use SingleEntityDefinition with scopeField instead. * Config entity — now absorbed into SingleEntityDefinition. */ type ConfigEntityDefinition = SingleEntityDefinition; /** * @deprecated Use CollectionEntityDefinition with `retention` instead. * Temp entities are now collections with retention policy for automatic cleanup. */ type TempEntityDefinition = Omit & { type: 'temp' | 'collection'; }; /** * Union of all entity definitions */ type EntityDefinition = CollectionEntityDefinition | SingleEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition | ViewEntityDefinition | ReferenceEntityDefinition | TreeEntityDefinition | DagEntityDefinition | EventEntityDefinition; export { type CoreServices as $, type ActionDefinition as A, type BaseAuthRequest as B, type ConfirmConfig as C, type DisplayMode as D, type EntityType as E, type CapabilitiesRegistry as F, type CaslAction as G, type CategoryMeta as H, type CollectionEntityService as I, type ComputedEntityService as J, type ConfigContext as K, type ConfigEntityDefinition as L, type ModuleContext as M, type ConfigEntityService as N, type ConfirmationType as O, type PostActionPipeline as P, type ContextCrypto as Q, type RealtimeMode as R, type SingleEntityDefinition as S, type TreeEntityDefinition as T, type ContextEvents as U, type ViewEntityDefinition as V, type WorkflowDTO as W, type ContextEventsHub as X, type ContextHelpers as Y, type ContextSocket as Z, type CoreContext as _, type Category as a, type SendMailOptions as a$, type CreateEntityServiceOptions as a0, type CustomRouteDefinition as a1, DEFAULT_TENANT_ID as a2, type DagEntityService as a3, type DatabaseAdapter as a4, type DbContext as a5, type EngineContext as a6, type EntityAction as a7, type EntityCaslConfig as a8, type EntityChangePayload as a9, type PaginatedResult as aA, type PaginationParams as aB, type PaginationParamsWithOffset as aC, type PluginConfigField as aD, type PluginConfigSchema as aE, type PluginEnvVar as aF, type PluginManifest as aG, type PluginSetupInfo as aH, type PostAction as aI, type RateLimitOptions as aJ, type ReferenceEntityDefinition as aK, type ReferenceEntityService as aL, type RegisterPluginOptions as aM, type RetentionPolicy as aN, type RolePermission as aO, type RouteParameterDefinition as aP, type RouteResponseDefinition as aQ, type RuntimeContext as aR, type SchemaAdapter as aS, type SchemaAlterTableBuilder as aT, type SchemaColumnBuilder as aU, type SchemaColumnInfo as aV, type SchemaForeignKeyBuilder as aW, type SchemaTableBuilder as aX, type ScopedCacheManager as aY, type SeedConfig as aZ, type SeedContext as a_, type EntityController as aa, type EntityHandler as ab, type EntityQuery as ac, type EntityRealtimeEmits as ad, type EntityRealtimeEvents as ae, type EntityServiceHooks as af, type EnvDefaults as ag, type EnvFieldMapping as ah, type EnvMapping as ai, type EventEmitterLike as aj, type EventEntityService as ak, type ExternalEntityDefinition as al, type ExternalEntityService as am, type FilterOperators as an, type FilterValue as ao, type ForbiddenErrorConstructor as ap, type ForbiddenErrorInstance as aq, type HttpMethod as ar, type LoggerReporter as as, type ManagedCache as at, type ModuleAbilities as au, type ModuleManifest as av, type ModuleMiddlewares as aw, type ModuleRequirements as ax, type PageDefinition as ay, type PageType as az, type PageDefinitionDTO as b, type SendMailResult as b0, type ServerEntityQuery as b1, type ServicesContext as b2, type SharedEntityProperties as b3, type SingleEntityService as b4, type SocketIOBroadcastOperator as b5, type SocketIOServer as b6, type TempEntityDefinition as b7, type TempEntityService as b8, type TempRetentionPolicy as b9, type TreeEntityService as ba, type TreeNode as bb, type TypedEntityQuery as bc, type ValidateSchemas as bd, type ValidationDetail as be, type ValidationSchema as bf, type ViewEntityService as bg, type VirtualEntityDefinition as bh, type VirtualEntityService as bi, type WidgetDefinition as bj, type WidgetLayout as bk, type WorkflowConfig as bl, type WorkflowState as bm, type WorkflowStateDTO as bn, type WorkflowTransition as bo, type WorkflowTransitionDTO as bp, entityRoom as bq, type ComputedEntityDefinition as c, type CollectionEntityDefinition as d, type EventEntityDefinition as e, type DagEntityDefinition as f, type EntityDefinition as g, type AbilityLike as h, type ActionEntityService as i, type ActionInjection as j, type AdaptersContext as k, type AppManifest as l, type AttachableConfig as m, type AuthRequest as n, type BaseEntityService as o, type BaseMailService as p, type BaseRolesService as q, type BaseUser as r, type BaseUsersService as s, type BridgeRule as t, type BridgeTarget as u, CATEGORIES as v, CATEGORY_ORDER as w, type CacheManagerContract as x, type CacheOptions as y, type CacheStats as z };