File

libs/acl/src/lib/acl-engine/condition.ts

Index

Properties

Properties

I18N_STRING
Type string
message
Type string
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
type ConditionFunction = (obj: any) => any;

/**
 * Filtrex calls `s()` on every evaluation instead of baking in its value, so a
 * compiled expression only ever goes stale if the store query itself changes.
 * Using the store query as the cache key also lets entries be garbage collected with it.
 */
const conditionFunctionCaches = new WeakMap<
  (key: string) => unknown,
  Map<string, ConditionFunction>
>();

/**
 * 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 conditionFunctionCache = conditionFunctionCaches.get(storeQuery);
  if (!conditionFunctionCache) {
    conditionFunctionCache = new Map<string, ConditionFunction>();
    conditionFunctionCaches.set(storeQuery, conditionFunctionCache);
  }

  let conditionFunction = conditionFunctionCache.get(condition);
  if (!conditionFunction) {
    conditionFunction = compileExpression(condition, {
      extraFunctions: {
        s: storeQuery
      }
    });
    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;
}

results matching ""

    No results matching ""