UNPKG

@mobtakronio/schemakit

Version:

Dynamic entity management system with runtime schema creation, validation, and CRUD operations for Node.js backends.

1,832 lines (1,806 loc) 86.9 kB
/** * Database adapter configuration options */ interface DatabaseAdapterConfig { filename?: string; host?: string; port?: number; database?: string; user?: string; password?: string; ssl?: boolean; connectionString?: string; [key: string]: any; } /** * Column definition for table creation */ interface ColumnDefinition { name: string; type: string; primaryKey?: boolean; notNull?: boolean; unique?: boolean; default?: any; references?: { table: string; column: string; onDelete?: 'CASCADE' | 'RESTRICT' | 'SET NULL'; }; } /** * Transaction callback function type */ type TransactionCallback<T> = (transaction: DatabaseAdapter) => Promise<T>; /** * Query filter interface (from EntityKit pattern) */ interface QueryFilter$1 { field: string; value: any; operator?: 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte' | 'like' | 'in' | 'nin' | 'contains' | 'startswith' | 'endswith'; } /** * Query options interface (from EntityKit pattern) */ interface QueryOptions$1 { orderBy?: { field: string; direction: 'ASC' | 'DESC'; }[]; limit?: number; offset?: number; } /** * Abstract database adapter class */ declare abstract class DatabaseAdapter { protected config: DatabaseAdapterConfig; /** * Create a new database adapter * @param config Configuration options */ constructor(config?: DatabaseAdapterConfig); /** * Connect to the database */ abstract connect(): Promise<void>; /** * Disconnect from the database */ abstract disconnect(): Promise<void>; /** * Check if connected to the database */ abstract isConnected(): boolean; /** * Execute a query that returns rows * @param sql SQL query * @param params Query parameters */ abstract query<T = any>(sql: string, params?: any[]): Promise<T[]>; /** * Execute a query that doesn't return rows * @param sql SQL query * @param params Query parameters */ abstract execute(sql: string, params?: any[]): Promise<{ changes: number; lastInsertId?: string | number; }>; /** * Execute a function within a transaction * @param callback Function to execute within transaction */ abstract transaction<T>(callback: TransactionCallback<T>): Promise<T>; /** * Check if a table exists * @param tableName Table name */ abstract tableExists(tableName: string): Promise<boolean>; /** * Create a new table * @param tableName Table name * @param columns Column definitions */ abstract createTable(tableName: string, columns: ColumnDefinition[]): Promise<void>; /** * Get column information for a table * @param tableName Table name */ abstract getTableColumns(tableName: string): Promise<ColumnDefinition[]>; /** * Select records from a table * @param table Table name * @param filters Query filters * @param options Query options */ abstract select(table: string, filters: QueryFilter$1[], options: QueryOptions$1): Promise<any[]>; /** * Insert a record into a table * @param table Table name * @param data Data to insert */ abstract insert(table: string, data: Record<string, any>): Promise<any>; /** * Update a record in a table * @param table Table name * @param id Record ID * @param data Data to update */ abstract update(table: string, id: string, data: Record<string, any>): Promise<any>; /** * Delete a record from a table * @param table Table name * @param id Record ID */ abstract delete(table: string, id: string): Promise<void>; /** * Count records in a table * @param table Table name * @param filters Query filters */ abstract count(table: string, filters: QueryFilter$1[]): Promise<number>; /** * Find a record by ID * @param table Table name * @param id Record ID */ abstract findById(table: string, id: string): Promise<any | null>; /** * Create a database schema (for multi-tenancy) * @param schemaName Schema name */ abstract createSchema(schemaName: string): Promise<void>; /** * Drop a database schema * @param schemaName Schema name */ abstract dropSchema(schemaName: string): Promise<void>; /** * List all database schemas */ abstract listSchemas(): Promise<string[]>; /** * Create a database adapter instance * @param type Adapter type ('sqlite', 'postgres', or 'inmemory') * @param config Configuration options */ static create(type?: string, config?: DatabaseAdapterConfig): Promise<DatabaseAdapter>; /** * Create a database adapter instance synchronously (for backward compatibility) * @param type Adapter type ('sqlite', 'postgres', or 'inmemory') * @param config Configuration options */ static createSync(type?: string, config?: DatabaseAdapterConfig): DatabaseAdapter; } type WhereClause = Record<string, any>; interface DBOptions { adapter: string; tenantId: string; config?: any; multiTenancy?: MultiTenancyConfig; } interface MultiTenancyConfig { strategy: 'schema' | 'table-prefix' | 'column' | 'none'; columnName?: string; separator?: string; } declare class DB { private adapter; private adapterConfig; private tenantId; private multiTenancy; private _isQueryBuilder; private _select; private _from; private _where; private _orWhere; private _orderBy; private _limit?; private _offset?; constructor(opts: DBOptions); /** * Initialize the database adapter * Must be called before using the DB instance */ init(): Promise<void>; /** * Ensure adapter is initialized */ private ensureAdapter; /** * Resolve table name based on multi-tenancy strategy */ private resolveTableName; /** * Add tenant filter based on multi-tenancy strategy */ private addTenantFilter; /** * Add tenant data to insert/update operations */ private addTenantData; /** * Create a forked query builder to avoid mutating shared DB state across concurrent queries */ private fork; private asBuilder; select(fields: string | string[]): this; from(table: string): this; where(conditions: WhereClause): this; orWhere(conditions: WhereClause): this; orderBy(field: string, dir?: "ASC" | "DESC"): this; limit(n: number): this; /** * Set result offset for pagination */ offset(n: number): this; /** * Helper to convert where/orWhere to QueryFilter[] */ private buildFilters; /** * Helper to convert orderBy/limit to QueryOptions */ private buildOptions; get(table?: string): Promise<any>; insert(table: string, data: Record<string, any>): Promise<any>; update(table: string, data: Record<string, any>): Promise<any>; delete(table: string): Promise<any>; reset(): this; /** * Get the underlying adapter instance (escape hatch) * @warning This bypasses some SchemaKit features. Use with caution. */ getAdapter(): Promise<DatabaseAdapter>; /** * Execute a raw query * @warning This bypasses multi-tenancy and other SchemaKit features */ raw<T = any>(sql: string, params?: any[]): Promise<T[]>; /** * Execute within a transaction */ transaction<T>(callback: (trx: DB) => Promise<T>): Promise<T>; /** * Create a new DB instance with a different tenant */ withTenant(tenantId: string): DB; /** * Get current tenant ID */ getTenantId(): string; /** * Get multi-tenancy configuration */ getMultiTenancyConfig(): MultiTenancyConfig; /** * Create schema for tenant (if using schema-based multi-tenancy) */ createTenantSchema(tenantId: string): Promise<void>; /** * Drop schema for tenant (if using schema-based multi-tenancy) */ dropTenantSchema(tenantId: string): Promise<void>; /** * List all tenant schemas */ listTenantSchemas(): Promise<string[]>; } /** * Common base types used across SchemaKit * * These fundamental types provide the building blocks for all SchemaKit functionality. * @since 0.1.0 */ /** * Supported field data types in SchemaKit entities * * @example * ```typescript * const field: FieldDefinition = { * name: 'email', * type: 'string', // FieldType * is_required: true * }; * ``` * * @since 0.1.0 */ type FieldType = 'string' | 'number' | 'integer' | 'boolean' | 'date' | 'datetime' | 'json' | 'object' | 'array' | 'reference' | 'computed'; /** * Standard CRUD operations supported by SchemaKit * * @example * ```typescript * const permission: PermissionDefinition = { * role: 'user', * action: 'read', // OperationType * is_allowed: true * }; * ``` * * @since 0.1.0 */ type OperationType = 'create' | 'read' | 'update' | 'delete' | 'list'; /** * Sort direction for query ordering * * @example * ```typescript * const sort: SortDefinition = { * field: 'created_at', * direction: 'desc' // SortDirection - newest first * }; * ``` * * @since 0.1.0 */ type SortDirection = 'asc' | 'desc'; /** * SQL JOIN types for entity relationships * * @example * ```typescript * const join: JoinDefinition = { * entity: 'users', * type: 'left', // JoinType - include all records from left table * on: 'orders.user_id = users.id' * }; * ``` * * @since 0.1.0 */ type JoinType = 'inner' | 'left' | 'right'; /** * Events that can trigger workflow execution * * @example * ```typescript * const workflow: WorkflowDefinition = { * trigger_event: 'create', // WorkflowTrigger - run after record creation * actions: [{ type: 'send-email', config: {...} }] * }; * ``` * * @since 0.1.0 */ type WorkflowTrigger = 'create' | 'update' | 'delete' | 'field_change'; /** * Core SchemaKit configuration and context types * * These types define how SchemaKit is configured and how execution context * is passed throughout the system for permissions and multi-tenancy. * * @since 0.1.0 */ /** * Configuration options for initializing SchemaKit * * @example * ```typescript * const schemaKit = new SchemaKit({ * adapter: { * type: 'postgres', * config: { * connectionString: process.env.DATABASE_URL * } * }, * cache: { * enabled: true, * ttl: 300 // 5 minutes * } * }); * ``` * * @since 0.1.0 */ interface SchemaKitOptions { /** Database adapter configuration */ adapter?: { /** Adapter type: 'postgres', 'sqlite', 'inmemory' */ type?: string; /** Adapter-specific configuration options */ config?: Record<string, any>; }; /** Entity caching configuration */ cache?: { /** Whether to enable entity definition caching */ enabled?: boolean; /** Cache time-to-live in seconds */ ttl?: number; }; } /** * Execution context for operations, permissions, and multi-tenancy * * Context flows through all SchemaKit operations to provide user information, * tenant isolation, and request metadata for permissions and auditing. * * @example * ```typescript * const context: Context = { * user: { * id: 'user-123', * roles: ['manager', 'user'], * department: 'sales' * }, * tenantId: 'company-abc', * request: { * ip: '192.168.1.1', * userAgent: 'Mozilla/5.0...' * } * }; * * // Context is used throughout operations * await entity.create(data, context); * await entity.view('sales-report', {}, context); * ``` * * @since 0.1.0 */ interface Context { /** Current user information for permissions and RLS */ user?: { /** Unique user identifier */ id?: string; /** User roles for permission checking */ roles?: string[]; /** Direct permissions granted to user */ permissions?: string[]; /** Additional user properties (department, etc.) */ [key: string]: any; }; /** Tenant ID for multi-tenant data isolation */ tenantId?: string; /** HTTP request information for auditing */ request?: { /** Client IP address */ ip?: string; /** Client user agent string */ userAgent?: string; /** Request timestamp */ timestamp?: string; /** Additional request metadata */ [key: string]: any; }; /** User session information */ session?: { /** Session identifier */ id?: string; /** Session expiration time */ expires?: string; /** Additional session data */ [key: string]: any; }; /** Additional context properties */ [key: string]: any; } /** * Workflow and automation types * * Workflows in SchemaKit provide automated responses to entity lifecycle * events, enabling business process automation and integration. * * @since 0.1.0 */ /** * Single action to execute in a workflow * * @example * ```typescript * // Send email action * const emailAction: WorkflowAction = { * type: 'send-email', * config: { * to: '{{user.email}}', * subject: 'Welcome to {{app.name}}', * template: 'welcome-email', * data: { * userName: '{{user.name}}', * activationLink: '{{user.activationUrl}}' * } * } * }; * * // API call action * const webhookAction: WorkflowAction = { * type: 'webhook', * config: { * url: 'https://api.example.com/user-created', * method: 'POST', * headers: { * 'Authorization': 'Bearer {{app.apiKey}}' * }, * body: { * userId: '{{record.id}}', * email: '{{record.email}}' * } * } * }; * * // Database action * const updateAction: WorkflowAction = { * type: 'update-record', * config: { * entity: 'user_stats', * where: { user_id: '{{record.id}}' }, * data: { * last_login: '{{now}}', * login_count: '{{increment}}' * } * } * }; * ``` * * @since 0.1.0 */ interface WorkflowAction { /** Action type (e.g., 'send-email', 'webhook', 'update-record') */ type: string; /** Action-specific configuration with template support */ config: Record<string, any>; } /** * Complete workflow definition stored in system_workflows table * * Workflows define automated processes that run in response to entity * lifecycle events, with optional conditions and multiple actions. * * @example * ```typescript * // User welcome workflow * const welcomeWorkflow: WorkflowDefinition = { * id: 'wf_001', * entity_id: 'ent_users_001', * name: 'user-welcome', * trigger_event: 'create', * conditions: { * // Only for active users * is_active: true, * // Only for specific user types * user_type: { $in: ['customer', 'premium'] } * }, * actions: [ * { * type: 'send-email', * config: { * template: 'welcome-email', * to: '{{record.email}}', * data: { name: '{{record.name}}' } * } * }, * { * type: 'webhook', * config: { * url: 'https://api.crm.com/users', * method: 'POST', * body: '{{record}}' * } * } * ], * is_active: true, * order_index: 1, * metadata: { * description: 'Welcome new users and sync with CRM', * created_by: 'admin', * version: '1.0' * } * }; * * // Order processing workflow * const orderWorkflow: WorkflowDefinition = { * id: 'wf_002', * entity_id: 'ent_orders_001', * name: 'order-processing', * trigger_event: 'update', * conditions: { * // Only when status changes to 'paid' * status: 'paid', * // Only for orders over $100 * total: { $gte: 100 } * }, * actions: [ * { * type: 'update-inventory', * config: { * items: '{{record.items}}', * operation: 'decrease' * } * }, * { * type: 'send-email', * config: { * template: 'order-confirmation', * to: '{{record.customer_email}}' * } * } * ], * is_active: true, * order_index: 1 * }; * ``` * * @since 0.1.0 */ interface WorkflowDefinition { /** Unique identifier for this workflow */ workflow_id: string; /** Entity this workflow applies to */ workflow_entity_id: string; /** Workflow name for reference */ workflow_name: string; /** Event that triggers this workflow */ workflow_trigger_event: WorkflowTrigger; /** Optional conditions that must be met to execute */ workflow_conditions?: Record<string, any>; /** Array of actions to execute in order */ workflow_actions: WorkflowAction[]; /** Whether this workflow is currently active */ workflow_is_active: boolean; /** Execution order when multiple workflows match */ workflow_order_index: number; /** Additional metadata */ workflow_metadata?: Record<string, any>; } /** * Definition for joining related entities in views * * @example * ```typescript * const join: JoinDefinition = { * entity: 'users', * type: 'left', * on: 'orders.user_id = users.id', * alias: 'creator' * }; * ``` * * @since 0.1.0 */ interface JoinDefinition { /** Entity name to join with */ entity: string; /** Type of join (default: 'inner') */ type?: JoinType; /** JOIN condition (e.g., 'orders.user_id = users.id') */ on: string; /** Optional alias for the joined entity */ alias?: string; } /** * Sorting configuration for views * * @example * ```typescript * const sort: SortDefinition = { * field: 'created_at', * direction: 'desc' // Newest first * }; * ``` * * @since 0.1.0 */ interface SortDefinition { /** Field name to sort by */ field: string; /** Sort direction */ direction: SortDirection; } /** * Pagination defaults for views * * @example * ```typescript * const pagination: PaginationDefinition = { * default_limit: 20, * max_limit: 100 * }; * ``` * * @since 0.1.0 */ interface PaginationDefinition { /** Default number of records per page */ default_limit: number; /** Maximum allowed records per page */ max_limit: number; } /** * Complete view definition stored in system_views table * * Views provide pre-configured query templates that users can execute * with optional runtime filters and pagination. * * @example * ```typescript * const view: ViewDefinition = { * id: 'view_001', * entity_id: 'ent_orders_001', * name: 'recent-orders', * query_config: { * filters: { * status: 'active', * created_at: { $gte: '2024-01-01' } * }, * sorting: [ * { field: 'created_at', direction: 'desc' }, * { field: 'priority', direction: 'asc' } * ], * joins: [{ * entity: 'users', * type: 'left', * on: 'orders.user_id = users.id' * }], * pagination: { * default_limit: 20, * max_limit: 100 * } * }, * fields: ['id', 'title', 'status', 'created_at', 'users.name'], * is_default: false, * created_by: 'admin', * is_public: true, * metadata: { description: 'Recent active orders with user info' } * }; * ``` * * @since 0.1.0 */ interface ViewDefinition { /** Unique identifier for this view */ view_id: string; /** Entity this view applies to */ view_entity_id: string; /** View name for reference */ view_name: string; /** Audit fields */ view_created_at?: string; view_updated_at?: string; view_created_by?: string; view_modified_by?: string; /** Ordering and placement */ view_weight?: number; view_status?: string; view_slot?: number; /** Fields to include in results (empty = all fields) */ view_fields?: string[]; /** Fixed filters applied to all executions */ view_filters?: Record<string, any>; /** Entity joins to include related data */ view_joins?: JoinDefinition[]; /** Default sorting order */ view_sort?: SortDefinition[]; /** Optional display title */ view_title?: string; /** Optional: retained for compatibility with older data; ignored by new code */ view_query_config?: { filters?: Record<string, any>; joins?: JoinDefinition[]; sorting?: SortDefinition[]; pagination?: PaginationDefinition; }; view_is_default?: boolean; view_is_public?: boolean; view_metadata?: Record<string, any>; } /** * Runtime options for executing views * * @example * ```typescript * const options: ViewOptions = { * filters: { * priority: 'high', * assignee: 'john-doe' * }, * pagination: { * page: 2, * limit: 50 * } * }; * * const result = await entity.view('active-tasks', options, context); * ``` * * @since 0.1.0 */ interface ViewOptions { /** Additional filters to apply at runtime */ filters?: Record<string, any>; /** Pagination options */ pagination?: { /** Page number (1-based) */ page: number; /** Records per page */ limit: number; }; /** Whether to compute totals/stats (grouped counts) */ stats?: boolean; } /** * Result from executing a view * * @example * ```typescript * const result: ViewResult = { * results: [ * { id: 1, name: 'John', department: 'Engineering' }, * { id: 2, name: 'Jane', department: 'Marketing' } * ], * total: 2, * fields: [ * { name: 'id', type: 'integer', display_name: 'ID' }, * { name: 'name', type: 'string', display_name: 'Full Name' } * ], * meta: { * entityName: 'users', * viewName: 'active-users', * query: 'SELECT id, name FROM users WHERE status = ?' * } * }; * ``` * * @since 0.1.0 */ interface ViewResult { /** Array of records returned by the view */ results: any[]; /** Total number of matching records (before pagination) */ total: number; /** Field definitions for the returned columns */ fields: FieldDefinition[]; /** Execution metadata */ meta: { /** Entity name the view was executed on */ entityName: string; /** Name of the view that was executed */ viewName: string; /** Generated SQL query (for debugging) */ query?: string; }; /** Optional: stats array similar to legacy temp output */ stats?: any[]; defaultField?: string; modelName?: string; modelSystemName?: string; isProcessOn?: boolean; addAllowed?: boolean; modifyAllowed?: boolean; manageAllowed?: boolean; } /** * Pagination types for queries and views */ interface PaginationOptions { page?: number; pageSize?: number; limit?: number; offset?: number; } interface PaginationResult<T = any> { data: T[]; pagination?: { current_page: number; per_page: number; total: number; total_pages: number; has_previous: boolean; has_next: boolean; }; meta?: { total: number; page: number; per_page: number; has_more: boolean; total_pages: number; }; } /** * Permission and authorization types * * These types define role-based access control (RBAC) and field-level * permissions for entities in SchemaKit. * * @since 0.1.0 */ /** * Permission rule for entity access control * * Stored in the `system_permissions` table, defines what roles can perform * which operations on entities, with optional conditions and field restrictions. * * @example * ```typescript * // Basic permission - managers can read customer records * const permission: PermissionDefinition = { * id: 'perm_001', * entity_id: 'ent_customers_001', * role: 'manager', * action: 'read', * is_allowed: true, * created_at: '2024-01-01T00:00:00Z' * }; * * // Conditional permission - users can only update their own records * const conditionalPerm: PermissionDefinition = { * id: 'perm_002', * entity_id: 'ent_orders_001', * role: 'user', * action: 'update', * conditions: { * field: 'created_by', * operator: 'eq', * value: '$user.id' * }, * is_allowed: true, * created_at: '2024-01-01T00:00:00Z' * }; * * // Field-level permissions - hide sensitive fields from regular users * const fieldPerm: PermissionDefinition = { * id: 'perm_003', * entity_id: 'ent_users_001', * role: 'user', * action: 'read', * is_allowed: true, * field_permissions: { * 'salary': false, // Hide salary field * 'email': true, // Show email field * 'phone_read': true, // Can read phone * 'phone_write': false // Cannot modify phone * }, * created_at: '2024-01-01T00:00:00Z' * }; * ``` * * @since 0.1.0 */ interface PermissionDefinition { /** Unique identifier for this permission rule */ permission_id: string; /** ID of the entity this permission applies to */ permission_entity_id: string; /** Role name this permission is granted to */ permission_role: string; /** Operation type this permission controls */ permission_action: OperationType; /** Optional conditions that must be met (JSON object) */ permission_conditions?: Record<string, any>; /** Whether this permission grants (true) or denies (false) access */ permission_is_allowed: boolean; /** Whether this permission rule is currently active */ permission_is_active?: boolean; /** ISO timestamp when permission was created */ permission_created_at: string; /** Field-level permissions (field_name: allowed, field_name_read: read_only) */ permission_field_permissions?: Record<string, boolean>; } /** * Row Level Security (RLS) types * * These types implement SchemaKit's advanced row-level security system, * allowing fine-grained control over which records users can access. * * @since 0.1.0 */ /** * Single condition for row-level security filtering * * Defines a filter condition that can be applied to queries to restrict * which rows a user can access based on their role and context. * * @example * ```typescript * // Users can only see records in their department * const departmentCondition: RLSCondition = { * field: 'department', * op: 'eq', * value: 'currentUser.department', * exposed: false // Hidden from user, automatically applied * }; * * // Analysts can filter by priority (user-modifiable) * const priorityCondition: RLSCondition = { * field: 'priority', * op: 'in', * value: ['high', 'urgent'], * exposed: true, // User can modify this filter * metadata: { * type: 'string', * options: ['low', 'medium', 'high', 'urgent'] * } * }; * ``` * * @since 0.1.0 */ interface RLSCondition { /** Field name to filter on */ field: string; /** Comparison operator */ op: 'eq' | 'ne' | 'gt' | 'lt' | 'gte' | 'lte' | 'in' | 'notIn' | 'like' | 'isNull' | 'notNull'; /** Value to compare against (can include context variables like 'currentUser.id') */ value: any; /** Whether users can see and modify this condition */ exposed?: boolean; /** Metadata for exposed conditions (validation, UI hints) */ metadata?: { /** Data type for validation */ type: 'number' | 'string'; /** Minimum value (for numbers) */ min?: number; /** Maximum value (for numbers) */ max?: number; /** Allowed values (for dropdowns) */ options?: string[]; }; } /** * Group of conditions combined with AND/OR logic * * @example * ```typescript * // Manager role: can see active records in their department * const managerRule: RLSRule = { * conditions: [ * { field: 'department', op: 'eq', value: 'currentUser.department' }, * { field: 'status', op: 'eq', value: 'active' } * ], * combinator: 'AND' // Both conditions must be true * }; * ``` * * @since 0.1.0 */ interface RLSRule { /** List of conditions to evaluate */ conditions: RLSCondition[]; /** How to combine conditions (AND = all must be true, OR = any can be true) */ combinator: 'AND' | 'OR'; } /** * Complete RLS configuration mapping roles to their restrictions * * @example * ```typescript * const restrictions: RoleRestrictions = { * // Admins see everything in their department * 'admin': [{ * conditions: [{ field: 'department', op: 'eq', value: 'currentUser.department' }], * combinator: 'AND' * }], * * // Users only see their own records * 'user': [{ * conditions: [{ field: 'created_by', op: 'eq', value: 'currentUser.id' }], * combinator: 'AND' * }], * * // Analysts have complex filtering options * 'analyst': [{ * conditions: [ * { field: 'priority', op: 'in', value: ['high'], exposed: true }, * { field: 'created_at', op: 'gte', value: '2024-01-01' } * ], * combinator: 'AND' * }] * }; * ``` * * @since 0.1.0 */ interface RoleRestrictions { /** Role name mapped to array of rules (multiple rules are combined with OR) */ [role: string]: RLSRule[]; } /** * Persistent RLS definition stored in system_rls table * * @example * ```typescript * const rlsDef: RLSDefinition = { * id: 'rls_001', * entity_id: 'ent_orders_001', * role: 'manager', * rls_config: { * relationbetweenconditions: 'and', * conditions: [ * { field: 'department', op: 'eq', value: '$context.user.department' } * ] * }, * is_active: true, * created_at: '2024-01-01T00:00:00Z', * updated_at: '2024-01-01T00:00:00Z' * }; * ``` * * @since 0.1.0 */ interface RLSDefinition { /** Unique identifier for this RLS rule */ rls_id: string; /** Entity this rule applies to */ rls_entity_id: string; /** Role this rule applies to */ rls_role: string; /** Optional view this rule is specific to */ rls_view_id?: string; /** RLS configuration */ rls_config: { /** How to combine multiple conditions */ relationbetweenconditions: 'and' | 'or'; /** Array of filter conditions */ conditions: RLSCondition[]; }; /** Whether this rule is currently active */ rls_is_active: boolean; /** ISO timestamp when rule was created */ rls_created_at: string; /** ISO timestamp when rule was last updated */ rls_updated_at: string; } /** * Runtime RLS conditions (internal use) * * @internal * @since 0.1.0 */ interface RLSConditions { /** Generated SQL WHERE clause (legacy) */ sql?: string; /** SQL parameters (legacy) */ params?: any[]; /** Structured conditions (new format) */ conditions?: Array<{ field: string; operator: string; value: any; }>; } /** * Complete definition of a SchemaKit entity (table) * * Stored in the `system_entities` table, this defines a business entity * with its metadata, table name, and display information. * * @example * ```typescript * const entity: EntityDefinition = { * entity_id: 'ent_customers_001', * entity_name: 'customers', * entity_table_name: 'app_customers', * entity_display_name: 'Customers', * entity_description: 'Customer database records', * entity_is_active: true, * entity_created_at: '2024-01-01T00:00:00Z', * entity_updated_at: '2024-01-01T00:00:00Z', * entity_metadata: { version: '1.0' } * }; * ``` * * @since 0.1.0 */ interface EntityDefinition { /** Unique identifier for this entity */ entity_id: string; /** Entity name used in code (e.g., 'users', 'orders') */ entity_name: string; /** Actual database table name (e.g., 'app_users') */ entity_table_name: string; /** Human-readable display name */ entity_display_name: string; /** Description of what this entity represents */ entity_description: string; /** Whether this entity is currently active */ entity_is_active: boolean; /** ISO timestamp when entity was created */ entity_created_at: string; /** ISO timestamp when entity was last updated */ entity_updated_at: string; /** Additional metadata as JSON object */ entity_metadata?: Record<string, any>; } /** * Definition of a field (column) within an entity * * Stored in the `system_fields` table, this defines individual columns * with their types, constraints, and display information. * * @example * ```typescript * const field: FieldDefinition = { * id: 'fld_email_001', * entity_id: 'ent_users_001', * name: 'email', * type: 'string', * is_required: true, * is_unique: true, * display_name: 'Email Address', * description: 'User email for login', * order_index: 2, * is_active: true, * validation_rules: { * format: 'email', * maxLength: 255 * } * }; * ``` * * @since 0.1.0 */ interface FieldDefinition { /** Unique identifier for this field */ field_id: string; /** ID of the entity this field belongs to */ field_entity_id: string; /** Field name used in code (e.g., 'email', 'created_at') */ field_name: string; /** Data type of this field */ field_type: FieldType; /** Whether this field must have a value */ field_is_required: boolean; /** Whether values in this field must be unique */ field_is_unique: boolean; /** Whether this field is part of the primary key */ field_is_primary_key?: boolean; /** Default value for new records (as string) */ field_default_value?: string; /** Validation rules as JSON object */ field_validation_rules?: Record<string, any>; /** Human-readable display name */ field_display_name: string; /** Description of what this field represents */ field_description?: string; /** Display order (0-based index) */ field_order_index: number; /** Whether this field is currently active */ field_is_active: boolean; /** Entity name this field references (for foreign keys) */ field_reference_entity?: string; /** Additional metadata as JSON object */ field_metadata?: Record<string, any>; } /** * Complete configuration for an entity including all related definitions * * This aggregates all the components that define an entity: the entity itself, * its fields, permissions, views, workflows, and RLS rules. * * @example * ```typescript * const config: EntityConfiguration = { * entity: entityDef, * fields: [emailField, nameField], * permissions: [readPermission, writePermission], * views: [listView, detailView], * workflows: [welcomeEmail], * rls: [departmentRule] * }; * * // Used internally by SchemaKit to represent complete entity structure * const entity = new Entity('users', 'tenant-123', db); * await entity.initialize(); // Loads EntityConfiguration * ``` * * @since 0.1.0 */ interface EntityConfiguration { /** The entity definition */ entity: EntityDefinition; /** All fields defined for this entity */ fields: FieldDefinition[]; /** Permission rules for this entity */ permissions: PermissionDefinition[]; /** View definitions for this entity */ views: ViewDefinition[]; /** Workflow definitions for this entity */ workflows: WorkflowDefinition[]; /** Row-level security rules for this entity */ rls: RLSDefinition[]; } /** * ViewManager - Execute views using loaded Entity metadata */ declare class ViewManager { private db; private entityName; private tableName; private fields; private views; private rlsManager; constructor(db: DB, entityName: string, tableName: string, fields: FieldDefinition[], views: ViewDefinition[]); /** * Set RLS restrictions for role-based filtering */ setRLSRestrictions(restrictions: RoleRestrictions): void; /** * Execute a view by name */ executeView(viewName: string, context?: Context, options?: ViewOptions): Promise<ViewResult>; /** * Apply view field selection to query */ private applyViewSelect; /** * Apply view-defined filters to query */ private applyViewFilters; /** * Apply user-provided filters to query */ private applyUserFilters; /** * Apply sorting from view definition */ private applySorting; /** * Apply pagination from user options */ private resolvePagination; private buildDefaultStats; private buildCountFilters; /** * Apply RLS restrictions to query */ private applyRLSRestrictions; /** * Get exposed conditions for user filtering */ getExposedConditions(context: Context): any[]; } type UnknownFieldPolicy = 'allow' | 'strip' | 'error'; type ValidationAction = 'create' | 'update'; interface ValidationIssue { path: string; message: string; code?: string; } interface ValidationResult$1<T = any> { ok: boolean; data?: T; errors?: ValidationIssue[]; } /** * Opaque compiled schema representation for a specific entity. */ interface CompiledSchema { entityId: string; fieldMap: Record<string, { type: string; required: boolean; }>; allowedKeys: Set<string>; unknownFieldPolicy: UnknownFieldPolicy; } interface ValidationAdapter { name: string; buildSchema(entityId: string, fields: FieldDefinition[], options?: { unknownFieldPolicy?: UnknownFieldPolicy; }): CompiledSchema; validate(action: ValidationAction, schema: CompiledSchema, input: unknown): ValidationResult$1<Record<string, any>>; sanitizeFilters?(schema: CompiledSchema, filters: Record<string, any>): { filters: Record<string, any>; errors?: ValidationIssue[]; }; } declare class Entity { private static cache; private readonly entityName; private readonly tenantId; private readonly db; private initialized; private validationAdapter; private compiledSchema; private unknownFieldPolicy; fields: FieldDefinition[]; permissions: PermissionDefinition[]; workflow: WorkflowDefinition[]; rls: RLSDefinition[]; views: ViewDefinition[]; private entityDefinition; private tableName; viewManager: ViewManager | null; static create(entityName: string, tenantId: string, db: DB): Entity; /** * Determine the primary key field name for this entity's table */ private getPrimaryKeyFieldName; private constructor(); get isInitialized(): boolean; get name(): string; get tenant(): string; /** * Configure validation for this entity instance. Safe to call multiple times; will rebuild schema on next initialize. */ setValidation(adapter: ValidationAdapter, unknownFieldPolicy?: UnknownFieldPolicy): void; initialize(context?: Context): Promise<void>; /** * Execute a view by name */ view(viewName: string, options?: ViewOptions, context?: Context): Promise<ViewResult>; /** * Read records with optional filters */ get(filters?: Record<string, any>, context?: Context): Promise<Record<string, any>[]>; /** * Find a record by ID */ getById(id: string | number, context?: Context): Promise<Record<string, any> | null>; /** * Create a new record */ insert(data: Record<string, any>, context?: Context): Promise<Record<string, any>>; /** * Update a record by ID */ update(id: string | number, data: Record<string, any>, context?: Context): Promise<Record<string, any>>; /** * Delete a record by ID */ delete(id: string | number, context?: Context): Promise<boolean>; private ensureInitialized; private loadEntityDefinition; private loadFields; private loadPermissions; private loadWorkflows; private loadRLS; private loadViews; private checkPermission; private validateData; private validateFieldType; private buildRLSConditions; private executeWorkflows; private ensureTable; static clearCache(entityName?: string, tenantId?: string): void; static getCacheStats(): { size: number; entities: string[]; }; } type SchemaKitInitOptions = { adapter?: string; config?: any; multiTenancy?: MultiTenancyConfig; cache?: { enabled?: boolean; ttl?: number; }; validation?: { adapter?: ValidationAdapter; unknownFieldPolicy?: UnknownFieldPolicy; }; }; declare class SchemaKit { private readonly options; readonly db: DB; private validationAdapter; constructor(options?: SchemaKitInitOptions); /** * Get or create an Entity instance * @param name Entity name * @param tenantId Tenant identifier (defaults to 'public') */ entity(name: string, tenantId?: string): Promise<Entity>; } /** * Validation types for data integrity and business rules * * SchemaKit provides comprehensive validation for entity fields, * supporting both built-in validators and custom business rules. * * @since 0.1.0 */ /** * Single validation error for a field * * @example * ```typescript * const error: ValidationError = { * field: 'email', * code: 'INVALID_FORMAT', * message: 'Email address must be valid', * value: 'invalid-email' * }; * ``` * * @since 0.1.0 */ interface ValidationError { /** Field name that failed validation */ field: string; /** Error code for programmatic handling */ code: string; /** Human-readable error message */ message: string; /** The value that failed validation */ value?: any; } /** * Non-blocking validation warning * * @example * ```typescript * const warning: ValidationWarning = { * field: 'phone', * message: 'Phone number format may not be recognized internationally', * value: '555-1234' * }; * ``` * * @since 0.1.0 */ interface ValidationWarning { /** Field name with the warning */ field: string; /** Warning message */ message: string; /** The value that triggered the warning */ value?: any; } /** * Complete validation result for data * * @example * ```typescript * // Successful validation * const result: ValidationResult = { * isValid: true, * errors: [], * warnings: [] * }; * * // Failed validation * const failedResult: ValidationResult = { * isValid: false, * errors: [ * { * field: 'email', * code: 'REQUIRED', * message: 'Email is required', * value: undefined * } * ], * warnings: [ * { * field: 'phone', * message: 'Consider using international format', * value: '555-1234' * } * ] * }; * * // Usage in entity operations * const validation = await entity.validate(data); * if (!validation.isValid) { * throw new ValidationError('Data validation failed', validation.errors); * } * ``` * * @since 0.1.0 */ interface ValidationResult { /** Whether all validations passed */ isValid: boolean; /** Array of validation errors (blocks operation if any) */ errors: ValidationError[]; /** Array of validation warnings (informational only) */ warnings?: ValidationWarning[]; } /** * ValidationManager - Simplified * Essential validation functionality only */ /** * Simplified ValidationManager class * Essential validation only */ declare class ValidationManager { /** * Validate entity data against schema */ validate(entityConfig: EntityConfiguration, data: Record<string, any>, operation: 'create' | 'update'): Promise<ValidationResult>; /** * Validate a single field */ private validateField; /** * Validate field type */ private validateFieldType; /** * Validate custom rules */ private validateCustomRules; } /** * TypeValidators * Handles type-specific validation logic */ declare class TypeValidators { /** * Validate field type */ static validateFieldType(field: FieldDefinition, value: any): ValidationError[]; /** * Check if value is a valid date */ private static isValidDate; /** * Check if value is valid JSON */ private static isValidJson; } /** * PermissionManager * Responsible for handling permissions and RLS */ /** * PermissionManager class * Single responsibility: Handle permissions and RLS */ declare class PermissionManager { private databaseAdapter; /** * Create a new PermissionManager instance * @param databaseAdapter Database adapter */ constructor(databaseAdapter: DatabaseAdapter); /** * Check if user has permission for action * @param entityConfig Entity configuration * @param action Action name * @param context User context * @returns True if user has permission */ checkPermission(entityConfig: EntityConfiguration, action: string, context?: Context): Promise<boolean>; /** * Get entity permissions for user * @param entityConfig Entity configuration * @param context User context */ getEntityPermissions(entityConfig: EntityConfiguration, context?: Context): Promise<Record<string, boolean>>; /** * Build RLS (Row Level Security) conditions * @param entityConfig Entity configuration * @param context User context * @returns RLS conditions */ buildRLSConditions(entityConfig: EntityConfiguration, context: Context): RLSConditions; /** * Check field-level permissions * @param entityConfig Entity configuration * @param fieldName Field name * @param action Action (read, write) * @param context User context */ checkFieldPermission(entityConfig: EntityConfiguration, fieldName: string, action: 'read' | 'write', context?: Context): boolean; /** * Filter fields based on permissions * @param entityConfig Entity configuration * @param data Data object * @param action Action (read, write) * @param context User context * @returns Filtered data object */ filterFieldsByPermissions(entityConfig: EntityConfiguration, data: Record<string, any>, action: 'read' | 'write', context?: Context): Record<string, any>; /** * Evaluate permission conditions * @param conditions Permission conditions * @param context User context * @returns True if conditions are met * @private */ private evaluatePermissionConditions; /** * Evaluate a single condition * @param condition Single condition object * @param context User context * @returns True if condition is met * @private */ private evaluateSingleCondition; } /** * WorkflowManager * Responsible for workflow execution */ /** * WorkflowManager class * Single responsibility: Handle workflow execution */ declare class WorkflowManager { private databaseAdapter; /** * Create a new WorkflowManager instance * @param databaseAdapter Database adapter */ constructor(databaseAdapter: DatabaseAdapter); /** * Execute workflows for an entity event * @param entityConfig Entity configuration * @param event Trigger event * @param oldData Old data (for update/delete) * @param newData New data (for create/update) * @param context User context */ executeWorkflows(entityConfig: EntityConfiguration, event: string, oldData: Record<string, any> | null, newData: Record<string, any> | null, context: Context): Promise<void>; /** * Execute a single workflow * @param workflow Workflow definition * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @private */ private executeWorkflow; /** * Evaluate workflow conditions * @param conditions Workflow conditions * @param oldData Old data * @param newData New data * @param context User context * @returns True if conditions are met * @private */ private evaluateWorkflowConditions; /** * Execute workflow actions * @param actions Array of workflow actions * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @private */ private executeWorkflowActions; /** * Execute a single workflow action * @param action Workflow action * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @private */ private executeWorkflowAction; /** * Evaluate a single condition * @param condition Single condition object * @param oldData Old data * @param newData New data * @param context User context * @returns True if condition is met * @private */ private evaluateSingleCondition; /** * Execute log action * @param action Action configuration * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @private */ private executeLogAction; /** * Execute email action * @param action Action configuration * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @private */ private executeEmailAction; /** * Execute webhook action * @param action Action configuration * @param event Trigger event * @param oldData Old data * @param newData New data * @param context User context * @