@variantlab/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/targeting/semver.ts","../../src/targeting/operators/app-version.ts","../../src/targeting/operators/attributes.ts","../../src/targeting/operators/locale.ts","../../src/targeting/operators/platform.ts","../../src/targeting/glob.ts","../../src/targeting/operators/routes.ts","../../src/targeting/operators/screen-size.ts","../../src/targeting/operators/user-id.ts","../../src/targeting/evaluator.ts","../../src/targeting/explain.ts","../../src/targeting/hash.ts"],"names":["v"],"mappings":"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","file":"targeting.cjs","sourcesContent":["/**\n * Hand-rolled semver subset. No dependencies, no regex on the hot path,\n * no backtracking. Supports the surface documented in\n * `docs/design/targeting-dsl.md`:\n *\n * - Comparators: `=`, `<`, `<=`, `>`, `>=`, and a bare version (treated as `=`)\n * - Caret: `^X.Y.Z` → `[>=X.Y.Z, <(X+1).0.0]`\n * - Tilde: `~X.Y.Z` → `[>=X.Y.Z, <X.(Y+1).0]`\n * - Hyphen range: `X.Y.Z - A.B.C` → `[>=X.Y.Z, <=A.B.C]`\n * - Compound (AND): space-separated comparators, e.g. `>=1.0.0 <2.0.0`\n * - OR: `||`-separated clauses\n *\n * Explicitly rejects prereleases (`1.0.0-beta`), build metadata\n * (`1.0.0+sha`), and `x` wildcards (`1.2.x`).\n */\n\nexport type Version = readonly [number, number, number];\nexport type Op = \"=\" | \"<\" | \"<=\" | \">\" | \">=\";\nexport interface Comparator {\n readonly op: Op;\n readonly v: Version;\n}\nexport type Clause = readonly Comparator[];\nexport type Range = readonly Clause[];\n\n/**\n * Pure character scanner. No regex. Returns `[major, minor, patch]`\n * or `null` if the string is not exactly three dot-separated\n * non-negative integers.\n */\nexport function parseVersion(s: string): Version | null {\n if (s.length === 0) return null;\n const parts: number[] = [0, 0, 0];\n let idx = 0;\n let part = 0;\n let seen = false;\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n if (c === 46 /* . */) {\n if (!seen) return null;\n parts[idx++] = part;\n if (idx > 2) return null;\n part = 0;\n seen = false;\n } else if (c >= 48 && c <= 57) {\n part = part * 10 + (c - 48);\n seen = true;\n } else return null;\n }\n if (!seen || idx !== 2) return null;\n parts[2] = part;\n return [parts[0] as number, parts[1] as number, parts[2] as number];\n}\n\n/** Numeric comparison, field by field. */\nexport function cmpVersion(a: Version, b: Version): number {\n return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];\n}\n\n/**\n * Parse a semver range string. Returns a `Range` on success or `null`\n * on any syntactic or structural error.\n */\nexport function parseSemver(s: string): Range | null {\n if (s.length === 0) return null;\n const clauses: Clause[] = [];\n for (const part of s.split(\"||\")) {\n const c = parseClause(part.trim());\n if (c === null) return null;\n clauses.push(c);\n }\n return clauses;\n}\n\nfunction parseClause(s: string): Clause | null {\n if (s.length === 0) return null;\n\n // Hyphen range: `X.Y.Z - A.B.C`. Whitespace-sensitive separator.\n const hy = s.indexOf(\" - \");\n if (hy >= 0) {\n const lo = parseVersion(s.slice(0, hy).trim());\n const hi = parseVersion(s.slice(hy + 3).trim());\n if (lo === null || hi === null) return null;\n return [\n { op: \">=\", v: lo },\n { op: \"<=\", v: hi },\n ];\n }\n\n // Compound: whitespace-separated comparators, all ANDed.\n const cmps: Comparator[] = [];\n for (const tok of s.split(/\\s+/)) {\n if (tok.length === 0) continue;\n const parsed = parseComparator(tok);\n if (parsed === null) return null;\n for (const c of parsed) cmps.push(c);\n }\n return cmps.length > 0 ? cmps : null;\n}\n\n/**\n * Parse a single comparator like `>=1.2.3`, `^1.2.3`, `~1.2.3`, or a\n * bare version. Returns an array so that caret/tilde can expand to\n * two comparators.\n */\nfunction parseComparator(s: string): Comparator[] | null {\n const c0 = s[0];\n if (c0 === \"^\" || c0 === \"~\") {\n const v = parseVersion(s.slice(1));\n if (v === null) return null;\n const upper: Version = c0 === \"^\" ? [v[0] + 1, 0, 0] : [v[0], v[1] + 1, 0];\n return [\n { op: \">=\", v },\n { op: \"<\", v: upper },\n ];\n }\n\n let op: Op = \"=\";\n let rest = s;\n if (c0 === \">\" || c0 === \"<\") {\n if (s[1] === \"=\") {\n op = `${c0}=` as Op;\n rest = s.slice(2);\n } else {\n op = c0;\n rest = s.slice(1);\n }\n } else if (c0 === \"=\") {\n rest = s.slice(1);\n }\n\n const v = parseVersion(rest);\n return v === null ? null : [{ op, v }];\n}\n\n/** Match a parsed range against a parsed version. */\nexport function matchCompiled(range: Range, version: Version): boolean {\n for (const clause of range) if (matchClause(clause, version)) return true;\n return false;\n}\n\nfunction matchClause(clause: Clause, version: Version): boolean {\n for (const c of clause) {\n const d = cmpVersion(version, c.v);\n const op = c.op;\n const fail =\n op === \"=\"\n ? d !== 0\n : op === \"<\"\n ? d >= 0\n : op === \"<=\"\n ? d > 0\n : op === \">\"\n ? d <= 0\n : d < 0;\n if (fail) return false;\n }\n return true;\n}\n\n/** Convenience: parse both sides and compare. Returns false on parse error. */\nexport function matchSemver(range: string, version: string): boolean {\n const r = parseSemver(range);\n if (r === null) return false;\n const v = parseVersion(version);\n return v !== null && matchCompiled(r, v);\n}\n","/**\n * App version operator. Wraps the semver matcher. Fail-closed if the\n * context version is missing or unparseable.\n */\n\nimport { matchSemver } from \"../semver.js\";\n\nexport function matchAppVersion(range: string, ctxVersion: string | undefined): boolean {\n if (ctxVersion === undefined) return false;\n return matchSemver(range, ctxVersion);\n}\n","/**\n * Attributes operator. For each specified key, strict-equality\n * against `context.attributes[key]`. An empty targeting object\n * trivially matches (no constraints).\n */\n\nexport function matchAttributes(\n target: { readonly [key: string]: unknown },\n ctxAttrs: { readonly [key: string]: unknown } | undefined,\n): boolean {\n for (const k of Object.keys(target)) {\n if (ctxAttrs === undefined || ctxAttrs[k] !== target[k]) return false;\n }\n return true;\n}\n","/**\n * Locale operator. For each target in the array, matches if the\n * context locale equals the target OR starts with `target + \"-\"`.\n *\n * Targeting `\"en\"` matches `\"en\"`, `\"en-US\"`, `\"en-GB\"`.\n * Targeting `\"en-US\"` matches `\"en-US\"`, `\"en-US-POSIX\"` but not\n * `\"en\"` or `\"en-GB\"`.\n *\n * Case-sensitive — config authors are expected to normalize.\n */\n\nexport function matchLocale(target: ReadonlyArray<string>, ctxLocale: string | undefined): boolean {\n if (ctxLocale === undefined) return false;\n for (const t of target) {\n if (ctxLocale === t || ctxLocale.startsWith(`${t}-`)) return true;\n }\n return false;\n}\n","/**\n * Platform operator. Set membership; fails if the context's platform\n * is undefined. Linear scan is cheap — at most 4 elements per the\n * config allow-list.\n */\n\nexport function matchPlatform(\n target: ReadonlyArray<string>,\n ctxPlatform: string | undefined,\n): boolean {\n return ctxPlatform !== undefined && target.includes(ctxPlatform);\n}\n","/**\n * Hand-rolled route glob subset. Linear-time matcher, no regex, no\n * backtracking, no character classes. Supports the surface in\n * `docs/design/targeting-dsl.md`:\n *\n * - Exact: `/about`\n * - Wildcard segment: `/blog/*`\n * - Wildcard deep: `/docs/**` (only allowed as the last segment)\n * - Parameter: `/user/:id` (treated as a single-segment wildcard)\n * - Trailing slash insensitive\n * - Whole-path forms: `*` (single-segment) and `**` (any number of segments)\n *\n * Rejects: character classes, braces, negation, `***`, mixed\n * literal+wildcard in a single segment (e.g. `/foo*bar`).\n */\n\nexport type Segment =\n | { readonly kind: \"literal\"; readonly value: string }\n | { readonly kind: \"param\" }\n | { readonly kind: \"rest\" };\n\n/**\n * Compile a glob pattern into a segment list. Returns `null` for\n * unsupported / malformed patterns. Compiled patterns can be reused\n * and are the hot-path form.\n */\nexport function compileGlob(pattern: string): Segment[] | null {\n if (pattern.length === 0) return null;\n\n // Whole-path shortcuts. `*` and `**` have no leading slash.\n if (pattern === \"*\") return [{ kind: \"param\" }];\n if (pattern === \"**\") return [{ kind: \"rest\" }];\n\n if (pattern[0] !== \"/\") return null;\n\n // Reject `***` anywhere outright.\n if (pattern.indexOf(\"***\") >= 0) return null;\n\n // Strip a trailing slash (except for the root `/` itself).\n const normalized = pattern.length > 1 && pattern.endsWith(\"/\") ? pattern.slice(0, -1) : pattern;\n\n // Root: single empty segment list means \"match `/` exactly\".\n if (normalized === \"/\") return [];\n\n const raw = normalized.slice(1).split(\"/\");\n const segs: Segment[] = [];\n for (let i = 0; i < raw.length; i++) {\n const part = raw[i] as string;\n if (part.length === 0) return null; // e.g. `//` or trailing empty\n if (part === \"**\") {\n // `**` must be the last segment.\n if (i !== raw.length - 1) return null;\n segs.push({ kind: \"rest\" });\n continue;\n }\n if (part === \"*\") {\n segs.push({ kind: \"param\" });\n continue;\n }\n if (part[0] === \":\") {\n // `:id` — single-segment wildcard. Disallow empty name `:` alone.\n if (part.length < 2) return null;\n segs.push({ kind: \"param\" });\n continue;\n }\n // Literal segment. Reject any wildcard chars or colons inside.\n // Compile-time regex (not hot path); linear, no backtracking.\n if (/[*:?[\\]{}!]/.test(part)) return null;\n segs.push({ kind: \"literal\", value: part });\n }\n return segs;\n}\n\n/**\n * Match a compiled glob against a path. O(n + m) linear, no\n * backtracking because `**` is only ever the final segment.\n */\nexport function matchCompiledRoute(segs: readonly Segment[], path: string): boolean {\n if (path.length === 0) return false;\n if (path[0] !== \"/\") return false;\n\n // Strip a trailing slash (except for the root `/`).\n const normalized = path.length > 1 && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n\n // Root `/` case: empty segment list matches exactly `/`.\n if (segs.length === 0) return normalized === \"/\";\n\n const parts = normalized === \"/\" ? [] : normalized.slice(1).split(\"/\");\n\n for (let i = 0; i < segs.length; i++) {\n const seg = segs[i] as Segment;\n if (seg.kind === \"rest\") {\n // `**` tail — matches zero or more remaining parts.\n return true;\n }\n if (i >= parts.length) return false;\n const part = parts[i] as string;\n if (part.length === 0) return false;\n if (seg.kind === \"literal\") {\n if (seg.value !== part) return false;\n }\n // `param` always matches a non-empty part.\n }\n return parts.length === segs.length;\n}\n\n/** Convenience: compile and match. Returns false on compile error. */\nexport function matchRoute(pattern: string, path: string): boolean {\n const segs = compileGlob(pattern);\n if (segs === null) return false;\n return matchCompiledRoute(segs, path);\n}\n","/**\n * Routes operator. Iterates patterns and returns true on the first\n * match. Each pattern is parsed per call in this session; Session 4's\n * engine will pre-compile at config load.\n */\n\nimport { matchRoute } from \"../glob.js\";\n\nexport function matchRoutes(target: ReadonlyArray<string>, ctxRoute: string | undefined): boolean {\n if (ctxRoute === undefined) return false;\n for (let i = 0; i < target.length; i++) {\n if (matchRoute(target[i] as string, ctxRoute)) return true;\n }\n return false;\n}\n","/**\n * Screen size operator. Set membership on pre-bucketed sizes. The\n * adapter (or the engine's context updater) is responsible for\n * deriving the bucket from actual pixel dimensions.\n */\n\nexport function matchScreenSize(\n target: ReadonlyArray<string>,\n ctxSize: string | undefined,\n): boolean {\n return ctxSize !== undefined && target.includes(ctxSize);\n}\n","/**\n * User ID operator. Two modes:\n *\n * 1. List: match if `context.userId` is in the explicit string list.\n * 2. Hash bucket: match if the precomputed `userIdBucket` (0..99)\n * is strictly less than `mod`. The evaluator is synchronous and\n * cannot compute sha256 inline; the engine precomputes this on\n * every `updateContext` call and stashes the result on the eval\n * context. If the bucket is missing, the operator fails closed.\n */\n\nimport type { EvalContext, Targeting } from \"../types.js\";\n\ntype Target = NonNullable<Targeting[\"userId\"]>;\n\nexport function matchUserId(target: Target, context: EvalContext): boolean {\n // `\"mod\" in target` narrows the discriminated union reliably — unlike\n // `Array.isArray()`, which does not narrow `ReadonlyArray<T>`.\n if (\"mod\" in target) {\n const bucket = context.userIdBucket;\n return typeof bucket === \"number\" && bucket < target.mod;\n }\n return context.userId !== undefined && target.includes(context.userId);\n}\n","/**\n * Synchronous targeting evaluator. Walks the operators in the order\n * documented in `docs/design/targeting-dsl.md` and short-circuits on\n * the first failure.\n *\n * Order: platform → screenSize → locale → appVersion → routes →\n * attributes → userId → predicate\n *\n * The `enabled` kill switch and `startDate`/`endDate` are experiment-\n * level and live in `explain()` + Session 4's engine — they're not\n * part of `evaluate()`.\n */\n\nimport { matchAppVersion } from \"./operators/app-version.js\";\nimport { matchAttributes } from \"./operators/attributes.js\";\nimport { matchLocale } from \"./operators/locale.js\";\nimport { matchPlatform } from \"./operators/platform.js\";\nimport { matchRoutes } from \"./operators/routes.js\";\nimport { matchScreenSize } from \"./operators/screen-size.js\";\nimport { matchUserId } from \"./operators/user-id.js\";\nimport type { EvalContext, EvaluableTargeting, TargetingResult, VariantContext } from \"./types.js\";\n\nexport function evaluate(\n targeting: EvaluableTargeting,\n context: VariantContext | EvalContext,\n): TargetingResult {\n const c = context as EvalContext;\n if (targeting.platform !== undefined && !matchPlatform(targeting.platform, c.platform))\n return { matched: false, reason: \"platform\" };\n if (targeting.screenSize !== undefined && !matchScreenSize(targeting.screenSize, c.screenSize))\n return { matched: false, reason: \"screenSize\" };\n if (targeting.locale !== undefined && !matchLocale(targeting.locale, c.locale))\n return { matched: false, reason: \"locale\" };\n if (targeting.appVersion !== undefined && !matchAppVersion(targeting.appVersion, c.appVersion))\n return { matched: false, reason: \"appVersion\" };\n if (targeting.routes !== undefined && !matchRoutes(targeting.routes, c.route))\n return { matched: false, reason: \"routes\" };\n if (targeting.attributes !== undefined && !matchAttributes(targeting.attributes, c.attributes))\n return { matched: false, reason: \"attributes\" };\n if (targeting.userId !== undefined && !matchUserId(targeting.userId, c))\n return { matched: false, reason: \"userId\" };\n if (targeting.predicate !== undefined && !targeting.predicate(c))\n return { matched: false, reason: \"predicate\" };\n return { matched: true };\n}\n\n/** Thin convenience wrapper — matches the `API.md` surface. */\nexport function matchTargeting(\n targeting: EvaluableTargeting,\n context: VariantContext | EvalContext,\n): boolean {\n return evaluate(targeting, context).matched;\n}\n","/**\n * Trace-producing evaluator. Walks startDate → endDate → targeting\n * (in the evaluator order), records every check performed, and\n * short-circuits at the first failure. The returned `steps` array\n * includes every check actually performed, ending with the failing\n * one when there is one.\n */\n\nimport { matchAppVersion } from \"./operators/app-version.js\";\nimport { matchAttributes } from \"./operators/attributes.js\";\nimport { matchLocale } from \"./operators/locale.js\";\nimport { matchPlatform } from \"./operators/platform.js\";\nimport { matchRoutes } from \"./operators/routes.js\";\nimport { matchScreenSize } from \"./operators/screen-size.js\";\nimport { matchUserId } from \"./operators/user-id.js\";\nimport type {\n EvalContext,\n EvaluableTargeting,\n Experiment,\n ExplainField,\n ExplainResult,\n ExplainStep,\n VariantContext,\n} from \"./types.js\";\n\nexport function explain(\n experiment: Experiment,\n context: VariantContext | EvalContext,\n): ExplainResult {\n const steps: ExplainStep[] = [];\n const record = (field: ExplainField, ok: boolean, detail: string): ExplainResult | null => {\n steps.push(ok ? { field, matched: true } : { field, matched: false, detail });\n return ok ? null : { matched: false, reason: field, steps };\n };\n\n if (experiment.startDate !== undefined) {\n const t = Date.parse(experiment.startDate);\n const r = record(\n \"startDate\",\n Number.isFinite(t) && Date.now() >= t,\n `now<${experiment.startDate}`,\n );\n if (r) return r;\n }\n if (experiment.endDate !== undefined) {\n const t = Date.parse(experiment.endDate);\n const r = record(\"endDate\", Number.isFinite(t) && Date.now() < t, `now>=${experiment.endDate}`);\n if (r) return r;\n }\n\n const tg = experiment.targeting as EvaluableTargeting | undefined;\n if (tg === undefined) return { matched: true, steps };\n const ctx = context as EvalContext;\n\n if (tg.platform !== undefined) {\n const r = record(\n \"platform\",\n matchPlatform(tg.platform, ctx.platform),\n `${ctx.platform} vs ${tg.platform}`,\n );\n if (r) return r;\n }\n if (tg.screenSize !== undefined) {\n const r = record(\n \"screenSize\",\n matchScreenSize(tg.screenSize, ctx.screenSize),\n `${ctx.screenSize} vs ${tg.screenSize}`,\n );\n if (r) return r;\n }\n if (tg.locale !== undefined) {\n const r = record(\"locale\", matchLocale(tg.locale, ctx.locale), `${ctx.locale} vs ${tg.locale}`);\n if (r) return r;\n }\n if (tg.appVersion !== undefined) {\n const r = record(\n \"appVersion\",\n matchAppVersion(tg.appVersion, ctx.appVersion),\n `${ctx.appVersion} vs ${tg.appVersion}`,\n );\n if (r) return r;\n }\n if (tg.routes !== undefined) {\n const r = record(\"routes\", matchRoutes(tg.routes, ctx.route), `${ctx.route} vs ${tg.routes}`);\n if (r) return r;\n }\n if (tg.attributes !== undefined) {\n const r = record(\"attributes\", matchAttributes(tg.attributes, ctx.attributes), \"mismatch\");\n if (r) return r;\n }\n if (tg.userId !== undefined) {\n const r = record(\"userId\", matchUserId(tg.userId, ctx), \"mismatch\");\n if (r) return r;\n }\n if (tg.predicate !== undefined) {\n const r = record(\"predicate\", tg.predicate(ctx), \"false\");\n if (r) return r;\n }\n return { matched: true, steps };\n}\n","/**\n * Web Crypto SHA-256 bucket hash for the `userId` hash-mod operator.\n * Returns an integer in [0, 99].\n *\n * The engine (Session 4) precomputes this on every `updateContext` and\n * stashes the result as `userIdBucket` in the eval context. The\n * operator reads it synchronously — `evaluate()` itself never awaits\n * anything.\n *\n * `globalThis.crypto.subtle` and `TextEncoder` are standardized in\n * Node 18.17+, browsers, Deno, Bun, and Edge runtimes. They are the\n * only platform globals `@variantlab/core` uses.\n */\n\ninterface CryptoLike {\n readonly subtle: {\n digest(algorithm: string, data: Uint8Array): Promise<ArrayBuffer>;\n };\n}\n\ndeclare class TextEncoder {\n encode(input?: string): Uint8Array;\n}\n\nconst encoder = new TextEncoder();\n\nexport async function hashUserId(userId: string): Promise<number> {\n const bytes = encoder.encode(userId);\n const g = globalThis as unknown as { readonly crypto: CryptoLike };\n const buf = await g.crypto.subtle.digest(\"SHA-256\", bytes);\n const view = new DataView(buf);\n const uint32 = view.getUint32(0, false); // big-endian\n return uint32 % 100;\n}\n"]}
@@ -0,0 +1,97 @@
1
+ import { b as VariantContext, T as Targeting, E as Experiment } from '../types-BkXPpEyg.cjs';
2
+
3
+ /**
4
+ * Public types for the targeting evaluator. Evaluator-only — the
5
+ * config-level `Targeting` type (which is JSON-safe) lives in
6
+ * `../config/types.ts`. These types layer the code-only `predicate`
7
+ * escape hatch and the precomputed `userIdBucket` on top.
8
+ */
9
+
10
+ /**
11
+ * Extended runtime context. Identical to `VariantContext` plus an
12
+ * optional precomputed sha256 bucket (0..99) for the hash-mod userId
13
+ * operator. The engine (Session 4) fills this on `updateContext`; the
14
+ * evaluator itself stays synchronous and never touches Web Crypto.
15
+ */
16
+ interface EvalContext extends VariantContext {
17
+ readonly userIdBucket?: number;
18
+ }
19
+ /**
20
+ * Targeting augmented with the optional application-provided predicate
21
+ * escape hatch. Functions can't live in JSON, so this type only exists
22
+ * at the evaluation layer.
23
+ */
24
+ interface EvaluableTargeting extends Targeting {
25
+ readonly predicate?: (context: VariantContext) => boolean;
26
+ }
27
+ /** Result of a single `evaluate()` call. */
28
+ interface TargetingResult {
29
+ readonly matched: boolean;
30
+ /** The first failing field name; undefined on match. */
31
+ readonly reason?: string;
32
+ }
33
+ /** Which field an `ExplainStep` corresponds to. */
34
+ type ExplainField = "startDate" | "endDate" | "platform" | "screenSize" | "locale" | "appVersion" | "routes" | "attributes" | "userId" | "predicate";
35
+ /** One step in an `explain()` trace. */
36
+ interface ExplainStep {
37
+ readonly field: ExplainField;
38
+ readonly matched: boolean;
39
+ /** Human-readable summary, e.g. "got 'ios', required one of ['android']". */
40
+ readonly detail?: string;
41
+ }
42
+ /** Result of `explain()` — a full trace of every check performed. */
43
+ interface ExplainResult {
44
+ readonly matched: boolean;
45
+ readonly reason?: ExplainField;
46
+ readonly steps: readonly ExplainStep[];
47
+ }
48
+
49
+ /**
50
+ * Synchronous targeting evaluator. Walks the operators in the order
51
+ * documented in `docs/design/targeting-dsl.md` and short-circuits on
52
+ * the first failure.
53
+ *
54
+ * Order: platform → screenSize → locale → appVersion → routes →
55
+ * attributes → userId → predicate
56
+ *
57
+ * The `enabled` kill switch and `startDate`/`endDate` are experiment-
58
+ * level and live in `explain()` + Session 4's engine — they're not
59
+ * part of `evaluate()`.
60
+ */
61
+
62
+ declare function evaluate(targeting: EvaluableTargeting, context: VariantContext | EvalContext): TargetingResult;
63
+ /** Thin convenience wrapper — matches the `API.md` surface. */
64
+ declare function matchTargeting(targeting: EvaluableTargeting, context: VariantContext | EvalContext): boolean;
65
+
66
+ /**
67
+ * Trace-producing evaluator. Walks startDate → endDate → targeting
68
+ * (in the evaluator order), records every check performed, and
69
+ * short-circuits at the first failure. The returned `steps` array
70
+ * includes every check actually performed, ending with the failing
71
+ * one when there is one.
72
+ */
73
+
74
+ declare function explain(experiment: Experiment, context: VariantContext | EvalContext): ExplainResult;
75
+
76
+ /** Convenience: compile and match. Returns false on compile error. */
77
+ declare function matchRoute(pattern: string, path: string): boolean;
78
+
79
+ /**
80
+ * Web Crypto SHA-256 bucket hash for the `userId` hash-mod operator.
81
+ * Returns an integer in [0, 99].
82
+ *
83
+ * The engine (Session 4) precomputes this on every `updateContext` and
84
+ * stashes the result as `userIdBucket` in the eval context. The
85
+ * operator reads it synchronously — `evaluate()` itself never awaits
86
+ * anything.
87
+ *
88
+ * `globalThis.crypto.subtle` and `TextEncoder` are standardized in
89
+ * Node 18.17+, browsers, Deno, Bun, and Edge runtimes. They are the
90
+ * only platform globals `@variantlab/core` uses.
91
+ */
92
+ declare function hashUserId(userId: string): Promise<number>;
93
+
94
+ /** Convenience: parse both sides and compare. Returns false on parse error. */
95
+ declare function matchSemver(range: string, version: string): boolean;
96
+
97
+ export { type EvalContext, type EvaluableTargeting, type ExplainField, type ExplainResult, type ExplainStep, type TargetingResult, evaluate, explain, hashUserId, matchRoute, matchSemver, matchTargeting };
@@ -0,0 +1,97 @@
1
+ import { b as VariantContext, T as Targeting, E as Experiment } from '../types-BkXPpEyg.js';
2
+
3
+ /**
4
+ * Public types for the targeting evaluator. Evaluator-only — the
5
+ * config-level `Targeting` type (which is JSON-safe) lives in
6
+ * `../config/types.ts`. These types layer the code-only `predicate`
7
+ * escape hatch and the precomputed `userIdBucket` on top.
8
+ */
9
+
10
+ /**
11
+ * Extended runtime context. Identical to `VariantContext` plus an
12
+ * optional precomputed sha256 bucket (0..99) for the hash-mod userId
13
+ * operator. The engine (Session 4) fills this on `updateContext`; the
14
+ * evaluator itself stays synchronous and never touches Web Crypto.
15
+ */
16
+ interface EvalContext extends VariantContext {
17
+ readonly userIdBucket?: number;
18
+ }
19
+ /**
20
+ * Targeting augmented with the optional application-provided predicate
21
+ * escape hatch. Functions can't live in JSON, so this type only exists
22
+ * at the evaluation layer.
23
+ */
24
+ interface EvaluableTargeting extends Targeting {
25
+ readonly predicate?: (context: VariantContext) => boolean;
26
+ }
27
+ /** Result of a single `evaluate()` call. */
28
+ interface TargetingResult {
29
+ readonly matched: boolean;
30
+ /** The first failing field name; undefined on match. */
31
+ readonly reason?: string;
32
+ }
33
+ /** Which field an `ExplainStep` corresponds to. */
34
+ type ExplainField = "startDate" | "endDate" | "platform" | "screenSize" | "locale" | "appVersion" | "routes" | "attributes" | "userId" | "predicate";
35
+ /** One step in an `explain()` trace. */
36
+ interface ExplainStep {
37
+ readonly field: ExplainField;
38
+ readonly matched: boolean;
39
+ /** Human-readable summary, e.g. "got 'ios', required one of ['android']". */
40
+ readonly detail?: string;
41
+ }
42
+ /** Result of `explain()` — a full trace of every check performed. */
43
+ interface ExplainResult {
44
+ readonly matched: boolean;
45
+ readonly reason?: ExplainField;
46
+ readonly steps: readonly ExplainStep[];
47
+ }
48
+
49
+ /**
50
+ * Synchronous targeting evaluator. Walks the operators in the order
51
+ * documented in `docs/design/targeting-dsl.md` and short-circuits on
52
+ * the first failure.
53
+ *
54
+ * Order: platform → screenSize → locale → appVersion → routes →
55
+ * attributes → userId → predicate
56
+ *
57
+ * The `enabled` kill switch and `startDate`/`endDate` are experiment-
58
+ * level and live in `explain()` + Session 4's engine — they're not
59
+ * part of `evaluate()`.
60
+ */
61
+
62
+ declare function evaluate(targeting: EvaluableTargeting, context: VariantContext | EvalContext): TargetingResult;
63
+ /** Thin convenience wrapper — matches the `API.md` surface. */
64
+ declare function matchTargeting(targeting: EvaluableTargeting, context: VariantContext | EvalContext): boolean;
65
+
66
+ /**
67
+ * Trace-producing evaluator. Walks startDate → endDate → targeting
68
+ * (in the evaluator order), records every check performed, and
69
+ * short-circuits at the first failure. The returned `steps` array
70
+ * includes every check actually performed, ending with the failing
71
+ * one when there is one.
72
+ */
73
+
74
+ declare function explain(experiment: Experiment, context: VariantContext | EvalContext): ExplainResult;
75
+
76
+ /** Convenience: compile and match. Returns false on compile error. */
77
+ declare function matchRoute(pattern: string, path: string): boolean;
78
+
79
+ /**
80
+ * Web Crypto SHA-256 bucket hash for the `userId` hash-mod operator.
81
+ * Returns an integer in [0, 99].
82
+ *
83
+ * The engine (Session 4) precomputes this on every `updateContext` and
84
+ * stashes the result as `userIdBucket` in the eval context. The
85
+ * operator reads it synchronously — `evaluate()` itself never awaits
86
+ * anything.
87
+ *
88
+ * `globalThis.crypto.subtle` and `TextEncoder` are standardized in
89
+ * Node 18.17+, browsers, Deno, Bun, and Edge runtimes. They are the
90
+ * only platform globals `@variantlab/core` uses.
91
+ */
92
+ declare function hashUserId(userId: string): Promise<number>;
93
+
94
+ /** Convenience: parse both sides and compare. Returns false on parse error. */
95
+ declare function matchSemver(range: string, version: string): boolean;
96
+
97
+ export { type EvalContext, type EvaluableTargeting, type ExplainField, type ExplainResult, type ExplainStep, type TargetingResult, evaluate, explain, hashUserId, matchRoute, matchSemver, matchTargeting };
@@ -0,0 +1,325 @@
1
+ // src/targeting/semver.ts
2
+ function parseVersion(s) {
3
+ if (s.length === 0) return null;
4
+ const parts = [0, 0, 0];
5
+ let idx = 0;
6
+ let part = 0;
7
+ let seen = false;
8
+ for (let i = 0; i < s.length; i++) {
9
+ const c = s.charCodeAt(i);
10
+ if (c === 46) {
11
+ if (!seen) return null;
12
+ parts[idx++] = part;
13
+ if (idx > 2) return null;
14
+ part = 0;
15
+ seen = false;
16
+ } else if (c >= 48 && c <= 57) {
17
+ part = part * 10 + (c - 48);
18
+ seen = true;
19
+ } else return null;
20
+ }
21
+ if (!seen || idx !== 2) return null;
22
+ parts[2] = part;
23
+ return [parts[0], parts[1], parts[2]];
24
+ }
25
+ function cmpVersion(a, b) {
26
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
27
+ }
28
+ function parseSemver(s) {
29
+ if (s.length === 0) return null;
30
+ const clauses = [];
31
+ for (const part of s.split("||")) {
32
+ const c = parseClause(part.trim());
33
+ if (c === null) return null;
34
+ clauses.push(c);
35
+ }
36
+ return clauses;
37
+ }
38
+ function parseClause(s) {
39
+ if (s.length === 0) return null;
40
+ const hy = s.indexOf(" - ");
41
+ if (hy >= 0) {
42
+ const lo = parseVersion(s.slice(0, hy).trim());
43
+ const hi = parseVersion(s.slice(hy + 3).trim());
44
+ if (lo === null || hi === null) return null;
45
+ return [
46
+ { op: ">=", v: lo },
47
+ { op: "<=", v: hi }
48
+ ];
49
+ }
50
+ const cmps = [];
51
+ for (const tok of s.split(/\s+/)) {
52
+ if (tok.length === 0) continue;
53
+ const parsed = parseComparator(tok);
54
+ if (parsed === null) return null;
55
+ for (const c of parsed) cmps.push(c);
56
+ }
57
+ return cmps.length > 0 ? cmps : null;
58
+ }
59
+ function parseComparator(s) {
60
+ const c0 = s[0];
61
+ if (c0 === "^" || c0 === "~") {
62
+ const v2 = parseVersion(s.slice(1));
63
+ if (v2 === null) return null;
64
+ const upper = c0 === "^" ? [v2[0] + 1, 0, 0] : [v2[0], v2[1] + 1, 0];
65
+ return [
66
+ { op: ">=", v: v2 },
67
+ { op: "<", v: upper }
68
+ ];
69
+ }
70
+ let op = "=";
71
+ let rest = s;
72
+ if (c0 === ">" || c0 === "<") {
73
+ if (s[1] === "=") {
74
+ op = `${c0}=`;
75
+ rest = s.slice(2);
76
+ } else {
77
+ op = c0;
78
+ rest = s.slice(1);
79
+ }
80
+ } else if (c0 === "=") {
81
+ rest = s.slice(1);
82
+ }
83
+ const v = parseVersion(rest);
84
+ return v === null ? null : [{ op, v }];
85
+ }
86
+ function matchCompiled(range, version) {
87
+ for (const clause of range) if (matchClause(clause, version)) return true;
88
+ return false;
89
+ }
90
+ function matchClause(clause, version) {
91
+ for (const c of clause) {
92
+ const d = cmpVersion(version, c.v);
93
+ const op = c.op;
94
+ const fail = op === "=" ? d !== 0 : op === "<" ? d >= 0 : op === "<=" ? d > 0 : op === ">" ? d <= 0 : d < 0;
95
+ if (fail) return false;
96
+ }
97
+ return true;
98
+ }
99
+ function matchSemver(range, version) {
100
+ const r = parseSemver(range);
101
+ if (r === null) return false;
102
+ const v = parseVersion(version);
103
+ return v !== null && matchCompiled(r, v);
104
+ }
105
+
106
+ // src/targeting/operators/app-version.ts
107
+ function matchAppVersion(range, ctxVersion) {
108
+ if (ctxVersion === void 0) return false;
109
+ return matchSemver(range, ctxVersion);
110
+ }
111
+
112
+ // src/targeting/operators/attributes.ts
113
+ function matchAttributes(target, ctxAttrs) {
114
+ for (const k of Object.keys(target)) {
115
+ if (ctxAttrs === void 0 || ctxAttrs[k] !== target[k]) return false;
116
+ }
117
+ return true;
118
+ }
119
+
120
+ // src/targeting/operators/locale.ts
121
+ function matchLocale(target, ctxLocale) {
122
+ if (ctxLocale === void 0) return false;
123
+ for (const t of target) {
124
+ if (ctxLocale === t || ctxLocale.startsWith(`${t}-`)) return true;
125
+ }
126
+ return false;
127
+ }
128
+
129
+ // src/targeting/operators/platform.ts
130
+ function matchPlatform(target, ctxPlatform) {
131
+ return ctxPlatform !== void 0 && target.includes(ctxPlatform);
132
+ }
133
+
134
+ // src/targeting/glob.ts
135
+ function compileGlob(pattern) {
136
+ if (pattern.length === 0) return null;
137
+ if (pattern === "*") return [{ kind: "param" }];
138
+ if (pattern === "**") return [{ kind: "rest" }];
139
+ if (pattern[0] !== "/") return null;
140
+ if (pattern.indexOf("***") >= 0) return null;
141
+ const normalized = pattern.length > 1 && pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
142
+ if (normalized === "/") return [];
143
+ const raw = normalized.slice(1).split("/");
144
+ const segs = [];
145
+ for (let i = 0; i < raw.length; i++) {
146
+ const part = raw[i];
147
+ if (part.length === 0) return null;
148
+ if (part === "**") {
149
+ if (i !== raw.length - 1) return null;
150
+ segs.push({ kind: "rest" });
151
+ continue;
152
+ }
153
+ if (part === "*") {
154
+ segs.push({ kind: "param" });
155
+ continue;
156
+ }
157
+ if (part[0] === ":") {
158
+ if (part.length < 2) return null;
159
+ segs.push({ kind: "param" });
160
+ continue;
161
+ }
162
+ if (/[*:?[\]{}!]/.test(part)) return null;
163
+ segs.push({ kind: "literal", value: part });
164
+ }
165
+ return segs;
166
+ }
167
+ function matchCompiledRoute(segs, path) {
168
+ if (path.length === 0) return false;
169
+ if (path[0] !== "/") return false;
170
+ const normalized = path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
171
+ if (segs.length === 0) return normalized === "/";
172
+ const parts = normalized === "/" ? [] : normalized.slice(1).split("/");
173
+ for (let i = 0; i < segs.length; i++) {
174
+ const seg = segs[i];
175
+ if (seg.kind === "rest") {
176
+ return true;
177
+ }
178
+ if (i >= parts.length) return false;
179
+ const part = parts[i];
180
+ if (part.length === 0) return false;
181
+ if (seg.kind === "literal") {
182
+ if (seg.value !== part) return false;
183
+ }
184
+ }
185
+ return parts.length === segs.length;
186
+ }
187
+ function matchRoute(pattern, path) {
188
+ const segs = compileGlob(pattern);
189
+ if (segs === null) return false;
190
+ return matchCompiledRoute(segs, path);
191
+ }
192
+
193
+ // src/targeting/operators/routes.ts
194
+ function matchRoutes(target, ctxRoute) {
195
+ if (ctxRoute === void 0) return false;
196
+ for (let i = 0; i < target.length; i++) {
197
+ if (matchRoute(target[i], ctxRoute)) return true;
198
+ }
199
+ return false;
200
+ }
201
+
202
+ // src/targeting/operators/screen-size.ts
203
+ function matchScreenSize(target, ctxSize) {
204
+ return ctxSize !== void 0 && target.includes(ctxSize);
205
+ }
206
+
207
+ // src/targeting/operators/user-id.ts
208
+ function matchUserId(target, context) {
209
+ if ("mod" in target) {
210
+ const bucket = context.userIdBucket;
211
+ return typeof bucket === "number" && bucket < target.mod;
212
+ }
213
+ return context.userId !== void 0 && target.includes(context.userId);
214
+ }
215
+
216
+ // src/targeting/evaluator.ts
217
+ function evaluate(targeting, context) {
218
+ const c = context;
219
+ if (targeting.platform !== void 0 && !matchPlatform(targeting.platform, c.platform))
220
+ return { matched: false, reason: "platform" };
221
+ if (targeting.screenSize !== void 0 && !matchScreenSize(targeting.screenSize, c.screenSize))
222
+ return { matched: false, reason: "screenSize" };
223
+ if (targeting.locale !== void 0 && !matchLocale(targeting.locale, c.locale))
224
+ return { matched: false, reason: "locale" };
225
+ if (targeting.appVersion !== void 0 && !matchAppVersion(targeting.appVersion, c.appVersion))
226
+ return { matched: false, reason: "appVersion" };
227
+ if (targeting.routes !== void 0 && !matchRoutes(targeting.routes, c.route))
228
+ return { matched: false, reason: "routes" };
229
+ if (targeting.attributes !== void 0 && !matchAttributes(targeting.attributes, c.attributes))
230
+ return { matched: false, reason: "attributes" };
231
+ if (targeting.userId !== void 0 && !matchUserId(targeting.userId, c))
232
+ return { matched: false, reason: "userId" };
233
+ if (targeting.predicate !== void 0 && !targeting.predicate(c))
234
+ return { matched: false, reason: "predicate" };
235
+ return { matched: true };
236
+ }
237
+ function matchTargeting(targeting, context) {
238
+ return evaluate(targeting, context).matched;
239
+ }
240
+
241
+ // src/targeting/explain.ts
242
+ function explain(experiment, context) {
243
+ const steps = [];
244
+ const record = (field, ok, detail) => {
245
+ steps.push(ok ? { field, matched: true } : { field, matched: false, detail });
246
+ return ok ? null : { matched: false, reason: field, steps };
247
+ };
248
+ if (experiment.startDate !== void 0) {
249
+ const t = Date.parse(experiment.startDate);
250
+ const r = record(
251
+ "startDate",
252
+ Number.isFinite(t) && Date.now() >= t,
253
+ `now<${experiment.startDate}`
254
+ );
255
+ if (r) return r;
256
+ }
257
+ if (experiment.endDate !== void 0) {
258
+ const t = Date.parse(experiment.endDate);
259
+ const r = record("endDate", Number.isFinite(t) && Date.now() < t, `now>=${experiment.endDate}`);
260
+ if (r) return r;
261
+ }
262
+ const tg = experiment.targeting;
263
+ if (tg === void 0) return { matched: true, steps };
264
+ const ctx = context;
265
+ if (tg.platform !== void 0) {
266
+ const r = record(
267
+ "platform",
268
+ matchPlatform(tg.platform, ctx.platform),
269
+ `${ctx.platform} vs ${tg.platform}`
270
+ );
271
+ if (r) return r;
272
+ }
273
+ if (tg.screenSize !== void 0) {
274
+ const r = record(
275
+ "screenSize",
276
+ matchScreenSize(tg.screenSize, ctx.screenSize),
277
+ `${ctx.screenSize} vs ${tg.screenSize}`
278
+ );
279
+ if (r) return r;
280
+ }
281
+ if (tg.locale !== void 0) {
282
+ const r = record("locale", matchLocale(tg.locale, ctx.locale), `${ctx.locale} vs ${tg.locale}`);
283
+ if (r) return r;
284
+ }
285
+ if (tg.appVersion !== void 0) {
286
+ const r = record(
287
+ "appVersion",
288
+ matchAppVersion(tg.appVersion, ctx.appVersion),
289
+ `${ctx.appVersion} vs ${tg.appVersion}`
290
+ );
291
+ if (r) return r;
292
+ }
293
+ if (tg.routes !== void 0) {
294
+ const r = record("routes", matchRoutes(tg.routes, ctx.route), `${ctx.route} vs ${tg.routes}`);
295
+ if (r) return r;
296
+ }
297
+ if (tg.attributes !== void 0) {
298
+ const r = record("attributes", matchAttributes(tg.attributes, ctx.attributes), "mismatch");
299
+ if (r) return r;
300
+ }
301
+ if (tg.userId !== void 0) {
302
+ const r = record("userId", matchUserId(tg.userId, ctx), "mismatch");
303
+ if (r) return r;
304
+ }
305
+ if (tg.predicate !== void 0) {
306
+ const r = record("predicate", tg.predicate(ctx), "false");
307
+ if (r) return r;
308
+ }
309
+ return { matched: true, steps };
310
+ }
311
+
312
+ // src/targeting/hash.ts
313
+ var encoder = new TextEncoder();
314
+ async function hashUserId(userId) {
315
+ const bytes = encoder.encode(userId);
316
+ const g = globalThis;
317
+ const buf = await g.crypto.subtle.digest("SHA-256", bytes);
318
+ const view = new DataView(buf);
319
+ const uint32 = view.getUint32(0, false);
320
+ return uint32 % 100;
321
+ }
322
+
323
+ export { evaluate, explain, hashUserId, matchRoute, matchSemver, matchTargeting };
324
+ //# sourceMappingURL=targeting.js.map
325
+ //# sourceMappingURL=targeting.js.map