@gradial/aci 0.1.47 → 0.1.50

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.50 - 2026-08-04
4
+
5
+ ### Fixed
6
+
7
+ - Scope the edge content selection (`x-aci-content-path` / `x-aci-variant-key`)
8
+ to the request's own routed path, carried in the new `x-aci-routed-path`
9
+ middleware header. Secondary route lookups (for example, a blog article's
10
+ render-time read of the blog index) select the target route's base payload
11
+ instead of failing as a cross-route integrity violation; a mismatched
12
+ selector on the routed path itself is still rejected.
13
+ - Treat `x-aci-routed-path` and `x-aci-variant-key` as reserved dispatch
14
+ signals: viewer-supplied values are rejected without framework origin auth,
15
+ stripped on the trusted path, and covered by the middleware matcher (a bare
16
+ viewer-supplied variant key could previously force an SSR 500).
17
+
18
+ ### Changed
19
+
20
+ - The canonical middleware matcher gains `x-aci-routed-path` and
21
+ `x-aci-variant-key` entries; sites that inline the matcher must adopt the
22
+ new value (conformance pins it).
23
+
3
24
  ## 0.1.45 - 2026-07-29
4
25
 
5
26
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.1.47` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.1.50` is a pre-1.0 public-surface reset for the ACI
4
4
  client SDK. Breaking changes from the old `0.1.x` accidental surface are
5
5
  intentional: the root import is framework-neutral, framework runtime behavior is
6
6
  behind explicit subpaths, and the first required customer path is React/Next.
@@ -8,7 +8,7 @@ behind explicit subpaths, and the first required customer path is React/Next.
8
8
  ## Install
9
9
 
10
10
  ```bash
11
- npm install @gradial/aci@0.1.47 zod@^4
11
+ npm install @gradial/aci@0.1.50 zod@^4
12
12
  ```
13
13
 
14
14
  ## Root Import
@@ -85,13 +85,13 @@ go through an app-owned HTTP endpoint backed server-side by a `ContentProvider`.
85
85
  The first RC scaffold path is Next:
86
86
 
87
87
  ```bash
88
- npx @gradial/aci@0.1.47 init my-site --template next-starter
88
+ npx @gradial/aci@0.1.50 init my-site --template next-starter
89
89
  ```
90
90
 
91
91
  List available templates:
92
92
 
93
93
  ```bash
94
- npx @gradial/aci@0.1.47 init --list
94
+ npx @gradial/aci@0.1.50 init --list
95
95
  ```
96
96
 
97
97
  ## Personalization: The One-Tag Install
