first commit
This commit is contained in:
92
docs/testkit/testing/ab-testing/src/targeting/targeting.md
Normal file
92
docs/testkit/testing/ab-testing/src/targeting/targeting.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# targeting.ts — Targeting Rule Evaluation
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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<TargetingRule>` — ordered list of predicates
|
||||
- **`attributes`** — `UserAttributes` (`Record<string, string | number | boolean>`)
|
||||
- **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`.
|
||||
Reference in New Issue
Block a user