@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 +21 -0
- package/README.md +4 -4
- package/dist/collect/aci.js +1 -1
- package/dist/collect/aci.js.sha256 +1 -1
- package/dist/collect/batcher.d.ts +6 -0
- package/dist/collect/deviceclass.d.ts +12 -0
- package/dist/collect/deviceclass.js +27 -0
- package/dist/collect/index.js +9 -0
- package/dist/collect/webvitals.d.ts +3 -0
- package/dist/collect/webvitals.js +21 -11
- package/dist/edge/personalization-core.js +6 -2
- package/dist/next/collect-route.js +37 -0
- package/dist/next/middleware.js +14 -1
- package/dist/next/personalization-host.d.ts +2 -0
- package/dist/next/personalization-host.js +16 -3
- package/dist/next/server.js +33 -0
- package/dist/testing/conformance/checks.js +8 -0
- package/package.json +7 -7
- package/templates/nextjs/starter-content/src/middleware.ts +8 -0
- package/templates/nextjs/template/src/middleware.ts +8 -0
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.
|
|
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.
|
|
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.
|
|
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.
|
|
94
|
+
npx @gradial/aci@0.1.50 init --list
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
## Personalization: The One-Tag Install
|
package/dist/collect/aci.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/dist/collect/index.js
CHANGED
|
@@ -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
|
};
|
|
@@ -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).
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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))
|
package/dist/next/middleware.js
CHANGED
|
@@ -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(
|
|
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(
|
|
100
|
+
requestHeaders.set(VARIANT_KEY_HEADER, variantKey);
|
|
88
101
|
}
|
|
89
102
|
return { requestHeaders, contentPath, variantKey, cookies: mutations };
|
|
90
103
|
}
|
package/dist/next/server.js
CHANGED
|
@@ -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.
|
|
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.
|
|
174
|
-
"@gradial/aci-cli-darwin-x64": "0.1.
|
|
175
|
-
"@gradial/aci-cli-linux-arm64": "0.1.
|
|
176
|
-
"@gradial/aci-cli-linux-x64": "0.1.
|
|
177
|
-
"@gradial/aci-cli-win32-arm64": "0.1.
|
|
178
|
-
"@gradial/aci-cli-win32-x64": "0.1.
|
|
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' }],
|