UNPKG

@vk-io/hear

Version:

Hear for the library vk-io

120 lines (116 loc) 4.3 kB
import { Composer } from 'vk-io'; import { skipMiddleware } from 'middleware-io'; const splitPath = (path) => (path .replace(/\[([^[\]]*)\]/g, '.$1.') .split('.') .filter(Boolean)); // eslint-disable-next-line @typescript-eslint/no-explicit-any const getObjectValue = (source, selectors) => { let link = source; for (const selector of selectors) { if (!link[selector]) { return undefined; } link = link[selector]; } return link; }; const unifyCondition = (condition) => { if (typeof condition === 'function') { return condition; } if (condition instanceof RegExp) { return (text) => (condition.test(text)); } if (Array.isArray(condition)) { const arrayConditions = condition.map(unifyCondition); return (value) => (Array.isArray(value) ? arrayConditions.every((cond) => (value.some((val) => cond(val)))) : arrayConditions.some((cond) => (cond(value)))); } return (value) => value === condition; }; class HearManager { constructor() { this.composer = Composer.builder(); this.fallbackHandler = skipMiddleware; this.recompose(); } get length() { return this.composer.length; } // eslint-disable-next-line @typescript-eslint/no-explicit-any get middleware() { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (context, next) => (this.composed(context, next)); } hear(hearConditions, handler) { const rawConditions = !Array.isArray(hearConditions) ? [hearConditions] : hearConditions; const hasConditions = rawConditions.every(Boolean); if (!hasConditions) { throw new Error('Condition should be not empty'); } if (typeof handler !== 'function') { throw new TypeError('Handler must be a function'); } let textCondition = false; let functionCondtion = false; const conditions = rawConditions.map((condition) => { if (typeof condition === 'object' && !(condition instanceof RegExp)) { functionCondtion = true; const entries = Object.entries(condition).map(([path, value]) => ([splitPath(path), unifyCondition(value)])); return (text, context) => (entries.every(([selectors, callback]) => { const value = getObjectValue(context, selectors); return callback(value, context); })); } if (typeof condition === 'function') { functionCondtion = true; return condition; } textCondition = true; if (condition instanceof RegExp) { return (text, context) => { const passed = condition.test(text); if (passed) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error context.$match = text.match(condition); } return passed; }; } const stringCondition = String(condition); return (text) => text === stringCondition; }); const needText = textCondition && functionCondtion === false; this.composer.use((context, next) => { const { text } = context; if (needText && text === undefined) { return next(); } const hasSome = conditions.some((condition) => (condition(text, context))); return hasSome ? handler(context, next) : next(); }); this.recompose(); return this; } /** * A handler that is called when handlers are not found */ onFallback(handler) { this.fallbackHandler = handler; this.recompose(); return this; } recompose() { this.composed = this.composer.clone() .use(this.fallbackHandler) .compose(); } } export { HearManager };