@sentientui/policy 0.2.0 → 0.3.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carlos Sánchez Campos / SentientUI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @sentientui/policy
2
+
3
+ Pure decision-policy functions shared by the SentientUI API and the keyless local
4
+ engine. This package is the single source of truth for *how a decision is made* —
5
+ Thompson sampling, empirical-Bayes pooling/shrinkage, arm encoding, slot
6
+ validation, and layout selection — so the server and the on-device engine always
7
+ agree.
8
+
9
+ Everything here is a **pure function**: no I/O, no global state, no side effects.
10
+ Randomized functions take an injectable `rand: () => number` (uniform `[0,1)`) so
11
+ results are fully reproducible when you pass a seeded PRNG. The default is
12
+ `Math.random`, which is **non-deterministic** — pass a seed for tests or
13
+ replayable decisions.
14
+
15
+ ## What's inside
16
+
17
+ **Bandit / Thompson sampling** (`bandit.ts`)
18
+ - `sampleBeta(alpha, beta, rand?)` — one draw from `Beta(alpha, beta)` (Marsaglia–Tsang gamma method).
19
+ - `sampleArm(arms, rand?)` — Thompson-samples each arm's Beta posterior and returns the argmax arm id (or `null` for no arms).
20
+
21
+ **Empirical-Bayes pooling & shrinkage** (`shrinkage.ts`, `pooling.ts`)
22
+ - `shrunkPosterior(persona, pooled, m?)` — one-axis read-time shrinkage; cells are born warm (`w = m / (m + exposures)`) and detach as their own data accumulates. `SHRINKAGE_M` is the default strength.
23
+ - `posteriorOfCounts({ exposures, conversions })` — `Beta` posterior from raw counts (`alpha = conversions + 1`, `beta = max(0, exposures − conversions) + 1`).
24
+ - `pooledPosterior(cells, personaKnown, m?)` — hierarchical partial pooling over `(segment, persona)`; reproduces legacy segment-only and persona-only behavior when only those cells are present. `POOL_ALL` is the `__all__` sentinel for marginal/global rows.
25
+ - `weightCellsFor(...)` — the write-side counterpart: which weight rows a single trial/credit must bump.
26
+
27
+ **Arm encoding & slot validation** (`arm-encoding.ts`)
28
+ - `canonicalArm(values)` / `parseArm(arm)` — stable string encoding of a multi-dimensional arm and its inverse.
29
+ - `marginalArmKey(dim, value)`, `slotBaselineArm(decl)`, `slotResultFor(decl, arm)` — arm helpers for a slot declaration.
30
+ - `validateSlotDecl(decl)` — structural validation of a `SlotDecl`, returning `{ ok: true }` or `{ ok: false, reason }`.
31
+
32
+ **Layout selection** (`layout-heuristics.ts`, `choose-layout.ts`, `hash.ts`)
33
+ - `candidateLayouts(sections, sectionTypes, persona)` — the candidate section orderings for a persona.
34
+ - `applyClusterHeuristic(sections, sectionTypes, persona)` — the persona's heuristic ordering (`CLUSTER_PRIORITY`), used as the fallback.
35
+ - `chooseLayout(sections, sectionTypes, persona, learned, rand?)` — Thompson-samples the learned layout posteriors over the candidates, falling back to the heuristic.
36
+ - `hashLayout(order)` — stable hash of a section order (the `layoutHash` key).
37
+
38
+ **Personas** (`personas.ts`)
39
+ - `PERSONAS`, `PersonaKey`, `UNKNOWN_PERSONA`, `PERSONA_DISPLAY` — the canonical persona set and display names.
40
+ - `canonicalPersona(label)` — normalize an arbitrary/legacy label to a `PersonaKey`.
41
+
42
+ **Deterministic helpers** (`deterministic.ts`)
43
+ - `fnv1a(input)` — FNV-1a hash.
44
+ - `pickDeterministicArm(sessionId, slotId, arms)` — hash-based, seed-free arm pick (stable per session/slot).
45
+ - `confidenceBand(c)` — map a `[0,1]` confidence to `'low' | 'medium' | 'high'`.
46
+
47
+ ## Usage
48
+
49
+ ```ts
50
+ import { sampleArm, posteriorOfCounts } from '@sentientui/policy';
51
+
52
+ // Thompson-sample the arm to serve from each arm's Beta posterior.
53
+ const arms = [
54
+ { arm: 'control', ...posteriorOfCounts({ exposures: 200, conversions: 20 }) },
55
+ { arm: 'variant_b', ...posteriorOfCounts({ exposures: 180, conversions: 27 }) },
56
+ ];
57
+
58
+ const chosen = sampleArm(arms); // e.g. 'variant_b' — uses Math.random
59
+
60
+ // Pass a seeded PRNG for reproducible selection (tests, replayable decisions):
61
+ const chosenSeeded = sampleArm(arms, mySeededRng);
62
+ ```
63
+
64
+ ```ts
65
+ import { chooseLayout, hashLayout, type LearnedLayout } from '@sentientui/policy';
66
+
67
+ const learned = new Map<string, LearnedLayout>(); // from your layout_weights store
68
+ const order = chooseLayout(sections, sectionTypes, 'buyer', learned);
69
+ const key = hashLayout(order);
70
+ ```
71
+
72
+ ## License
73
+
74
+ MIT
package/dist/index.d.cts CHANGED
@@ -57,6 +57,10 @@ type LearnedLayout = {
57
57
  * orderings, using learned posteriors from layout_weights. Candidates with no
58
58
  * learned row use the uniform 1/1 prior — identical to variant cold start.
59
59
  * Falls back to the persona's heuristic only if sampling yields no candidate.
60
+ *
61
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
62
+ * NON-DETERMINISTIC. Pass a seeded PRNG when you need a reproducible layout
63
+ * (tests, replayable decisions) — otherwise the sampled order varies per call.
60
64
  */
61
65
  declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, persona: PersonaKey, learned: Map<string, LearnedLayout>, rand?: () => number): string[];
