@gradial/aci 0.1.48 → 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/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 Le=Object.defineProperty;var Oe=(t,e,n)=>e in t?Le(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var y=(t,e,n)=>Oe(t,typeof e!="symbol"?e+"":e,n);var x="data-aci-event",De="data-aci-on",q="data-aci-prop-",Re="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 Be(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(x));if(n===void 0){e==null||e("capture",{reason:"malformed_declaration",attr:x});return}return{eventName:n,properties:Be(t)}}function $(t){var e,n;return(n=(e=t.getAttribute(De))==null?void 0:e.trim().toLowerCase())!=null?n:"click"}function K(t,e,n){let{document:r,debug:o}=e;r.addEventListener("click",l=>{var d,p;try{let u=l.target;if((d=u==null?void 0:u.closest)==null?void 0:d.call(u,`[${Re}]`)){n==null||n();return}let m=(p=u==null?void 0:u.closest)==null?void 0:p.call(u,`[${x}]`);if(!m||$(m)!=="click")return;let g=X(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(l=>$(l)==="visible");if(s.length===0)return;let a=new WeakSet,c=new i((l,d)=>{try{for(let p of l){if(!p.isIntersecting||p.intersectionRatio<.5||a.has(p.target))continue;a.add(p.target),d.unobserve(p.target);let u=X(p.target,o);u&&t(u)}}catch(p){o==null||o("capture",{reason:"visible_handler_failed",error:String(p)})}},{threshold:.5});for(let l of s)c.observe(l)}function Y(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function J(t,e,n,r){let o=(...a)=>{try{let[c,...l]=a;if(c==="track"){let d=D(l[0]);if(d===void 0){r==null||r("capture",{reason:"malformed_track_name",name:l[0]});return}let p=l[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=l[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",Ve='script[type="application/json"][data-aci-assignments]';function Me(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 We(t,e){try{let n=t.querySelector(Ve);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=Me(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=We(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 Ge(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:Ge)(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,He=5e3,te='{"envelopeId":"',ne='","events":[',re="]}",qe=26,A=te.length+qe+ne.length+re.length,Ue=new TextEncoder,Xe=t=>Ue.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:je}get pending(){return this.queue.length}push(e){let n=JSON.stringify(e),r=Xe(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,l)=>setInterval(c,l),clearInterval:c=>clearInterval(c)};this.intervalId=e.setInterval(()=>this.flush(),(i=this.options.flushIntervalMs)!=null?i:He);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>=ze)break;let l=s+c.bytes+(i.length>0?1:0);if(l>this.maxBytes)break;i.push(c.json),s=l,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 $e=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&&$e.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,Ke=2048,Ye=32,Je=128,Qe=512,Ze=2147483647,et=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/,tt=/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g,nt=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>Ke)return null;if(nt.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>Ze||!Array.isArray(r.p)||r.p.length>Ye||!r.p.every(i=>typeof i=="string")||r.p.some(i=>R.encode(i).byteLength>Je)||r.t!==void 0&&r.t!==null&&(typeof r.t!="string"||r.t===""||!rt(r.t))||r.src!==void 0&&r.src!==null&&typeof r.src!="string"||typeof r.src=="string"&&R.encode(r.src).byteLength>Qe?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(tt,"")}function rt(t){let e=et.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>ot(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 ot(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",F="aci_consent",V=1,it=365*24*60*60;function st(t,e){var r;let n=`${F}=${t.encoded}; Path=/; Max-Age=${it}; 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=at(t,e,n.now);return r===null?!1:(B(r.snapshot)||(o=n.gapBuffer)==null||o.purge(),st(r,n),!0)}function ae(t){for(let e of t.cookie.split(";")){let n=w(e);if(n.startsWith(F+"="))return n.slice(F.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:V,p:e});return(r=_(n,!1))==null?void 0:r.purposes}function fe(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 at(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 ct="cmp-",ut=/^[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(!ut.test(o))return null;return r}function lt(t,e){return e.filter(r=>r in t.categories).some(r=>t.categories[r]===!0)?[se]:[]}function E(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 l=lt(t,a);I(l,ct+e,{document:n.document,gapBuffer:n.gapBuffer,now:n.now}),(i=n.debug)==null||i.call(n,"consent",{reason:"written",preset:e,purposes:l})}catch(a){(s=n.debug)==null||s.call(n,"consent",{reason:"consent_bridge_failed",preset:e,error:String(a)})}}var le=250,dt=1e4;function k(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(dt/le),i=()=>{var c,l;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){(l=n==null?void 0:n.debug)==null||l.call(n,"consent",{reason:"cmp_not_found"});return}r.setTimeout(i,le)};i()}function b(t,e){try{e()}catch(n){t==null||t("consent",{reason:"adapter_failed",error:String(n)})}}var pt="c15t";function mt(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 gt(t,e){return typeof t=="object"&&t!==null?"consents"in t?t.consents:t:e.consents}function de(t){k(()=>{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=mt(o);i&&E(i,pt,t)};n(e.consents),(r=e.onConsentChange)==null||r.call(e,o=>b(t.debug,()=>n(gt(o,e))))})},t)}var vt="cookiebot",ht=["CookiebotOnConsentReady","CookiebotOnAccept","CookiebotOnDecline"];function pe(t){let e=t.Cookiebot;return typeof e=="object"&&e!==null?e:void 0}function bt(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=bt(r.consent);o&&E(o,vt,t)},n=t.window;if(typeof n.addEventListener=="function")for(let r of ht)n.addEventListener(r,()=>b(t.debug,e));k(()=>pe(t.window),()=>b(t.debug,e),t)}var ge="onetrust",yt=/^c\d{4}$/;function Et(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)&&yt.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);E(Et(o,i),ge,t)},r=e.OptanonWrapper;e.OptanonWrapper=()=>{if(typeof r=="function")try{r()}catch{}b(t.debug,n)},k(()=>{let o=e.OnetrustActiveGroups;return typeof o=="string"?o:void 0},()=>b(t.debug,n),t)}var kt="trustarc",Ct={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 wt(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(Ct))n[r]=e>=o;return{categories:n}}function St(t){return typeof t=="string"?t.includes(he):typeof t=="object"&&t!==null&&he in t}function _t(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=wt(r("getConsentDecision",_t(t.window)));o&&E(o,kt,t)},n=t.window;typeof n.addEventListener=="function"&&n.addEventListener("message",r=>b(t.debug,()=>{St(r==null?void 0:r.data)&&e()})),k(()=>be(t.window),()=>b(t.debug,e),t)}var N="aci:buf";var Ee="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 ke(t){for(let e of t.cookie.split(";")){let n=e.trim();if(n.startsWith(Ee+"="))return n.slice(Ee.length+1)!==""}return!1}function Ce(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 xt="rum.link_click";function we(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,l;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 u=p;t({eventName:xt,url:o==null?void 0:o.href,properties:{linkText:(l=(c=u.textContent)==null?void 0:c.trim())!=null?l:"",linkHref:u.href}})}catch(d){r==null||r("capture",{reason:"link_click_failed",error:String(d)})}},{capture:!0})}var At="rum.page_view";function Se(t){let e=t.indexOf("#");return e===-1?t:t.slice(0,e)}function _e(t,e){let n=Se(t.location.href),r=()=>{let s=Se(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 l=a(...c);return r(),l}};i("pushState"),i("replaceState"),t.addEventListener("popstate",r)}function xe(t,e){let n=()=>t({eventName:At,url:e.location.href,referrer:e.document.referrer});return n(),n}var G="/collect";function Ae(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 Te(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 Tt="rum.variation_view",j="data-aci-variation";function Pe(t,e){var l,d,p;let{document:n,debug:r}=e,o=e.MutationObserver;if(typeof o!="function")return;let i=(l=n.defaultView)==null?void 0:l.location,s=new WeakSet,a=u=>{let h=u.getAttribute(j);h!=null&&(s.has(u)||(s.add(u),t({eventName:Tt,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=(d=n.body)!=null?d: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 f=0;f<g.addedNodes.length;f++){let v=g.addedNodes[f];if((v==null?void 0:v.nodeType)!==1)continue;let C=v.element;C&&c(C)}}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 Pt="rum.web_vital";function Ie(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=(f,v)=>{a.has(f)||(a.add(f),t({eventName:Pt,url:i,properties:{vitalName:f,vitalValue:v}}))};try{let f=r==null?void 0:r.getEntriesByType("navigation")[0];f&&typeof f.responseStart=="number"&&typeof f.requestStart=="number"&&c("TTFB",f.responseStart-f.requestStart)}catch(f){o==null||o("capture",{reason:"ttfb_failed",error:String(f)})}let l=0,d=0,p=(f,v,C)=>{try{new n(Ne=>{try{for(let O of Ne.getEntries())v(O)}catch(O){o==null||o("capture",{reason:`${f}_handler_failed`,error:String(O)})}}).observe({type:f,buffered:!0,...C})}catch(H){o==null||o("capture",{reason:`${f}_observe_failed`,error:String(H)})}};p("largest-contentful-paint",f=>{c("LCP",f.startTime)}),p("first-input",f=>{typeof f.processingStart=="number"&&c("FID",f.processingStart-f.startTime)}),p("layout-shift",f=>{var v;f.hadRecentInput||(l+=(v=f.value)!=null?v:0)}),p("event",f=>{var C;if(!f.interactionId)return;let v=(C=f.duration)!=null?C:0;v>d&&(d=v)},{durationThreshold:40}),p("paint",f=>{f.name==="first-contentful-paint"&&c("FCP",f.startTime)});let u=()=>{try{a.has("CLS")||c("CLS",l),a.has("INP")||c("INP",d)}catch(f){o==null||o("capture",{reason:"visibility_handler_failed",error:String(f)})}};s&&s.addEventListener("visibilitychange",()=>{s.visibilityState==="hidden"&&u()}),(g=e.window)==null||g.addEventListener("pagehide",u)}var z={c15t:de,onetrust:ve,cookiebot:me,trustarc:ye};function It(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 Lt(t){var o;let e=t!=null?t:typeof window!="undefined"?window:void 0;if(!e||e.__aci_loaded||(e.__aci_loaded=!0,!It(e)))return;let n=oe(e.document.currentScript),r=ie(n.debug);try{let i=e.document,s=new L({storage:Nt(e),debug:r}),a=()=>{try{return ce(ae(i))}catch{return!1}},c=()=>{try{return ke(i)}catch{return!1}},l={navigator:e.navigator,fetch:typeof e.fetch=="function"?e.fetch.bind(e):void 0,debug:r},d;d=new T({send:m=>Ae(m,l),debug:r,onTick:()=>W(s,d,c,r),timers:{setInterval:(m,g)=>e.setInterval(m,g),clearInterval:m=>e.clearInterval(m)},documentTarget:i,windowTarget:e});let p=Z({document:i,window:e,debug:r}),u=m=>{Ce({...Q(m,p),eventId:S(void 0,g=>e.crypto.getRandomValues(g))},{grants:a,marker:c,gapBuffer:s,batcher:d})};p.start(u),J(e,n,{track:(m,g)=>u({eventName:m,properties:g}),consent:({p:m,src:g})=>{let f=ue(m);if(f===void 0){r("consent",{reason:"malformed_purposes"});return}let v=fe(g);if(v===void 0){r("consent",{reason:"malformed_source"});return}I(f,v,{document:i,gapBuffer:s})||r("consent",{reason:"oversized_envelope"})}},r);let h=xe(u,e);_e(e,[()=>p.rearm(),h]),K(u,{document:i,intersectionObserver:e.IntersectionObserver,debug:r},()=>{Te(l).then(m=>{m&&e.location.reload()})}),we(u,{document:i,debug:r}),Ie(u,{performance:e.performance,PerformanceObserver:e.PerformanceObserver,window:e,document:i,debug:r}),Pe(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)}})),d.start(),W(s,d,c,r),r("init",{config:n})}catch(i){r("init",{error:String(i)})}}Lt();})();
|
|
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
|
};
|
|
@@ -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' }],
|