Files
parcer/mx-interpreter/preprocessor.ts
2024-07-31 05:37:09 +05:30

111 lines
3.2 KiB
TypeScript

import Joi from 'joi';
import {
Workflow, WorkflowFile, ParamType, SelectorArray, Where,
} from './types/workflow';
import { operators } from './types/logic';
/**
* Class for static processing the workflow files/objects.
*/
export default class Preprocessor {
static validateWorkflow(workflow: WorkflowFile) : any {
const regex = Joi.object({
$regex: Joi.string().required(),
});
const whereSchema = Joi.object({
url: [Joi.string().uri(), regex],
selectors: Joi.array().items(Joi.string()),
cookies: Joi.object({}).pattern(Joi.string(), Joi.string()),
$after: [Joi.string(), regex],
$before: [Joi.string(), regex],
$and: Joi.array().items(Joi.link('#whereSchema')),
$or: Joi.array().items(Joi.link('#whereSchema')),
$not: Joi.link('#whereSchema'),
}).id('whereSchema');
const schema = Joi.object({
meta: Joi.object({
name: Joi.string(),
desc: Joi.string(),
}),
workflow: Joi.array().items(
Joi.object({
id: Joi.string(),
where: whereSchema.required(),
what: Joi.array().items({
action: Joi.string().required(),
args: Joi.array().items(Joi.any()),
}).required(),
}),
).required(),
});
const { error } = schema.validate(workflow);
return error;
}
/**
* Extracts parameter names from the workflow.
* @param {WorkflowFile} workflow The given workflow
* @returns {String[]} List of parameters' names.
*/
static getParams(workflow: WorkflowFile) : string[] {
const getParamsRecurse = (object : any) : string[] => {
if (typeof object === 'object') {
// Recursion base case
if (object.$param) {
return [object.$param];
}
// Recursion general case
return Object.values(object)
.reduce((p: string[], v : any) : string[] => [...p, ...getParamsRecurse(v)], []);
}
return [];
};
return getParamsRecurse(workflow.workflow);
}
/**
* List all the selectors used in the given workflow (only literal "selector"
* field in WHERE clauses so far)
*/
// TODO : add recursive selector search (also in click/fill etc. events?)
static extractSelectors(workflow: Workflow) : SelectorArray {
/**
* Given a Where condition, this function extracts
* all the existing selectors from it (recursively).
*/
const selectorsFromCondition = (where: Where) : SelectorArray => {
// the `selectors` field is either on the top level
let out = where.selectors ?? [];
if (!Array.isArray(out)) {
out = [out];
}
// or nested in the "operator" array
operators.forEach((op) => {
let condWhere = where[op];
if (condWhere) {
condWhere = Array.isArray(condWhere) ? condWhere : [condWhere];
(condWhere).forEach((subWhere) => {
out = [...out, ...selectorsFromCondition(subWhere)];
});
}
});
return out;
};
// Iterate through all the steps and extract the selectors from all of them.
return workflow.reduce((p: SelectorArray, step) => [
...p,
...selectorsFromCondition(step.where).filter((x) => !p.includes(x)),
], []);
}
}