62
66
 
@@ -66,13 +70,23 @@ type ArmPosterior = {
66
70
  alpha: number;
67
71
  beta: number;
68
72
  };
69
- /** One draw from Beta(alpha, beta). Moved verbatim from apps/api/src/domain/bandit.ts. */
73
+ /**
74
+ * One draw from Beta(alpha, beta). Moved verbatim from apps/api/src/domain/bandit.ts.
75
+ *
76
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
77
+ * NON-DETERMINISTIC. Pass a seeded PRNG when you need reproducible output
78
+ * (tests, replayable decisions, snapshotting) — otherwise results vary per call.
79
+ */
70
80
  declare function sampleBeta(alpha: number, beta: number, rand?: () => number): number;
71
81
  /**
72
82
  * Thompson Sampling selection: samples Beta(alpha, beta) per arm and returns
73
83
  * the argmax arm id, or null when no arms are given. Uncertain arms get
74
84
  * explored; confident winners get exploited — same semantics as the legacy
75
85
  * chooseVariant, generalized to arbitrary arm strings.
86
+ *
87
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
88
+ * NON-DETERMINISTIC. Pass a seeded PRNG for reproducible selection (tests,
89
+ * replayable assignments) — otherwise the chosen arm varies per call.
76
90
  */
77
91
  declare function sampleArm(arms: ArmPosterior[], rand?: () => number): string | null;
78
92
 
