/* * Copyright © 2024 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ import { type OnEventOptions } from '@nestjs/event-emitter/dist/interfaces'; import type { Listener, OnOptions } from 'eventemitter2'; import type { Document, Query } from 'mongoose'; import { type Socket } from 'socket.io'; import { type BotStats } from '@/analytics/schemas/bot-stats.schema'; import { type Attachment } from '@/attachment/schemas/attachment.schema'; import type EventWrapper from '@/channel/lib/EventWrapper'; import { type SubscriberUpdateDto } from '@/chat/dto/subscriber.dto'; import type { Block, BlockFull } from '@/chat/schemas/block.schema'; import { type Category } from '@/chat/schemas/category.schema'; import { type ContextVar } from '@/chat/schemas/context-var.schema'; import { type Conversation } from '@/chat/schemas/conversation.schema'; import type { Label, LabelDocument } from '@/chat/schemas/label.schema'; import { type Message } from '@/chat/schemas/message.schema'; import { type Subscriber } from '@/chat/schemas/subscriber.schema'; import { type ContentType } from '@/cms/schemas/content-type.schema'; import { type Content } from '@/cms/schemas/content.schema'; import { type Menu } from '@/cms/schemas/menu.schema'; import { type Language } from '@/i18n/schemas/language.schema'; import { type Translation } from '@/i18n/schemas/translation.schema'; import type { NlpEntity, NlpEntityDocument, } from '@/nlp/schemas/nlp-entity.schema'; import { type NlpSampleEntity } from '@/nlp/schemas/nlp-sample-entity.schema'; import { type NlpSample } from '@/nlp/schemas/nlp-sample.schema'; import { NlpValueDocument, type NlpValue, } from '@/nlp/schemas/nlp-value.schema'; import { type Setting } from '@/setting/schemas/setting.schema'; import { type Invitation } from '@/user/schemas/invitation.schema'; import { type Model } from '@/user/schemas/model.schema'; import { type Permission } from '@/user/schemas/permission.schema'; import { type Role } from '@/user/schemas/role.schema'; import { type User } from '@/user/schemas/user.schema'; import { EHook, type DeleteResult } from '@/utils/generics/base-repository'; import { TFilterQuery, THydratedDocument } from '@/utils/types/filter.types'; import '@nestjs/event-emitter'; /** * @description Module declaration that extends the NestJS EventEmitter with custom event types and methods. */ declare module '@nestjs/event-emitter' { interface TDefinition { schema: S; operations: O; } interface IHookExtensionsOperationMap {} interface IHookSettingsGroupLabelOperationMap { chatbot_settings: TDefinition< object, { global_fallback: Setting; fallback_block: Setting; fallback_message: Setting; } >; contact: TDefinition< object, { contact_email_recipient: Setting; company_name: Setting; company_phone: Setting; company_email: Setting; company_address1: Setting; company_address2: Setting; company_city: Setting; company_zipcode: Setting; company_state: Setting; company_country: Setting; } >; } /* custom hooks */ interface IHookOperationMap extends IHookSettingsGroupLabelOperationMap, IHookExtensionsOperationMap { analytics: TDefinition< object, { block: BlockFull; passation: Subscriber; 'fallback-local': BlockFull; 'fallback-global': EventWrapper; intervention: Subscriber; } >; chatbot: TDefinition< object, { sent: unknown; received: unknown; message: unknown; delivery: unknown; read: unknown; typing: unknown; follow: unknown; echo: unknown; } >; websocket: TDefinition< object, { connection: Socket; } >; } /* hooks */ interface IHookEntityOperationMap extends IHookOperationMap { stats: TDefinition; attachment: TDefinition; block: TDefinition; category: TDefinition; contextVar: TDefinition; conversation: TDefinition; label: TDefinition< Label, { create: LabelDocument; delete: Label | Label[] } >; message: TDefinition; subscriber: TDefinition; contentType: TDefinition; content: TDefinition; menu: TDefinition; language: TDefinition; translation: TDefinition; nlpEntity: TDefinition< NlpEntity, { create: NlpEntityDocument; update: NlpEntity; delete: NlpEntity | NlpEntity[]; } >; nlpSampleEntity: TDefinition; nlpSample: TDefinition; nlpValue: TDefinition< NlpValue, { create: NlpValueDocument; update: NlpValue; delete: NlpValue | NlpValue[]; } >; setting: TDefinition; invitation: TDefinition; model: TDefinition; permission: TDefinition; role: TDefinition; user: TDefinition; } /* entities hooks having schemas */ type IHookEntities = keyof Omit< IHookEntityOperationMap, keyof IHookOperationMap >; /** * @description A constrained string type that allows specific string values while preserving type safety. */ type ConstrainedString = string & Record; type EventNamespaces = keyof IHookEntityOperationMap; /* pre hooks */ type TPreValidate = THydratedDocument; type TPreCreate = THydratedDocument; type TPreUpdate = TFilterQuery & object; type TPreDelete = Query< DeleteResult, Document, unknown, T, 'deleteOne', Record >; type TPreUnion = | TPreValidate | TPreCreate | TPreUpdate | TPreDelete; /* post hooks */ type TPostValidate = THydratedDocument; type TPostCreate = THydratedDocument; type TPostUpdate = THydratedDocument; type TPostDelete = DeleteResult; type TPostUnion = | TPostValidate | TPostCreate | TPostUpdate | TPostDelete; type TCustomOperations = IHookEntityOperationMap[E]['operations'][keyof IHookEntityOperationMap[E]['operations']]; /* union hooks */ type TUnion = E extends keyof IHookEntityOperationMap ? E extends keyof IHookOperationMap ? TCustomOperations : TPreUnion | TPostUnion | TCustomOperations : never; /* Normalized hook */ enum EHookPrefix { pre = 'pre', post = 'post', } type TCompatibleHook< P extends `${EHookPrefix}`, T = `${EHook}`, > = T extends `${P}${infer I}` ? `${P}${I}` : never; type TPreHook = TCompatibleHook; type TPostHook = TCompatibleHook; type TNormalizedEvents = '*' | TPreHook | TPostHook; type TNormalizedHooks< E extends keyof IHookEntityOperationMap, T = IHookEntityOperationMap[E]['schema'], > = | { [EHook.preValidate]: TPreValidate; } | { [EHook.preCreate]: TPreCreate; } | { [EHook.preUpdate]: TPreUpdate; } | { [EHook.preDelete]: TPreDelete; } | { [EHook.postValidate]: TPostValidate; } | { [EHook.postCreate]: TPostCreate; } | { [EHook.postUpdate]: TPostUpdate; } | { [EHook.postDelete]: TPostDelete; }; type TNormalizedHook = Extract< TNormalizedHooks, { [key in O]: unknown } >[O]; /* Extended hook */ type TExtendedHook< E extends keyof IHookEntityOperationMap, O extends keyof IHookEntityOperationMap[E]['operations'], > = IHookEntityOperationMap[E]['operations'][O]; type EventValueOf = G extends `hook:${infer E}:${infer O}` ? O extends '*' ? TUnion : E extends keyof IHookEntityOperationMap ? O extends keyof IHookEntityOperationMap[E]['operations'] ? TExtendedHook : TNormalizedHook : never : never; type IsHookEvent = G extends EventNamespaces ? true : G extends `hook:${infer N}:${string}` ? N extends keyof IHookEntityOperationMap ? true : false : false; type TCustomEvents = keyof IHookEntityOperationMap[G]['operations'] & string; type customEvent = G extends EventNamespaces ? G extends `hook:${string}` ? G : `hook:${G}:${TNormalizedEvents | TCustomEvents}` : never; interface ListenerFn { (value: EventValueOf, ...values: any[]): void; } class EventEmitter2 { emit( customEvent: customEvent, value: EventValueOf, ...values: any[] ): boolean; emitAsync( customEvent: customEvent, value: EventValueOf, ...values: any[] ): Promise; addListener( customEvent: customEvent, listener: ListenerFn, ): this | Listener; on( customEvent: customEvent, listener: ListenerFn, options?: boolean | OnOptions, ): this | Listener; once( customEvent: customEvent, listener: ListenerFn, options?: true | OnOptions, ): this | Listener; prependOnceListener< G extends EventNamespaces | ConstrainedString, H extends G, >( customEvent: customEvent, listener: ListenerFn, options?: boolean | OnOptions, ): this | Listener; many( customEvent: customEvent, timesToListen: number, listener: ListenerFn, options?: boolean | OnOptions, ): this | Listener; prependMany( customEvent: customEvent, timesToListen: number, listener: ListenerFn, options?: boolean | OnOptions, ): this | Listener; removeListener( customEvent: customEvent, listener: ListenerFn, ): this; off( customEvent: customEvent, listener: ListenerFn, ): this; } declare type OnEventMethodDecorator< G extends EventNamespaces | ConstrainedString, > = ( target: IsHookEvent extends true ? [T[K]] extends [(params: EventValueOf, ...rest: any[]) => any] ? T : never : T, propertyKey: K, ) => void; declare function OnEvent< G extends EventNamespaces | ConstrainedString, H extends G, >( event: customEvent, options?: OnEventOptions | undefined, ): OnEventMethodDecorator; }