# targeting.ts — Targeting Rule Evaluation _(no submodules)_ ## Purpose Provides the `evaluateTargeting` function that evaluates an ordered list of targeting rules against a set of user attributes. The function implements AND semantics — every rule in the list must pass for the user to be considered eligible for the experiment. This single-function module is intentionally small and pure: it has no side effects, no I/O, no external dependencies, and is trivially unit-testable. Targeting rules are the primary mechanism for gating experiment exposure to specific user segments. Common use cases include restricting to paid users, specific geographic regions, device types, or beta-program membership. ## Features - Eight comparison operators: `eq`, `neq`, `in`, `notIn`, `gt`, `lt`, `gte`, `lte` - Numeric ordering operators (`gt`, `lt`, `gte`, `lte`) require both sides to be numbers; any type mismatch returns `false` rather than throwing - `in` / `notIn` accept any array on the right-hand side - An empty rules array always returns `true` (no restriction) - Absent attributes evaluate to `undefined` and fail equality/ordering tests gracefully without exceptions ## Usage: Basic Example ```ts import { evaluateTargeting } from '@suaveplan/ab-testing/targeting'; const eligible = evaluateTargeting( [{ attribute: 'plan', operator: 'eq', value: 'premium' }], { plan: 'premium', country: 'US' } ); console.log(eligible); // true ``` ## Usage: Advanced Example ```ts import { evaluateTargeting } from '@suaveplan/ab-testing/targeting'; // Multi-rule targeting: premium users in North America with high session count const rules = [ { attribute: 'plan', operator: 'eq' as const, value: 'premium' }, { attribute: 'country', operator: 'in' as const, value: ['US', 'CA', 'MX'] }, { attribute: 'sessionCount', operator: 'gte' as const, value: 10 }, { attribute: 'blocklisted', operator: 'neq' as const, value: true }, ]; const user1 = { plan: 'premium', country: 'CA', sessionCount: 15, blocklisted: false }; const user2 = { plan: 'free', country: 'US', sessionCount: 30, blocklisted: false }; console.log(evaluateTargeting(rules, user1)); // true (all 4 rules pass) console.log(evaluateTargeting(rules, user2)); // false (plan !== 'premium') ``` ## API Reference ### `evaluateTargeting(rules, attributes): boolean` - **`rules`** — `ReadonlyArray` — ordered list of predicates - **`attributes`** — `UserAttributes` (`Record`) - **Returns** — `boolean` — `true` iff every rule is satisfied (or `rules` is empty) #### Supported operators | Operator | Description | RHS type | |---|---|---| | `eq` | Strict equality (`===`) | any | | `neq` | Strict inequality (`!==`) | any | | `in` | LHS is contained in RHS array | `unknown[]` | | `notIn` | LHS is not contained in RHS array | `unknown[]` | | `gt` | LHS > RHS | `number` | | `lt` | LHS < RHS | `number` | | `gte` | LHS >= RHS | `number` | | `lte` | LHS <= RHS | `number` | ## Implementation Notes All comparisons are intentionally strict (`===` / `!==`) to avoid surprising truthy/falsy coercions. When the RHS is not of the expected type for ordering operators (e.g. a string is passed as the threshold for `gt`), the function returns `false` rather than throwing, making it safe to evaluate partially invalid rule sets without crashing the assignment pipeline. The `in` / `notIn` operators use `Array.isArray` to check the RHS type before calling `Array.prototype.includes`. When the RHS is not an array, the rule evaluates to `false`.