/** * ObjectQL * Copyright (c) 2026-present ObjectStack Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * Standard CRUD operations supported by hooks. */ export type HookOperation = 'find' | 'count' | 'create' | 'update' | 'delete'; /** * Execution timing relative to the database operation. */ export type HookTiming = 'before' | 'after'; /** * Minimal API surface exposed to hooks for performing side-effects or checks. */ export interface HookAPI { find(objectName: string, query?: Record): Promise[]>; findOne(objectName: string, id: string | number): Promise | null>; count(objectName: string, query?: Record): Promise; create(objectName: string, data: Record): Promise>; update(objectName: string, id: string | number, data: Record): Promise>; delete(objectName: string, id: string | number): Promise>; } /** * Base context available in all hooks. */ export interface BaseHookContext<_T = Record> { /** The name of the object (entity) being acted upon. */ objectName: string; /** The triggering operation. */ operation: HookOperation; /** Access to the database/engine to perform extra queries. */ api: HookAPI; /** User/Session context (Authentication info). */ user?: { id: string | number; [key: string]: unknown; }; /** * Shared state for passing data between matching 'before' and 'after' hooks. * e.g. Calculate a diff in 'beforeUpdate' and read it in 'afterUpdate'. */ state: Record; } /** * Context for Retrieval operations (Find, Count). */ export interface RetrievalHookContext> extends BaseHookContext { operation: 'find' | 'count'; /** * The query criteria being executed. Modifiable in 'before' hooks. * Typed as `object` (not `Record`) because named interfaces * like UnifiedQuery lack index signatures and aren't assignable to Record types. */ query: object; /** The result of the query. Only available in 'after' hooks. */ result?: T[] | number; } /** * Context for Modification operations (Create, Update, Delete). */ export interface MutationHookContext> extends BaseHookContext { operation: 'create' | 'update' | 'delete'; /** The record ID. Undefined for 'create'. */ id?: string | number; /** * The incoming data changes. * - For 'create': The full object to insert. * - For 'update': The partial fields to update. * - For 'delete': Undefined. */ data?: Partial; /** * The final result record from the database. * Only available in 'after' hooks. */ result?: T; /** * The existing record fetched from DB before operation. * Available in 'update' and 'delete' hooks. */ previousData?: T; } /** * Specialized context for Updates, including change tracking. */ export interface UpdateHookContext> extends MutationHookContext { operation: 'update'; /** * Helper to check if a specific field is being modified. * Checks if the field exists in 'data' AND is different from 'previousData'. */ isModified(field: keyof T): boolean; } /** * Definition interface for a set of hooks for a specific object. */ export interface ObjectHookDefinition> { beforeFind?: (ctx: RetrievalHookContext) => Promise | void; afterFind?: (ctx: RetrievalHookContext) => Promise | void; beforeCount?: (ctx: RetrievalHookContext) => Promise | void; afterCount?: (ctx: RetrievalHookContext) => Promise | void; beforeDelete?: (ctx: MutationHookContext) => Promise | void; afterDelete?: (ctx: MutationHookContext) => Promise | void; beforeCreate?: (ctx: MutationHookContext) => Promise | void; afterCreate?: (ctx: MutationHookContext) => Promise | void; beforeUpdate?: (ctx: UpdateHookContext) => Promise | void; afterUpdate?: (ctx: UpdateHookContext) => Promise | void; } export type HookName = keyof ObjectHookDefinition; export type HookContext> = RetrievalHookContext | MutationHookContext | UpdateHookContext; export type HookHandler> = (ctx: HookContext) => Promise | void;