import "./lucid_augmentations"; import { HttpContext } from '@adonisjs/core/http'; import { GenerateModelSchemaOptions } from "./open_api_schema_service"; import { type BaseModel } from '@adonisjs/lucid/orm'; import type { ApplicationService } from '@adonisjs/core/types'; import type { EventMap, Key, Listener } from "../bundled_nhtio_tiny_typed_emitter"; import type { NormalizeConstructor } from '@adonisjs/core/types/helpers'; import type { AnySchema, ObjectSchema } from "../bundled_nhtio_validation"; import type { RelationsCacheErrorHandler } from "./utils/cache_error_handler"; import type { RelationsCacheEventHandler } from "./utils/cache_event_handler"; import type { DatabaseQueryBuilderContract } from '@adonisjs/lucid/types/querybuilder'; import type { CacheMiddlewareInput, CacheMiddlewarePipeline } from "./utils/cache_middleware"; import type { RelationCacheEntryOptions, RelationsCacheServiceResolver } from "./utils/cache_service_resolver"; import type { LucidModel } from '@adonisjs/lucid/types/model'; import type { ExternalDocumentationObject, PromiseAble, ResourcefulColumnDefinition, ResourcefulComputedAccessorDefinition, ResourcefulDataType, ResourcefulGeneralAccessControlFilter, ResourcefulModelMetaSchema, ResourcefulModelOpenApiSchema, ResourcefulPropertySchema, ResourcefulRelationshipDefinition, ResourcefulResourceAccessControlFilter } from "../types"; /** * Result object returned by the resourceful index/list operation. * * Contains paginated records along with metadata about the query execution * and pagination state. The records are filtered according to field-level * access control and only contain the requested fields. * * @template ReturnType - The shape of records after field selection and ACL filtering */ export interface ResourcefulIndexResult { /** Array of records with only the requested and accessible fields */ records: Array>; /** Total number of records matching the filter (before pagination) */ total: number; /** The current page number (1-based) */ page: number; /** Number of records per page */ perPage: number; /** Aggregation results organized by field name and operation type */ aggregations: ResourcefulIndexAggregateResults; /** SQL query string used for counting total records */ countQuery: string; /** SQL query string used for fetching the paginated records */ recordsQuery: string; } export interface ServableResourcefulIndexResult extends Omit, 'countQuery' | 'recordsQuery'> { } /** * Function type for generating payload validation schemas based on request context. * * These functions are called during create and update operations to generate * context-specific validation schemas using Joi. They provide the core model-level * validation that applies regardless of request-specific hooks. * * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @returns Promise resolving to a Joi schema for validating the request payload */ export interface ResourcefulPayloadValidatorGetter { (ctx: HttpContext, app: ApplicationService): PromiseAble; } /** * Function type for generating request-specific payload validation schemas. * * These functions are provided via hooks during create and update operations * to add additional validation constraints beyond the base model validation. * They enable request-specific validation logic based on context. * * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @returns Promise resolving to a Joi ObjectSchema for additional validation, or null to skip */ export interface ResourcefulPayloadSchemaGetter { (ctx: HttpContext, app: ApplicationService): PromiseAble; } /** * Function type for applying query-level scoping to database queries. * * These callbacks are applied to queries during CRUD operations to enforce * data access boundaries based on request context. They modify the query * in-place by adding WHERE clauses, JOINs, or other constraints. * * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param query - Database query builder to modify with scoping constraints * @param model - The resourceful model class the query is being built for * @returns Promise that resolves when the query has been modified */ export interface ResourcefulQueryScopeCallback { (ctx: HttpContext, app: ApplicationService, query: DatabaseQueryBuilderContract, model: ResourcefulModel): PromiseAble; } /** * Array of query scope callback functions. * * Used for operations that need to apply query-level scoping (index, read, delete). * Callbacks are applied in sequence to build up the complete set of access constraints. */ export type ResourcefulScopeHooks = ResourcefulQueryScopeCallback[]; /** * Array of payload validation schema getter functions. * * Used for operations that accept request payloads (create, update). * Each function can return additional validation constraints based on request context. */ export type ResourcefulValidationHooks = ResourcefulPayloadSchemaGetter[]; /** * Combined hook interface providing both query scoping and payload validation. * * Used by the update operation which needs both query scoping (to find the record) * and payload validation (to validate the update data). */ export interface ResourcefulHooks { /** Query scope callbacks for constraining database queries */ queryScopeCallbacks: ResourcefulScopeHooks; /** Payload validation schema getters for additional validation */ payloadValidationSchemas: ResourcefulValidationHooks; } export type ResourcefulMixinEventMap> = EventMap<{ 'acl:error': [ unknown, HttpContext, ApplicationService, ModelInstance | undefined ]; 'validation:scope:error': [ unknown, HttpContext, ApplicationService, string, ResourcefulDataType ]; }>; /** * Enhanced Lucid model interface providing resourceful CRUD functionality. * * This interface extends the base LucidModel with metadata-driven CRUD operations, * field-level access control, query scoping, OpenAPI schema generation, and event * handling capabilities. Models implementing this interface gain static methods * for handling HTTP requests with built-in validation, pagination, filtering, * and comprehensive security features. * * The interface defines both metadata storage properties and operational methods * that work together to provide a complete resourceful API layer on top of * AdonisJS Lucid ORM models. * * @extends LucidModel * * @example * ```typescript * import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' * import { withResourceful, resourceful } from 'lucid-resourceful' * * class User extends withResourceful()(BaseModel) implements ResourcefulModel { * @column({ isPrimary: true }) * @resourceful({ type: 'number', nullable: false }) * public id: number * * @column() * @resourceful({ type: 'string', nullable: false }) * public name: string * * // Automatically gains all ResourcefulModel methods * // User.$onResourcefulIndex, User.$onResourcefulRead, etc. * } * ``` */ export interface ResourcefulModel extends LucidModel { /** The display name for this model used in API documentation and error messages */ $resourcefulName: string; /** The column used as the title of the model instance when displayed in a list */ $resourcefulTitleColumn?: string; /** Optional description for OpenAPI schema documentation */ $resourcefulMetaDescription?: string; /** Optional external documentation reference for OpenAPI schema */ $resourcefulMetaExternalDocs?: ExternalDocumentationObject; /** Optional example value for OpenAPI schema documentation */ $resourcefulMetaExample?: string; /** Map of column property names to their resourceful metadata definitions */ $resourcefulColumns: Map>; /** Map of relationship property names to their resourceful metadata definitions */ $resourcefulRelationships: Map; /** Map of computed accessor property names to their resourceful metadata definitions */ $resourcefulComputedAccessors: Map>; /** Set of enforced preloads for the model */ $resourcefulEnforcedPreloads: Set; /** Pre-merged per-relation cache entry options for cacheable 1:1 relations */ $resourcefulRelationsCache: Map; /** Model-level cache entry defaults applied when a relation cache config is `true` */ $resourcefulRelationsCacheDefaults: RelationCacheEntryOptions; /** Optional per-model cache resolver overriding the package-level default */ $resourcefulRelationsCacheService?: RelationsCacheServiceResolver; /** Optional per-model relation-cache error handler overriding the package-level default (ADR-009) */ $resourcefulOnRelationsCacheError?: RelationsCacheErrorHandler; /** Optional per-model relation-cache event hook overriding the package-level default */ $resourcefulOnRelationsCacheEvent?: RelationsCacheEventHandler; /** Pre-built per-model cache-write middleware pipeline run before encode (ADR-012) */ $resourcefulCacheMiddlewarePrepare?: CacheMiddlewarePipeline | null; /** Pre-built per-model cache-read middleware pipeline run after decode (ADR-012) */ $resourcefulCacheMiddlewareConsume?: CacheMiddlewarePipeline | null; /** Access control filter functions organized by CRUD operation type */ $resourcefulAccessControlFilters: ResourcefulMixinOptions['accessControlFilters']; /** Error handling strategy when ACL evaluation fails */ $resourcefulOnACLError: ResourcefulMixinOptions['onACLError']; /** Query scope callback functions for constraining database queries */ $resourcefulQueryScopeCallbacks: ResourcefulMixinOptions['queryScopeCallbacks']; /** Payload validation schema builder functions for create and update operations */ $resourcefulPayloadValidationSchemaBuilders: ResourcefulMixinOptions['payloadValidationSchemaBuilders']; /** * Registers an event listener for resourceful model events. * * This method provides a way to listen for events emitted during resourceful * operations such as ACL errors or validation scope errors. Useful for logging, * monitoring, or implementing custom error handling logic. * * @template K - The event key type from the resourceful event map * @param event - The name of the event to listen for ('acl:error', 'validation:scope:error') * @param listener - Function to call when the event is emitted * @returns The model class for method chaining * * @example * ```typescript * User.$onResourcefulEvent('acl:error', (error, ctx, app, instance) => { * console.error('ACL error occurred:', error); * }); * ``` */ $onResourcefulEvent(event: Key>, listener: Listener>): this; /** * Registers a one-time event listener for resourceful model events. * * Similar to $onResourcefulEvent but the listener is automatically removed * after being called once. Useful for handling events that should only * trigger a single response. * * @template K - The event key type from the resourceful event map * @param event - The name of the event to listen for * @param listener - Function to call when the event is emitted * @returns The model class for method chaining * * @example * ```typescript * User.$onceResourcefulEvent('validation:scope:error', (error, ctx, app, key, datatype) => { * console.warn('Validation scope error (one-time):', error); * }); * ``` */ $onceResourcefulEvent(event: Key>, listener: Listener>): this; /** * Removes an event listener for resourceful model events. * * Unregisters a previously registered event listener. If no listener is * provided, all listeners for the specified event are removed. * * @template K - The event key type from the resourceful event map * @param event - The name of the event to stop listening for * @param listener - Optional specific listener function to remove * @returns The model class for method chaining * * @example * ```typescript * // Remove specific listener * User.$offResourcefulEvent('acl:error', myErrorHandler); * * // Remove all listeners for an event * User.$offResourcefulEvent('acl:error'); * ``` */ $offResourcefulEvent(event: Key>, listener?: Listener>): this; $getAsResourcefulForContext(ctx: HttpContext, app: ApplicationService): Promise; $getResourcefulWriteValidationSchema(ctx: HttpContext, app: ApplicationService, forUpdate?: boolean): Promise<{ schema: ResourcefulModelOpenApiSchema; validator: ObjectSchema; }>; /** * Performs paginated listing and searching of model records with comprehensive filtering and aggregations. * * This method provides the core implementation for resourceful index/list operations, * supporting pagination, field selection, access control, aggregations, and Lucene query syntax for * filtering. It validates all inputs, applies ACL filters, executes the query with * proper field mapping between serialized names and database columns, and returns * structured results with query metadata and computed aggregations. * * @template SelectedFields - Array of field names that can be selected from the model's serializable attributes * @template ReturnType - The resulting type after picking the selected fields from the model's serializable attributes * * @param filter - Lucene-style query string for filtering records (e.g., "name:john AND email:*.com"). * If null or undefined, defaults to empty string (no filtering). * @param page - The page number for pagination (must be ≥ 1). Used with perPage to calculate offset. * @param perPage - Number of records per page (must be ≥ 1 and ≤ 100 by default). * @param fields - Array of field names to include in the response. If null, undefined, or empty, * defaults to just the primary key field. Field names should use serialized names * (as they appear in API responses), not database column names. * @param sort - Array of sort specifications as [fieldName, direction] tuples. Field names must be * marked as sortable in their column definitions. * @param ctx - HTTP context containing request information, authentication, and other request-scoped data. * @param app - Application service instance providing access to application-level services and configuration. * @param hooks - Optional array of query scope callbacks to apply additional filtering constraints. * @param aggregations - Optional object specifying aggregations to compute. Maps field names to arrays * of aggregation methods. Only fields marked as aggregatable can be aggregated. * * @returns Promise resolving to ResourcefulIndexResult containing: * - `records`: Array of partial record objects with only the requested fields * - `total`: Total number of records matching the filter (before pagination) * - `page`: The requested page number (echoed back) * - `perPage`: The requested per-page limit (echoed back) * - `aggregations`: Computed aggregation results organized by field and method * - `countQuery`: SQL query string used for counting total records * - `recordsQuery`: SQL query string used for fetching the actual records * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters * @throws {E_INVALID_COLUMN_ACCESS} When no fields are available for access after ACL filtering * @throws {E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION} When input validation fails * @throws {E_INVALID_AGGREGATION_FIELD} When attempting to aggregate on non-aggregatable fields * @throws {E_INVALID_AGGREGATION_OPERATION} When using invalid aggregation methods * * @example * ```typescript * // Basic usage with pagination * const result = await User.$onResourcefulIndex( * 'name:john', * 1, * 10, * ['id', 'name', 'email'], * null, * ctx, * app * ); * * // Complex filtering with date ranges and aggregations * const result = await User.$onResourcefulIndex( * 'status:active AND createdAt:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]', * 2, * 25, * ['id', 'name', 'totalSales'], * [['createdAt', 'desc']], * ctx, * app, * [], * { * totalSales: ['sum', 'avg', 'max'], * orderCount: ['sum'], * customerId: ['countDistinct'] * } * ); * ``` */ $onResourcefulIndex(filter: string | null | undefined, page: number, perPage: number, fields: string | string[] | null | undefined, sort: Array<[ string, 'asc' | 'desc' ]> | null | undefined, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks, aggregations?: ResourcefulIndexAggregateOptions): Promise>; /** * Retrieves a single model record by its unique identifier with access control. * * This method implements secure record retrieval by first applying query scope callbacks * to verify the record exists within the user's access scope, then fetching the full * model instance for field-level ACL evaluation. Only fields that pass ACL checks * are included in the response. * * @param uid - The unique identifier of the record to retrieve * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param hooks - Optional array of query scope callbacks to apply additional filtering constraints * * @returns Promise resolving to the record object with only accessible fields * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access * @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters * * @example * ```typescript * // Retrieve a user by ID * const user = await User.$onResourcefulRead(123, ctx, app); * * // With additional query scoping * const user = await User.$onResourcefulRead(123, ctx, app, [ * (ctx, app, query) => query.where('tenant_id', ctx.auth.user.tenantId) * ]); * ``` */ $onResourcefulRead(uid: number, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks): Promise; /** * Loads a specific relationship for a model record with pagination, filtering, and access control. * * This method provides paginated relationship loading by leveraging the related model's * $onResourcefulIndex method with automatically generated relationship constraints. * It supports full filtering, sorting, and pagination capabilities while maintaining * proper access control and scoping. * * @param uid - The unique identifier of the parent record * @param relationshipKey - The name of the relationship property to load * @param filter - Lucene-style query string for filtering related records * @param page - The page number for pagination (must be ≥ 1) * @param perPage - Number of records per page (must be ≥ 1 and ≤ 100) * @param fields - Array of field names to include in the response from the related model * @param sort - Array of sort criteria as [field, direction] tuples * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param hooks - Optional array of additional query scope callbacks * * @returns Promise resolving to paginated relationship results * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_INVALID_RELATIONSHIP_EXCEPTION} When the specified relationship doesn't exist * @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters * @throws {E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION} When input validation fails * * @example * ```typescript * // Load user's posts with filtering and pagination * const userPosts = await Post.$onResourcefulReadRelationship( * 123, // user ID * 'posts', // relationship name * 'status:published', // filter * 1, // page * 10, // perPage * ['id', 'title'], // fields * [['createdAt', 'desc']], // sort * ctx, * app * ); * * // Load user's skills (many-to-many) * const userSkills = await Skill.$onResourcefulReadRelationship( * 123, * 'skills', * null, // no filter * 1, * 50, * null, // all fields * null, // default sort * ctx, * app * ); * ``` */ $onResourcefulReadRelationship(uid: any, relationshipKey: string, filter: string | null | undefined, page: number, perPage: number, fields: string | string[] | null | undefined, sort: Array<[ string, 'asc' | 'desc' ]> | null | undefined, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks, aggregations?: ResourcefulIndexAggregateOptions): Promise>; /** * Creates a new model record with payload validation and access control. * * This method handles secure record creation by validating the request payload * against both model-level and request-specific validation schemas, checking * field-level write permissions, and returning the created record with appropriate * field filtering applied. * * @param payload - The data object containing field values for the new record * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param hooks - Optional array of validation schema getters for additional payload validation * * @returns Promise resolving to the created record with only accessible fields * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_INVALID_PAYLOAD_EXCEPTION} When core model validation fails * @throws {E_FORBIDDEN_PAYLOAD_EXCEPTION} When request-specific validation fails * @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters * * @example * ```typescript * // Create a new user * const user = await User.$onResourcefulCreate({ * name: 'John Doe', * email: 'john@example.com' * }, ctx, app); * * // With additional validation * const user = await User.$onResourcefulCreate(payload, ctx, app, [ * (ctx, app) => joi.object({ email: joi.string().domain('company.com') }) * ]); * ``` */ $onResourcefulCreate(payload: any, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulValidationHooks): Promise; /** * Updates an existing model record with payload validation and access control. * * This method implements secure record updates by first verifying the record exists * and is accessible via the read operation, validating the update payload, checking * field-level write permissions, and returning the updated record with appropriate * field filtering applied. * * @param uid - The unique identifier of the record to update * @param payload - The data object containing field values to update * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param hooks - Optional object containing query scope callbacks and validation schema getters * * @returns Promise resolving to the updated record with only accessible fields * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access * @throws {E_INVALID_PAYLOAD_EXCEPTION} When core model validation fails * @throws {E_FORBIDDEN_PAYLOAD_EXCEPTION} When request-specific validation fails * @throws {E_FORBIDDEN} When access is denied by model-level or field-level ACL filters * * @example * ```typescript * // Update a user * const user = await User.$onResourcefulUpdate(123, { * name: 'Jane Doe' * }, ctx, app); * * // With additional scoping and validation * const user = await User.$onResourcefulUpdate(123, payload, ctx, app, { * queryScopeCallbacks: [(ctx, app, query) => query.where('active', true)], * payloadValidationSchemas: [(ctx, app) => customValidationSchema] * }); * ``` */ $onResourcefulUpdate(uid: number, payload: any, ctx: HttpContext, app: ApplicationService, hooks?: Partial): Promise; /** * Deletes an existing model record with access control. * * This method implements secure record deletion by applying query scope callbacks * to verify the record exists within the user's access scope, checking delete * permissions via ACL filters, and then removing the record from the database. * * @param uid - The unique identifier of the record to delete * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param hooks - Optional array of query scope callbacks to apply additional filtering constraints * * @returns Promise that resolves when the record has been successfully deleted * * @throws {E_MISSING_PRIMARY_KEY_EXCEPTION} When the model has no identifiable primary key * @throws {E_RECORD_NOT_FOUND_EXCEPTION} When no record exists with the given ID or user lacks access * @throws {E_FORBIDDEN} When access is denied by model-level ACL filters * * @example * ```typescript * // Delete a user * await User.$onResourcefulDelete(123, ctx, app); * * // With additional query scoping * await User.$onResourcefulDelete(123, ctx, app, [ * (ctx, app, query) => query.where('tenant_id', ctx.auth.user.tenantId) * ]); * ``` */ $onResourcefulDelete(uid: number, ctx: HttpContext, app: ApplicationService, hooks?: ResourcefulScopeHooks): Promise; $onResourcefulBulkUpdate(filter: string | null | undefined, payload: any, ctx: HttpContext, app: ApplicationService, hooks?: Partial): Promise<{ [key: string]: any | Error; } | Error>; $onResourcefulBulkUpdateByUid(uids: (number | string)[], payload: any, ctx: HttpContext, app: ApplicationService, hooks?: Partial): Promise<{ [key: string]: any | Error; }>; /** * Generates an OpenAPI schema object for this model with context-aware field filtering. * * This method creates a complete OpenAPI v3 schema representation of the model * by evaluating field-level access control permissions for the given request context. * Only fields that pass ACL checks are included in the generated schema, ensuring * that API documentation accurately reflects what data is accessible to the current user. * * The method processes all resourceful properties (columns, computed accessors, and * relationships) and converts them to their OpenAPI schema equivalents while respecting * access control constraints and applying proper type mappings. * * @param ctx - HTTP context containing request information and authentication * @param app - Application service instance for accessing app-level services * @param operation - The type of operation. To honor field-level ACL * * @returns Promise resolving to a complete OpenAPI schema object with: * - `type`: Always 'object' for model schemas * - `title`: The model's resourceful name * - `description`: Optional model description from metadata * - `properties`: Object containing schema definitions for accessible fields * - `required`: Array of required field names (non-nullable fields) * - `externalDocs`: Optional external documentation reference * - `example`: Optional example value for the schema * * @example * ```typescript * // Generate OpenAPI schema for current user context * const schema = await User.$asOpenApiSchemaObject(ctx, app, 'read'); * * // Result structure: * { * type: 'object', * title: 'User', * properties: { * id: { type: 'number', readOnly: true }, * name: { type: 'string' }, * email: { type: 'string', format: 'email' } * }, * required: ['id', 'name', 'email'] * } * ``` * * @see {@link ResourcefulModelOpenApiSchema} for the complete schema structure */ $asOpenApiSchemaObject(ctx: HttpContext, app: ApplicationService, operation?: 'read' | 'write', methodOptions?: GenerateModelSchemaOptions): Promise; /** * Creates a new model instance from HTTP context data. * * @param ctx - The HTTP context containing the request data * @returns A Promise that resolves to a new model instance */ $createFromHttpContext(ctx: HttpContext): Promise>; } export declare const ResourcefulErrorHandlerMethod: ("bubble" | "pass" | "fail")[]; export type ResourcefulErrorHandlerMethod = (typeof ResourcefulErrorHandlerMethod)[number]; export declare const ResourcefulACLOperationType: ("read" | "write")[]; export type ResourcefulACLOperationType = (typeof ResourcefulACLOperationType)[number]; /** * Supported aggregation methods for numeric fields in resourceful queries. * * These methods can be applied to fields marked as `aggregatable: true` in their * column definitions to compute statistical values across filtered datasets. * * @example * ```typescript * // Request aggregations on sales data * const result = await Order.$onResourcefulIndex('status:completed', 1, 10, ['id'], null, ctx, app, [], { * totalAmount: ['sum', 'avg', 'max'], * quantity: ['sum', 'countDistinct'] * }) * ``` */ export declare const ResourcefulAggregateMethods: ("max" | "avg" | "min" | "sum" | "countDistinct" | "sumDistinct" | "avgDistinct")[]; export type ResourcefulAggregateMethods = (typeof ResourcefulAggregateMethods)[number]; /** * Configuration object for specifying aggregations to perform on index operations. * * Maps field names to arrays of aggregation methods to compute. Only fields marked * as `aggregatable: true` in their column definitions can be aggregated. * * @example * ```typescript * const aggregations: ResourcefulIndexAggregateOptions = { * totalSales: ['sum', 'avg', 'max', 'min'], * orderCount: ['sum'], * customerId: ['countDistinct'] * } * ``` */ export interface ResourcefulIndexAggregateOptions { [key: string]: ResourcefulAggregateMethods[]; } /** * Results object containing computed aggregation values organized by field and method. * * The structure mirrors the requested aggregations, with each field containing * the computed values for the requested aggregation methods. * * @example * ```typescript * const results: ResourcefulIndexAggregateResults = { * totalSales: { sum: 50000, avg: 125.5, max: 999.99, min: 10.00 }, * orderCount: { sum: 400 }, * customerId: { countDistinct: 150 } * } * ``` */ export interface ResourcefulIndexAggregateResults { [key: string]: Partial>; } export interface AdvancedResourcefulMixinOptions { propertyEvaluationConcurrency: number; aclEvaluationConcurrency: number; } export interface ResourcefulMixinOptions> { name: string; resourcefulTitleColumn?: string; readRequiredForWrite: boolean; accessControlFilters: { list: ResourcefulGeneralAccessControlFilter[]; create: ResourcefulGeneralAccessControlFilter[]; read: ResourcefulResourceAccessControlFilter[]; update: ResourcefulResourceAccessControlFilter[]; delete: ResourcefulResourceAccessControlFilter[]; }; payloadValidationSchemaBuilders: { create: ResourcefulPayloadValidatorGetter[]; update: ResourcefulPayloadValidatorGetter[]; }; onACLError: ResourcefulErrorHandlerMethod; onValidationScopeError: ResourcefulErrorHandlerMethod; queryScopeCallbacks: { list: ResourcefulQueryScopeCallback[]; access: ResourcefulQueryScopeCallback[]; }; description?: string; externalDocs?: ExternalDocumentationObject; example?: string; advanced: AdvancedResourcefulMixinOptions; enforcedPreloads?: string[]; relationsCache?: Record; relationsCacheDefaults?: RelationCacheEntryOptions; relationsCacheService?: RelationsCacheServiceResolver; onRelationsCacheError?: RelationsCacheErrorHandler; onRelationsCacheEvent?: RelationsCacheEventHandler; cacheMiddlewarePrepare?: CacheMiddlewareInput; cacheMiddlewareConsume?: CacheMiddlewareInput; } /** * Creates a mixin that adds resourceful CRUD functionality to Lucid models. * * This function implements the mixin pattern to enhance AdonisJS Lucid models with * metadata-driven CRUD operations, field-level access control, query scoping, * and OpenAPI schema generation capabilities. The resulting model gains static * methods for handling HTTP requests with built-in validation, pagination, * filtering, and security features. * * The mixin validates and normalizes configuration options, creates isolated * metadata maps for each model class to prevent inheritance conflicts, and * establishes an event emitter for monitoring ACL and validation operations. * * @param options - Configuration object for customizing mixin behavior * @param options.name - Display name for the model (defaults to class name) * @param options.readRequiredForWrite - Whether read access is required before write operations * @param options.accessControlFilters - Object containing arrays of ACL filter functions for different operations * @param options.accessControlFilters.list - Filters applied during listing/search operations * @param options.accessControlFilters.create - Filters applied during record creation * @param options.accessControlFilters.read - Filters applied during record reading * @param options.accessControlFilters.update - Filters applied during record updates * @param options.accessControlFilters.delete - Filters applied during record deletion * @param options.onACLError - Error handling strategy when ACL evaluation fails ('throw', 'pass', 'fail') * @param options.onValidationScopeError - Error handling strategy when validation scope functions fail * @param options.queryScopeCallbacks - Object containing query scope callback functions * @param options.queryScopeCallbacks.list - Callbacks applied to listing/search queries * @param options.queryScopeCallbacks.access - Callbacks applied to individual record access queries * @param options.description - Optional description for OpenAPI documentation * @param options.externalDocs - Optional external documentation reference for OpenAPI * @param options.example - Optional example value for OpenAPI documentation * @param options.advanced - Advanced configuration options for performance tuning * @param options.advanced.propertyEvaluationConcurrency - Concurrency limit for property ACL evaluation * @param options.advanced.aclEvaluationConcurrency - Concurrency limit for ACL filter evaluation * * @returns A mixin function that accepts a Lucid model class and returns the enhanced model * * @throws {E_INVALID_RESOURCEFUL_MIXIN_OPTIONS} When the provided options fail validation * * @example * ```typescript * import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' * import { withResourceful, resourceful } from 'lucid-resourceful' * * class User extends withResourceful({ * name: 'User', * readRequiredForWrite: true, * accessControlFilters: { * read: [(ctx) => ctx.auth.user?.id === ctx.params.id], * update: [(ctx) => ctx.auth.user?.isAdmin] * } * })(BaseModel) { * @column({ isPrimary: true }) * @resourceful({ type: 'number', nullable: false }) * public id: number * * @column() * @resourceful({ type: 'string', nullable: false }) * public name: string * } * * // Generated controller with CRUD operations * const UserController = User.generateController() * ``` * * @see {@link ResourcefulMixinOptions} for detailed configuration options * @see {@link ResourcefulModel} for the enhanced model interface */ export declare function withResourceful(options?: Partial): >(superclass: Model) => Model & ResourcefulModel;