@@ -142,6 +156,57 @@ declare function shrunkPosterior(persona: {
142
156
  beta: number;
143
157
  };
144
158
 
159
+ /** Sentinel segment/persona value for marginal and global weight rows. */
160
+ declare const POOL_ALL = "__all__";
161
+ type PoolCounts = {
162
+ exposures: number;
163
+ conversions: number;
164
+ };
165
+ type PoolCells = {
166
+ /** (segment, persona) — the specific serving context */
167
+ child?: PoolCounts;
168
+ /** (segment, '__all__') — persona-agnostic marginal (legacy variant rows) */
169
+ segment?: PoolCounts;
170
+ /** ('__all__', persona) — segment-agnostic marginal (legacy slot rows) */
171
+ persona?: PoolCounts;
172
+ /** ('__all__', '__all__') — global */
173
+ global?: PoolCounts;
174
+ };
175
+ /** Pinned formulas: alpha = conversions + 1; beta = max(0, exposures − conversions) + 1. */
176
+ declare function posteriorOfCounts(c: PoolCounts): {
177
+ alpha: number;
178
+ beta: number;
179
+ };
180
+ /**
181
+ * Hierarchical partial pooling over (segment, persona), built by nesting the
182
+ * pinned one-axis shrinkage (shrunkPosterior, m = SHRINKAGE_M):
183
+ *
184
+ * segLevel = shrink(segment ← global)
185
+ * perLevel = shrink(persona ← global) (persona axis, when known)
186
+ * parent = evidence-weighted blend of segLevel and perLevel
187
+ * final = shrink(child ← parent)
188
+ *
189
+ * personaKnown=false consults ONLY segment+global — this is the invariant that
190
+ * keeps unknown-persona traffic on exactly the segment-marginal policy.
191
+ * Every cell is optional; an absent cell contributes Beta(1,1)-with-0-evidence,
192
+ * which is what lets the same function reproduce the legacy variant (segment-only)
193
+ * and legacy slot (persona-only) behaviors on day one after migration.
194
+ */
195
+ declare function pooledPosterior(cells: PoolCells, personaKnown: boolean, m?: number): {
196
+ alpha: number;
197
+ beta: number;
198
+ };
199
+ /**
200
+ * Write-side cell expansion: which weight rows one trial/credit must bump.
201
+ * Unknown persona bumps ONLY the segment marginal + global — no 'unknown'
202
+ * child or persona-marginal rows exist (unknown traffic serves the segment
203
+ * marginal, so that is where its evidence must live).
204
+ */
205
+ declare function weightCellsFor(segment: string, persona: string): Array<{
206
+ segment: string;
207
+ persona: string;
208
+ }>;
209
+
145
210
  /**
146
211
  * FNV-1a 32-bit hash. Same algorithm (offset basis, prime-by-shifts, >>> 0)
147
212
  * as the private hashUnit in apps/api/src/domain/holdout.ts — parity is
@@ -163,4 +228,4 @@ declare function pickDeterministicArm(sessionId: string, slotId: string, arms: s
163
228
  */
164
229
  declare function confidenceBand(c: number): 'low' | 'medium' | 'high';
165
230
 
166
- export { type ArmPosterior, CLUSTER_PRIORITY, LEGACY_PERSONA_MAP, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, type Persona, type PersonaKey, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, applyClusterHeuristic, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, confidenceBand, fnv1a, hashLayout, marginalArmKey, parseArm, pickDeterministicArm, sampleArm, sampleBeta, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl };
231
+ export { type ArmPosterior, CLUSTER_PRIORITY, LEGACY_PERSONA_MAP, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, POOL_ALL, type Persona, type PersonaKey, type PoolCells, type PoolCounts, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, applyClusterHeuristic, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, confidenceBand, fnv1a, hashLayout, marginalArmKey, parseArm, pickDeterministicArm, pooledPosterior, posteriorOfCounts, sampleArm, sampleBeta, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, weightCellsFor };
package/dist/index.d.ts CHANGED
@@ -57,6 +57,10 @@ type LearnedLayout = {
57
57
  * orderings, using learned posteriors from layout_weights. Candidates with no
58
58
  * learned row use the uniform 1/1 prior — identical to variant cold start.
59
59
  * Falls back to the persona's heuristic only if sampling yields no candidate.
60
+ *
61
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
62
+ * NON-DETERMINISTIC. Pass a seeded PRNG when you need a reproducible layout
63
+ * (tests, replayable decisions) — otherwise the sampled order varies per call.
60
64
  */
61
65
  declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, persona: PersonaKey, learned: Map<string, LearnedLayout>, rand?: () => number): string[];
62
66
 
@@ -66,13 +70,23 @@ type ArmPosterior = {
66
70
  alpha: number;
67
71
  beta: number;
68
72
  };
69
- /** One draw from Beta(alpha, beta). Moved verbatim from apps/api/src/domain/bandit.ts. */
73
+ /**
74
+ * One draw from Beta(alpha, beta). Moved verbatim from apps/api/src/domain/bandit.ts.
75
+ *
76
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
77
+ * NON-DETERMINISTIC. Pass a seeded PRNG when you need reproducible output
78
+ * (tests, replayable decisions, snapshotting) — otherwise results vary per call.
79
+ */
70
80
  declare function sampleBeta(alpha: number, beta: number, rand?: () => number): number;
71
81
  /**
72
82
  * Thompson Sampling selection: samples Beta(alpha, beta) per arm and returns
73
83
  * the argmax arm id, or null when no arms are given. Uncertain arms get
74
84
  * explored; confident winners get exploited — same semantics as the legacy
75
85
  * chooseVariant, generalized to arbitrary arm strings.
86
+ *
87
+ * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
88
+ * NON-DETERMINISTIC. Pass a seeded PRNG for reproducible selection (tests,
89
+ * replayable assignments) — otherwise the chosen arm varies per call.
76
90
  */
77
91
  declare function sampleArm(arms: ArmPosterior[], rand?: () => number): string | null;
78
92
 
@@ -142,6 +156,57 @@ declare function shrunkPosterior(persona: {
142
156
  beta: number;
143
157
  };
144
158
 
159
+ /** Sentinel segment/persona value for marginal and global weight rows. */
160
+ declare const POOL_ALL = "__all__";
161
+ type PoolCounts = {
162
+ exposures: number;
163
+ conversions: number;
164
+ };
165
+ type PoolCells = {
166
+ /** (segment, persona) — the specific serving context */
167
+ child?: PoolCounts;
168
+ /** (segment, '__all__') — persona-agnostic marginal (legacy variant rows) */
169
+ segment?: PoolCounts;
170
+ /** ('__all__', persona) — segment-agnostic marginal (legacy slot rows) */
171
+ persona?: PoolCounts;
172
+ /** ('__all__', '__all__') — global */
173
+ global?: PoolCounts;
174
+ };
175
+ /** Pinned formulas: alpha = conversions + 1; beta = max(0, exposures − conversions) + 1. */
176
+ declare function posteriorOfCounts(c: PoolCounts): {
177
+ alpha: number;
178
+ beta: number;
179
+ };
180
+ /**
181
+ * Hierarchical partial pooling over (segment, persona), built by nesting the
182
+ * pinned one-axis shrinkage (shrunkPosterior, m = SHRINKAGE_M):
183
+ *
184
+ * segLevel = shrink(segment ← global)
185
+ * perLevel = shrink(persona ← global) (persona axis, when known)
186
+ * parent = evidence-weighted blend of segLevel and perLevel
187
+ * final = shrink(child ← parent)
188
+ *
189
+ * personaKnown=false consults ONLY segment+global — this is the invariant that
190
+ * keeps unknown-persona traffic on exactly the segment-marginal policy.
191
+ * Every cell is optional; an absent cell contributes Beta(1,1)-with-0-evidence,
192
+ * which is what lets the same function reproduce the legacy variant (segment-only)
193
+ * and legacy slot (persona-only) behaviors on day one after migration.
194
+ */
195
+ declare function pooledPosterior(cells: PoolCells, personaKnown: boolean, m?: number): {
196
+ alpha: number;
197
+ beta: number;
198
+ };
199
+ /**
200
+ * Write-side cell expansion: which weight rows one trial/credit must bump.
201
+ * Unknown persona bumps ONLY the segment marginal + global — no 'unknown'
202
+ * child or persona-marginal rows exist (unknown traffic serves the segment
203
+ * marginal, so that is where its evidence must live).
204
+ */
205
+ declare function weightCellsFor(segment: string, persona: string): Array<{
206
+ segment: string;
207
+ persona: string;
208
+ }>;
209
+
145
210
  /**
146
211
  * FNV-1a 32-bit hash. Same algorithm (offset basis, prime-by-shifts, >>> 0)
147
212
  * as the private hashUnit in apps/api/src/domain/holdout.ts — parity is
@@ -163,4 +228,4 @@ declare function pickDeterministicArm(sessionId: string, slotId: string, arms: s
163
228
  */
164
229
  declare function confidenceBand(c: number): 'low' | 'medium' | 'high';
165
230
 
166
- export { type ArmPosterior, CLUSTER_PRIORITY, LEGACY_PERSONA_MAP, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, type Persona, type PersonaKey, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, applyClusterHeuristic, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, confidenceBand, fnv1a, hashLayout, marginalArmKey, parseArm, pickDeterministicArm, sampleArm, sampleBeta, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl };
231
+ export { type ArmPosterior, CLUSTER_PRIORITY, LEGACY_PERSONA_MAP, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, POOL_ALL, type Persona, type PersonaKey, type PoolCells, type PoolCounts, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, applyClusterHeuristic, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, confidenceBand, fnv1a, hashLayout, marginalArmKey, parseArm, pickDeterministicArm, pooledPosterior, posteriorOfCounts, sampleArm, sampleBeta, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, weightCellsFor };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var v=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var X=(e,r)=>{for(var t in r)v(e,t,{get:r[t],enumerable:!0})},Z=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of J(r))!Q.call(e,o)&&o!==t&&v(e,o,{get:()=>r[o],enumerable:!(n=V(r,o))||n.enumerable});return e};var ee=e=>Z(v({},"__esModule",{value:!0}),e);var me={};X(me,{CLUSTER_PRIORITY:()=>G,LEGACY_PERSONA_MAP:()=>B,PERSONAS:()=>L,PERSONA_DISPLAY:()=>re,SHRINKAGE_M:()=>we,UNKNOWN_PERSONA:()=>k,applyClusterHeuristic:()=>S,candidateLayouts:()=>q,canonicalArm:()=>U,canonicalPersona:()=>te,chooseLayout:()=>oe,confidenceBand:()=>le,fnv1a:()=>W,hashLayout:()=>E,marginalArmKey:()=>ae,parseArm:()=>H,pickDeterministicArm:()=>fe,sampleArm:()=>j,sampleBeta:()=>$,shrunkPosterior:()=>ue,slotBaselineArm:()=>z,slotResultFor:()=>ce,validateSlotDecl:()=>ie});module.exports=ee(me);var L=["buyer","researcher","deal_seeker","browser"],k="unknown",re={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},B={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function te(e){var t;if(e==null)return k;let r=e.trim().toLowerCase();return(t=B[r])!=null?t:k}var ne=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function b(e,r){return e>>>r|e<<32-r}function C(e){return(e>>>0).toString(16).padStart(8,"0")}function se(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,o=(t+8>>6)+1<<6,s=new Uint8Array(o);s.set(r),s[t]=128;let a=new DataView(s.buffer);a.setUint32(o-8,Math.floor(n/4294967296),!1),a.setUint32(o-4,n>>>0,!1);let u=1779033703,c=3144134277,f=1013904242,p=2773480762,d=1359893119,m=2600822924,w=528734635,R=1541459225,l=new Uint32Array(64);for(let M=0;M<o;M+=64){for(let i=0;i<16;i++)l[i]=a.getUint32(M+i*4,!1);for(let i=16;i<64;i++){let _=b(l[i-15],7)^b(l[i-15],18)^l[i-15]>>>3,K=b(l[i-2],17)^b(l[i-2],19)^l[i-2]>>>10;l[i]=l[i-16]+_+l[i-7]+K>>>0}let x=u,h=c,y=f,O=p,g=d,A=m,P=w,N=R;for(let i=0;i<64;i++){let _=b(g,6)^b(g,11)^b(g,25),K=g&A^~g&P,I=N+_+K+ne[i]+l[i]>>>0,Y=b(x,2)^b(x,13)^b(x,22),F=x&h^x&y^h&y,T=Y+F>>>0;N=P,P=A,A=g,g=O+I>>>0,O=y,y=h,h=x,x=I+T>>>0}u=u+x>>>0,c=c+h>>>0,f=f+y>>>0,p=p+O>>>0,d=d+g>>>0,m=m+A>>>0,w=w+P>>>0,R=R+N>>>0}return C(u)+C(c)}function E(e){return se(e.join("|"))}var G={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function S(e,r,t){let n=t===k?void 0:G[t];return n?[...e].sort((o,s)=>{var c,f;let a=(c=r.get(o))!=null?c:"generic",u=(f=r.get(s))!=null?f:"generic";return n.indexOf(a)-n.indexOf(u)}):e}function q(e,r,t){let n=new Map;for(let o of[...L,t]){let s=S(e,r,o);n.set(E(s),s)}return n}function D(e,r){if(e<1)return D(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let o,s;do{let u=Math.max(1e-15,r()),c=r();o=Math.sqrt(-2*Math.log(u))*Math.cos(2*Math.PI*c),s=1+n*o}while(s<=0);s=s*s*s;let a=r();if(a<1-.0331*o*o*o*o||Math.log(a)<.5*o*o+t*(1-s+Math.log(s)))return t*s}}function $(e,r,t=Math.random){let n=D(e,t),o=D(r,t),s=n+o;return s<=0?e/(e+r):n/s}function j(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=$(t.alpha,t.beta,r);for(let o=1;o<e.length;o++){let s=e[o],a=$(s.alpha,s.beta,r);a>n&&(t=s,n=a)}return t.arm}function oe(e,r,t,n,o=Math.random){var f,p;let s=q(e,r,t),a=[];for(let d of s.keys()){let m=n.get(d);a.push({arm:d,alpha:(f=m==null?void 0:m.alpha)!=null?f:1,beta:(p=m==null?void 0:m.beta)!=null?p:1})}let u=j(a,o),c=u?s.get(u):void 0;return c!=null?c:S(e,r,t)}function U(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function H(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let o=t.slice(0,n);if(o in r)return null;r[o]=t.slice(n+1)}return r}function ae(e,r){return`${e}=${r}`}function z(e){var t,n,o;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return U(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[s,a]of Object.entries((n=e.dims)!=null?n:{}))r[s]=(o=a[0])!=null?o:"";return U(r)}function ie(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let s=e.arms;if(s.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(s.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(s).size!==s.length)return{ok:!1,reason:"arms must be unique"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!s.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let o=1;for(let[s,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${s}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${s}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${s}" has duplicate values`};o*=a.length}if(o>64)return{ok:!1,reason:`declared space of ${o} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let s=e.baseline,a=n.map(([c])=>c).sort(),u=Object.keys(s).sort();if(a.join(" ")!==u.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[c,f]of n)if(!f.includes(s[c]))return{ok:!1,reason:`baseline value for dim "${c}" is not declared`}}return{ok:!0}}function ce(e,r){var t,n;return e.dims!=null?(n=(t=H(r))!=null?t:H(z(e)))!=null?n:{}:r}var we=20;function ue(e,r,t=20){let n=t/(t+e.exposures);return{alpha:e.alpha+n*r.alpha,beta:e.beta+n*r.beta}}function W(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function fe(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[W(`${e}:${r}`)%n.length]}function le(e){return e>=.3?e<.7?"medium":"high":"low"}0&&(module.exports={CLUSTER_PRIORITY,LEGACY_PERSONA_MAP,PERSONAS,PERSONA_DISPLAY,SHRINKAGE_M,UNKNOWN_PERSONA,applyClusterHeuristic,candidateLayouts,canonicalArm,canonicalPersona,chooseLayout,confidenceBand,fnv1a,hashLayout,marginalArmKey,parseArm,pickDeterministicArm,sampleArm,sampleBeta,shrunkPosterior,slotBaselineArm,slotResultFor,validateSlotDecl});
1
+ "use strict";var O=Object.defineProperty,se=Object.defineProperties,ae=Object.getOwnPropertyDescriptor,ie=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,F=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,ce=Object.prototype.propertyIsEnumerable;var T=(e,r,t)=>r in e?O(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,v=(e,r)=>{for(var t in r||(r={}))V.call(r,t)&&T(e,t,r[t]);if(F)for(var t of F(r))ce.call(r,t)&&T(e,t,r[t]);return e},L=(e,r)=>se(e,ie(r));var le=(e,r)=>{for(var t in r)O(e,t,{get:r[t],enumerable:!0})},fe=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of ue(r))!V.call(e,s)&&s!==t&&O(e,s,{get:()=>r[s],enumerable:!(n=ae(r,s))||n.enumerable});return e};var be=e=>fe(O({},"__esModule",{value:!0}),e);var Re={};le(Re,{CLUSTER_PRIORITY:()=>Q,LEGACY_PERSONA_MAP:()=>Z,PERSONAS:()=>j,PERSONA_DISPLAY:()=>me,POOL_ALL:()=>p,SHRINKAGE_M:()=>ee,UNKNOWN_PERSONA:()=>S,applyClusterHeuristic:()=>N,candidateLayouts:()=>H,canonicalArm:()=>z,canonicalPersona:()=>xe,chooseLayout:()=>de,confidenceBand:()=>we,fnv1a:()=>re,hashLayout:()=>U,marginalArmKey:()=>he,parseArm:()=>W,pickDeterministicArm:()=>Se,pooledPosterior:()=>ke,posteriorOfCounts:()=>R,sampleArm:()=>G,sampleBeta:()=>B,shrunkPosterior:()=>w,slotBaselineArm:()=>X,slotResultFor:()=>Pe,validateSlotDecl:()=>ye,weightCellsFor:()=>Ae});module.exports=be(Re);var j=["buyer","researcher","deal_seeker","browser"],S="unknown",me={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},Z={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function xe(e){var t;if(e==null)return S;let r=e.trim().toLowerCase();return(t=Z[r])!=null?t:S}var pe=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function x(e,r){return e>>>r|e<<32-r}function J(e){return(e>>>0).toString(16).padStart(8,"0")}function ge(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,s=(t+8>>6)+1<<6,o=new Uint8Array(s);o.set(r),o[t]=128;let a=new DataView(o.buffer);a.setUint32(s-8,Math.floor(n/4294967296),!1),a.setUint32(s-4,n>>>0,!1);let c=1779033703,i=3144134277,l=1013904242,m=2773480762,g=1359893119,b=2600822924,y=528734635,P=1541459225,f=new Uint32Array(64);for(let C=0;C<s;C+=64){for(let u=0;u<16;u++)f[u]=a.getUint32(C+u*4,!1);for(let u=16;u<64;u++){let D=x(f[u-15],7)^x(f[u-15],18)^f[u-15]>>>3,$=x(f[u-2],17)^x(f[u-2],19)^f[u-2]>>>10;f[u]=f[u-16]+D+f[u-7]+$>>>0}let d=c,k=i,A=l,E=m,h=g,M=b,_=y,q=P;for(let u=0;u<64;u++){let D=x(h,6)^x(h,11)^x(h,25),$=h&M^~h&_,Y=q+D+$+pe[u]+f[u]>>>0,te=x(d,2)^x(d,13)^x(d,22),ne=d&k^d&A^k&A,oe=te+ne>>>0;q=_,_=M,M=h,h=E+Y>>>0,E=A,A=k,k=d,d=Y+oe>>>0}c=c+d>>>0,i=i+k>>>0,l=l+A>>>0,m=m+E>>>0,g=g+h>>>0,b=b+M>>>0,y=y+_>>>0,P=P+q>>>0}return J(c)+J(i)}function U(e){return ge(e.join("|"))}var Q={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function N(e,r,t){let n=t===S?void 0:Q[t];return n?[...e].sort((s,o)=>{var i,l;let a=(i=r.get(s))!=null?i:"generic",c=(l=r.get(o))!=null?l:"generic";return n.indexOf(a)-n.indexOf(c)}):e}function H(e,r,t){let n=new Map;for(let s of[...j,t]){let o=N(e,r,s);n.set(U(o),o)}return n}function I(e,r){if(e<1)return I(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let s,o;do{let c=Math.max(1e-15,r()),i=r();s=Math.sqrt(-2*Math.log(c))*Math.cos(2*Math.PI*i),o=1+n*s}while(o<=0);o=o*o*o;let a=r();if(a<1-.0331*s*s*s*s||Math.log(a)<.5*s*s+t*(1-o+Math.log(o)))return t*o}}function B(e,r,t=Math.random){let n=I(e,t),s=I(r,t),o=n+s;return o<=0?e/(e+r):n/o}function G(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=B(t.alpha,t.beta,r);for(let s=1;s<e.length;s++){let o=e[s],a=B(o.alpha,o.beta,r);a>n&&(t=o,n=a)}return t.arm}function de(e,r,t,n,s=Math.random){var l,m;let o=H(e,r,t),a=[];for(let g of o.keys()){let b=n.get(g);a.push({arm:g,alpha:(l=b==null?void 0:b.alpha)!=null?l:1,beta:(m=b==null?void 0:b.beta)!=null?m:1})}let c=G(a,s),i=c?o.get(c):void 0;return i!=null?i:N(e,r,t)}function z(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function W(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let s=t.slice(0,n);if(s in r)return null;r[s]=t.slice(n+1)}return r}function he(e,r){return`${e}=${r}`}function X(e){var t,n,s;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return z(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[o,a]of Object.entries((n=e.dims)!=null?n:{}))r[o]=(s=a[0])!=null?s:"";return z(r)}function ye(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let o=e.arms;if(o.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(o.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(o).size!==o.length)return{ok:!1,reason:"arms must be unique"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!o.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let s=1;for(let[o,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${o}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${o}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${o}" has duplicate values`};s*=a.length}if(s>64)return{ok:!1,reason:`declared space of ${s} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let o=e.baseline,a=n.map(([i])=>i).sort(),c=Object.keys(o).sort();if(a.join(" ")!==c.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[i,l]of n)if(!l.includes(o[i]))return{ok:!1,reason:`baseline value for dim "${i}" is not declared`}}return{ok:!0}}function Pe(e,r){var t,n;return e.dims!=null?(n=(t=W(r))!=null?t:W(X(e)))!=null?n:{}:r}var ee=20;function w(e,r,t=20){let n=t/(t+e.exposures);return{alpha:e.alpha+n*r.alpha,beta:e.beta+n*r.beta}}var p="__all__",K={exposures:0,conversions:0};function R(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function ke(e,r,t=20){var b,y,P,f;let n=(b=e.segment)!=null?b:K,s=(y=e.global)!=null?y:K,o=R(s),a=w(L(v({},R(n)),{exposures:n.exposures}),o,t);if(!r)return a;let c=(P=e.persona)!=null?P:K,i=(f=e.child)!=null?f:K,l=w(L(v({},R(c)),{exposures:c.exposures}),o,t),m=(n.exposures+1)/(n.exposures+c.exposures+2),g={alpha:m*a.alpha+(1-m)*l.alpha,beta:m*a.beta+(1-m)*l.beta};return w(L(v({},R(i)),{exposures:i.exposures}),g,t)}function Ae(e,r){return r==="unknown"||r===p?[{segment:e,persona:p},{segment:p,persona:p}]:[{segment:e,persona:r},{segment:e,persona:p},{segment:p,persona:r},{segment:p,persona:p}]}function re(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function Se(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[re(`${e}:${r}`)%n.length]}function we(e){return e>=.3?e<.7?"medium":"high":"low"}0&&(module.exports={CLUSTER_PRIORITY,LEGACY_PERSONA_MAP,PERSONAS,PERSONA_DISPLAY,POOL_ALL,SHRINKAGE_M,UNKNOWN_PERSONA,applyClusterHeuristic,candidateLayouts,canonicalArm,canonicalPersona,chooseLayout,confidenceBand,fnv1a,hashLayout,marginalArmKey,parseArm,pickDeterministicArm,pooledPosterior,posteriorOfCounts,sampleArm,sampleBeta,shrunkPosterior,slotBaselineArm,slotResultFor,validateSlotDecl,weightCellsFor});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var E=["buyer","researcher","deal_seeker","browser"],P="unknown",J={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},z={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function Q(e){var t;if(e==null)return P;let r=e.trim().toLowerCase();return(t=z[r])!=null?t:P}var W=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function b(e,r){return e>>>r|e<<32-r}function q(e){return(e>>>0).toString(16).padStart(8,"0")}function Y(e){let r=new TextEncoder().encode(e),t=r.length,s=t*8,o=(t+8>>6)+1<<6,n=new Uint8Array(o);n.set(r),n[t]=128;let a=new DataView(n.buffer);a.setUint32(o-8,Math.floor(s/4294967296),!1),a.setUint32(o-4,s>>>0,!1);let u=1779033703,c=3144134277,f=1013904242,p=2773480762,d=1359893119,m=2600822924,S=528734635,w=1541459225,l=new Uint32Array(64);for(let R=0;R<o;R+=64){for(let i=0;i<16;i++)l[i]=a.getUint32(R+i*4,!1);for(let i=16;i<64;i++){let N=b(l[i-15],7)^b(l[i-15],18)^l[i-15]>>>3,_=b(l[i-2],17)^b(l[i-2],19)^l[i-2]>>>10;l[i]=l[i-16]+N+l[i-7]+_>>>0}let x=u,h=c,y=f,M=p,g=d,k=m,A=S,O=w;for(let i=0;i<64;i++){let N=b(g,6)^b(g,11)^b(g,25),_=g&k^~g&A,L=O+N+_+W[i]+l[i]>>>0,B=b(x,2)^b(x,13)^b(x,22),C=x&h^x&y^h&y,G=B+C>>>0;O=A,A=k,k=g,g=M+L>>>0,M=y,y=h,h=x,x=L+G>>>0}u=u+x>>>0,c=c+h>>>0,f=f+y>>>0,p=p+M>>>0,d=d+g>>>0,m=m+k>>>0,S=S+A>>>0,w=w+O>>>0}return q(u)+q(c)}function D(e){return Y(e.join("|"))}var F={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function K(e,r,t){let s=t===P?void 0:F[t];return s?[...e].sort((o,n)=>{var c,f;let a=(c=r.get(o))!=null?c:"generic",u=(f=r.get(n))!=null?f:"generic";return s.indexOf(a)-s.indexOf(u)}):e}function $(e,r,t){let s=new Map;for(let o of[...E,t]){let n=K(e,r,o);s.set(D(n),n)}return s}function v(e,r){if(e<1)return v(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,s=1/Math.sqrt(9*t);for(;;){let o,n;do{let u=Math.max(1e-15,r()),c=r();o=Math.sqrt(-2*Math.log(u))*Math.cos(2*Math.PI*c),n=1+s*o}while(n<=0);n=n*n*n;let a=r();if(a<1-.0331*o*o*o*o||Math.log(a)<.5*o*o+t*(1-n+Math.log(n)))return t*n}}function j(e,r,t=Math.random){let s=v(e,t),o=v(r,t),n=s+o;return n<=0?e/(e+r):s/n}function U(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],s=j(t.alpha,t.beta,r);for(let o=1;o<e.length;o++){let n=e[o],a=j(n.alpha,n.beta,r);a>s&&(t=n,s=a)}return t.arm}function ae(e,r,t,s,o=Math.random){var f,p;let n=$(e,r,t),a=[];for(let d of n.keys()){let m=s.get(d);a.push({arm:d,alpha:(f=m==null?void 0:m.alpha)!=null?f:1,beta:(p=m==null?void 0:m.beta)!=null?p:1})}let u=U(a,o),c=u?n.get(u):void 0;return c!=null?c:K(e,r,t)}function H(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function I(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let s=t.indexOf("=");if(s<=0||s!==t.lastIndexOf("=")||s===t.length-1)return null;let o=t.slice(0,s);if(o in r)return null;r[o]=t.slice(s+1)}return r}function ce(e,r){return`${e}=${r}`}function T(e){var t,s,o;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return H(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[n,a]of Object.entries((s=e.dims)!=null?s:{}))r[n]=(o=a[0])!=null?o:"";return H(r)}function ue(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let n=e.arms;if(n.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(n.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(n).size!==n.length)return{ok:!1,reason:"arms must be unique"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!n.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let s=Object.entries(e.dims);if(s.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(s.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let o=1;for(let[n,a]of s){if(a.length<2)return{ok:!1,reason:`dim "${n}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${n}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${n}" has duplicate values`};o*=a.length}if(o>64)return{ok:!1,reason:`declared space of ${o} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let n=e.baseline,a=s.map(([c])=>c).sort(),u=Object.keys(n).sort();if(a.join(" ")!==u.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[c,f]of s)if(!f.includes(n[c]))return{ok:!1,reason:`baseline value for dim "${c}" is not declared`}}return{ok:!0}}function fe(e,r){var t,s;return e.dims!=null?(s=(t=I(r))!=null?t:I(T(e)))!=null?s:{}:r}var me=20;function be(e,r,t=20){let s=t/(t+e.exposures);return{alpha:e.alpha+s*r.alpha,beta:e.beta+s*r.beta}}function V(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function ge(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let s=[...t].sort();return s[V(`${e}:${r}`)%s.length]}function pe(e){return e>=.3?e<.7?"medium":"high":"low"}export{F as CLUSTER_PRIORITY,z as LEGACY_PERSONA_MAP,E as PERSONAS,J as PERSONA_DISPLAY,me as SHRINKAGE_M,P as UNKNOWN_PERSONA,K as applyClusterHeuristic,$ as candidateLayouts,H as canonicalArm,Q as canonicalPersona,ae as chooseLayout,pe as confidenceBand,V as fnv1a,D as hashLayout,ce as marginalArmKey,I as parseArm,ge as pickDeterministicArm,U as sampleArm,j as sampleBeta,be as shrunkPosterior,T as slotBaselineArm,fe as slotResultFor,ue as validateSlotDecl};
1
+ var X=Object.defineProperty,ee=Object.defineProperties;var re=Object.getOwnPropertyDescriptors;var U=Object.getOwnPropertySymbols;var te=Object.prototype.hasOwnProperty,ne=Object.prototype.propertyIsEnumerable;var H=(e,r,t)=>r in e?X(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,R=(e,r)=>{for(var t in r||(r={}))te.call(r,t)&&H(e,t,r[t]);if(U)for(var t of U(r))ne.call(r,t)&&H(e,t,r[t]);return e},M=(e,r)=>ee(e,re(r));var I=["buyer","researcher","deal_seeker","browser"],_="unknown",fe={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},oe={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function be(e){var t;if(e==null)return _;let r=e.trim().toLowerCase();return(t=oe[r])!=null?t:_}var se=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function x(e,r){return e>>>r|e<<32-r}function B(e){return(e>>>0).toString(16).padStart(8,"0")}function ae(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,s=(t+8>>6)+1<<6,o=new Uint8Array(s);o.set(r),o[t]=128;let a=new DataView(o.buffer);a.setUint32(s-8,Math.floor(n/4294967296),!1),a.setUint32(s-4,n>>>0,!1);let c=1779033703,i=3144134277,l=1013904242,m=2773480762,p=1359893119,b=2600822924,y=528734635,P=1541459225,f=new Uint32Array(64);for(let N=0;N<s;N+=64){for(let u=0;u<16;u++)f[u]=a.getUint32(N+u*4,!1);for(let u=16;u<64;u++){let E=x(f[u-15],7)^x(f[u-15],18)^f[u-15]>>>3,q=x(f[u-2],17)^x(f[u-2],19)^f[u-2]>>>10;f[u]=f[u-16]+E+f[u-7]+q>>>0}let g=c,k=i,A=l,K=m,d=p,S=b,w=y,C=P;for(let u=0;u<64;u++){let E=x(d,6)^x(d,11)^x(d,25),q=d&S^~d&w,j=C+E+q+se[u]+f[u]>>>0,Z=x(g,2)^x(g,13)^x(g,22),J=g&k^g&A^k&A,Q=Z+J>>>0;C=w,w=S,S=d,d=K+j>>>0,K=A,A=k,k=g,g=j+Q>>>0}c=c+g>>>0,i=i+k>>>0,l=l+A>>>0,m=m+K>>>0,p=p+d>>>0,b=b+S>>>0,y=y+w>>>0,P=P+C>>>0}return B(c)+B(i)}function G(e){return ae(e.join("|"))}var ie={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function D(e,r,t){let n=t===_?void 0:ie[t];return n?[...e].sort((s,o)=>{var i,l;let a=(i=r.get(s))!=null?i:"generic",c=(l=r.get(o))!=null?l:"generic";return n.indexOf(a)-n.indexOf(c)}):e}function z(e,r,t){let n=new Map;for(let s of[...I,t]){let o=D(e,r,s);n.set(G(o),o)}return n}function $(e,r){if(e<1)return $(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let s,o;do{let c=Math.max(1e-15,r()),i=r();s=Math.sqrt(-2*Math.log(c))*Math.cos(2*Math.PI*i),o=1+n*s}while(o<=0);o=o*o*o;let a=r();if(a<1-.0331*s*s*s*s||Math.log(a)<.5*s*s+t*(1-o+Math.log(o)))return t*o}}function W(e,r,t=Math.random){let n=$(e,t),s=$(r,t),o=n+s;return o<=0?e/(e+r):n/o}function Y(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=W(t.alpha,t.beta,r);for(let s=1;s<e.length;s++){let o=e[s],a=W(o.alpha,o.beta,r);a>n&&(t=o,n=a)}return t.arm}function ke(e,r,t,n,s=Math.random){var l,m;let o=z(e,r,t),a=[];for(let p of o.keys()){let b=n.get(p);a.push({arm:p,alpha:(l=b==null?void 0:b.alpha)!=null?l:1,beta:(m=b==null?void 0:b.beta)!=null?m:1})}let c=Y(a,s),i=c?o.get(c):void 0;return i!=null?i:D(e,r,t)}function F(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function T(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let s=t.slice(0,n);if(s in r)return null;r[s]=t.slice(n+1)}return r}function Se(e,r){return`${e}=${r}`}function ue(e){var t,n,s;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return F(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[o,a]of Object.entries((n=e.dims)!=null?n:{}))r[o]=(s=a[0])!=null?s:"";return F(r)}function we(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let o=e.arms;if(o.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(o.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(o).size!==o.length)return{ok:!1,reason:"arms must be unique"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!o.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let s=1;for(let[o,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${o}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${o}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${o}" has duplicate values`};s*=a.length}if(s>64)return{ok:!1,reason:`declared space of ${s} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let o=e.baseline,a=n.map(([i])=>i).sort(),c=Object.keys(o).sort();if(a.join(" ")!==c.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[i,l]of n)if(!l.includes(o[i]))return{ok:!1,reason:`baseline value for dim "${i}" is not declared`}}return{ok:!0}}function Re(e,r){var t,n;return e.dims!=null?(n=(t=T(r))!=null?t:T(ue(e)))!=null?n:{}:r}var V=20;function O(e,r,t=20){let n=t/(t+e.exposures);return{alpha:e.alpha+n*r.alpha,beta:e.beta+n*r.beta}}var h="__all__",v={exposures:0,conversions:0};function L(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function ve(e,r,t=20){var b,y,P,f;let n=(b=e.segment)!=null?b:v,s=(y=e.global)!=null?y:v,o=L(s),a=O(M(R({},L(n)),{exposures:n.exposures}),o,t);if(!r)return a;let c=(P=e.persona)!=null?P:v,i=(f=e.child)!=null?f:v,l=O(M(R({},L(c)),{exposures:c.exposures}),o,t),m=(n.exposures+1)/(n.exposures+c.exposures+2),p={alpha:m*a.alpha+(1-m)*l.alpha,beta:m*a.beta+(1-m)*l.beta};return O(M(R({},L(i)),{exposures:i.exposures}),p,t)}function Le(e,r){return r==="unknown"||r===h?[{segment:e,persona:h},{segment:h,persona:h}]:[{segment:e,persona:r},{segment:e,persona:h},{segment:h,persona:r},{segment:h,persona:h}]}function ce(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function Ce(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[ce(`${e}:${r}`)%n.length]}function Ee(e){return e>=.3?e<.7?"medium":"high":"low"}export{ie as CLUSTER_PRIORITY,oe as LEGACY_PERSONA_MAP,I as PERSONAS,fe as PERSONA_DISPLAY,h as POOL_ALL,V as SHRINKAGE_M,_ as UNKNOWN_PERSONA,D as applyClusterHeuristic,z as candidateLayouts,F as canonicalArm,be as canonicalPersona,ke as chooseLayout,Ee as confidenceBand,ce as fnv1a,G as hashLayout,Se as marginalArmKey,T as parseArm,Ce as pickDeterministicArm,ve as pooledPosterior,L as posteriorOfCounts,Y as sampleArm,W as sampleBeta,O as shrunkPosterior,ue as slotBaselineArm,Re as slotResultFor,we as validateSlotDecl,Le as weightCellsFor};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentientui/policy",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Pure decision-policy functions shared by the SentientUI API and the keyless local engine",
6
6
  "license": "MIT",
@@ -17,7 +17,8 @@
17
17
  }
18
18
  },
19
19
  "files": [
20
- "dist"
20
+ "dist",
21
+ "README.md"
21
22
  ],
22
23
  "devDependencies": {
23
24
  "@types/node": "^22.10.2",