@@ -1 +1 @@
1
- "use strict";(()=>{var Ne=Object.defineProperty;var Le=(t,e,n)=>e in t?Ne(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var y=(t,e,n)=>Le(t,typeof e!="symbol"?e+"":e,n);var x="data-aci-event",Oe="data-aci-on",H="data-aci-prop-",De="data-aci-refresh",q="custom.";function D(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(!(e===""||/[\s/]/.test(e)))return e.startsWith(q)?e:q+e}function Re(t){let e;for(let n of Array.from(t.attributes)){if(!n.name.startsWith(H))continue;let r=n.name.slice(H.length);r!==""&&((e!=null?e:e={})[r]=n.value)}return e}function U(t,e){let n=D(t.getAttribute(x));if(n===void 0){e==null||e("capture",{reason:"malformed_declaration",attr:x});return}return{eventName:n,properties:Re(t)}}function X(t){var e,n;return(n=(e=t.getAttribute(Oe))==null?void 0:e.trim().toLowerCase())!=null?n:"click"}function $(t,e,n){let{document:r,debug:o}=e;r.addEventListener("click",f=>{var l,p;try{let u=f.target;if((l=u==null?void 0:u.closest)==null?void 0:l.call(u,`[${De}]`)){n==null||n();return}let m=(p=u==null?void 0:u.closest)==null?void 0:p.call(u,`[${x}]`);if(!m||X(m)!=="click")return;let g=U(m,o);g&&t(g)}catch(u){o==null||o("capture",{reason:"click_handler_failed",error:String(u)})}});let i=e.intersectionObserver;if(typeof i!="function")return;let s=Array.from(r.querySelectorAll(`[${x}]`)).filter(f=>X(f)==="visible");if(s.length===0)return;let a=new WeakSet,c=new i((f,l)=>{try{for(let p of f){if(!p.isIntersecting||p.intersectionRatio<.5||a.has(p.target))continue;a.add(p.target),l.unobserve(p.target);let u=U(p.target,o);u&&t(u)}}catch(p){o==null||o("capture",{reason:"visible_handler_failed",error:String(p)})}},{threshold:.5});for(let f of s)c.observe(f)}function K(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Y(t,e,n,r){let o=(...a)=>{try{let[c,...f]=a;if(c==="track"){let l=D(f[0]);if(l===void 0){r==null||r("capture",{reason:"malformed_track_name",name:f[0]});return}let p=f[1];if(p!==void 0&&!K(p)){r==null||r("capture",{reason:"malformed_track_properties",name:l});return}n.track(l,p);return}if(c==="consent"){if(e.preset!=="manual"){r==null||r("consent",{reason:"preset_authoritative",preset:e.preset});return}let l=f[0];if(!K(l)){r==null||r("consent",{reason:"malformed_consent_envelope"});return}n.consent({p:l.p,src:l.src});return}r==null||r("capture",{reason:"unknown_command",command:c})}catch(c){r==null||r("capture",{reason:"command_failed",error:String(c)})}},i=t.aci,s=i==null?void 0:i.q;if(t.aci=o,s!=null)for(let a of Array.from(s)){let c=Array.from(a);o(...c)}}var Be="experiment.exposure",Fe='script[type="application/json"][data-aci-assignments]';function Ve(t){if(typeof t!="object"||t===null)return null;let e=t,n=e.experimentId,r=e.armId,o=e.allocationVersion;return typeof n!="string"||n===""||typeof r!="string"||r===""||typeof o!="string"?null:{experimentId:n,armId:r,allocationVersion:o}}function Me(t,e){try{let n=t.querySelector(Fe);if(!n)return[];let r=n.textContent;if(r==null||r.trim()==="")return[];let o=JSON.parse(r);if(typeof o!="object"||o===null)return e==null||e("capture",{reason:"assignments_malformed"}),[];let i=o;if(i.v!==1||!Array.isArray(i.experiments))return e==null||e("capture",{reason:"assignments_malformed"}),[];let s=[];for(let a of i.experiments){let c=Ve(a);c&&s.push(c)}return s}catch(n){return e==null||e("capture",{reason:"assignments_parse_failed",error:String(n)}),[]}}function J(t,e){if(!e||t.experiment!==void 0)return t;let n=e.getActiveAssignments()[0];if(!n)return t;let r={experimentId:n.experimentId,armId:n.armId,allocationVersion:n.allocationVersion};return{...t,experiment:r}}function Q(t){let e=[],n=new Set,r,o=()=>{e=Me(t.document,t.debug);for(let i of e)n.has(i.experimentId)||(n.add(i.experimentId),r==null||r({eventName:Be,url:t.window.location.href,experiment:{experimentId:i.experimentId,armId:i.armId,allocationVersion:i.allocationVersion}}))};return{getActiveAssignments:()=>e.map(i=>({...i})),start(i){r=i,o()},rearm(){n.clear(),o()}}}var Z="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function We(t){globalThis.crypto.getRandomValues(t)}function S(t,e){let n=t!=null?t:Date.now(),r="";for(let c=0;c<10;c++)r=Z[n%32]+r,n=Math.floor(n/32);let o=new Uint8Array(10);(e!=null?e:We)(o);let i="",s=0,a=0;for(let c of o)for(s=s<<8|c,a+=8;a>=5;)i+=Z[s>>>a-5&31],a-=5;return r+i}var Ge=60*1024,je=100,ze=5e3,ee='{"envelopeId":"',te='","events":[',ne="]}",He=26,A=ee.length+He+te.length+ne.length,qe=new TextEncoder,Ue=t=>qe.encode(t).length,T=class{constructor(e){y(this,"options");y(this,"maxBytes");y(this,"queue",[]);y(this,"queuedBytes",0);y(this,"flushing",!1);y(this,"intervalId");var n;this.options=e,this.maxBytes=(n=e.maxBatchBytes)!=null?n:Ge}get pending(){return this.queue.length}push(e){let n=JSON.stringify(e),r=Ue(n);this.queue.push({json:n,bytes:r,eventId:e.eventId}),this.queuedBytes+=r,!this.flushing&&this.envelopeBytes()>=this.maxBytes&&this.flush()}flush(){var e,n;if(!this.flushing){this.flushing=!0;try{(n=(e=this.options).onTick)==null||n.call(e),this.drain()}finally{this.flushing=!1}}}start(){var o,i,s,a;let e=(o=this.options.timers)!=null?o:{setInterval:(c,f)=>setInterval(c,f),clearInterval:c=>clearInterval(c)};this.intervalId=e.setInterval(()=>this.flush(),(i=this.options.flushIntervalMs)!=null?i:ze);let n=(s=this.options.documentTarget)!=null?s:typeof document!="undefined"?document:void 0;n==null||n.addEventListener("visibilitychange",()=>{n.visibilityState==="hidden"&&this.flush()});let r=(a=this.options.windowTarget)!=null?a:typeof window!="undefined"?window:void 0;r==null||r.addEventListener("pagehide",()=>this.flush()),this.flush()}stop(){if(this.intervalId!==void 0){let e=this.options.timers;e?e.clearInterval(this.intervalId):clearInterval(this.intervalId),this.intervalId=void 0}}envelopeBytes(){let e=this.queue.length>1?this.queue.length-1:0;return A+this.queuedBytes+e}drain(){var e,n,r;for(;this.queue.length>0;){let o=this.queue[0];if(A+o.bytes>this.maxBytes){this.queue.shift(),this.queuedBytes-=o.bytes,(n=(e=this.options).debug)==null||n.call(e,"flush",{reason:"oversize_event",eventId:o.eventId,bytes:o.bytes});continue}let i=[],s=A;for(;this.queue.length>0;){let c=this.queue[0];if(A+c.bytes>this.maxBytes||i.length>=je)break;let f=s+c.bytes+(i.length>0?1:0);if(f>this.maxBytes)break;i.push(c.json),s=f,this.queue.shift(),this.queuedBytes-=c.bytes}if(i.length===0)continue;let a=((r=this.options.createEnvelopeId)!=null?r:S)();this.options.send(ee+a+te+i.join(",")+ne)}}};var Xe=new Set(["c15t","onetrust","cookiebot","trustarc","manual"]);function P(t,e){var o;let n=(o=t==null?void 0:t.getAttribute)==null?void 0:o.call(t,e);if(n==null)return;let r=n.trim();return r===""?void 0:r}function re(t){var r;let e=(r=P(t,"data-aci-consent"))==null?void 0:r.toLowerCase(),n=e!==void 0&&Xe.has(e)?e:"manual";return{site:P(t,"data-aci-site"),preset:n,categories:P(t,"data-aci-consent-categories"),debug:P(t,"data-aci-debug")==="1"}}function oe(t,e,n){if(!t)return()=>{};let r=e!=null?e:(i=>console.debug("[aci]",i)),o=n!=null?n:Date.now;return(i,s)=>{try{r({t:o(),kind:i,detail:s})}catch{}}}var R=new TextEncoder,$e=2048,Ke=32,Ye=128,Je=512,Qe=2147483647,Ze=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/,et=/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g,tt=new Set(["true","all","granted","yes","personalization","personalized"]);function _(t,e=!0){let n=w(t);if(!n)return null;if(e)try{n=decodeURIComponent(n)}catch{}if(n=w(n),!n||R.encode(n).byteLength>$e)return null;if(tt.has(n.toLowerCase()))return{version:0,purposes:["personalization"],grantedAt:""};try{let r=JSON.parse(n),o=typeof r.t=="string"?r.t:"";return!Number.isInteger(r.v)||r.v<0||r.v>Qe||!Array.isArray(r.p)||r.p.length>Ke||!r.p.every(i=>typeof i=="string")||r.p.some(i=>R.encode(i).byteLength>Ye)||r.t!==void 0&&r.t!==null&&(typeof r.t!="string"||r.t===""||!nt(r.t))||r.src!==void 0&&r.src!==null&&typeof r.src!="string"||typeof r.src=="string"&&R.encode(r.src).byteLength>Je?null:{version:r.v,purposes:r.p,grantedAt:o,source:typeof r.src=="string"?r.src:void 0}}catch{return null}}function B(t){return(t==null?void 0:t.purposes.some(e=>w(e).toLowerCase()==="personalization"))||!1}function w(t){return t.replace(et,"")}function nt(t){let e=Ze.exec(t);if(!e||Number.isNaN(Date.parse(t)))return!1;let n=Number(e[1]),r=Number(e[2]),o=Number(e[3]),i=Number(e[4]),s=Number(e[5]),a=Number(e[6]);if(r<1||r>12||o<1||o>rt(n,r)||i>23||s>59||a>59)return!1;let c=e[8];return c==="Z"||Number(c.slice(1,3))<=23&&Number(c.slice(4,6))<=59}function rt(t,e){return e===2?t%4===0&&(t%100!==0||t%400===0)?29:28:[4,6,9,11].includes(e)?30:31}var ie="personalization",F="aci_consent",V=1,ot=365*24*60*60;function it(t,e){var r;let n=`${F}=${t.encoded}; Path=/; Max-Age=${ot}; SameSite=Lax`;((r=e.document.location)==null?void 0:r.protocol)==="https:"&&(n+="; Secure"),e.document.cookie=n}function I(t,e,n){var o;let r=st(t,e,n.now);return r===null?!1:(B(r.snapshot)||(o=n.gapBuffer)==null||o.purge(),it(r,n),!0)}function se(t){for(let e of t.cookie.split(";")){let n=w(e);if(n.startsWith(F+"="))return n.slice(F.length+1)}}function ae(t){return t===void 0||t===""?!1:B(_(t,!0))}function ce(t){var r;if(!Array.isArray(t))return;let e=[];for(let o of t){if(typeof o!="string")return;e.push(w(o))}let n=JSON.stringify({v:V,p:e});return(r=_(n,!1))==null?void 0:r.purposes}function ue(t){var o;if(t!==void 0&&typeof t!="string")return;let e=typeof t=="string"?w(t):"",r=JSON.stringify({v:V,p:[],src:e===""?"manual":e});return(o=_(r,!1))==null?void 0:o.source}function st(t,e,n){try{let r=JSON.stringify({v:V,p:t,t:new Date((n!=null?n:Date.now)()).toISOString(),src:e}),o=_(r,!1);return o===null?null:{encoded:encodeURIComponent(r),snapshot:o}}catch{return null}}var at="cmp-",ct=/^[a-z0-9._-]+$/,ut={c15t:"measurement,marketing",onetrust:"c0002,c0004",cookiebot:"statistics,marketing",trustarc:"functional,advertising"};function M(t,e){let n=e!=null?e:ut[t];if(n===void 0)return null;let r=n.split(",").map(o=>o.trim().toLowerCase()).filter(o=>o!=="");if(r.length===0)return null;for(let o of r)if(!ct.test(o))return null;return r}function ft(t,e){return e.filter(r=>r in t.categories).some(r=>t.categories[r]===!0)?[ie]:[]}function k(t,e,n){var r,o,i,s;try{let a=M(e,n.categories);if(a===null){(r=n.debug)==null||r.call(n,"consent",{reason:"unparseable_mapping",preset:e});return}if(a.filter(l=>l in t.categories).length===0){(o=n.debug)==null||o.call(n,"consent",{reason:"unmappable_cmp_state",preset:e});return}let f=ft(t,a);I(f,at+e,{document:n.document,gapBuffer:n.gapBuffer,now:n.now}),(i=n.debug)==null||i.call(n,"consent",{reason:"written",preset:e,purposes:f})}catch(a){(s=n.debug)==null||s.call(n,"consent",{reason:"consent_bridge_failed",preset:e,error:String(a)})}}var fe=250,lt=1e4;function C(t,e,n){var s;let r=(s=n==null?void 0:n.scheduler)!=null?s:{setTimeout:(a,c)=>setTimeout(a,c),clearTimeout:a=>clearTimeout(a)},o=Math.floor(lt/fe),i=()=>{var c,f;let a;try{a=t()}catch(l){(c=n==null?void 0:n.debug)==null||c.call(n,"consent",{reason:"cmp_read_failed",error:String(l)});return}if(a!==void 0){e(a);return}if(o-=1,o<=0){(f=n==null?void 0:n.debug)==null||f.call(n,"consent",{reason:"cmp_not_found"});return}r.setTimeout(i,fe)};i()}function b(t,e){try{e()}catch(n){t==null||t("consent",{reason:"adapter_failed",error:String(n)})}}var dt="c15t";function pt(t){if(typeof t!="object"||t===null)return;let e={};for(let[n,r]of Object.entries(t))typeof r=="boolean"&&(e[n.toLowerCase()]=r);return{categories:e}}function mt(t,e){return typeof t=="object"&&t!==null?"consents"in t?t.consents:t:e.consents}function le(t){C(()=>{let e=t.window.c15t;return typeof e=="object"&&e!==null?e:void 0},e=>{b(t.debug,()=>{var r;let n=o=>{let i=pt(o);i&&k(i,dt,t)};n(e.consents),(r=e.onConsentChange)==null||r.call(e,o=>b(t.debug,()=>n(mt(o,e))))})},t)}var gt="cookiebot",vt=["CookiebotOnConsentReady","CookiebotOnAccept","CookiebotOnDecline"];function de(t){let e=t.Cookiebot;return typeof e=="object"&&e!==null?e:void 0}function ht(t){if(typeof t!="object"||t===null)return;let e={};for(let[n,r]of Object.entries(t))typeof r=="boolean"&&(e[n.toLowerCase()]=r);return{categories:e}}function pe(t){let e=()=>{let r=de(t.window);if(!r||r.hasResponse!==!0)return;let o=ht(r.consent);o&&k(o,gt,t)},n=t.window;if(typeof n.addEventListener=="function")for(let r of vt)n.addEventListener(r,()=>b(t.debug,e));C(()=>de(t.window),()=>b(t.debug,e),t)}var me="onetrust",bt=/^c\d{4}$/;function yt(t,e){let n={};for(let r of t.split(",")){let o=r.trim().toLowerCase();o!==""&&(n[o]=!0)}for(let r of e!=null?e:[])!(r in n)&&bt.test(r)&&(n[r]=!1);return{categories:n}}function ge(t){let e=t.window,n=()=>{let o=e.OnetrustActiveGroups;if(typeof o!="string")return;let i=M(me,t.categories);k(yt(o,i),me,t)},r=e.OptanonWrapper;e.OptanonWrapper=()=>{if(typeof r=="function")try{r()}catch{}b(t.debug,n)},C(()=>{let o=e.OnetrustActiveGroups;return typeof o=="string"?o:void 0},()=>b(t.debug,n),t)}var Et="trustarc",kt={required:1,functional:2,advertising:3},ve="PrivacyManagerAPI";function he(t){let e=t.truste,n=e==null?void 0:e.cma,r=n==null?void 0:n.callApi;return typeof r=="function"?r.bind(n):void 0}function Ct(t){if(typeof t!="object"||t===null)return;let e=t.consentDecision;if(typeof e!="number"||!Number.isFinite(e)||e<1)return;let n={};for(let[r,o]of Object.entries(kt))n[r]=e>=o;return{categories:n}}function wt(t){return typeof t=="string"?t.includes(ve):typeof t=="object"&&t!==null&&ve in t}function St(t){let e=t.location;return typeof(e==null?void 0:e.hostname)=="string"?e.hostname:void 0}function be(t){let e=()=>{let r=he(t.window);if(!r)return;let o=Ct(r("getConsentDecision",St(t.window)));o&&k(o,Et,t)},n=t.window;typeof n.addEventListener=="function"&&n.addEventListener("message",r=>b(t.debug,()=>{wt(r==null?void 0:r.data)&&e()})),C(()=>he(t.window),()=>b(t.debug,e),t)}var N="aci:buf";var ye="aci_m",L=class{constructor(e){y(this,"events");y(this,"storage");y(this,"debug");this.debug=e==null?void 0:e.debug,this.storage=e==null?void 0:e.storage,this.events=this.restore()}push(e){var n;this.events.push(e),this.events.length>50&&(this.events.splice(0,this.events.length-50),(n=this.debug)==null||n.call(this,"buffer",{reason:"cap_drop_oldest"})),this.persist()}drain(){let e=this.events;return this.events=[],this.persist(),e}purge(){var e;this.events.length>0&&((e=this.debug)==null||e.call(this,"buffer",{reason:"purged",count:this.events.length})),this.events=[],this.persist()}size(){return this.events.length}restore(){var r;let e=this.storage;if(!e)return[];let n;try{n=e.getItem(N)}catch(o){return this.storage=void 0,(r=this.debug)==null||r.call(this,"buffer",{reason:"storage_unavailable",error:String(o)}),[]}if(n==null)return[];try{let o=JSON.parse(n);if(Array.isArray(o))return o}catch{}try{e.removeItem(N)}catch{this.storage=void 0}return[]}persist(){var n;let e=this.storage;if(e)try{this.events.length===0?e.removeItem(N):e.setItem(N,JSON.stringify(this.events))}catch(r){this.storage=void 0,(n=this.debug)==null||n.call(this,"buffer",{reason:"storage_unavailable",error:String(r)})}}};function Ee(t){for(let e of t.cookie.split(";")){let n=e.trim();if(n.startsWith(ye+"="))return n.slice(ye.length+1)!==""}return!1}function ke(t,e){if(e.grants()&&!e.marker()){e.gapBuffer.push(t);return}e.batcher.push(t)}function W(t,e,n,r){if(t.size()===0||!n())return;let o=t.drain();for(let i of o)e.push(i);r==null||r("buffer",{reason:"flushed",count:o.length})}var _t="rum.link_click";function Ce(t,e){var i;let{document:n,debug:r}=e,o=(i=n.defaultView)==null?void 0:i.location;n.addEventListener("click",s=>{var a,c,f;try{let l=s.target,p=(a=l==null?void 0:l.closest)==null?void 0:a.call(l,"a[href]");if(!p)return;let u=p;t({eventName:_t,url:o==null?void 0:o.href,properties:{linkText:(f=(c=u.textContent)==null?void 0:c.trim())!=null?f:"",linkHref:u.href}})}catch(l){r==null||r("capture",{reason:"link_click_failed",error:String(l)})}},{capture:!0})}var xt="rum.page_view";function we(t){let e=t.indexOf("#");return e===-1?t:t.slice(0,e)}function Se(t,e){let n=we(t.location.href),r=()=>{let s=we(t.location.href);if(s!==n){n=s;for(let a of e)a()}},o=t.history,i=s=>{let a=o[s].bind(o);o[s]=(...c)=>{let f=a(...c);return r(),f}};i("pushState"),i("replaceState"),t.addEventListener("popstate",r)}function _e(t,e){let n=()=>t({eventName:xt,url:e.location.href,referrer:e.document.referrer});return n(),n}var G="/collect";function xe(t,e){let n=e==null?void 0:e.debug,r=e!==void 0&&"navigator"in e?e.navigator:globalThis.navigator,o=e!==void 0&&"fetch"in e?e.fetch:globalThis.fetch,i;try{i=new Blob([t],{type:"text/plain"})}catch{n==null||n("flush",{reason:"no_blob"});return}if(r!=null&&r.sendBeacon)try{if(r.sendBeacon(G,i))return}catch{}if(!o){n==null||n("flush",{reason:"no_transport"});return}try{o(G,{method:"POST",body:i,keepalive:!0}).catch(s=>{n==null||n("flush",{reason:"fetch_failed",error:String(s)})})}catch(s){n==null||n("flush",{reason:"fetch_failed",error:String(s)})}}function Ae(t){var n,r;let e=t!==void 0&&"fetch"in t?t.fetch:globalThis.fetch;if(!e)return Promise.resolve(!1);try{let o=((n=t==null?void 0:t.createEnvelopeId)!=null?n:S)(),i=new Blob([`{"envelopeId":"${o}","events":[]}`],{type:"text/plain"});return e(G,{method:"POST",body:i,keepalive:!0,credentials:"same-origin"}).then(s=>{var a;return typeof s=="object"&&s!==null&&"ok"in s&&s.ok===!1?((a=t==null?void 0:t.debug)==null||a.call(t,"flush",{reason:"refresh_failed",status:"not_ok"}),!1):!0}).catch(s=>{var a;return(a=t==null?void 0:t.debug)==null||a.call(t,"flush",{reason:"refresh_failed",error:String(s)}),!1})}catch(o){return(r=t==null?void 0:t.debug)==null||r.call(t,"flush",{reason:"refresh_failed",error:String(o)}),Promise.resolve(!1)}}var At="rum.variation_view",j="data-aci-variation";function Te(t,e){var f,l,p;let{document:n,debug:r}=e,o=e.MutationObserver;if(typeof o!="function")return;let i=(f=n.defaultView)==null?void 0:f.location,s=new WeakSet,a=u=>{let h=u.getAttribute(j);h!=null&&(s.has(u)||(s.add(u),t({eventName:At,url:i==null?void 0:i.href,properties:{variantKey:h}})))},c=u=>{try{u.hasAttribute(j)&&a(u);for(let h of Array.from(u.querySelectorAll(`[${j}]`)))a(h)}catch(h){r==null||r("capture",{reason:"variation_scan_failed",error:String(h)})}};try{let u=(l=n.body)!=null?l:n.documentElement;u&&c(u)}catch(u){r==null||r("capture",{reason:"variation_initial_scan_failed",error:String(u)})}try{let u=(p=n.body)!=null?p:n.documentElement;if(!u)return;new o(m=>{try{for(let g of m)for(let d=0;d<g.addedNodes.length;d++){let v=g.addedNodes[d];if((v==null?void 0:v.nodeType)!==1)continue;let E=v.element;E&&c(E)}}catch(g){r==null||r("capture",{reason:"variation_observer_failed",error:String(g)})}}).observe(u,{subtree:!0,childList:!0})}catch(u){r==null||r("capture",{reason:"variation_observe_failed",error:String(u)})}}var Tt="rum.web_vital";function Pe(t,e){var h,m,g;let n=e.PerformanceObserver;if(typeof n!="function")return;let{performance:r,debug:o}=e,i=(m=(h=e.window)==null?void 0:h.location)==null?void 0:m.href,s=e.document,a=new Set,c=(d,v)=>{a.has(d)||(a.add(d),t({eventName:Tt,url:i,properties:{vitalName:d,vitalValue:v}}))};try{let d=r==null?void 0:r.getEntriesByType("navigation")[0];d&&typeof d.responseStart=="number"&&typeof d.requestStart=="number"&&c("TTFB",d.responseStart-d.requestStart)}catch(d){o==null||o("capture",{reason:"ttfb_failed",error:String(d)})}let f=0,l=0,p=(d,v)=>{try{new n(Ie=>{try{for(let O of Ie.getEntries())v(O)}catch(O){o==null||o("capture",{reason:`${d}_handler_failed`,error:String(O)})}}).observe({type:d,buffered:!0})}catch(E){o==null||o("capture",{reason:`${d}_observe_failed`,error:String(E)})}};p("largest-contentful-paint",d=>{c("LCP",d.startTime)}),p("first-input",d=>{c("FID",d.startTime)}),p("layout-shift",d=>{var v;d.hadRecentInput||(f+=(v=d.value)!=null?v:0)}),p("event",d=>{var E;let v=(E=d.value)!=null?E:0;v>l&&(l=v)}),p("paint",d=>{d.name==="first-contentful-paint"&&c("FCP",d.startTime)});let u=()=>{try{a.has("CLS")||c("CLS",f),a.has("INP")||c("INP",l)}catch(d){o==null||o("capture",{reason:"visibility_handler_failed",error:String(d)})}};s&&s.addEventListener("visibilitychange",()=>{s.visibilityState==="hidden"&&u()}),(g=e.window)==null||g.addEventListener("pagehide",u)}var z={c15t:le,onetrust:ge,cookiebot:pe,trustarc:be};function Pt(t){var e,n;return typeof((e=t.navigator)==null?void 0:e.sendBeacon)=="function"&&typeof t.IntersectionObserver=="function"&&typeof((n=t.crypto)==null?void 0:n.getRandomValues)=="function"}function It(t){try{return t.sessionStorage}catch{return}}function Nt(t){var o;let e=t!=null?t:typeof window!="undefined"?window:void 0;if(!e||e.__aci_loaded||(e.__aci_loaded=!0,!Pt(e)))return;let n=re(e.document.currentScript),r=oe(n.debug);try{let i=e.document,s=new L({storage:It(e),debug:r}),a=()=>{try{return ae(se(i))}catch{return!1}},c=()=>{try{return Ee(i)}catch{return!1}},f={navigator:e.navigator,fetch:typeof e.fetch=="function"?e.fetch.bind(e):void 0,debug:r},l;l=new T({send:m=>xe(m,f),debug:r,onTick:()=>W(s,l,c,r),timers:{setInterval:(m,g)=>e.setInterval(m,g),clearInterval:m=>e.clearInterval(m)},documentTarget:i,windowTarget:e});let p=Q({document:i,window:e,debug:r}),u=m=>{ke({...J(m,p),eventId:S(void 0,g=>e.crypto.getRandomValues(g))},{grants:a,marker:c,gapBuffer:s,batcher:l})};p.start(u),Y(e,n,{track:(m,g)=>u({eventName:m,properties:g}),consent:({p:m,src:g})=>{let d=ce(m);if(d===void 0){r("consent",{reason:"malformed_purposes"});return}let v=ue(g);if(v===void 0){r("consent",{reason:"malformed_source"});return}I(d,v,{document:i,gapBuffer:s})||r("consent",{reason:"oversized_envelope"})}},r);let h=_e(u,e);Se(e,[()=>p.rearm(),h]),$(u,{document:i,intersectionObserver:e.IntersectionObserver,debug:r},()=>{Ae(f).then(m=>{m&&e.location.reload()})}),Ce(u,{document:i,debug:r}),Pe(u,{performance:e.performance,PerformanceObserver:e.PerformanceObserver,window:e,document:i,debug:r}),Te(u,{document:i,MutationObserver:e.MutationObserver,debug:r}),n.preset!=="manual"&&((o=z[n.preset])==null||o.call(z,{window:e,document:i,categories:n.categories,gapBuffer:s,debug:r,scheduler:{setTimeout:(m,g)=>e.setTimeout(m,g),clearTimeout:m=>e.clearTimeout(m)}})),l.start(),W(s,l,c,r),r("init",{config:n})}catch(i){r("init",{error:String(i)})}}Nt();})();
1
+ "use strict";(()=>{var Oe=Object.defineProperty;var De=(t,e,n)=>e in t?Oe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var b=(t,e,n)=>De(t,typeof e!="symbol"?e+"":e,n);var A="data-aci-event",Re="data-aci-on",q="data-aci-prop-",Be="data-aci-refresh",U="custom.";function D(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(!(e===""||/[\s/]/.test(e)))return e.startsWith(U)?e:U+e}function Ve(t){let e;for(let n of Array.from(t.attributes)){if(!n.name.startsWith(q))continue;let r=n.name.slice(q.length);r!==""&&((e!=null?e:e={})[r]=n.value)}return e}function X(t,e){let n=D(t.getAttribute(A));if(n===void 0){e==null||e("capture",{reason:"malformed_declaration",attr:A});return}return{eventName:n,properties:Ve(t)}}function $(t){var e,n;return(n=(e=t.getAttribute(Re))==null?void 0:e.trim().toLowerCase())!=null?n:"click"}function K(t,e,n){let{document:r,debug:o}=e;r.addEventListener("click",f=>{var d,p;try{let l=f.target;if((d=l==null?void 0:l.closest)==null?void 0:d.call(l,`[${Be}]`)){n==null||n();return}let y=(p=l==null?void 0:l.closest)==null?void 0:p.call(l,`[${A}]`);if(!y||$(y)!=="click")return;let m=X(y,o);m&&t(m)}catch(l){o==null||o("capture",{reason:"click_handler_failed",error:String(l)})}});let i=e.intersectionObserver;if(typeof i!="function")return;let s=Array.from(r.querySelectorAll(`[${A}]`)).filter(f=>$(f)==="visible");if(s.length===0)return;let a=new WeakSet,c=new i((f,d)=>{try{for(let p of f){if(!p.isIntersecting||p.intersectionRatio<.5||a.has(p.target))continue;a.add(p.target),d.unobserve(p.target);let l=X(p.target,o);l&&t(l)}}catch(p){o==null||o("capture",{reason:"visible_handler_failed",error:String(p)})}},{threshold:.5});for(let f of s)c.observe(f)}function Y(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function J(t,e,n,r){let o=(...a)=>{try{let[c,...f]=a;if(c==="track"){let d=D(f[0]);if(d===void 0){r==null||r("capture",{reason:"malformed_track_name",name:f[0]});return}let p=f[1];if(p!==void 0&&!Y(p)){r==null||r("capture",{reason:"malformed_track_properties",name:d});return}n.track(d,p);return}if(c==="consent"){if(e.preset!=="manual"){r==null||r("consent",{reason:"preset_authoritative",preset:e.preset});return}let d=f[0];if(!Y(d)){r==null||r("consent",{reason:"malformed_consent_envelope"});return}n.consent({p:d.p,src:d.src});return}r==null||r("capture",{reason:"unknown_command",command:c})}catch(c){r==null||r("capture",{reason:"command_failed",error:String(c)})}},i=t.aci,s=i==null?void 0:i.q;if(t.aci=o,s!=null)for(let a of Array.from(s)){let c=Array.from(a);o(...c)}}var Fe="experiment.exposure",Me='script[type="application/json"][data-aci-assignments]';function We(t){if(typeof t!="object"||t===null)return null;let e=t,n=e.experimentId,r=e.armId,o=e.allocationVersion;return typeof n!="string"||n===""||typeof r!="string"||r===""||typeof o!="string"?null:{experimentId:n,armId:r,allocationVersion:o}}function Ge(t,e){try{let n=t.querySelector(Me);if(!n)return[];let r=n.textContent;if(r==null||r.trim()==="")return[];let o=JSON.parse(r);if(typeof o!="object"||o===null)return e==null||e("capture",{reason:"assignments_malformed"}),[];let i=o;if(i.v!==1||!Array.isArray(i.experiments))return e==null||e("capture",{reason:"assignments_malformed"}),[];let s=[];for(let a of i.experiments){let c=We(a);c&&s.push(c)}return s}catch(n){return e==null||e("capture",{reason:"assignments_parse_failed",error:String(n)}),[]}}function Q(t,e){if(!e||t.experiment!==void 0)return t;let n=e.getActiveAssignments()[0];if(!n)return t;let r={experimentId:n.experimentId,armId:n.armId,allocationVersion:n.allocationVersion};return{...t,experiment:r}}function Z(t){let e=[],n=new Set,r,o=()=>{e=Ge(t.document,t.debug);for(let i of e)n.has(i.experimentId)||(n.add(i.experimentId),r==null||r({eventName:Fe,url:t.window.location.href,experiment:{experimentId:i.experimentId,armId:i.armId,allocationVersion:i.allocationVersion}}))};return{getActiveAssignments:()=>e.map(i=>({...i})),start(i){r=i,o()},rearm(){n.clear(),o()}}}var ee="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function He(t){globalThis.crypto.getRandomValues(t)}function S(t,e){let n=t!=null?t:Date.now(),r="";for(let c=0;c<10;c++)r=ee[n%32]+r,n=Math.floor(n/32);let o=new Uint8Array(10);(e!=null?e:He)(o);let i="",s=0,a=0;for(let c of o)for(s=s<<8|c,a+=8;a>=5;)i+=ee[s>>>a-5&31],a-=5;return r+i}var je=60*1024,ze=100,qe=5e3,te='{"envelopeId":"',ne='","events":[',re="]}",Ue=26,T=te.length+Ue+ne.length+re.length,Xe=new TextEncoder,$e=t=>Xe.encode(t).length,x=class{constructor(e){b(this,"options");b(this,"maxBytes");b(this,"queue",[]);b(this,"queuedBytes",0);b(this,"flushing",!1);b(this,"intervalId");var n;this.options=e,this.maxBytes=(n=e.maxBatchBytes)!=null?n:je}get pending(){return this.queue.length}push(e){let n=JSON.stringify(e),r=$e(n);this.queue.push({json:n,bytes:r,eventId:e.eventId}),this.queuedBytes+=r,!this.flushing&&this.envelopeBytes()>=this.maxBytes&&this.flush()}flush(){var e,n;if(!this.flushing){this.flushing=!0;try{(n=(e=this.options).onTick)==null||n.call(e),this.drain()}finally{this.flushing=!1}}}start(){var o,i,s,a;let e=(o=this.options.timers)!=null?o:{setInterval:(c,f)=>setInterval(c,f),clearInterval:c=>clearInterval(c)};this.intervalId=e.setInterval(()=>this.flush(),(i=this.options.flushIntervalMs)!=null?i:qe);let n=(s=this.options.documentTarget)!=null?s:typeof document!="undefined"?document:void 0;n==null||n.addEventListener("visibilitychange",()=>{n.visibilityState==="hidden"&&this.flush()});let r=(a=this.options.windowTarget)!=null?a:typeof window!="undefined"?window:void 0;r==null||r.addEventListener("pagehide",()=>this.flush()),this.flush()}stop(){if(this.intervalId!==void 0){let e=this.options.timers;e?e.clearInterval(this.intervalId):clearInterval(this.intervalId),this.intervalId=void 0}}envelopeBytes(){let e=this.queue.length>1?this.queue.length-1:0;return T+this.queuedBytes+e}drain(){var e,n,r;for(;this.queue.length>0;){let o=this.queue[0];if(T+o.bytes>this.maxBytes){this.queue.shift(),this.queuedBytes-=o.bytes,(n=(e=this.options).debug)==null||n.call(e,"flush",{reason:"oversize_event",eventId:o.eventId,bytes:o.bytes});continue}let i=[],s=T;for(;this.queue.length>0;){let c=this.queue[0];if(T+c.bytes>this.maxBytes||i.length>=ze)break;let f=s+c.bytes+(i.length>0?1:0);if(f>this.maxBytes)break;i.push(c.json),s=f,this.queue.shift(),this.queuedBytes-=c.bytes}if(i.length===0)continue;let a=((r=this.options.createEnvelopeId)!=null?r:S)();this.options.send(te+a+ne+i.join(",")+re)}}};var Ke=new Set(["c15t","onetrust","cookiebot","trustarc","manual"]);function P(t,e){var o;let n=(o=t==null?void 0:t.getAttribute)==null?void 0:o.call(t,e);if(n==null)return;let r=n.trim();return r===""?void 0:r}function oe(t){var r;let e=(r=P(t,"data-aci-consent"))==null?void 0:r.toLowerCase(),n=e!==void 0&&Ke.has(e)?e:"manual";return{site:P(t,"data-aci-site"),preset:n,categories:P(t,"data-aci-consent-categories"),debug:P(t,"data-aci-debug")==="1"}}function ie(t,e,n){if(!t)return()=>{};let r=e!=null?e:(i=>console.debug("[aci]",i)),o=n!=null?n:Date.now;return(i,s)=>{try{r({t:o(),kind:i,detail:s})}catch{}}}var R=new TextEncoder,Ye=2048,Je=32,Qe=128,Ze=512,et=2147483647,tt=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/,nt=/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g,rt=new Set(["true","all","granted","yes","personalization","personalized"]);function _(t,e=!0){let n=w(t);if(!n)return null;if(e)try{n=decodeURIComponent(n)}catch{}if(n=w(n),!n||R.encode(n).byteLength>Ye)return null;if(rt.has(n.toLowerCase()))return{version:0,purposes:["personalization"],grantedAt:""};try{let r=JSON.parse(n),o=typeof r.t=="string"?r.t:"";return!Number.isInteger(r.v)||r.v<0||r.v>et||!Array.isArray(r.p)||r.p.length>Je||!r.p.every(i=>typeof i=="string")||r.p.some(i=>R.encode(i).byteLength>Qe)||r.t!==void 0&&r.t!==null&&(typeof r.t!="string"||r.t===""||!ot(r.t))||r.src!==void 0&&r.src!==null&&typeof r.src!="string"||typeof r.src=="string"&&R.encode(r.src).byteLength>Ze?null:{version:r.v,purposes:r.p,grantedAt:o,source:typeof r.src=="string"?r.src:void 0}}catch{return null}}function B(t){return(t==null?void 0:t.purposes.some(e=>w(e).toLowerCase()==="personalization"))||!1}function w(t){return t.replace(nt,"")}function ot(t){let e=tt.exec(t);if(!e||Number.isNaN(Date.parse(t)))return!1;let n=Number(e[1]),r=Number(e[2]),o=Number(e[3]),i=Number(e[4]),s=Number(e[5]),a=Number(e[6]);if(r<1||r>12||o<1||o>it(n,r)||i>23||s>59||a>59)return!1;let c=e[8];return c==="Z"||Number(c.slice(1,3))<=23&&Number(c.slice(4,6))<=59}function it(t,e){return e===2?t%4===0&&(t%100!==0||t%400===0)?29:28:[4,6,9,11].includes(e)?30:31}var se="personalization",V="aci_consent",F=1,st=365*24*60*60;function at(t,e){var r;let n=`${V}=${t.encoded}; Path=/; Max-Age=${st}; SameSite=Lax`;((r=e.document.location)==null?void 0:r.protocol)==="https:"&&(n+="; Secure"),e.document.cookie=n}function I(t,e,n){var o;let r=ct(t,e,n.now);return r===null?!1:(B(r.snapshot)||(o=n.gapBuffer)==null||o.purge(),at(r,n),!0)}function ae(t){for(let e of t.cookie.split(";")){let n=w(e);if(n.startsWith(V+"="))return n.slice(V.length+1)}}function ce(t){return t===void 0||t===""?!1:B(_(t,!0))}function ue(t){var r;if(!Array.isArray(t))return;let e=[];for(let o of t){if(typeof o!="string")return;e.push(w(o))}let n=JSON.stringify({v:F,p:e});return(r=_(n,!1))==null?void 0:r.purposes}function le(t){var o;if(t!==void 0&&typeof t!="string")return;let e=typeof t=="string"?w(t):"",r=JSON.stringify({v:F,p:[],src:e===""?"manual":e});return(o=_(r,!1))==null?void 0:o.source}function ct(t,e,n){try{let r=JSON.stringify({v:F,p:t,t:new Date((n!=null?n:Date.now)()).toISOString(),src:e}),o=_(r,!1);return o===null?null:{encoded:encodeURIComponent(r),snapshot:o}}catch{return null}}var ut="cmp-",lt=/^[a-z0-9._-]+$/,ft={c15t:"measurement,marketing",onetrust:"c0002,c0004",cookiebot:"statistics,marketing",trustarc:"functional,advertising"};function M(t,e){let n=e!=null?e:ft[t];if(n===void 0)return null;let r=n.split(",").map(o=>o.trim().toLowerCase()).filter(o=>o!=="");if(r.length===0)return null;for(let o of r)if(!lt.test(o))return null;return r}function dt(t,e){return e.filter(r=>r in t.categories).some(r=>t.categories[r]===!0)?[se]:[]}function k(t,e,n){var r,o,i,s;try{let a=M(e,n.categories);if(a===null){(r=n.debug)==null||r.call(n,"consent",{reason:"unparseable_mapping",preset:e});return}if(a.filter(d=>d in t.categories).length===0){(o=n.debug)==null||o.call(n,"consent",{reason:"unmappable_cmp_state",preset:e});return}let f=dt(t,a);I(f,ut+e,{document:n.document,gapBuffer:n.gapBuffer,now:n.now}),(i=n.debug)==null||i.call(n,"consent",{reason:"written",preset:e,purposes:f})}catch(a){(s=n.debug)==null||s.call(n,"consent",{reason:"consent_bridge_failed",preset:e,error:String(a)})}}var fe=250,pt=1e4;function C(t,e,n){var s;let r=(s=n==null?void 0:n.scheduler)!=null?s:{setTimeout:(a,c)=>setTimeout(a,c),clearTimeout:a=>clearTimeout(a)},o=Math.floor(pt/fe),i=()=>{var c,f;let a;try{a=t()}catch(d){(c=n==null?void 0:n.debug)==null||c.call(n,"consent",{reason:"cmp_read_failed",error:String(d)});return}if(a!==void 0){e(a);return}if(o-=1,o<=0){(f=n==null?void 0:n.debug)==null||f.call(n,"consent",{reason:"cmp_not_found"});return}r.setTimeout(i,fe)};i()}function h(t,e){try{e()}catch(n){t==null||t("consent",{reason:"adapter_failed",error:String(n)})}}var mt="c15t";function gt(t){if(typeof t!="object"||t===null)return;let e={};for(let[n,r]of Object.entries(t))typeof r=="boolean"&&(e[n.toLowerCase()]=r);return{categories:e}}function vt(t,e){return typeof t=="object"&&t!==null?"consents"in t?t.consents:t:e.consents}function de(t){C(()=>{let e=t.window.c15t;return typeof e=="object"&&e!==null?e:void 0},e=>{h(t.debug,()=>{var r;let n=o=>{let i=gt(o);i&&k(i,mt,t)};n(e.consents),(r=e.onConsentChange)==null||r.call(e,o=>h(t.debug,()=>n(vt(o,e))))})},t)}var ht="cookiebot",bt=["CookiebotOnConsentReady","CookiebotOnAccept","CookiebotOnDecline"];function pe(t){let e=t.Cookiebot;return typeof e=="object"&&e!==null?e:void 0}function yt(t){if(typeof t!="object"||t===null)return;let e={};for(let[n,r]of Object.entries(t))typeof r=="boolean"&&(e[n.toLowerCase()]=r);return{categories:e}}function me(t){let e=()=>{let r=pe(t.window);if(!r||r.hasResponse!==!0)return;let o=yt(r.consent);o&&k(o,ht,t)},n=t.window;if(typeof n.addEventListener=="function")for(let r of bt)n.addEventListener(r,()=>h(t.debug,e));C(()=>pe(t.window),()=>h(t.debug,e),t)}var ge="onetrust",Et=/^c\d{4}$/;function kt(t,e){let n={};for(let r of t.split(",")){let o=r.trim().toLowerCase();o!==""&&(n[o]=!0)}for(let r of e!=null?e:[])!(r in n)&&Et.test(r)&&(n[r]=!1);return{categories:n}}function ve(t){let e=t.window,n=()=>{let o=e.OnetrustActiveGroups;if(typeof o!="string")return;let i=M(ge,t.categories);k(kt(o,i),ge,t)},r=e.OptanonWrapper;e.OptanonWrapper=()=>{if(typeof r=="function")try{r()}catch{}h(t.debug,n)},C(()=>{let o=e.OnetrustActiveGroups;return typeof o=="string"?o:void 0},()=>h(t.debug,n),t)}var Ct="trustarc",wt={required:1,functional:2,advertising:3},he="PrivacyManagerAPI";function be(t){let e=t.truste,n=e==null?void 0:e.cma,r=n==null?void 0:n.callApi;return typeof r=="function"?r.bind(n):void 0}function St(t){if(typeof t!="object"||t===null)return;let e=t.consentDecision;if(typeof e!="number"||!Number.isFinite(e)||e<1)return;let n={};for(let[r,o]of Object.entries(wt))n[r]=e>=o;return{categories:n}}function _t(t){return typeof t=="string"?t.includes(he):typeof t=="object"&&t!==null&&he in t}function At(t){let e=t.location;return typeof(e==null?void 0:e.hostname)=="string"?e.hostname:void 0}function ye(t){let e=()=>{let r=be(t.window);if(!r)return;let o=St(r("getConsentDecision",At(t.window)));o&&k(o,Ct,t)},n=t.window;typeof n.addEventListener=="function"&&n.addEventListener("message",r=>h(t.debug,()=>{_t(r==null?void 0:r.data)&&e()})),C(()=>be(t.window),()=>h(t.debug,e),t)}function Ee(t){var n;let e=(n=t.userAgentData)==null?void 0:n.mobile;return e===!0?"mobile":e===!1?"desktop":typeof t.innerWidth=="number"&&Number.isFinite(t.innerWidth)&&t.innerWidth>0?t.innerWidth<=767?"mobile":t.innerWidth<=1024?"tablet":"desktop":"unknown"}var L="aci:buf";var ke="aci_m",N=class{constructor(e){b(this,"events");b(this,"storage");b(this,"debug");this.debug=e==null?void 0:e.debug,this.storage=e==null?void 0:e.storage,this.events=this.restore()}push(e){var n;this.events.push(e),this.events.length>50&&(this.events.splice(0,this.events.length-50),(n=this.debug)==null||n.call(this,"buffer",{reason:"cap_drop_oldest"})),this.persist()}drain(){let e=this.events;return this.events=[],this.persist(),e}purge(){var e;this.events.length>0&&((e=this.debug)==null||e.call(this,"buffer",{reason:"purged",count:this.events.length})),this.events=[],this.persist()}size(){return this.events.length}restore(){var r;let e=this.storage;if(!e)return[];let n;try{n=e.getItem(L)}catch(o){return this.storage=void 0,(r=this.debug)==null||r.call(this,"buffer",{reason:"storage_unavailable",error:String(o)}),[]}if(n==null)return[];try{let o=JSON.parse(n);if(Array.isArray(o))return o}catch{}try{e.removeItem(L)}catch{this.storage=void 0}return[]}persist(){var n;let e=this.storage;if(e)try{this.events.length===0?e.removeItem(L):e.setItem(L,JSON.stringify(this.events))}catch(r){this.storage=void 0,(n=this.debug)==null||n.call(this,"buffer",{reason:"storage_unavailable",error:String(r)})}}};function Ce(t){for(let e of t.cookie.split(";")){let n=e.trim();if(n.startsWith(ke+"="))return n.slice(ke.length+1)!==""}return!1}function we(t,e){if(e.grants()&&!e.marker()){e.gapBuffer.push(t);return}e.batcher.push(t)}function W(t,e,n,r){if(t.size()===0||!n())return;let o=t.drain();for(let i of o)e.push(i);r==null||r("buffer",{reason:"flushed",count:o.length})}var Tt="rum.link_click";function Se(t,e){var i;let{document:n,debug:r}=e,o=(i=n.defaultView)==null?void 0:i.location;n.addEventListener("click",s=>{var a,c,f;try{let d=s.target,p=(a=d==null?void 0:d.closest)==null?void 0:a.call(d,"a[href]");if(!p)return;let l=p;t({eventName:Tt,url:o==null?void 0:o.href,properties:{linkText:(f=(c=l.textContent)==null?void 0:c.trim())!=null?f:"",linkHref:l.href}})}catch(d){r==null||r("capture",{reason:"link_click_failed",error:String(d)})}},{capture:!0})}var xt="rum.page_view";function _e(t){let e=t.indexOf("#");return e===-1?t:t.slice(0,e)}function Ae(t,e){let n=_e(t.location.href),r=()=>{let s=_e(t.location.href);if(s!==n){n=s;for(let a of e)a()}},o=t.history,i=s=>{let a=o[s].bind(o);o[s]=(...c)=>{let f=a(...c);return r(),f}};i("pushState"),i("replaceState"),t.addEventListener("popstate",r)}function Te(t,e){let n=()=>t({eventName:xt,url:e.location.href,referrer:e.document.referrer});return n(),n}var G="/collect";function xe(t,e){let n=e==null?void 0:e.debug,r=e!==void 0&&"navigator"in e?e.navigator:globalThis.navigator,o=e!==void 0&&"fetch"in e?e.fetch:globalThis.fetch,i;try{i=new Blob([t],{type:"text/plain"})}catch{n==null||n("flush",{reason:"no_blob"});return}if(r!=null&&r.sendBeacon)try{if(r.sendBeacon(G,i))return}catch{}if(!o){n==null||n("flush",{reason:"no_transport"});return}try{o(G,{method:"POST",body:i,keepalive:!0}).catch(s=>{n==null||n("flush",{reason:"fetch_failed",error:String(s)})})}catch(s){n==null||n("flush",{reason:"fetch_failed",error:String(s)})}}function Pe(t){var n,r;let e=t!==void 0&&"fetch"in t?t.fetch:globalThis.fetch;if(!e)return Promise.resolve(!1);try{let o=((n=t==null?void 0:t.createEnvelopeId)!=null?n:S)(),i=new Blob([`{"envelopeId":"${o}","events":[]}`],{type:"text/plain"});return e(G,{method:"POST",body:i,keepalive:!0,credentials:"same-origin"}).then(s=>{var a;return typeof s=="object"&&s!==null&&"ok"in s&&s.ok===!1?((a=t==null?void 0:t.debug)==null||a.call(t,"flush",{reason:"refresh_failed",status:"not_ok"}),!1):!0}).catch(s=>{var a;return(a=t==null?void 0:t.debug)==null||a.call(t,"flush",{reason:"refresh_failed",error:String(s)}),!1})}catch(o){return(r=t==null?void 0:t.debug)==null||r.call(t,"flush",{reason:"refresh_failed",error:String(o)}),Promise.resolve(!1)}}var Pt="rum.variation_view",H="data-aci-variation";function Ie(t,e){var f,d,p;let{document:n,debug:r}=e,o=e.MutationObserver;if(typeof o!="function")return;let i=(f=n.defaultView)==null?void 0:f.location,s=new WeakSet,a=l=>{let g=l.getAttribute(H);g!=null&&(s.has(l)||(s.add(l),t({eventName:Pt,url:i==null?void 0:i.href,properties:{variantKey:g}})))},c=l=>{try{l.hasAttribute(H)&&a(l);for(let g of Array.from(l.querySelectorAll(`[${H}]`)))a(g)}catch(g){r==null||r("capture",{reason:"variation_scan_failed",error:String(g)})}};try{let l=(d=n.body)!=null?d:n.documentElement;l&&c(l)}catch(l){r==null||r("capture",{reason:"variation_initial_scan_failed",error:String(l)})}try{let l=(p=n.body)!=null?p:n.documentElement;if(!l)return;new o(y=>{try{for(let m of y)for(let u=0;u<m.addedNodes.length;u++){let v=m.addedNodes[u];if((v==null?void 0:v.nodeType)!==1)continue;let E=v.element;E&&c(E)}}catch(m){r==null||r("capture",{reason:"variation_observer_failed",error:String(m)})}}).observe(l,{subtree:!0,childList:!0})}catch(l){r==null||r("capture",{reason:"variation_observe_failed",error:String(l)})}}var It="rum.web_vital";function Le(t,e){var g,y,m;let n=e.PerformanceObserver;if(typeof n!="function")return;let{performance:r,debug:o}=e,i=(y=(g=e.window)==null?void 0:g.location)==null?void 0:y.href,s=e.document,a=new Set,c=(u,v)=>{a.has(u)||(a.add(u),t({eventName:It,url:i,properties:{vitalName:u,vitalValue:v}}))};try{let u=r==null?void 0:r.getEntriesByType("navigation")[0];u&&typeof u.responseStart=="number"&&typeof u.requestStart=="number"&&c("TTFB",u.responseStart-u.requestStart)}catch(u){o==null||o("capture",{reason:"ttfb_failed",error:String(u)})}let f=0,d=0,p=(u,v,E)=>{try{new n(Ne=>{try{for(let O of Ne.getEntries())v(O)}catch(O){o==null||o("capture",{reason:`${u}_handler_failed`,error:String(O)})}}).observe({type:u,buffered:!0,...E})}catch(z){o==null||o("capture",{reason:`${u}_observe_failed`,error:String(z)})}};p("largest-contentful-paint",u=>{c("LCP",u.startTime)}),p("first-input",u=>{typeof u.processingStart=="number"&&c("FID",u.processingStart-u.startTime)}),p("layout-shift",u=>{var v;u.hadRecentInput||(f+=(v=u.value)!=null?v:0)}),p("event",u=>{var E;if(!u.interactionId)return;let v=(E=u.duration)!=null?E:0;v>d&&(d=v)},{durationThreshold:40}),p("paint",u=>{u.name==="first-contentful-paint"&&c("FCP",u.startTime)});let l=()=>{try{a.has("CLS")||c("CLS",f),a.has("INP")||c("INP",d)}catch(u){o==null||o("capture",{reason:"visibility_handler_failed",error:String(u)})}};s&&s.addEventListener("visibilitychange",()=>{s.visibilityState==="hidden"&&l()}),(m=e.window)==null||m.addEventListener("pagehide",l)}var j={c15t:de,onetrust:ve,cookiebot:me,trustarc:ye};function Lt(t){var e,n;return typeof((e=t.navigator)==null?void 0:e.sendBeacon)=="function"&&typeof t.IntersectionObserver=="function"&&typeof((n=t.crypto)==null?void 0:n.getRandomValues)=="function"}function Nt(t){try{return t.sessionStorage}catch{return}}function Ot(t){var o;let e=t!=null?t:typeof window!="undefined"?window:void 0;if(!e||e.__aci_loaded||(e.__aci_loaded=!0,!Lt(e)))return;let n=oe(e.document.currentScript),r=ie(n.debug);try{let i=e.document,s=new N({storage:Nt(e),debug:r}),a=()=>{try{return ce(ae(i))}catch{return!1}},c=()=>{try{return Ce(i)}catch{return!1}},f={navigator:e.navigator,fetch:typeof e.fetch=="function"?e.fetch.bind(e):void 0,debug:r},d;d=new x({send:m=>xe(m,f),debug:r,onTick:()=>W(s,d,c,r),timers:{setInterval:(m,u)=>e.setInterval(m,u),clearInterval:m=>e.clearInterval(m)},documentTarget:i,windowTarget:e});let p=Z({document:i,window:e,debug:r}),l=Ee({userAgentData:e.navigator.userAgentData,innerWidth:e.innerWidth}),g=m=>{we({...Q(m,p),deviceClass:l,eventId:S(void 0,u=>e.crypto.getRandomValues(u))},{grants:a,marker:c,gapBuffer:s,batcher:d})};p.start(g),J(e,n,{track:(m,u)=>g({eventName:m,properties:u}),consent:({p:m,src:u})=>{let v=ue(m);if(v===void 0){r("consent",{reason:"malformed_purposes"});return}let E=le(u);if(E===void 0){r("consent",{reason:"malformed_source"});return}I(v,E,{document:i,gapBuffer:s})||r("consent",{reason:"oversized_envelope"})}},r);let y=Te(g,e);Ae(e,[()=>p.rearm(),y]),K(g,{document:i,intersectionObserver:e.IntersectionObserver,debug:r},()=>{Pe(f).then(m=>{m&&e.location.reload()})}),Se(g,{document:i,debug:r}),Le(g,{performance:e.performance,PerformanceObserver:e.PerformanceObserver,window:e,document:i,debug:r}),Ie(g,{document:i,MutationObserver:e.MutationObserver,debug:r}),n.preset!=="manual"&&((o=j[n.preset])==null||o.call(j,{window:e,document:i,categories:n.categories,gapBuffer:s,debug:r,scheduler:{setTimeout:(m,u)=>e.setTimeout(m,u),clearTimeout:m=>e.clearTimeout(m)}})),d.start(),W(s,d,c,r),r("init",{config:n})}catch(i){r("init",{error:String(i)})}}Ot();})();
@@ -1 +1 @@
1
- 46f87bbce4c938e9540939102be1094f5cd956d0f3a5836e9af5c3b0b8c582ad
1
+ adac880f8cf557fb4488d38395cfb5e47a1aa6427a660b936bfdade317d800f9
@@ -20,6 +20,12 @@ export interface CollectEvent {
20
20
  url?: string;
21
21
  referrer?: string;
22
22
  experiment?: ExperimentContext;
23
+ /**
24
+ * Coarse device class computed once at init (deviceclass.ts) and stamped
25
+ * on every event. Closed enum — the collectors floor anything else to
26
+ * "unknown", and the value eventually joins a rollup primary key.
27
+ */
28
+ deviceClass?: "desktop" | "mobile" | "tablet" | "unknown";
23
29
  }
24
30
  /** An event at capture time, before the eventId is stamped. */
25
31
  export type PendingEvent = Omit<CollectEvent, "eventId">;
@@ -0,0 +1,12 @@
1
+ export type DeviceClass = "desktop" | "mobile" | "tablet" | "unknown";
2
+ export declare const MOBILE_MAX_VIEWPORT_WIDTH = 767;
3
+ export declare const TABLET_MAX_VIEWPORT_WIDTH = 1024;
4
+ export interface DeviceClassEnv {
5
+ /** navigator.userAgentData — absent on Firefox and Safari. */
6
+ userAgentData?: {
7
+ mobile?: unknown;
8
+ } | null;
9
+ /** window.innerWidth at init (layout viewport, CSS px). */
10
+ innerWidth?: unknown;
11
+ }
12
+ export declare function computeDeviceClass(env: DeviceClassEnv): DeviceClass;
@@ -0,0 +1,27 @@
1
+ // Device class (personalization-reporting D-PR-22): one coarse, closed-enum
2
+ // classification computed ONCE at init and stamped on every batched event.
3
+ // Server-side UA parsing is deliberately absent — CloudFront strips
4
+ // User-Agent before the origin, and a parsing dependency is unwarranted for
5
+ // four bounded values that eventually join a rollup primary key.
6
+ //
7
+ // Precedence: navigator.userAgentData.mobile (UA-CH) wins where supported;
8
+ // the layout-viewport width is the fallback for Firefox/Safari. The fallback
9
+ // is intentionally crude — a narrow desktop window can misclassify — which
10
+ // is why UA-CH always wins when present.
11
+ export const MOBILE_MAX_VIEWPORT_WIDTH = 767;
12
+ export const TABLET_MAX_VIEWPORT_WIDTH = 1024;
13
+ export function computeDeviceClass(env) {
14
+ const uaMobile = env.userAgentData?.mobile;
15
+ if (uaMobile === true)
16
+ return "mobile";
17
+ if (uaMobile === false)
18
+ return "desktop";
19
+ if (typeof env.innerWidth === "number" && Number.isFinite(env.innerWidth) && env.innerWidth > 0) {
20
+ if (env.innerWidth <= MOBILE_MAX_VIEWPORT_WIDTH)
21
+ return "mobile";
22
+ if (env.innerWidth <= TABLET_MAX_VIEWPORT_WIDTH)
23
+ return "tablet";
24
+ return "desktop";
25
+ }
26
+ return "unknown";
27
+ }
@@ -12,6 +12,7 @@ import { start as startOneTrust } from "./consent/adapters/onetrust.js";
12
12
  import { start as startTrustArc } from "./consent/adapters/trustarc.js";
13
13
  import { applyConsent, envelopeGrantsPersonalization, readConsentCookie, sanitizeConsentSource, sanitizePurposes, } from "./consent/envelope.js";
14
14
  import { startDeclarative } from "./declarative.js";
15
+ import { computeDeviceClass } from "./deviceclass.js";
15
16
  import { GapBuffer, hasMintMarker, maybeFlushGapBuffer, routeEvent, } from "./gapbuffer.js";
16
17
  import { startLinkClicks } from "./linkclicks.js";
17
18
  import { installNavigationHook, startPageViews, } from "./pageviews.js";
@@ -101,9 +102,17 @@ export function init(win) {
101
102
  // is created (without emitting) BEFORE emit so every dependency of emit
102
103
  // exists before emit can run; exposures flow at start() below.
103
104
  const assignments = createAssignments({ document: doc, window: w, debug });
105
+ // One coarse classification per page load (D-PR-22): UA-CH where the
106
+ // browser has it, viewport width as the fallback. Stamped on every
107
+ // event below so it survives the gap buffer with the event.
108
+ const deviceClass = computeDeviceClass({
109
+ userAgentData: w.navigator.userAgentData,
110
+ innerWidth: w.innerWidth,
111
+ });
104
112
  const emit = (event) => {
105
113
  routeEvent({
106
114
  ...withAssignmentContext(event, assignments),
115
+ deviceClass,
107
116
  eventId: ulid(undefined, (bytes) => w.crypto.getRandomValues(bytes)),
108
117
  }, { grants, marker, gapBuffer, batcher });
109
118
  };
@@ -6,6 +6,9 @@ interface PerformanceEntryLike {
6
6
  name?: string;
7
7
  startTime: number;
8
8
  value?: number;
9
+ duration?: number;
10
+ interactionId?: number;
11
+ processingStart?: number;
9
12
  responseStart?: number;
10
13
  requestStart?: number;
11
14
  hadRecentInput?: boolean;
@@ -2,10 +2,13 @@
2
2
  // vitalValue }. Captures LCP, FID, CLS, INP, TTFB and FCP via
3
3
  // PerformanceObserver. LCP/FID/FCP fire as their entries arrive; TTFB is read
4
4
  // once from the navigation entry on init; CLS and INP accumulate and are
5
- // reported on visibilitychange to hidden (session-window semantics). Each
6
- // vital is emitted at most once per page load — there is no SPA reset in this
7
- // first version. If PerformanceObserver is missing the collector is a no-op.
8
- // Errors are swallowed silently.
5
+ // reported on visibilitychange to hidden (session-window semantics). FID is
6
+ // the input delay (processingStart - startTime); INP is the worst interaction
7
+ // duration (entries with a nonzero interactionId), a max approximation of the
8
+ // spec's high-percentile definition. Each vital is emitted at most once per
9
+ // page load — there is no SPA reset in this first version. If
10
+ // PerformanceObserver is missing the collector is a no-op. Errors are
11
+ // swallowed silently.
9
12
  export const WEB_VITAL_EVENT = "rum.web_vital";
10
13
  /**
11
14
  * startWebVitals observes Core Web Vitals and emits one rum.web_vital per
@@ -40,7 +43,7 @@ export function startWebVitals(emit, env) {
40
43
  }
41
44
  let clsValue = 0;
42
45
  let inpWorst = 0;
43
- const observe = (type, handler) => {
46
+ const observe = (type, handler, extraOptions) => {
44
47
  try {
45
48
  const obs = new Observer((list) => {
46
49
  try {
@@ -51,7 +54,7 @@ export function startWebVitals(emit, env) {
51
54
  debug?.("capture", { reason: `${type}_handler_failed`, error: String(err) });
52
55
  }
53
56
  });
54
- obs.observe({ type, buffered: true });
57
+ obs.observe({ type, buffered: true, ...extraOptions });
55
58
  }
56
59
  catch (err) {
57
60
  debug?.("capture", { reason: `${type}_observe_failed`, error: String(err) });
@@ -61,7 +64,9 @@ export function startWebVitals(emit, env) {
61
64
  emitVital("LCP", entry.startTime);
62
65
  });
63
66
  observe("first-input", (entry) => {
64
- emitVital("FID", entry.startTime);
67
+ if (typeof entry.processingStart !== "number")
68
+ return;
69
+ emitVital("FID", entry.processingStart - entry.startTime);
65
70
  });
66
71
  observe("layout-shift", (entry) => {
67
72
  if (entry.hadRecentInput)
@@ -69,10 +74,15 @@ export function startWebVitals(emit, env) {
69
74
  clsValue += entry.value ?? 0;
70
75
  });
71
76
  observe("event", (entry) => {
72
- const value = entry.value ?? 0;
73
- if (value > inpWorst)
74
- inpWorst = value;
75
- });
77
+ if (!entry.interactionId)
78
+ return;
79
+ const duration = entry.duration ?? 0;
80
+ if (duration > inpWorst)
81
+ inpWorst = duration;
82
+ },
83
+ // The event-timing default durationThreshold is 104ms; lower it so
84
+ // ordinary sub-100ms interactions are represented in INP.
85
+ { durationThreshold: 40 });
76
86
  observe("paint", (entry) => {
77
87
  if (entry.name === "first-contentful-paint") {
78
88
  emitVital("FCP", entry.startTime);
@@ -7,7 +7,7 @@ export function selectPersonalizationVariant(request, route, claims, experiments
7
7
  export function resolvePersonalizationDecision(request, route, claims, experiments, identity) {
8
8
  if (!route)
9
9
  return { variant: null, experiment: null };
10
- let variant = selectRuleVariant(request, route, claims);
10
+ let variant = selectRuleVariant(request, route, claims, experiments);
11
11
  let experiment = null;
12
12
  if (identity && identity.usable && route.variants && experiments) {
13
13
  const candidates = Object.values(experiments)
@@ -30,13 +30,17 @@ export function resolvePersonalizationDecision(request, route, claims, experimen
30
30
  }
31
31
  return { variant: variant || null, experiment };
32
32
  }
33
- function selectRuleVariant(request, route, claims) {
33
+ function selectRuleVariant(request, route, claims, experiments) {
34
34
  if (!route.variants)
35
35
  return null;
36
36
  const matches = [];
37
37
  for (const key in route.variants) {
38
38
  const variant = route.variants[key];
39
39
  const values = variant.variantValues || variantValuesFromKey(key);
40
+ const experimentID = String(values.experiment || '').trim();
41
+ if (experimentID && (!experiments || !experiments[experimentID]
42
+ || experiments[experimentID].status !== 'running'))
43
+ continue;
40
44
  if (!requestDimensionsMatch(request, values))
41
45
  continue;
42
46
  const membershipMatch = (values.audience && claims && (claims.aud || []).includes(values.audience))
@@ -18,7 +18,9 @@ const EVENT_FIELDS = new Set([
18
18
  'utm',
19
19
  'viewport',
20
20
  'experiment',
21
+ 'deviceClass',
21
22
  ]);
23
+ const DEVICE_CLASSES = new Set(['desktop', 'mobile', 'tablet', 'unknown']);
22
24
  function identityHashShard(identityHash) {
23
25
  const hash = identityHash.trim().toLowerCase();
24
26
  if (hash.length < 2)
@@ -112,6 +114,7 @@ export function createPersonalizationCollectRoute(config) {
112
114
  await putSuppression('withdrawal');
113
115
  }
114
116
  const identityHash = await identityHashHex('aci_id', rawIdentity);
117
+ const geo = collectGeo(request);
115
118
  const collectedAt = now.toISOString();
116
119
  const expiresAt = new Date(now.getTime() +
117
120
  (config.retentionSeconds ?? DEFAULT_RETENTION_SECONDS) * 1000).toISOString();
@@ -142,6 +145,10 @@ export function createPersonalizationCollectRoute(config) {
142
145
  ...(normalized.utm ? { utm: normalized.utm } : {}),
143
146
  ...(normalized.viewport ? { viewport: normalized.viewport } : {}),
144
147
  ...(normalized.experiment ? { experiment: normalized.experiment } : {}),
148
+ // Server-stamped geo (country + region only, D-PDP-14) from the
149
+ // platform-injected viewer headers; never from the event body.
150
+ ...(geo ? { geo } : {}),
151
+ deviceClass: normalized.deviceClass,
145
152
  occurredAt: normalized.occurredAt,
146
153
  collectedAt,
147
154
  expiresAt,
@@ -225,6 +232,9 @@ function validateEvent(value) {
225
232
  if (value.utm !== undefined && (!value.utm || typeof value.utm !== 'object' || Array.isArray(value.utm))) {
226
233
  throw new CollectValidationError('utm must be an object');
227
234
  }
235
+ if (value.deviceClass !== undefined && typeof value.deviceClass !== 'string') {
236
+ throw new CollectValidationError('deviceClass must be a string');
237
+ }
228
238
  normalizeViewport(value.viewport);
229
239
  normalizeExperiment(value.experiment);
230
240
  }
@@ -246,6 +256,7 @@ function normalizeEvent(event, now) {
246
256
  eventName,
247
257
  eventCategory,
248
258
  occurredAt,
259
+ deviceClass: normalizeDeviceClass(event.deviceClass),
249
260
  properties: event.properties === undefined ? undefined : sanitizeProperties(event.properties, 'properties', 0),
250
261
  page: {
251
262
  ...(pageURL ? { url: pageURL } : {}),
@@ -258,6 +269,32 @@ function normalizeEvent(event, now) {
258
269
  experiment: normalizeExperiment(event.experiment),
259
270
  };
260
271
  }
272
+ // collectGeo stamps the request geo from the platform-injected viewer
273
+ // headers (Vercel overwrites x-vercel-ip-* on every request, so they are
274
+ // trusted). Country + region only — city is never captured (D-PDP-14).
275
+ // Returns undefined when neither dimension normalizes, so the lake record
276
+ // keeps its historical byte shape for no-source requests.
277
+ function collectGeo(request) {
278
+ const country = normalizeGeoCountry(request.headers.get('x-vercel-ip-country'));
279
+ const region = normalizeGeoRegion(request.headers.get('x-vercel-ip-country-region'));
280
+ if (!country && !region)
281
+ return undefined;
282
+ return { ...(country ? { country } : {}), ...(region ? { region } : {}) };
283
+ }
284
+ function normalizeGeoCountry(value) {
285
+ const clean = (value ?? '').trim();
286
+ return /^[a-zA-Z]{2}$/.test(clean) ? clean.toUpperCase() : '';
287
+ }
288
+ function normalizeGeoRegion(value) {
289
+ const clean = (value ?? '').trim();
290
+ return /^[a-zA-Z0-9]{1,3}$/.test(clean) ? clean.toUpperCase() : '';
291
+ }
292
+ // normalizeDeviceClass floors any client value onto the closed enum —
293
+ // never a free-form echo (the value joins a rollup primary key, D-PR-22).
294
+ function normalizeDeviceClass(value) {
295
+ const clean = typeof value === 'string' ? value.trim().toLowerCase() : '';
296
+ return DEVICE_CLASSES.has(clean) ? clean : 'unknown';
297
+ }
261
298
  function normalizeEventName(value) {
262
299
  const name = value.trim().toLowerCase();
263
300
  if (!name || name.length > 128 || /[\s/\\]/.test(name))
@@ -1,7 +1,7 @@
1
1
  import { NextResponse } from 'next/server.js';
2
2
  import { getUncachedEdgeConfigValue } from './edge-config.js';
3
3
  import { PREVIEW_TOKEN_COOKIE, PREVIEW_RELEASE_COOKIE, extractPreviewToken, resolvePreviewContext, createPreviewCookies, previewSignKey, } from '../preview/core.js';
4
- import { CONTENT_PATH_HEADER, CONTENT_PATH_CACHE_PARAM, resolvePersonalizationRequest, } from './personalization-host.js';
4
+ import { CONTENT_PATH_HEADER, CONTENT_PATH_CACHE_PARAM, ROUTED_PATH_HEADER, VARIANT_KEY_HEADER, resolvePersonalizationRequest, } from './personalization-host.js';
5
5
  import { activeReleaseV1Key, clampDeploymentCompatibleReleaseV1, decodeActiveReleaseV1, decodeDeploymentCompatibilityV1, deploymentCompatibilityKey, } from './vercel-edge-projection-v1.js';
6
6
  const RELEASE_SCOPED_PREFIX = '/__r';
7
7
  const FRAMEWORK_ORIGIN_AUTH_HEADER = 'x-gradial-framework-origin-auth';
@@ -219,6 +219,8 @@ async function resolveFrameworkDispatch(request, config) {
219
219
  || forwardedContentRef !== null
220
220
  || request.headers.has(FRAMEWORK_CONTENT_BASE_URL_HEADER)
221
221
  || request.headers.has(CONTENT_PATH_HEADER)
222
+ || request.headers.has(ROUTED_PATH_HEADER)
223
+ || request.headers.has(VARIANT_KEY_HEADER)
222
224
  || contentPathValues.length > 0;
223
225
  if (!hasReservedDispatch) {
224
226
  return { kind: 'direct' };
@@ -276,6 +278,7 @@ function frameworkRequestHeaders(request, contentPath) {
276
278
  const requestHeaders = sanitizedFrameworkRequestHeaders(request);
277
279
  if (contentPath) {
278
280
  requestHeaders.set(CONTENT_PATH_HEADER, contentPath);
281
+ requestHeaders.set(ROUTED_PATH_HEADER, request.nextUrl.pathname);
279
282
  }
280
283
  return requestHeaders;
281
284
  }
@@ -287,6 +290,8 @@ function sanitizedFrameworkRequestHeaders(request) {
287
290
  requestHeaders.delete(FRAMEWORK_CONTENT_REF_HEADER);
288
291
  requestHeaders.delete(FRAMEWORK_CONTENT_BASE_URL_HEADER);
289
292
  requestHeaders.delete(CONTENT_PATH_HEADER);
293
+ requestHeaders.delete(ROUTED_PATH_HEADER);
294
+ requestHeaders.delete(VARIANT_KEY_HEADER);
290
295
  return requestHeaders;
291
296
  }
292
297
  function rejectedFrameworkDispatch(status, message) {
@@ -451,6 +456,14 @@ export const aciMiddlewareMatcher = [
451
456
  source: '/:path*',
452
457
  has: [{ type: 'header', key: 'x-aci-content-path' }],
453
458
  },
459
+ {
460
+ source: '/:path*',
461
+ has: [{ type: 'header', key: 'x-aci-routed-path' }],
462
+ },
463
+ {
464
+ source: '/:path*',
465
+ has: [{ type: 'header', key: 'x-aci-variant-key' }],
466
+ },
454
467
  {
455
468
  source: '/:path*',
456
469
  has: [{ type: 'query', key: '__aci_content_path' }],
@@ -2,6 +2,8 @@ import { type PersonalizationExperiment } from '../edge/personalization-core.js'
2
2
  import { type PortableConsentSnapshot } from '../consent/portable-envelope.js';
3
3
  export declare const CONTENT_PATH_HEADER = "x-aci-content-path";
4
4
  export declare const CONTENT_PATH_CACHE_PARAM = "__aci_content_path";
5
+ export declare const ROUTED_PATH_HEADER = "x-aci-routed-path";
6
+ export declare const VARIANT_KEY_HEADER = "x-aci-variant-key";
5
7
  export interface EdgeDispatch {
6
8
  kind: 'static-direct' | 'compose-origin' | 'framework-origin' | string;
7
9
  artifactPath?: string;
@@ -5,6 +5,16 @@ const DEFAULT_CLAIMS_TTL_SECONDS = 24 * 60 * 60;
5
5
  const textEncoder = new TextEncoder();
6
6
  export const CONTENT_PATH_HEADER = 'x-aci-content-path';
7
7
  export const CONTENT_PATH_CACHE_PARAM = '__aci_content_path';
8
+ // Names the public route the edge decision was made FOR. The server render
9
+ // applies the content-path selector only to lookups of this route; content
10
+ // reads for any other route fall back to that route's base payload instead of
11
+ // inheriting a selector that names a different route's content.
12
+ export const ROUTED_PATH_HEADER = 'x-aci-routed-path';
13
+ // Names the variant the edge decision selected. Like the content path and
14
+ // routed path, it is a reserved dispatch signal: only the middleware may set
15
+ // it, and a viewer-supplied value must never reach the SSR render (a bare
16
+ // variant key without a matching ref is an integrity error there).
17
+ export const VARIANT_KEY_HEADER = 'x-aci-variant-key';
8
18
  export class IdentityErasureFencedError extends Error {
9
19
  name = 'IdentityErasureFencedError';
10
20
  constructor() {
@@ -74,17 +84,20 @@ export async function resolvePersonalizationRequest(request, releaseId, config)
74
84
  const experiment = decision.experiment
75
85
  ? { id: decision.experiment.experimentId, arm: decision.experiment.armId, allocationVersion: decision.experiment.allocationVersion }
76
86
  : undefined;
77
- if (contentPath)
87
+ if (contentPath) {
78
88
  requestHeaders.set(CONTENT_PATH_HEADER, contentPath);
89
+ requestHeaders.set(ROUTED_PATH_HEADER, normalizePath(new URL(request.url).pathname));
90
+ }
79
91
  if (variantKey)
80
- requestHeaders.set('x-aci-variant-key', variantKey);
92
+ requestHeaders.set(VARIANT_KEY_HEADER, variantKey);
81
93
  return { requestHeaders, contentPath, variantKey, experiment, cookies: mutations };
82
94
  }
83
95
  if (contentPath) {
84
96
  requestHeaders.set(CONTENT_PATH_HEADER, contentPath);
97
+ requestHeaders.set(ROUTED_PATH_HEADER, normalizePath(new URL(request.url).pathname));
85
98
  }
86
99
  if (variantKey) {
87
- requestHeaders.set('x-aci-variant-key', variantKey);
100
+ requestHeaders.set(VARIANT_KEY_HEADER, variantKey);
88
101
  }
89
102
  return { requestHeaders, contentPath, variantKey, cookies: mutations };
90
103
  }
@@ -1,5 +1,6 @@
1
1
  import { headers } from 'next/headers.js';
2
2
  import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
3
+ import { normalizeRoute } from '../content/routes.js';
3
4
  import { getCachedManifest, getCachedSiteConfig } from '../content/cache.js';
4
5
  import { S3ContentProvider } from '../providers/s3.js';
5
6
  import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInput } from '../runtime/page.js';
@@ -88,8 +89,31 @@ export async function getPageRuntimeRenderInput() {
88
89
  // payload — otherwise every visitor gets the base page and edge decisioning
89
90
  // is cosmetic. The S3 provider only serves refs the release manifest
90
91
  // declares.
92
+ //
93
+ // The selection is bound to ONE route: the request's own routed path, carried
94
+ // in x-aci-routed-path by the same middleware that sets the content path. A
95
+ // render may also read OTHER routes' content (e.g. a blog article's
96
+ // related-articles fallback calling getPage('/blog')); those lookups must not
97
+ // inherit the routed page's selector — the ref names a different route's
98
+ // payload and would be rejected as a cross-route integrity violation. Such
99
+ // lookups read the target route's base payload instead.
91
100
  const ACI_CONTENT_PATH_HEADER = 'x-aci-content-path';
92
101
  const ACI_VARIANT_KEY_HEADER = 'x-aci-variant-key';
102
+ const ACI_ROUTED_PATH_HEADER = 'x-aci-routed-path';
103
+ // The route argument arrives URL-decoded (Next catch-all params); the routed
104
+ // path header carries the request pathname, which may still be
105
+ // percent-encoded. Accept either form.
106
+ function routeMatchesRoutedPath(route, routedPath) {
107
+ const normalized = normalizeRoute(route);
108
+ if (normalized === normalizeRoute(routedPath))
109
+ return true;
110
+ try {
111
+ return normalized === normalizeRoute(decodeURIComponent(routedPath));
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
93
117
  async function selectRenderPage(provider, route) {
94
118
  if (!(provider instanceof S3ContentProvider)) {
95
119
  return { page: await provider.getPage(route) };
@@ -110,6 +134,15 @@ async function selectRenderPage(provider, route) {
110
134
  const selectedVariantKey = current.get(ACI_VARIANT_KEY_HEADER);
111
135
  variantKeyPresent = selectedVariantKey !== null;
112
136
  variantKey = selectedVariantKey ?? '';
137
+ const routedPath = current.get(ACI_ROUTED_PATH_HEADER);
138
+ if (contentRefPresent && routedPath !== null && !routeMatchesRoutedPath(route, routedPath)) {
139
+ // Secondary route lookup: the request's selector names the routed
140
+ // page's payload, not this route's. Serve this route's base tuple.
141
+ contentRef = '';
142
+ contentRefPresent = false;
143
+ variantKey = '';
144
+ variantKeyPresent = false;
145
+ }
113
146
  }
114
147
  catch {
115
148
  // `headers()` has no request context in static generation and unit tests.
@@ -304,6 +304,14 @@ export function checkMiddleware(ctx) {
304
304
  source: '/:path*',
305
305
  has: [{ type: 'header', key: 'x-aci-content-path' }],
306
306
  },
307
+ {
308
+ source: '/:path*',
309
+ has: [{ type: 'header', key: 'x-aci-routed-path' }],
310
+ },
311
+ {
312
+ source: '/:path*',
313
+ has: [{ type: 'header', key: 'x-aci-variant-key' }],
314
+ },
307
315
  {
308
316
  source: '/:path*',
309
317
  has: [{ type: 'query', key: '__aci_content_path' }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradial/aci",
3
- "version": "0.1.47",
3
+ "version": "0.1.50",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/gradial/bare-metal-go.git",
@@ -170,11 +170,11 @@
170
170
  "zod": "^4.0.0"
171
171
  },
172
172
  "optionalDependencies": {
173
- "@gradial/aci-cli-darwin-arm64": "0.1.47",
174
- "@gradial/aci-cli-darwin-x64": "0.1.47",
175
- "@gradial/aci-cli-linux-arm64": "0.1.47",
176
- "@gradial/aci-cli-linux-x64": "0.1.47",
177
- "@gradial/aci-cli-win32-arm64": "0.1.47",
178
- "@gradial/aci-cli-win32-x64": "0.1.47"
173
+ "@gradial/aci-cli-darwin-arm64": "0.1.50",
174
+ "@gradial/aci-cli-darwin-x64": "0.1.50",
175
+ "@gradial/aci-cli-linux-arm64": "0.1.50",
176
+ "@gradial/aci-cli-linux-x64": "0.1.50",
177
+ "@gradial/aci-cli-win32-arm64": "0.1.50",
178
+ "@gradial/aci-cli-win32-x64": "0.1.50"
179
179
  }
180
180
  }
@@ -33,6 +33,14 @@ export const config = {
33
33
  source: '/:path*',
34
34
  has: [{ type: 'header', key: 'x-aci-content-path' }],
35
35
  },
36
+ {
37
+ source: '/:path*',
38
+ has: [{ type: 'header', key: 'x-aci-routed-path' }],
39
+ },
40
+ {
41
+ source: '/:path*',
42
+ has: [{ type: 'header', key: 'x-aci-variant-key' }],
43
+ },
36
44
  {
37
45
  source: '/:path*',
38
46
  has: [{ type: 'query', key: '__aci_content_path' }],
@@ -33,6 +33,14 @@ export const config = {
33
33
  source: '/:path*',
34
34
  has: [{ type: 'header', key: 'x-aci-content-path' }],
35
35
  },
36
+ {
37
+ source: '/:path*',
38
+ has: [{ type: 'header', key: 'x-aci-routed-path' }],
39
+ },
40
+ {
41
+ source: '/:path*',
42
+ has: [{ type: 'header', key: 'x-aci-variant-key' }],
43
+ },
36
44
  {
37
45
  source: '/:path*',
38
46
  has: [{ type: 'query', key: '__aci_content_path' }],