import { compileExpression } from 'filtrex';
interface FiltrexError {
I18N_STRING: string;
message: string;
}
// filtrex returns a type of `(obj: any) => any`. eslint is not happy
// about the `any` types. But we want to use that here so we ignore the warning
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const conditionFunctionCache = new Map<string, (obj: any) => any>();
/**
* Filtrex ONLY uses double quotes (") for strings, never single quotes (').
* However, inside bracket expressions (e.g. s("$['my-key']")) and inside
* double-quoted strings (e.g. "Radio's option"), single quotes are valid.
* Single quotes outside these contexts cause silent failures because filtrex treats them as property references, returning undefined.
*/
const SINGLE_QUOTE_OUTSIDE_BRACKETS = /^("[^"]*"|\[.*'.*]|(?!').)*$/;
export function isConditionMet(
condition: string,
environment: Record<string, unknown>,
storeQuery: (key: string) => unknown
): boolean {
if (condition.trim().length === 0) {
return true;
}
if (!SINGLE_QUOTE_OUTSIDE_BRACKETS.test(condition)) {
console.warn(
`ACL: Condition "${condition}" contains single quotes (') outside of bracket expressions ([...]). ` +
`Filtrex only uses double quotes (") for strings. ` +
`Single quotes cause silent failures because filtrex treats them as property references, returning undefined.`
);
}
let conditionFunction = conditionFunctionCache.get(condition);
if (!conditionFunction) {
conditionFunction = compileExpression(condition, {
extraFunctions: {
s: storeQuery
}
});
// filtrex bakes the store value into the compiled expression,
// so we can't cache conditions that use the store.
const hasStoreQuery = condition.includes('s(');
if (!hasStoreQuery) {
conditionFunctionCache.set(condition, conditionFunction);
}
}
const result: boolean | FiltrexError | undefined = conditionFunction(environment);
if (result && isFiltrexError(result)) {
console.warn(`ACL: Error evaluating the condition "${condition}": ${result.message}`);
}
return Boolean(result);
}
function isFiltrexError(result: boolean | FiltrexError): result is FiltrexError {
return (result as FiltrexError).I18N_STRING !== undefined;
}