UNPKG

trpc-shield

Version:

tRPC permissions as another layer of abstraction!

89 lines (80 loc) 2.54 kB
import { ILogicRule, IRule, IRules, ShieldRule } from './types'; import { flattenObjectOf, isLogicRule, isRuleFunction } from './utils'; /** * * @param ruleTree * * Validates the rule tree declaration by checking references of rule * functions. We deem rule tree valid if no two rules with the same name point * to different rules. * */ export function validateRuleTree<TContext extends Record<string, any>>( ruleTree: IRules<TContext>, ): { status: 'ok' } | { status: 'err'; message: string } { const rules = extractRules(ruleTree); const valid = rules.reduce<{ map: Map<string, IRule<TContext>>; duplicates: string[] }>( ({ map, duplicates }, rule) => { if (!map.has(rule.name)) { return { map: map.set(rule.name, rule), duplicates }; } else if (!map.get(rule.name)!.equals(rule) && !duplicates.includes(rule.name)) { return { map: map.set(rule.name, rule), duplicates: [...duplicates, rule.name], }; } else { return { map, duplicates }; } }, { map: new Map<string, IRule<TContext>>(), duplicates: [] }, ); if (valid.duplicates.length === 0) { return { status: 'ok' }; } else { const duplicates = valid.duplicates.join(', '); return { status: 'err', message: `There seem to be multiple definitions of these rules: ${duplicates}`, }; } /* Helper functions */ /** * * @param ruleTree * * Extracts rules from rule tree. * */ function extractRules<TContext extends Record<string, any>>(ruleTree: IRules<TContext>): IRule<TContext>[] { const resolvers = flattenObjectOf<ShieldRule<TContext>>(ruleTree, isRuleFunction); const rules = resolvers.reduce<IRule<TContext>[]>((rules, rule) => { if (isLogicRule(rule)) { return [...rules, ...extractLogicRules(rule)]; } else { return [...rules, rule as any]; } }, []); return rules; } /** * * Recursively extracts Rules from LogicRule * * @param rule */ function extractLogicRules<TContext extends Record<string, any>>(rule: ILogicRule<TContext>): IRule<TContext>[] { return rule.getRules().reduce<IRule<TContext>[]>((acc, shieldRule) => { if (isLogicRule(shieldRule)) { return [...acc, ...extractLogicRules(shieldRule)]; } else { return [...acc, shieldRule as any]; } }, []); } } export class ValidationError extends Error { constructor(message: string) { super(message); this.name = 'ValidationError'; } }