@vk-io/hear
Version:
Hear for the library vk-io
120 lines (116 loc) • 4.3 kB
JavaScript
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 };