@gradial/aci 0.1.52-rc.34 → 0.1.52-rc.40
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 +41 -0
- package/README.md +4 -4
- package/dist/audience-data-models.d.ts +0 -3
- package/dist/audience-data-models.js +0 -1
- package/dist/collect/aci.js +1 -1
- package/dist/collect/aci.js.sha256 +1 -1
- package/dist/collect/index.js +11 -1
- package/dist/internal/domain/edge/identity.d.ts +1 -4
- package/dist/internal/domain/edge/identity.js +0 -16
- package/dist/internal/domain/edge/index.d.ts +0 -1
- package/dist/internal/domain/edge/index.js +0 -1
- package/dist/internal/domain/edge/personalization-core.js +4 -5
- package/dist/internal/domain/edge/personalization_index.js +1 -2
- package/dist/internal/domain/edge/types.d.ts +0 -37
- package/dist/internal/domain/edge/variant_key.d.ts +1 -0
- package/dist/internal/domain/edge/variant_key.js +12 -0
- package/dist/next/page.d.ts +2 -1
- package/dist/next/page.js +14 -25
- package/dist/next/release-context.d.ts +2 -1
- package/dist/next/release-context.js +2 -1
- package/migrations/experiment-goal-ids.md +43 -0
- package/package.json +7 -7
- package/dist/internal/domain/edge/compose.d.ts +0 -24
- package/dist/internal/domain/edge/compose.js +0 -89
- package/dist/internal/domain/edge/record/codec.d.ts +0 -45
- package/dist/internal/domain/edge/record/codec.js +0 -319
- package/dist/mimic/core.d.ts +0 -2
- package/dist/mimic/core.js +0 -31
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.52-rc.40
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Mimic pages retain Next hydration and native 404 rendering while the hosting
|
|
8
|
+
platform owns their private response and session banner. Browser measurement
|
|
9
|
+
stays disabled for the lifetime of each mimic document, including after its
|
|
10
|
+
cookie expires or another tab exits the session. Rebuild and redeploy sites
|
|
11
|
+
with this SDK and the matching hosting platform to pick up the fix.
|
|
12
|
+
|
|
13
|
+
## 0.1.52-rc.38
|
|
14
|
+
|
|
15
|
+
### Migration required
|
|
16
|
+
|
|
17
|
+
- Link secondary experiment goals with `goalIds` instead of the retired
|
|
18
|
+
`goalRefs` field. Remove `goalRefs` from authored documents and authoring
|
|
19
|
+
integrations; `primaryGoalRef` remains supported. See the
|
|
20
|
+
[secondary-goal migration](./migrations/experiment-goal-ids.md).
|
|
21
|
+
|
|
22
|
+
## 0.1.52-rc.37
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Unused edge identity helpers and types. The shared runtime retains the
|
|
27
|
+
production identity and routing behavior.
|
|
28
|
+
|
|
29
|
+
## 0.1.52-rc.36
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
- The unused compact route-record codec and static compose terminal target.
|
|
34
|
+
Static delivery selects complete HTML through the shared edge runtime.
|
|
35
|
+
|
|
36
|
+
## 0.1.52-rc.35
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- Next returns HTTP 404 for missing native assets and API endpoints that reach
|
|
41
|
+
the CMS catch-all without a routed release, including `/favicon.ico`, instead
|
|
42
|
+
of failing metadata and page rendering with a release-selection error.
|
|
43
|
+
|
|
3
44
|
## 0.1.52-rc.34
|
|
4
45
|
|
|
5
46
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Content Infrastructure SDK
|
|
2
2
|
|
|
3
|
-
`@gradial/aci` `0.1.52-rc.
|
|
3
|
+
`@gradial/aci` `0.1.52-rc.40` 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.52-rc.
|
|
11
|
+
npm install @gradial/aci@0.1.52-rc.40 zod@^4
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Root Import
|
|
@@ -137,13 +137,13 @@ host bundles select and serve immutable release artifacts.
|
|
|
137
137
|
The first RC scaffold path is Next:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
npx @gradial/aci@0.1.52-rc.
|
|
140
|
+
npx @gradial/aci@0.1.52-rc.40 init my-site --template next-starter
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
List available templates:
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
npx @gradial/aci@0.1.52-rc.
|
|
146
|
+
npx @gradial/aci@0.1.52-rc.40 init --list
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
## Personalization: The One-Tag Install
|
|
@@ -428,7 +428,6 @@ export declare const experimentDataModel: import("./index.js").DataModelContract
|
|
|
428
428
|
}, z.core.$strict>>;
|
|
429
429
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
430
430
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
431
|
-
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
432
431
|
goalIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
433
432
|
promotedArmId: z.ZodOptional<z.ZodString>;
|
|
434
433
|
promotedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -490,7 +489,6 @@ export declare const experimentationDataModels: (import("./index.js").DataModelC
|
|
|
490
489
|
}, z.core.$strict>>;
|
|
491
490
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
492
491
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
493
|
-
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
494
492
|
goalIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
495
493
|
promotedArmId: z.ZodOptional<z.ZodString>;
|
|
496
494
|
promotedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -923,7 +921,6 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
|
|
|
923
921
|
}, z.core.$strict>>;
|
|
924
922
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
925
923
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
926
|
-
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
927
924
|
goalIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
928
925
|
promotedArmId: z.ZodOptional<z.ZodString>;
|
|
929
926
|
promotedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -385,7 +385,6 @@ export const experimentDataModel = defineDataModel({
|
|
|
385
385
|
}).strict().optional(),
|
|
386
386
|
primaryGoalRef: z.string().optional(),
|
|
387
387
|
primaryGoalId: z.string().optional(),
|
|
388
|
-
goalRefs: z.array(z.string().regex(/\S/, 'entries must be non-blank')).optional(),
|
|
389
388
|
goalIds: z.array(z.string().regex(/\S/, 'entries must be non-blank')).optional(),
|
|
390
389
|
promotedArmId: z.string().optional(),
|
|
391
390
|
promotedAt: z.string().optional(),
|
package/dist/collect/aci.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(()=>{var Me=Object.defineProperty;var je=(t,e,n)=>e in t?Me(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var b=(t,e,n)=>je(t,typeof e!="symbol"?e+"":e,n);var We="experiment.exposure",Ge='script[type="application/json"][data-aci-assignments]';function H(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 M(t,e){try{let n=t.querySelector(Ge);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=H(a);c&&!s.some(u=>u.experimentId===c.experimentId)&&s.push(c)}return s}catch(n){return e==null||e("capture",{reason:"assignments_parse_failed",error:String(n)}),[]}}function ne(t,e){if(!e||t.experiments!==void 0)return t;let n=e.getActiveAssignments();return n.length===0?t:{...t,experiments:n}}function te(t){let e=[],n=new Set,r,o=i=>{e=(i!=null?i:M(t.document,t.debug)).map(s=>({...s}));for(let s of e)n.has(s.experimentId)||(n.add(s.experimentId),r==null||r({eventName:We,url:t.window.location.href,experiments:[{experimentId:s.experimentId,armId:s.armId,allocationVersion:s.allocationVersion}]}))};return{getActiveAssignments:()=>e.map(i=>({...i})),start(i){r=i,o()},clear(){e=[]},rearm(i){n.clear(),o(i)}}}var L="data-aci-event",ze="data-aci-on",re="data-aci-prop-",qe="data-aci-refresh",oe="custom.";function j(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(!(e===""||/[\s/]/.test(e)))return e.startsWith(oe)?e:oe+e}function $e(t){let e;for(let n of Array.from(t.attributes)){if(!n.name.startsWith(re))continue;let r=n.name.slice(re.length);r!==""&&((e!=null?e:e={})[r]=n.value)}return e}function ie(t,e,n){let r=j(t.getAttribute(L));if(r===void 0){n==null||n("capture",{reason:"malformed_declaration",attr:L});return}return{eventName:r,url:e,properties:$e(t)}}function se(t){var e,n;return(n=(e=t.getAttribute(ze))==null?void 0:e.trim().toLowerCase())!=null?n:"click"}function ae(t,e,n){var d;let{document:r,debug:o}=e,i=(d=r.defaultView)==null?void 0:d.location;r.addEventListener("click",p=>{var f,g;try{let m=p.target;if((f=m==null?void 0:m.closest)==null?void 0:f.call(m,`[${qe}]`)){n==null||n();return}let l=(g=m==null?void 0:m.closest)==null?void 0:g.call(m,`[${L}]`);if(!l||se(l)!=="click")return;let h=ie(l,i==null?void 0:i.href,o);h&&t(h)}catch(m){o==null||o("capture",{reason:"click_handler_failed",error:String(m)})}});let s=e.intersectionObserver;if(typeof s!="function")return;let a=Array.from(r.querySelectorAll(`[${L}]`)).filter(p=>se(p)==="visible");if(a.length===0)return;let c=new WeakSet,u=new s((p,f)=>{try{for(let g of p){if(!g.isIntersecting||g.intersectionRatio<.5||c.has(g.target))continue;c.add(g.target),f.unobserve(g.target);let m=ie(g.target,i==null?void 0:i.href,o);m&&t(m)}}catch(g){o==null||o("capture",{reason:"visible_handler_failed",error:String(g)})}},{threshold:.5});for(let p of a)u.observe(p)}function W(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function ce(t,e,n){let r=(...s)=>{try{let[a,...c]=s;if(a==="navigation-complete"){let u=c[0];if(!W(u)||typeof u.url!="string"||!/^https?:\/\//.test(u.url)||!Array.isArray(u.assignments)||u.assignments.length>16){n==null||n("capture",{reason:"malformed_navigation_completion"});return}let d=[];for(let p of u.assignments){let f=H(p);if(!f||d.some(g=>g.experimentId===f.experimentId)){n==null||n("capture",{reason:"malformed_navigation_completion"});return}d.push(f)}e.navigationComplete({url:u.url,assignments:d});return}if(a==="track"){let u=j(c[0]);if(u===void 0){n==null||n("capture",{reason:"malformed_track_name",name:c[0]});return}let d=c[1];if(d!==void 0&&!W(d)){n==null||n("capture",{reason:"malformed_track_properties",name:u});return}e.track(u,d);return}if(a==="consent"){let u=c[0];if(!W(u)){n==null||n("consent",{reason:"malformed_consent_envelope"});return}e.consent({p:u.p,src:u.src});return}n==null||n("capture",{reason:"unknown_command",command:a})}catch(a){n==null||n("capture",{reason:"command_failed",error:String(a)})}},o=t.aci,i=o==null?void 0:o.q;if(t.aci=r,i!=null)for(let s of Array.from(i)){let a=Array.from(s);r(...a)}}var ue="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ue(t){globalThis.crypto.getRandomValues(t)}function A(t,e){let n=t!=null?t:Date.now(),r="";for(let c=0;c<10;c++)r=ue[n%32]+r,n=Math.floor(n/32);let o=new Uint8Array(10);(e!=null?e:Ue)(o);let i="",s=0,a=0;for(let c of o)for(s=s<<8|c,a+=8;a>=5;)i+=ue[s>>>a-5&31],a-=5;return r+i}var Xe=60*1024,Ke=100,Ye=5e3,le='{"envelopeId":"',de='","events":[',fe="]}",Je=26,I=le.length+Je+de.length+fe.length,Ze=new TextEncoder,Qe=t=>Ze.encode(t).length,O=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:Xe}get pending(){return this.queue.length}push(e){let n=JSON.stringify(e),r=Qe(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,u)=>setInterval(c,u),clearInterval:c=>clearInterval(c)};this.intervalId=e.setInterval(()=>this.flush(),(i=this.options.flushIntervalMs)!=null?i:Ye);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 I+this.queuedBytes+e}drain(){var e,n,r;for(;this.queue.length>0;){let o=this.queue[0];if(I+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=I;for(;this.queue.length>0;){let c=this.queue[0];if(I+c.bytes>this.maxBytes||i.length>=Ke)break;let u=s+c.bytes+(i.length>0?1:0);if(u>this.maxBytes)break;i.push(c.json),s=u,this.queue.shift(),this.queuedBytes-=c.bytes}if(i.length===0)continue;let a=((r=this.options.createEnvelopeId)!=null?r:A)();this.options.send(le+a+de+i.join(",")+fe)}}};var G=new TextEncoder,en=2048,nn=32,tn=128,rn=512,on=2147483647,sn=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/,an=/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g,cn=new Set(["true","all","granted","yes","personalization","personalized"]);function x(t,e=!0){let n=S(t);if(!n)return null;if(e)try{n=decodeURIComponent(n)}catch{}if(n=S(n),!n||G.encode(n).byteLength>en)return null;if(cn.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>on||!Array.isArray(r.p)||r.p.length>nn||!r.p.every(i=>typeof i=="string")||r.p.some(i=>G.encode(i).byteLength>tn)||r.t!==void 0&&r.t!==null&&(typeof r.t!="string"||r.t===""||!un(r.t))||r.src!==void 0&&r.src!==null&&typeof r.src!="string"||typeof r.src=="string"&&G.encode(r.src).byteLength>rn?null:{version:r.v,purposes:r.p,grantedAt:o,source:typeof r.src=="string"?r.src:void 0}}catch{return null}}function z(t){return(t==null?void 0:t.purposes.some(e=>S(e).toLowerCase()==="personalization"))||!1}function S(t){return t.replace(an,"")}function un(t){let e=sn.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>ln(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 ln(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 $="personalization",q="aci_consent",U=1,dn=365*24*60*60;function fn(t,e){var r;let n=`${q}=${t.encoded}; Path=/; Max-Age=${dn}; SameSite=Lax`;((r=e.document.location)==null?void 0:r.protocol)==="https:"&&(n+="; Secure"),e.document.cookie=n}function N(t,e,n){var o,i;let r=pn(t,e,n.now);return r===null?!1:(z(r.snapshot)||(o=n.gapBuffer)==null||o.purge(),fn(r,n),(i=n.onDecision)==null||i.call(n),!0)}function P(t){for(let e of t.cookie.split(";")){let n=S(e);if(n.startsWith(q+"="))return n.slice(q.length+1)}}function pe(t){return t===void 0||t===""?!1:z(x(t,!0))}function me(t){var r;if(!Array.isArray(t))return;let e=[];for(let o of t){if(typeof o!="string")return;e.push(S(o))}let n=JSON.stringify({v:U,p:e});return(r=x(n,!1))==null?void 0:r.purposes}function ge(t){var o;if(t!==void 0&&typeof t!="string")return;let e=typeof t=="string"?S(t):"",r=JSON.stringify({v:U,p:[],src:e===""?"manual":e});return(o=x(r,!1))==null?void 0:o.source}function pn(t,e,n){try{let r=JSON.stringify({v:U,p:t,t:new Date((n!=null?n:Date.now)()).toISOString(),src:e}),o=x(r,!1);return o===null?null:{encoded:encodeURIComponent(r),snapshot:o}}catch{return null}}var mn="data-aci-consent-banner";var gn="data-aci-consent-banner-mount",ve="aci:consent-banner-decided";function vn(t){return P(t)!==void 0}function he(t){return vn(t)?!1:t.querySelector(`[${gn}], [${mn}]`)!==null}var hn=new Set(["c15t","onetrust","cookiebot","trustarc","manual"]);function D(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 be(t){var r;let e=(r=D(t,"data-aci-consent"))==null?void 0:r.toLowerCase(),n=e!==void 0&&hn.has(e)?e:"manual";return{site:D(t,"data-aci-site"),preset:n,categories:D(t,"data-aci-consent-categories"),debug:D(t,"data-aci-debug")==="1"}}function Ee(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=class{constructor(e){b(this,"events",[]);b(this,"now");b(this,"debug");var n;this.now=(n=e==null?void 0:e.now)!=null?n:Date.now,this.debug=e==null?void 0:e.debug}push(e){var n;if(this.expire(),this.events.length>=50){(n=this.debug)==null||n.call(this,"buffer",{reason:"consent_hold_cap_drop_newest",eventId:e.eventId});return}this.events.push({event:e,heldAt:this.now()})}size(){return this.events.length}release(){this.expire();let e=this.events.map(n=>n.event);return this.events=[],e}discard(e){var n;this.events.length>0&&((n=this.debug)==null||n.call(this,"buffer",{reason:`consent_hold_discarded_${e}`,count:this.events.length})),this.events=[]}expire(){var r;let e=this.now()-18e5,n=0;for(;n<this.events.length&&this.events[n].heldAt<e;)n+=1;n>0&&(this.events.splice(0,n),(r=this.debug)==null||r.call(this,"buffer",{reason:"consent_hold_expired",count:n}))}};function ye(t,e){var r;if(t.size()===0||!e.decided())return;if(!e.grants()){t.discard("denied");return}let n=t.release();for(let o of n)e.batcher.push(o);(r=e.debug)==null||r.call(e,"buffer",{reason:"consent_hold_released",count:n.length})}var bn="cmp-",En=/^[a-z0-9._-]+$/,yn={c15t:"measurement,marketing",onetrust:"c0002,c0004",cookiebot:"statistics,marketing",trustarc:"functional,advertising"};function X(t,e){let n=e!=null?e:yn[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(!En.test(o))return null;return r}function Cn(t,e){return e.filter(r=>r in t.categories).some(r=>t.categories[r]===!0)?[$]:[]}function _(t,e,n){var r,o,i,s;try{let a=X(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 u=Cn(t,a);N(u,bn+e,{document:n.document,gapBuffer:n.gapBuffer,now:n.now,onDecision:n.onDecision}),(i=n.debug)==null||i.call(n,"consent",{reason:"written",preset:e,purposes:u})}catch(a){(s=n.debug)==null||s.call(n,"consent",{reason:"consent_bridge_failed",preset:e,error:String(a)})}}var Ce=250,kn=1e4;function w(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(kn/Ce),i=()=>{var c,u;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){(u=n==null?void 0:n.debug)==null||u.call(n,"consent",{reason:"cmp_not_found"});return}r.setTimeout(i,Ce)};i()}function y(t,e){try{e()}catch(n){t==null||t("consent",{reason:"adapter_failed",error:String(n)})}}var _n="c15t";function wn(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 Sn(t,e){return typeof t=="object"&&t!==null?"consents"in t?t.consents:t:e.consents}function ke(t){w(()=>{let e=t.window.c15t;return typeof e=="object"&&e!==null?e:void 0},e=>{y(t.debug,()=>{var r;let n=o=>{let i=wn(o);i&&_(i,_n,t)};n(e.consents),(r=e.onConsentChange)==null||r.call(e,o=>y(t.debug,()=>n(Sn(o,e))))})},t)}var An="cookiebot",xn=["CookiebotOnConsentReady","CookiebotOnAccept","CookiebotOnDecline"];function _e(t){let e=t.Cookiebot;return typeof e=="object"&&e!==null?e:void 0}function Nn(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 we(t){let e=()=>{let r=_e(t.window);if(!r||r.hasResponse!==!0)return;let o=Nn(r.consent);o&&_(o,An,t)},n=t.window;if(typeof n.addEventListener=="function")for(let r of xn)n.addEventListener(r,()=>y(t.debug,e));w(()=>_e(t.window),()=>y(t.debug,e),t)}var Se="onetrust",Pn=/^c\d{4}$/;function Tn(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)&&Pn.test(r)&&(n[r]=!1);return{categories:n}}function Ae(t){let e=t.window,n=()=>{let o=e.OnetrustActiveGroups;if(typeof o!="string")return;let i=X(Se,t.categories);_(Tn(o,i),Se,t)},r=e.OptanonWrapper;e.OptanonWrapper=()=>{if(typeof r=="function")try{r()}catch{}y(t.debug,n)},w(()=>{let o=e.OnetrustActiveGroups;return typeof o=="string"?o:void 0},()=>y(t.debug,n),t)}var Ln="trustarc",In={required:1,functional:2,advertising:3},xe="PrivacyManagerAPI";function Ne(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 On(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(In))n[r]=e>=o;return{categories:n}}function Dn(t){return typeof t=="string"?t.includes(xe):typeof t=="object"&&t!==null&&xe in t}function Rn(t){let e=t.location;return typeof(e==null?void 0:e.hostname)=="string"?e.hostname:void 0}function Pe(t){let e=()=>{let r=Ne(t.window);if(!r)return;let o=On(r("getConsentDecision",Rn(t.window)));o&&_(o,Ln,t)},n=t.window;typeof n.addEventListener=="function"&&n.addEventListener("message",r=>y(t.debug,()=>{Dn(r==null?void 0:r.data)&&e()})),w(()=>Ne(t.window),()=>y(t.debug,e),t)}var B="aci:buf";var Te="aci_m",F=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}push(e){var n;this.events=this.restore(),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.restore();return this.events=[],this.persist(),e}purge(){var e;this.events=this.restore(),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.restore().length}restore(){var r;let e=this.storage;if(!e)return this.events;let n;try{n=e.getItem(B)}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(B)}catch{this.storage=void 0}return[]}persist(){var n;let e=this.storage;if(e)try{this.events.length===0?e.removeItem(B):e.setItem(B,JSON.stringify(this.events)),this.events=[]}catch(r){this.storage=void 0,(n=this.debug)==null||n.call(this,"buffer",{reason:"storage_unavailable",error:String(r)})}}};function Le(t){for(let e of t.cookie.split(";")){let n=e.trim();if(n.startsWith(Te+"="))return n.slice(Te.length+1)!==""}return!1}function Ie(t,e){var n;if(e.consentHold&&((n=e.consentPending)!=null&&n.call(e))){e.consentHold.push(t);return}if(e.grants()&&!e.marker()){e.gapBuffer.push(t);return}e.batcher.push(t)}function K(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 Bn="rum.link_click";function Oe(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,u;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 f=p;t({eventName:Bn,url:o==null?void 0:o.href,properties:{linkText:(u=(c=f.textContent)==null?void 0:c.trim())!=null?u:"",linkHref:f.href}})}catch(d){r==null||r("capture",{reason:"link_click_failed",error:String(d)})}},{capture:!0})}var Fn="rum.page_view";function V(t){let e=t.indexOf("#");return e===-1?t:t.slice(0,e)}function De(t,e,n=()=>{}){let r=V(t.location.href),o=r,i=!1,s=()=>{let c=V(t.location.href);c!==r&&(r=c,i=!0,n())},a=t.history;for(let c of["pushState","replaceState"]){let u=a[c].bind(a);a[c]=(...d)=>{let p=u(...d);return s(),p}}return t.addEventListener("popstate",s),{complete({url:c,assignments:u}){let d=V(t.location.href);if(V(c)===d&&(s(),!(!i&&o===d))){i=!1,o=d;for(let p of e)p(u)}}}}function Re(t,e){let n=()=>t({eventName:Fn,url:e.location.href,referrer:e.document.referrer});return n(),n}var Y="/collect";function Be(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(Y,i))return}catch{}if(!o){n==null||n("flush",{reason:"no_transport"});return}try{o(Y,{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 Fe(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:A)(),i=new Blob([`{"envelopeId":"${o}","events":[]}`],{type:"text/plain"});return e(Y,{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 Vn="rum.variation_view",J="data-aci-variation";function Ve(t,e){var u,d,p;let{document:n,debug:r}=e,o=e.MutationObserver;if(typeof o!="function")return;let i=(u=n.defaultView)==null?void 0:u.location,s=new WeakSet,a=f=>{let g=f.getAttribute(J);g!=null&&(s.has(f)||(s.add(f),t({eventName:Vn,url:i==null?void 0:i.href,properties:{variantKey:g}})))},c=f=>{try{f.hasAttribute(J)&&a(f);for(let g of Array.from(f.querySelectorAll(`[${J}]`)))a(g)}catch(g){r==null||r("capture",{reason:"variation_scan_failed",error:String(g)})}};try{let f=(d=n.body)!=null?d:n.documentElement;f&&c(f)}catch(f){r==null||r("capture",{reason:"variation_initial_scan_failed",error:String(f)})}try{let f=(p=n.body)!=null?p:n.documentElement;if(!f)return;new o(m=>{try{for(let E of m)for(let l=0;l<E.addedNodes.length;l++){let h=E.addedNodes[l];if((h==null?void 0:h.nodeType)!==1)continue;let C=h.element;C&&c(C)}}catch(E){r==null||r("capture",{reason:"variation_observer_failed",error:String(E)})}}).observe(f,{subtree:!0,childList:!0})}catch(f){r==null||r("capture",{reason:"variation_observe_failed",error:String(f)})}}var Hn="rum.web_vital";function He(t,e){var g,m,E;let n=e.PerformanceObserver;if(typeof n!="function")return;let{performance:r,debug:o}=e,i=(m=(g=e.window)==null?void 0:g.location)==null?void 0:m.href,s=e.document,a=new Set,c=(l,h)=>{a.has(l)||(a.add(l),t({eventName:Hn,url:i,properties:{vitalName:l,vitalValue:h}}))};try{let l=r==null?void 0:r.getEntriesByType("navigation")[0];l&&typeof l.responseStart=="number"&&typeof l.requestStart=="number"&&c("TTFB",l.responseStart-l.requestStart)}catch(l){o==null||o("capture",{reason:"ttfb_failed",error:String(l)})}let u=0,d=0,p=(l,h)=>{try{new n(T=>{try{for(let v of T.getEntries())h(v)}catch(v){o==null||o("capture",{reason:`${l}_handler_failed`,error:String(v)})}}).observe({type:l,buffered:!0})}catch(C){o==null||o("capture",{reason:`${l}_observe_failed`,error:String(C)})}};p("largest-contentful-paint",l=>{c("LCP",l.startTime)}),p("first-input",l=>{c("FID",l.startTime)}),p("layout-shift",l=>{var h;l.hadRecentInput||(u+=(h=l.value)!=null?h:0)}),p("event",l=>{var C;let h=(C=l.value)!=null?C:0;h>d&&(d=h)}),p("paint",l=>{l.name==="first-contentful-paint"&&c("FCP",l.startTime)});let f=()=>{try{a.has("CLS")||c("CLS",u),a.has("INP")||c("INP",d)}catch(l){o==null||o("capture",{reason:"visibility_handler_failed",error:String(l)})}};s&&s.addEventListener("visibilitychange",()=>{s.visibilityState==="hidden"&&f()}),(E=e.window)==null||E.addEventListener("pagehide",f)}var Z={c15t:ke,onetrust:Ae,cookiebot:we,trustarc:Pe};function Mn(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 jn(t){try{return t.sessionStorage}catch{return}}function Wn(t){var o;let e=t!=null?t:typeof window!="undefined"?window:void 0;if(!e||e.__aci_loaded||(e.__aci_loaded=!0,!Mn(e)))return;let n=be(e.document.currentScript),r=Ee(n.debug);try{let i=e.document,s=new F({storage:jn(e),debug:r}),a=()=>{try{return pe(P(i))}catch{return!1}},c=()=>{try{return Le(i)}catch{return!1}},u=()=>{try{return P(i)!==void 0}catch{return!1}},d=n.preset==="manual"?()=>{try{return he(i)}catch{return!1}}:()=>!0,p=new R({debug:r}),f=()=>!u()&&!c()&&d(),g={navigator:e.navigator,fetch:typeof e.fetch=="function"?e.fetch.bind(e):void 0,debug:r},m;m=new O({send:v=>Be(v,g),debug:r,onTick:()=>{ye(p,{decided:u,grants:a,batcher:m,debug:r}),K(s,m,c,r)},timers:{setInterval:(v,k)=>e.setInterval(v,k),clearInterval:v=>e.clearInterval(v)},documentTarget:i,windowTarget:e});let E=te({document:i,window:e,debug:r}),l=v=>{Ie({...ne(v,E),eventId:A(void 0,k=>e.crypto.getRandomValues(k))},{grants:a,marker:c,gapBuffer:s,batcher:m,consentHold:p,consentPending:f})},h=()=>m.flush();i.addEventListener(ve,h),E.start(l);let C=()=>{},T=De(e,[v=>E.rearm(v),()=>C()],()=>E.clear());i.addEventListener("astro:page-load",()=>T.complete({url:e.location.href,assignments:M(i,r)})),ce(e,{navigationComplete:v=>T.complete(v),track:(v,k)=>l({eventName:v,properties:k,url:e.location.href}),consent:({p:v,src:k})=>{let Q=me(v);if(Q===void 0){r("consent",{reason:"malformed_purposes"});return}let ee=ge(k);if(ee===void 0){r("consent",{reason:"malformed_source"});return}N(Q,ee,{document:i,gapBuffer:s,onDecision:h})||r("consent",{reason:"oversized_envelope"})}},r),C=Re(l,e),ae(l,{document:i,intersectionObserver:e.IntersectionObserver,debug:r},()=>{Fe(g).then(v=>{v&&e.location.reload()})}),Oe(l,{document:i,debug:r}),He(l,{performance:e.performance,PerformanceObserver:e.PerformanceObserver,window:e,document:i,debug:r}),Ve(l,{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,onDecision:h,debug:r,scheduler:{setTimeout:(v,k)=>e.setTimeout(v,k),clearTimeout:v=>e.clearTimeout(v)}})),m.start(),e.addEventListener("pagehide",()=>p.discard("unload")),K(s,m,c,r),r("init",{config:n})}catch(i){r("init",{error:String(i)})}}Wn();})();
|
|
1
|
+
"use strict";(()=>{var je=Object.defineProperty;var We=(t,e,n)=>e in t?je(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var E=(t,e,n)=>We(t,typeof e!="symbol"?e+"":e,n);var Ge="experiment.exposure",ze='script[type="application/json"][data-aci-assignments]';function M(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 j(t,e){try{let n=t.querySelector(ze);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=M(a);c&&!s.some(u=>u.experimentId===c.experimentId)&&s.push(c)}return s}catch(n){return e==null||e("capture",{reason:"assignments_parse_failed",error:String(n)}),[]}}function te(t,e){if(!e||t.experiments!==void 0)return t;let n=e.getActiveAssignments();return n.length===0?t:{...t,experiments:n}}function re(t){let e=[],n=new Set,r,o=i=>{e=(i!=null?i:j(t.document,t.debug)).map(s=>({...s}));for(let s of e)n.has(s.experimentId)||(n.add(s.experimentId),r==null||r({eventName:Ge,url:t.window.location.href,experiments:[{experimentId:s.experimentId,armId:s.armId,allocationVersion:s.allocationVersion}]}))};return{getActiveAssignments:()=>e.map(i=>({...i})),start(i){r=i,o()},clear(){e=[]},rearm(i){n.clear(),o(i)}}}var I="data-aci-event",qe="data-aci-on",oe="data-aci-prop-",$e="data-aci-refresh",ie="custom.";function W(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(!(e===""||/[\s/]/.test(e)))return e.startsWith(ie)?e:ie+e}function Ue(t){let e;for(let n of Array.from(t.attributes)){if(!n.name.startsWith(oe))continue;let r=n.name.slice(oe.length);r!==""&&((e!=null?e:e={})[r]=n.value)}return e}function se(t,e,n){let r=W(t.getAttribute(I));if(r===void 0){n==null||n("capture",{reason:"malformed_declaration",attr:I});return}return{eventName:r,url:e,properties:Ue(t)}}function ae(t){var e,n;return(n=(e=t.getAttribute(qe))==null?void 0:e.trim().toLowerCase())!=null?n:"click"}function ce(t,e,n){var d;let{document:r,debug:o}=e,i=(d=r.defaultView)==null?void 0:d.location;r.addEventListener("click",p=>{var f,m;try{let v=p.target;if((f=v==null?void 0:v.closest)==null?void 0:f.call(v,`[${$e}]`)){n==null||n();return}let l=(m=v==null?void 0:v.closest)==null?void 0:m.call(v,`[${I}]`);if(!l||ae(l)!=="click")return;let g=se(l,i==null?void 0:i.href,o);g&&t(g)}catch(v){o==null||o("capture",{reason:"click_handler_failed",error:String(v)})}});let s=e.intersectionObserver;if(typeof s!="function")return;let a=Array.from(r.querySelectorAll(`[${I}]`)).filter(p=>ae(p)==="visible");if(a.length===0)return;let c=new WeakSet,u=new s((p,f)=>{try{for(let m of p){if(!m.isIntersecting||m.intersectionRatio<.5||c.has(m.target))continue;c.add(m.target),f.unobserve(m.target);let v=se(m.target,i==null?void 0:i.href,o);v&&t(v)}}catch(m){o==null||o("capture",{reason:"visible_handler_failed",error:String(m)})}},{threshold:.5});for(let p of a)u.observe(p)}function G(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function ue(t,e,n){let r=(...s)=>{try{let[a,...c]=s;if(a==="navigation-complete"){let u=c[0];if(!G(u)||typeof u.url!="string"||!/^https?:\/\//.test(u.url)||!Array.isArray(u.assignments)||u.assignments.length>16){n==null||n("capture",{reason:"malformed_navigation_completion"});return}let d=[];for(let p of u.assignments){let f=M(p);if(!f||d.some(m=>m.experimentId===f.experimentId)){n==null||n("capture",{reason:"malformed_navigation_completion"});return}d.push(f)}e.navigationComplete({url:u.url,assignments:d});return}if(a==="track"){let u=W(c[0]);if(u===void 0){n==null||n("capture",{reason:"malformed_track_name",name:c[0]});return}let d=c[1];if(d!==void 0&&!G(d)){n==null||n("capture",{reason:"malformed_track_properties",name:u});return}e.track(u,d);return}if(a==="consent"){let u=c[0];if(!G(u)){n==null||n("consent",{reason:"malformed_consent_envelope"});return}e.consent({p:u.p,src:u.src});return}n==null||n("capture",{reason:"unknown_command",command:a})}catch(a){n==null||n("capture",{reason:"command_failed",error:String(a)})}},o=t.aci,i=o==null?void 0:o.q;if(t.aci=r,i!=null)for(let s of Array.from(i)){let a=Array.from(s);r(...a)}}var le="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Xe(t){globalThis.crypto.getRandomValues(t)}function x(t,e){let n=t!=null?t:Date.now(),r="";for(let c=0;c<10;c++)r=le[n%32]+r,n=Math.floor(n/32);let o=new Uint8Array(10);(e!=null?e:Xe)(o);let i="",s=0,a=0;for(let c of o)for(s=s<<8|c,a+=8;a>=5;)i+=le[s>>>a-5&31],a-=5;return r+i}var Ke=60*1024,Ye=100,Je=5e3,de='{"envelopeId":"',fe='","events":[',pe="]}",Ze=26,O=de.length+Ze+fe.length+pe.length,Qe=new TextEncoder,en=t=>Qe.encode(t).length,D=class{constructor(e){E(this,"options");E(this,"maxBytes");E(this,"queue",[]);E(this,"queuedBytes",0);E(this,"flushing",!1);E(this,"intervalId");var n;this.options=e,this.maxBytes=(n=e.maxBatchBytes)!=null?n:Ke}get pending(){return this.queue.length}push(e){let n=JSON.stringify(e),r=en(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,u)=>setInterval(c,u),clearInterval:c=>clearInterval(c)};this.intervalId=e.setInterval(()=>this.flush(),(i=this.options.flushIntervalMs)!=null?i:Je);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 O+this.queuedBytes+e}drain(){var e,n,r;for(;this.queue.length>0;){let o=this.queue[0];if(O+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=O;for(;this.queue.length>0;){let c=this.queue[0];if(O+c.bytes>this.maxBytes||i.length>=Ye)break;let u=s+c.bytes+(i.length>0?1:0);if(u>this.maxBytes)break;i.push(c.json),s=u,this.queue.shift(),this.queuedBytes-=c.bytes}if(i.length===0)continue;let a=((r=this.options.createEnvelopeId)!=null?r:x)();this.options.send(de+a+fe+i.join(",")+pe)}}};var z=new TextEncoder,nn=2048,tn=32,rn=128,on=512,sn=2147483647,an=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/,cn=/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g,un=new Set(["true","all","granted","yes","personalization","personalized"]);function N(t,e=!0){let n=S(t);if(!n)return null;if(e)try{n=decodeURIComponent(n)}catch{}if(n=S(n),!n||z.encode(n).byteLength>nn)return null;if(un.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>sn||!Array.isArray(r.p)||r.p.length>tn||!r.p.every(i=>typeof i=="string")||r.p.some(i=>z.encode(i).byteLength>rn)||r.t!==void 0&&r.t!==null&&(typeof r.t!="string"||r.t===""||!ln(r.t))||r.src!==void 0&&r.src!==null&&typeof r.src!="string"||typeof r.src=="string"&&z.encode(r.src).byteLength>on?null:{version:r.v,purposes:r.p,grantedAt:o,source:typeof r.src=="string"?r.src:void 0}}catch{return null}}function q(t){return(t==null?void 0:t.purposes.some(e=>S(e).toLowerCase()==="personalization"))||!1}function S(t){return t.replace(cn,"")}function ln(t){let e=an.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>dn(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 dn(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 U="personalization",$="aci_consent",X=1,fn=365*24*60*60;function pn(t,e){var r;let n=`${$}=${t.encoded}; Path=/; Max-Age=${fn}; SameSite=Lax`;((r=e.document.location)==null?void 0:r.protocol)==="https:"&&(n+="; Secure"),e.document.cookie=n}function P(t,e,n){var o,i;let r=mn(t,e,n.now);return r===null?!1:(q(r.snapshot)||(o=n.gapBuffer)==null||o.purge(),pn(r,n),(i=n.onDecision)==null||i.call(n),!0)}function T(t){for(let e of t.cookie.split(";")){let n=S(e);if(n.startsWith($+"="))return n.slice($.length+1)}}function me(t){return t===void 0||t===""?!1:q(N(t,!0))}function ge(t){var r;if(!Array.isArray(t))return;let e=[];for(let o of t){if(typeof o!="string")return;e.push(S(o))}let n=JSON.stringify({v:X,p:e});return(r=N(n,!1))==null?void 0:r.purposes}function ve(t){var o;if(t!==void 0&&typeof t!="string")return;let e=typeof t=="string"?S(t):"",r=JSON.stringify({v:X,p:[],src:e===""?"manual":e});return(o=N(r,!1))==null?void 0:o.source}function mn(t,e,n){try{let r=JSON.stringify({v:X,p:t,t:new Date((n!=null?n:Date.now)()).toISOString(),src:e}),o=N(r,!1);return o===null?null:{encoded:encodeURIComponent(r),snapshot:o}}catch{return null}}var gn="data-aci-consent-banner";var vn="data-aci-consent-banner-mount",he="aci:consent-banner-decided";function hn(t){return T(t)!==void 0}function be(t){return hn(t)?!1:t.querySelector(`[${vn}], [${gn}]`)!==null}var bn=new Set(["c15t","onetrust","cookiebot","trustarc","manual"]);function R(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 Ee(t){var r;let e=(r=R(t,"data-aci-consent"))==null?void 0:r.toLowerCase(),n=e!==void 0&&bn.has(e)?e:"manual";return{site:R(t,"data-aci-site"),preset:n,categories:R(t,"data-aci-consent-categories"),debug:R(t,"data-aci-debug")==="1"}}function ye(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 B=class{constructor(e){E(this,"events",[]);E(this,"now");E(this,"debug");var n;this.now=(n=e==null?void 0:e.now)!=null?n:Date.now,this.debug=e==null?void 0:e.debug}push(e){var n;if(this.expire(),this.events.length>=50){(n=this.debug)==null||n.call(this,"buffer",{reason:"consent_hold_cap_drop_newest",eventId:e.eventId});return}this.events.push({event:e,heldAt:this.now()})}size(){return this.events.length}release(){this.expire();let e=this.events.map(n=>n.event);return this.events=[],e}discard(e){var n;this.events.length>0&&((n=this.debug)==null||n.call(this,"buffer",{reason:`consent_hold_discarded_${e}`,count:this.events.length})),this.events=[]}expire(){var r;let e=this.now()-18e5,n=0;for(;n<this.events.length&&this.events[n].heldAt<e;)n+=1;n>0&&(this.events.splice(0,n),(r=this.debug)==null||r.call(this,"buffer",{reason:"consent_hold_expired",count:n}))}};function Ce(t,e){var r;if(t.size()===0||!e.decided())return;if(!e.grants()){t.discard("denied");return}let n=t.release();for(let o of n)e.batcher.push(o);(r=e.debug)==null||r.call(e,"buffer",{reason:"consent_hold_released",count:n.length})}var En="cmp-",yn=/^[a-z0-9._-]+$/,Cn={c15t:"measurement,marketing",onetrust:"c0002,c0004",cookiebot:"statistics,marketing",trustarc:"functional,advertising"};function K(t,e){let n=e!=null?e:Cn[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(!yn.test(o))return null;return r}function kn(t,e){return e.filter(r=>r in t.categories).some(r=>t.categories[r]===!0)?[U]:[]}function _(t,e,n){var r,o,i,s;try{let a=K(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 u=kn(t,a);P(u,En+e,{document:n.document,gapBuffer:n.gapBuffer,now:n.now,onDecision:n.onDecision}),(i=n.debug)==null||i.call(n,"consent",{reason:"written",preset:e,purposes:u})}catch(a){(s=n.debug)==null||s.call(n,"consent",{reason:"consent_bridge_failed",preset:e,error:String(a)})}}var ke=250,_n=1e4;function w(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(_n/ke),i=()=>{var c,u;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){(u=n==null?void 0:n.debug)==null||u.call(n,"consent",{reason:"cmp_not_found"});return}r.setTimeout(i,ke)};i()}function C(t,e){try{e()}catch(n){t==null||t("consent",{reason:"adapter_failed",error:String(n)})}}var wn="c15t";function Sn(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 An(t,e){return typeof t=="object"&&t!==null?"consents"in t?t.consents:t:e.consents}function _e(t){w(()=>{let e=t.window.c15t;return typeof e=="object"&&e!==null?e:void 0},e=>{C(t.debug,()=>{var r;let n=o=>{let i=Sn(o);i&&_(i,wn,t)};n(e.consents),(r=e.onConsentChange)==null||r.call(e,o=>C(t.debug,()=>n(An(o,e))))})},t)}var xn="cookiebot",Nn=["CookiebotOnConsentReady","CookiebotOnAccept","CookiebotOnDecline"];function we(t){let e=t.Cookiebot;return typeof e=="object"&&e!==null?e:void 0}function Pn(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 Se(t){let e=()=>{let r=we(t.window);if(!r||r.hasResponse!==!0)return;let o=Pn(r.consent);o&&_(o,xn,t)},n=t.window;if(typeof n.addEventListener=="function")for(let r of Nn)n.addEventListener(r,()=>C(t.debug,e));w(()=>we(t.window),()=>C(t.debug,e),t)}var Ae="onetrust",Tn=/^c\d{4}$/;function Ln(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)&&Tn.test(r)&&(n[r]=!1);return{categories:n}}function xe(t){let e=t.window,n=()=>{let o=e.OnetrustActiveGroups;if(typeof o!="string")return;let i=K(Ae,t.categories);_(Ln(o,i),Ae,t)},r=e.OptanonWrapper;e.OptanonWrapper=()=>{if(typeof r=="function")try{r()}catch{}C(t.debug,n)},w(()=>{let o=e.OnetrustActiveGroups;return typeof o=="string"?o:void 0},()=>C(t.debug,n),t)}var In="trustarc",On={required:1,functional:2,advertising:3},Ne="PrivacyManagerAPI";function Pe(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 Dn(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(On))n[r]=e>=o;return{categories:n}}function Rn(t){return typeof t=="string"?t.includes(Ne):typeof t=="object"&&t!==null&&Ne in t}function Bn(t){let e=t.location;return typeof(e==null?void 0:e.hostname)=="string"?e.hostname:void 0}function Te(t){let e=()=>{let r=Pe(t.window);if(!r)return;let o=Dn(r("getConsentDecision",Bn(t.window)));o&&_(o,In,t)},n=t.window;typeof n.addEventListener=="function"&&n.addEventListener("message",r=>C(t.debug,()=>{Rn(r==null?void 0:r.data)&&e()})),w(()=>Pe(t.window),()=>C(t.debug,e),t)}var F="aci:buf";var Le="aci_m",V=class{constructor(e){E(this,"events",[]);E(this,"storage");E(this,"debug");this.debug=e==null?void 0:e.debug,this.storage=e==null?void 0:e.storage}push(e){var n;this.events=this.restore(),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.restore();return this.events=[],this.persist(),e}purge(){var e;this.events=this.restore(),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.restore().length}restore(){var r;let e=this.storage;if(!e)return this.events;let n;try{n=e.getItem(F)}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(F)}catch{this.storage=void 0}return[]}persist(){var n;let e=this.storage;if(e)try{this.events.length===0?e.removeItem(F):e.setItem(F,JSON.stringify(this.events)),this.events=[]}catch(r){this.storage=void 0,(n=this.debug)==null||n.call(this,"buffer",{reason:"storage_unavailable",error:String(r)})}}};function Ie(t){for(let e of t.cookie.split(";")){let n=e.trim();if(n.startsWith(Le+"="))return n.slice(Le.length+1)!==""}return!1}function Oe(t,e){var n;if(e.consentHold&&((n=e.consentPending)!=null&&n.call(e))){e.consentHold.push(t);return}if(e.grants()&&!e.marker()){e.gapBuffer.push(t);return}e.batcher.push(t)}function Y(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 Fn="rum.link_click";function De(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,u;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 f=p;t({eventName:Fn,url:o==null?void 0:o.href,properties:{linkText:(u=(c=f.textContent)==null?void 0:c.trim())!=null?u:"",linkHref:f.href}})}catch(d){r==null||r("capture",{reason:"link_click_failed",error:String(d)})}},{capture:!0})}var Vn="rum.page_view";function H(t){let e=t.indexOf("#");return e===-1?t:t.slice(0,e)}function Re(t,e,n=()=>{}){let r=H(t.location.href),o=r,i=!1,s=()=>{let c=H(t.location.href);c!==r&&(r=c,i=!0,n())},a=t.history;for(let c of["pushState","replaceState"]){let u=a[c].bind(a);a[c]=(...d)=>{let p=u(...d);return s(),p}}return t.addEventListener("popstate",s),{complete({url:c,assignments:u}){let d=H(t.location.href);if(H(c)===d&&(s(),!(!i&&o===d))){i=!1,o=d;for(let p of e)p(u)}}}}function Be(t,e){let n=()=>t({eventName:Vn,url:e.location.href,referrer:e.document.referrer});return n(),n}var J="/collect";function Fe(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(J,i))return}catch{}if(!o){n==null||n("flush",{reason:"no_transport"});return}try{o(J,{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 Ve(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:x)(),i=new Blob([`{"envelopeId":"${o}","events":[]}`],{type:"text/plain"});return e(J,{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 Hn="rum.variation_view",Z="data-aci-variation";function He(t,e){var u,d,p;let{document:n,debug:r}=e,o=e.MutationObserver;if(typeof o!="function")return;let i=(u=n.defaultView)==null?void 0:u.location,s=new WeakSet,a=f=>{let m=f.getAttribute(Z);m!=null&&(s.has(f)||(s.add(f),t({eventName:Hn,url:i==null?void 0:i.href,properties:{variantKey:m}})))},c=f=>{try{f.hasAttribute(Z)&&a(f);for(let m of Array.from(f.querySelectorAll(`[${Z}]`)))a(m)}catch(m){r==null||r("capture",{reason:"variation_scan_failed",error:String(m)})}};try{let f=(d=n.body)!=null?d:n.documentElement;f&&c(f)}catch(f){r==null||r("capture",{reason:"variation_initial_scan_failed",error:String(f)})}try{let f=(p=n.body)!=null?p:n.documentElement;if(!f)return;new o(v=>{try{for(let b of v)for(let l=0;l<b.addedNodes.length;l++){let g=b.addedNodes[l];if((g==null?void 0:g.nodeType)!==1)continue;let y=g.element;y&&c(y)}}catch(b){r==null||r("capture",{reason:"variation_observer_failed",error:String(b)})}}).observe(f,{subtree:!0,childList:!0})}catch(f){r==null||r("capture",{reason:"variation_observe_failed",error:String(f)})}}var Mn="rum.web_vital";function Me(t,e){var m,v,b;let n=e.PerformanceObserver;if(typeof n!="function")return;let{performance:r,debug:o}=e,i=(v=(m=e.window)==null?void 0:m.location)==null?void 0:v.href,s=e.document,a=new Set,c=(l,g)=>{a.has(l)||(a.add(l),t({eventName:Mn,url:i,properties:{vitalName:l,vitalValue:g}}))};try{let l=r==null?void 0:r.getEntriesByType("navigation")[0];l&&typeof l.responseStart=="number"&&typeof l.requestStart=="number"&&c("TTFB",l.responseStart-l.requestStart)}catch(l){o==null||o("capture",{reason:"ttfb_failed",error:String(l)})}let u=0,d=0,p=(l,g)=>{try{new n(L=>{try{for(let A of L.getEntries())g(A)}catch(A){o==null||o("capture",{reason:`${l}_handler_failed`,error:String(A)})}}).observe({type:l,buffered:!0})}catch(y){o==null||o("capture",{reason:`${l}_observe_failed`,error:String(y)})}};p("largest-contentful-paint",l=>{c("LCP",l.startTime)}),p("first-input",l=>{c("FID",l.startTime)}),p("layout-shift",l=>{var g;l.hadRecentInput||(u+=(g=l.value)!=null?g:0)}),p("event",l=>{var y;let g=(y=l.value)!=null?y:0;g>d&&(d=g)}),p("paint",l=>{l.name==="first-contentful-paint"&&c("FCP",l.startTime)});let f=()=>{try{a.has("CLS")||c("CLS",u),a.has("INP")||c("INP",d)}catch(l){o==null||o("capture",{reason:"visibility_handler_failed",error:String(l)})}};s&&s.addEventListener("visibilitychange",()=>{s.visibilityState==="hidden"&&f()}),(b=e.window)==null||b.addEventListener("pagehide",f)}var Q={c15t:_e,onetrust:xe,cookiebot:Se,trustarc:Te};function jn(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 Wn(t){try{return t.sessionStorage}catch{return}}function Gn(t){var i;let e=t!=null?t:typeof window!="undefined"?window:void 0;if(!e||e.__aci_loaded||(e.__aci_loaded=!0,!jn(e)))return;let n=e.__aci_measurement_disabled===!0,r=Ee(e.document.currentScript),o=ye(r.debug);try{let s=e.document,a=new V({storage:Wn(e),debug:o}),c=()=>{try{return me(T(s))}catch{return!1}},u=()=>{try{return Ie(s)}catch{return!1}},d=()=>{try{return T(s)!==void 0}catch{return!1}},p=r.preset==="manual"?()=>{try{return be(s)}catch{return!1}}:()=>!0,f=new B({debug:o}),m=()=>!d()&&!u()&&p(),v={navigator:e.navigator,fetch:typeof e.fetch=="function"?e.fetch.bind(e):void 0,debug:o},b;b=new D({send:h=>Fe(h,v),debug:o,onTick:()=>{n||(Ce(f,{decided:d,grants:c,batcher:b,debug:o}),Y(a,b,u,o))},timers:{setInterval:(h,k)=>e.setInterval(h,k),clearInterval:h=>e.clearInterval(h)},documentTarget:s,windowTarget:e});let l=re({document:s,window:e,debug:o}),g=h=>{n||Oe({...te(h,l),eventId:x(void 0,k=>e.crypto.getRandomValues(k))},{grants:c,marker:u,gapBuffer:a,batcher:b,consentHold:f,consentPending:m})},y=()=>b.flush();s.addEventListener(he,y),l.start(g);let L=()=>{},A=Re(e,[h=>l.rearm(h),()=>L()],()=>l.clear());s.addEventListener("astro:page-load",()=>A.complete({url:e.location.href,assignments:j(s,o)})),ue(e,{navigationComplete:h=>A.complete(h),track:(h,k)=>g({eventName:h,properties:k,url:e.location.href}),consent:({p:h,src:k})=>{let ee=ge(h);if(ee===void 0){o("consent",{reason:"malformed_purposes"});return}let ne=ve(k);if(ne===void 0){o("consent",{reason:"malformed_source"});return}P(ee,ne,{document:s,gapBuffer:a,onDecision:y})||o("consent",{reason:"oversized_envelope"})}},o),L=Be(g,e),ce(g,{document:s,intersectionObserver:e.IntersectionObserver,debug:o},()=>{n||Ve(v).then(h=>{h&&e.location.reload()})}),De(g,{document:s,debug:o}),Me(g,{performance:e.performance,PerformanceObserver:e.PerformanceObserver,window:e,document:s,debug:o}),He(g,{document:s,MutationObserver:e.MutationObserver,debug:o}),r.preset!=="manual"&&((i=Q[r.preset])==null||i.call(Q,{window:e,document:s,categories:r.categories,gapBuffer:a,onDecision:y,debug:o,scheduler:{setTimeout:(h,k)=>e.setTimeout(h,k),clearTimeout:h=>e.clearTimeout(h)}})),b.start(),e.addEventListener("pagehide",()=>f.discard("unload")),n||Y(a,b,u,o),o("init",{config:r})}catch(s){o("init",{error:String(s)})}}Gn();})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c330eb304d6a7aa01375646c97c67be0f68ce9137974da03aa339696e0f01de6
|
package/dist/collect/index.js
CHANGED
|
@@ -55,6 +55,9 @@ export function init(win) {
|
|
|
55
55
|
w.__aci_loaded = true;
|
|
56
56
|
if (!hasBaseline(w))
|
|
57
57
|
return;
|
|
58
|
+
// Snapshot before exposures or queued commands. Cookie expiry, hydration
|
|
59
|
+
// and BFCache never turn this synthetic document into a genuine visit.
|
|
60
|
+
const measurementDisabled = w.__aci_measurement_disabled === true;
|
|
58
61
|
const cfg = readTagConfig(w.document.currentScript);
|
|
59
62
|
const debug = createDebug(cfg.debug);
|
|
60
63
|
try {
|
|
@@ -119,6 +122,8 @@ export function init(win) {
|
|
|
119
122
|
// and then the marker (so a mint by another tab or navigation is
|
|
120
123
|
// noticed). Held events are older than gap-buffered ones: release first.
|
|
121
124
|
onTick: () => {
|
|
125
|
+
if (measurementDisabled)
|
|
126
|
+
return;
|
|
122
127
|
resolveConsentHold(consentHold, { decided, grants, batcher, debug });
|
|
123
128
|
maybeFlushGapBuffer(gapBuffer, batcher, marker, debug);
|
|
124
129
|
},
|
|
@@ -135,6 +140,8 @@ export function init(win) {
|
|
|
135
140
|
// exists before emit can run; exposures flow at start() below.
|
|
136
141
|
const assignments = createAssignments({ document: doc, window: w, debug });
|
|
137
142
|
const emit = (event) => {
|
|
143
|
+
if (measurementDisabled)
|
|
144
|
+
return;
|
|
138
145
|
routeEvent({
|
|
139
146
|
...withAssignmentContext(event, assignments),
|
|
140
147
|
eventId: ulid(undefined, (bytes) => w.crypto.getRandomValues(bytes)),
|
|
@@ -187,6 +194,8 @@ export function init(win) {
|
|
|
187
194
|
intersectionObserver: w.IntersectionObserver,
|
|
188
195
|
debug,
|
|
189
196
|
}, () => {
|
|
197
|
+
if (measurementDisabled)
|
|
198
|
+
return;
|
|
190
199
|
void refreshClaims(transportEnv).then((refreshed) => {
|
|
191
200
|
if (refreshed)
|
|
192
201
|
w.location.reload();
|
|
@@ -224,7 +233,8 @@ export function init(win) {
|
|
|
224
233
|
// has already released a hold whose decision exists; whatever remains is
|
|
225
234
|
// undecided and never leaves the page.
|
|
226
235
|
w.addEventListener("pagehide", () => consentHold.discard("unload"));
|
|
227
|
-
|
|
236
|
+
if (!measurementDisabled)
|
|
237
|
+
maybeFlushGapBuffer(gapBuffer, batcher, marker, debug);
|
|
228
238
|
debug("init", { config: cfg });
|
|
229
239
|
}
|
|
230
240
|
catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IdentitySigningKeys, SigningKey,
|
|
1
|
+
import type { IdentitySigningKeys, SigningKey, ConsentSnapshot, ConsentSignals } from './types.js';
|
|
2
2
|
export declare const DEFAULT_IDENTITY_TTL_SECONDS: number;
|
|
3
3
|
export declare function parseCookieHeader(value: string | null): Map<string, string>;
|
|
4
4
|
export declare function consentSignals(request: Request, cookies: Map<string, string>): ConsentSignals;
|
|
@@ -9,10 +9,7 @@ export declare function verifyIdentityCookie(value: string, keys: IdentitySignin
|
|
|
9
9
|
export declare function signIdentity(rawID: string, key: SigningKey): Promise<string>;
|
|
10
10
|
export declare function randomIdentity(random?: (length: number) => Uint8Array): string;
|
|
11
11
|
export declare function identityHashHex(namespace: string, value: string): Promise<string>;
|
|
12
|
-
export declare function sha256Hex(value: string): Promise<string>;
|
|
13
12
|
export declare function verifyClaims(value: string, keys: Record<string, string>, now: Date): Promise<Record<string, unknown> | null>;
|
|
14
|
-
export declare function cookieMutation(name: string, value: string, maxAge: number, now: Date, secure: boolean, httpOnly: boolean): CookieMutation;
|
|
15
|
-
export declare function expireCookie(name: string, secure: boolean): CookieMutation;
|
|
16
13
|
export declare function verifyHMAC(value: string, signature: string, secret: string): Promise<boolean>;
|
|
17
14
|
export declare function hmacSHA256(value: string, secret: string): Promise<string>;
|
|
18
15
|
export declare function base64URLEncode(bytes: Uint8Array): string;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
// - Consent signal extraction (GPC/DNT + portable consent envelope)
|
|
6
6
|
// - Identity cookie verification and minting (aci_id)
|
|
7
7
|
// - Claims cookie verification (aci_claims)
|
|
8
|
-
// - Cookie mutation helpers
|
|
9
8
|
//
|
|
10
9
|
// Shared by every static edge adapter; this logic has no SDK host wrapper.
|
|
11
10
|
// internal/domain/audience/runtime/collect.ts imports from here as well.
|
|
@@ -143,10 +142,6 @@ export async function identityHashHex(namespace, value) {
|
|
|
143
142
|
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(`${namespace.trim().toLowerCase()}\x00${value.trim()}`));
|
|
144
143
|
return bytesToHex(new Uint8Array(digest));
|
|
145
144
|
}
|
|
146
|
-
export async function sha256Hex(value) {
|
|
147
|
-
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(value));
|
|
148
|
-
return bytesToHex(new Uint8Array(digest));
|
|
149
|
-
}
|
|
150
145
|
function bytesToHex(bytes) {
|
|
151
146
|
let value = '';
|
|
152
147
|
for (const byte of bytes)
|
|
@@ -174,17 +169,6 @@ export async function verifyClaims(value, keys, now) {
|
|
|
174
169
|
return null;
|
|
175
170
|
}
|
|
176
171
|
}
|
|
177
|
-
// ── Cookie mutation helpers ──
|
|
178
|
-
export function cookieMutation(name, value, maxAge, now, secure, httpOnly) {
|
|
179
|
-
return {
|
|
180
|
-
name,
|
|
181
|
-
value,
|
|
182
|
-
options: { path: '/', httpOnly, secure, sameSite: 'lax', maxAge, expires: new Date(now.getTime() + maxAge * 1000) },
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
export function expireCookie(name, secure) {
|
|
186
|
-
return { name, value: '', options: { path: '/', httpOnly: true, secure, sameSite: 'lax', maxAge: 0, expires: new Date(0) } };
|
|
187
|
-
}
|
|
188
172
|
// ── Internal crypto helpers ──
|
|
189
173
|
// verifyHMAC returns a verdict about the signature, which is the only visitor
|
|
190
174
|
// input here: a malformed or wrong signature is `false`. A fault importing the
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// This is the pure decision algorithm — no I/O, no cookies, no identity
|
|
6
6
|
// minting. The caller resolves identity and claims, then calls this.
|
|
7
|
-
import { variantValuesFromKey } from './
|
|
8
|
-
import { canonicalVariantKey } from './variant_key.js';
|
|
7
|
+
import { canonicalVariantKey, variantValuesFromKey } from './variant_key.js';
|
|
9
8
|
import { personalizationSelection } from './personalization_index.js';
|
|
10
9
|
export { preparePersonalizationRoute } from './personalization_index.js';
|
|
11
10
|
export class PersonalizationResolutionError extends Error {
|
|
@@ -171,8 +170,8 @@ function experimentEligible(item, claims, selectedLegAudienceId) {
|
|
|
171
170
|
// contentbuild planner's leg factors, candidates, and Lookup. A
|
|
172
171
|
// selected-mode experiment with an unresolved eligibility audience is a
|
|
173
172
|
// factor of no leg, base included. Both inputs are canonical by variant-key
|
|
174
|
-
// construction (the
|
|
175
|
-
// them), so this comparison needs no normalization — the Go twin's TrimSpace
|
|
173
|
+
// construction (the variant-key reader and the strict document decode
|
|
174
|
+
// pre-trim them), so this comparison needs no normalization — the Go twin's TrimSpace
|
|
176
175
|
// is defensive only and never changes the outcome.
|
|
177
176
|
function selectedModeFactorOf(item, legAudienceId) {
|
|
178
177
|
const audienceID = String(item.eligibilityAudienceId || '').trim();
|
|
@@ -385,7 +384,7 @@ function validateExpressionMembers(path, group, members, depth, diagnostics) {
|
|
|
385
384
|
// topLevel carries nesting's one asymmetry: the top envelope always records
|
|
386
385
|
// both group rows because they express the scope's empty-envelope policy,
|
|
387
386
|
// while a nested node records only for a list it carries members in — which
|
|
388
|
-
// also keeps the trace stable across a
|
|
387
|
+
// also keeps the trace stable across a serialization that drops empty lists.
|
|
389
388
|
function qualifyRequestExpression(all, any, resolve, trace, path = 'expression', topLevel = true) {
|
|
390
389
|
let qualified = true;
|
|
391
390
|
for (const [group, members] of [['all', all], ['any', any]]) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { variantValuesFromKey } from './
|
|
2
|
-
import { compareUTF8 } from './variant_key.js';
|
|
1
|
+
import { compareUTF8, variantValuesFromKey } from './variant_key.js';
|
|
3
2
|
const preparedSelection = Symbol('prepared personalization selection');
|
|
4
3
|
// Call once when an immutable partition is decoded. The derived index is a
|
|
5
4
|
// non-enumerable symbol property, so it cannot alter any serialized record.
|
|
@@ -33,20 +33,6 @@ export interface EdgeRouteEntry {
|
|
|
33
33
|
dispatch: EdgeDispatch;
|
|
34
34
|
variants?: Record<string, EdgeRouteVariant>;
|
|
35
35
|
}
|
|
36
|
-
export interface CollectSDKEntry {
|
|
37
|
-
hash: string;
|
|
38
|
-
path: string;
|
|
39
|
-
}
|
|
40
|
-
export interface EdgeRouteManifest {
|
|
41
|
-
manifestVersion: string;
|
|
42
|
-
releaseId: string;
|
|
43
|
-
siteId: string;
|
|
44
|
-
routes: Record<string, EdgeRouteEntry>;
|
|
45
|
-
experiments?: Record<string, PersonalizationExperiment>;
|
|
46
|
-
collectSdk?: CollectSDKEntry;
|
|
47
|
-
notFoundPage?: string;
|
|
48
|
-
errorPage?: string;
|
|
49
|
-
}
|
|
50
36
|
export interface PersonalizationRequestContext {
|
|
51
37
|
path: string;
|
|
52
38
|
query: Record<string, string[]>;
|
|
@@ -141,18 +127,6 @@ export interface IdentitySigningKeys {
|
|
|
141
127
|
current: SigningKey;
|
|
142
128
|
previous?: SigningKey;
|
|
143
129
|
}
|
|
144
|
-
export interface CookieMutation {
|
|
145
|
-
name: string;
|
|
146
|
-
value: string;
|
|
147
|
-
options: {
|
|
148
|
-
path: '/';
|
|
149
|
-
httpOnly?: boolean;
|
|
150
|
-
secure?: boolean;
|
|
151
|
-
sameSite: 'lax';
|
|
152
|
-
maxAge: number;
|
|
153
|
-
expires?: Date;
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
130
|
export interface ConsentSnapshot {
|
|
157
131
|
version: number;
|
|
158
132
|
purposes: string[];
|
|
@@ -219,15 +193,4 @@ export interface PersonalizationResolution {
|
|
|
219
193
|
forced?: boolean;
|
|
220
194
|
}>;
|
|
221
195
|
}
|
|
222
|
-
export interface CompositionDescriptor {
|
|
223
|
-
layoutArtifact: string;
|
|
224
|
-
fragments?: Record<string, string>;
|
|
225
|
-
}
|
|
226
|
-
export type SelectedTargetKind = 'direct' | 'compose' | 'render';
|
|
227
|
-
export interface SelectedTarget {
|
|
228
|
-
kind: SelectedTargetKind;
|
|
229
|
-
path: string;
|
|
230
|
-
digest: string;
|
|
231
|
-
composition?: CompositionDescriptor;
|
|
232
|
-
}
|
|
233
196
|
export { IdentityErasureFencedError } from '../../platform/collectionstore/errors.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare function compareUTF8(left: string, right: string): number;
|
|
2
2
|
export declare function canonicalVariantKey(values: Record<string, string>): string;
|
|
3
3
|
export declare function parseVariantKey(key: string): Record<string, string>;
|
|
4
|
+
export declare function variantValuesFromKey(key: string): Record<string, string>;
|
|
@@ -39,6 +39,18 @@ export function parseVariantKey(key) {
|
|
|
39
39
|
throw new Error('variant key is not canonically ordered');
|
|
40
40
|
return values;
|
|
41
41
|
}
|
|
42
|
+
// Lenient reader for a route record's variant key when the record carries no
|
|
43
|
+
// variantValues. Unlike parseVariantKey it never throws and also accepts `&`
|
|
44
|
+
// as a separator, so it is not interchangeable with the canonical parser.
|
|
45
|
+
export function variantValuesFromKey(key) {
|
|
46
|
+
const values = {};
|
|
47
|
+
for (const part of String(key || '').replace(/&/g, ';').split(';')) {
|
|
48
|
+
const sep = part.indexOf('=');
|
|
49
|
+
if (sep > 0)
|
|
50
|
+
values[part.slice(0, sep).trim()] = part.slice(sep + 1).trim();
|
|
51
|
+
}
|
|
52
|
+
return values;
|
|
53
|
+
}
|
|
42
54
|
function validToken(value) {
|
|
43
55
|
return value !== '' && value !== '.' && value !== '..' && !/[\p{White_Space};=/]/u.test(value);
|
|
44
56
|
}
|
package/dist/next/page.d.ts
CHANGED
|
@@ -34,7 +34,8 @@ export interface CreatePageOptions {
|
|
|
34
34
|
* to `/__r/{releaseId}/{targetRef}`. The target ref selects the exact immutable
|
|
35
35
|
* base or variant page while its prefix identifies the logical public route.
|
|
36
36
|
* createPage passes both identities to the content loader, giving each target
|
|
37
|
-
* a distinct CDN cache key without sending selection headers into Next.
|
|
37
|
+
* a distinct CDN cache key without sending selection headers into Next. The
|
|
38
|
+
* mimic response is decorated by the hosting platform after rendering.
|
|
38
39
|
*
|
|
39
40
|
* Dev pass-through (ACI_CONTENT_PROVIDER=file): the middleware passes
|
|
40
41
|
* requests through unrewritten, so `/` arrives with an empty/undefined
|
package/dist/next/page.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { notFound } from 'next/navigation.js';
|
|
3
|
-
import { headers } from 'next/headers.js';
|
|
4
3
|
import { Fragment } from 'react';
|
|
5
4
|
import { PageNotFoundError, getRenderInput, getLayout, getFragment, } from './server.js';
|
|
6
|
-
import { MIMIC_BANNER_HEADER, decodeMimicBanner } from '../mimic/core.js';
|
|
7
5
|
import { routeMetadataForContent, resolveSlots, } from '../content/index.js';
|
|
8
6
|
import { renderBlocks } from './render.js';
|
|
9
7
|
import { AssignmentsScript } from './assignments-script.js';
|
|
@@ -34,7 +32,8 @@ import { parseHostedPageTarget } from './hosted-page-target.js';
|
|
|
34
32
|
* to `/__r/{releaseId}/{targetRef}`. The target ref selects the exact immutable
|
|
35
33
|
* base or variant page while its prefix identifies the logical public route.
|
|
36
34
|
* createPage passes both identities to the content loader, giving each target
|
|
37
|
-
* a distinct CDN cache key without sending selection headers into Next.
|
|
35
|
+
* a distinct CDN cache key without sending selection headers into Next. The
|
|
36
|
+
* mimic response is decorated by the hosting platform after rendering.
|
|
38
37
|
*
|
|
39
38
|
* Dev pass-through (ACI_CONTENT_PROVIDER=file): the middleware passes
|
|
40
39
|
* requests through unrewritten, so `/` arrives with an empty/undefined
|
|
@@ -86,7 +85,7 @@ export function createPage(registry, options) {
|
|
|
86
85
|
}
|
|
87
86
|
return (_jsx(Fragment, { children: renderedBlocks }, slot.name));
|
|
88
87
|
}));
|
|
89
|
-
return (_jsxs(_Fragment, { children: [renderedSlots.filter(Boolean), _jsx(AssignmentsScript, { page: input.page }), _jsx(CollectScript, { siteConfig: input.siteConfig }),
|
|
88
|
+
return (_jsxs(_Fragment, { children: [renderedSlots.filter(Boolean), _jsx(AssignmentsScript, { page: input.page }), _jsx(CollectScript, { siteConfig: input.siteConfig }), renderProofContent
|
|
90
89
|
? _jsx("meta", { name: "aci-render-proof", content: renderProofContent })
|
|
91
90
|
: null] }));
|
|
92
91
|
}
|
|
@@ -102,6 +101,8 @@ export function createPage(registry, options) {
|
|
|
102
101
|
default: CatchAllPage,
|
|
103
102
|
};
|
|
104
103
|
}
|
|
104
|
+
// Immutable render identity, shared by ordinary and platform-fetched HTML.
|
|
105
|
+
const SHARED_RENDER_SEGMENT = '__r';
|
|
105
106
|
/**
|
|
106
107
|
* Resolves the logical route and releaseId from Next.js catch-all params.
|
|
107
108
|
*
|
|
@@ -121,7 +122,7 @@ export function createPage(registry, options) {
|
|
|
121
122
|
export async function resolveRouteAndRelease(params) {
|
|
122
123
|
const resolved = await params;
|
|
123
124
|
const slug = resolved.slug ?? [];
|
|
124
|
-
if (slug.length >= 2 && slug[0] ===
|
|
125
|
+
if (slug.length >= 2 && slug[0] === SHARED_RENDER_SEGMENT) {
|
|
125
126
|
const releaseId = decodeURIComponent(slug[1]);
|
|
126
127
|
if (!releaseId || releaseId.includes('/') || releaseId === '.' || releaseId === '..') {
|
|
127
128
|
return { route: '/' };
|
|
@@ -135,6 +136,14 @@ export async function resolveRouteAndRelease(params) {
|
|
|
135
136
|
pageRef: target.pageRef,
|
|
136
137
|
};
|
|
137
138
|
}
|
|
139
|
+
// These namespaces belong to native Next/static/API handlers. A missing
|
|
140
|
+
// file can fall through to this catch-all without the hosting router's
|
|
141
|
+
// release selection; it is a native 404, not a CMS content request. The
|
|
142
|
+
// shared resolver guards both page rendering and generateMetadata.
|
|
143
|
+
if (slug[0] === 'favicon.ico'
|
|
144
|
+
|| ['assets', '_astro', '_next', 'api'].includes(slug[0])) {
|
|
145
|
+
notFound();
|
|
146
|
+
}
|
|
138
147
|
const route = routeFromSlug(slug);
|
|
139
148
|
return { route };
|
|
140
149
|
}
|
|
@@ -143,23 +152,3 @@ function routeFromSlug(slug) {
|
|
|
143
152
|
return '/';
|
|
144
153
|
return `/${slug.join('/')}/`;
|
|
145
154
|
}
|
|
146
|
-
// The middleware emits the banner HTML in a reserved request header only
|
|
147
|
-
// after validating a signed mimic token; viewer-forged copies are stripped at
|
|
148
|
-
// the dispatch gate (specs/features/audience-mimic REQ-4). The header read
|
|
149
|
-
// happens here (the page already awaits request-scoped data) so the banner
|
|
150
|
-
// itself stays a synchronous leaf.
|
|
151
|
-
async function readMimicBannerHTML() {
|
|
152
|
-
try {
|
|
153
|
-
const current = await headers();
|
|
154
|
-
return decodeMimicBanner(current.get(MIMIC_BANNER_HEADER) || '');
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
// headers() has no request context in static generation and unit tests.
|
|
158
|
-
return '';
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function MimicBanner({ html }) {
|
|
162
|
-
if (!html)
|
|
163
|
-
return null;
|
|
164
|
-
return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
|
|
165
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The release identity of a request the hosting router rewrote to
|
|
3
|
-
* `/__r/{releaseId}/…`. The
|
|
3
|
+
* `/__r/{releaseId}/…`. The
|
|
4
|
+
* catch-all reads the release from that path; Next
|
|
4
5
|
* code that receives no route params (the not-found seat, release-less SDK
|
|
5
6
|
* reads such as a secondary `getPage('/blog')`) reads it from this header.
|
|
6
7
|
*
|
|
@@ -2,7 +2,8 @@ import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { headers } from 'next/headers.js';
|
|
3
3
|
/**
|
|
4
4
|
* The release identity of a request the hosting router rewrote to
|
|
5
|
-
* `/__r/{releaseId}/…`. The
|
|
5
|
+
* `/__r/{releaseId}/…`. The
|
|
6
|
+
* catch-all reads the release from that path; Next
|
|
6
7
|
* code that receives no route params (the not-found seat, release-less SDK
|
|
7
8
|
* reads such as a secondary `getPage('/blog')`) reads it from this header.
|
|
8
9
|
*
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Secondary experiment goals use goalIds
|
|
2
|
+
|
|
3
|
+
`goalIds` is the supported field for secondary experiment goal links. The
|
|
4
|
+
API rejects an experiment document containing `goalRefs`, including an empty
|
|
5
|
+
array or `null`, rather than silently discarding its saved goal links. The
|
|
6
|
+
SDK authoring model no longer declares `goalRefs`.
|
|
7
|
+
|
|
8
|
+
## Convert the field
|
|
9
|
+
|
|
10
|
+
In experiment `props`, replace reference strings with the corresponding bare
|
|
11
|
+
goal IDs and remove `goalRefs` entirely.
|
|
12
|
+
|
|
13
|
+
Before:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"goalRefs": ["$ref:data/conversion-goals/request-demo"]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"goalIds": ["request-demo"]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Preserve the linked goal set and the primary goal. `primaryGoalId` and
|
|
30
|
+
`primaryGoalRef` remain supported; this migration concerns only the secondary
|
|
31
|
+
list.
|
|
32
|
+
|
|
33
|
+
## Upgrade existing content
|
|
34
|
+
|
|
35
|
+
Check experiment documents, seeds, templates, and authoring integrations for
|
|
36
|
+
`goalRefs`. If any existing content uses it, submit the spelling change
|
|
37
|
+
through the normal content API while the preceding API version still accepts
|
|
38
|
+
both spellings, and validate the resulting experiment before upgrading the
|
|
39
|
+
API image that retires `goalRefs`. Existing horizon and settled-goal rules
|
|
40
|
+
continue to apply.
|
|
41
|
+
|
|
42
|
+
If no document or authoring integration uses `goalRefs`, no content migration
|
|
43
|
+
is needed. Use `goalIds` for new secondary-goal links.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.52-rc.
|
|
3
|
+
"version": "0.1.52-rc.40",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/gradial/bare-metal-go.git",
|
|
@@ -160,11 +160,11 @@
|
|
|
160
160
|
"zod": "^4.0.0"
|
|
161
161
|
},
|
|
162
162
|
"optionalDependencies": {
|
|
163
|
-
"@gradial/aci-cli-darwin-arm64": "0.1.52-rc.
|
|
164
|
-
"@gradial/aci-cli-darwin-x64": "0.1.52-rc.
|
|
165
|
-
"@gradial/aci-cli-linux-arm64": "0.1.52-rc.
|
|
166
|
-
"@gradial/aci-cli-linux-x64": "0.1.52-rc.
|
|
167
|
-
"@gradial/aci-cli-win32-arm64": "0.1.52-rc.
|
|
168
|
-
"@gradial/aci-cli-win32-x64": "0.1.52-rc.
|
|
163
|
+
"@gradial/aci-cli-darwin-arm64": "0.1.52-rc.40",
|
|
164
|
+
"@gradial/aci-cli-darwin-x64": "0.1.52-rc.40",
|
|
165
|
+
"@gradial/aci-cli-linux-arm64": "0.1.52-rc.40",
|
|
166
|
+
"@gradial/aci-cli-linux-x64": "0.1.52-rc.40",
|
|
167
|
+
"@gradial/aci-cli-win32-arm64": "0.1.52-rc.40",
|
|
168
|
+
"@gradial/aci-cli-win32-x64": "0.1.52-rc.40"
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export interface ObjectReader {
|
|
2
|
-
readObject(path: string): Promise<Uint8Array>;
|
|
3
|
-
}
|
|
4
|
-
export interface ComposeObjectRef {
|
|
5
|
-
path: string;
|
|
6
|
-
digest: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ComposeSlot {
|
|
9
|
-
name: string;
|
|
10
|
-
required: boolean;
|
|
11
|
-
placeholder: string;
|
|
12
|
-
parts: ComposeObjectRef[];
|
|
13
|
-
}
|
|
14
|
-
export interface ComposeEnvelope {
|
|
15
|
-
schema: 'gradial.compose-envelope.v1';
|
|
16
|
-
algorithm: 'aci-html-compose-v1';
|
|
17
|
-
shell: ComposeObjectRef;
|
|
18
|
-
slots: ComposeSlot[];
|
|
19
|
-
}
|
|
20
|
-
export declare function composeEnvelope(reader: ObjectReader, envelopePath: string, envelopeDigest: string): Promise<{
|
|
21
|
-
html: string;
|
|
22
|
-
contentType: string;
|
|
23
|
-
}>;
|
|
24
|
-
export declare function validateEnvelope(envelope: unknown): ComposeEnvelope;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
2
|
-
export async function composeEnvelope(reader, envelopePath, envelopeDigest) {
|
|
3
|
-
const envelopeBytes = await reader.readObject(envelopePath);
|
|
4
|
-
await requireDigest(envelopeBytes, envelopeDigest, 'compose envelope');
|
|
5
|
-
let parsed;
|
|
6
|
-
try {
|
|
7
|
-
parsed = JSON.parse(decoder.decode(envelopeBytes));
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
throw new Error('compose envelope is malformed');
|
|
11
|
-
}
|
|
12
|
-
const envelope = validateEnvelope(parsed);
|
|
13
|
-
const shellBytes = await reader.readObject(envelope.shell.path);
|
|
14
|
-
await requireDigest(shellBytes, envelope.shell.digest, 'compose shell');
|
|
15
|
-
let html = decoder.decode(shellBytes);
|
|
16
|
-
for (const slot of envelope.slots) {
|
|
17
|
-
if (occurrences(html, slot.placeholder) !== 1) {
|
|
18
|
-
throw new Error(`compose slot ${JSON.stringify(slot.name)} placeholder must occur exactly once`);
|
|
19
|
-
}
|
|
20
|
-
if (slot.required && slot.parts.length === 0) {
|
|
21
|
-
throw new Error(`compose slot ${JSON.stringify(slot.name)} is required`);
|
|
22
|
-
}
|
|
23
|
-
const parts = [];
|
|
24
|
-
for (const part of slot.parts) {
|
|
25
|
-
const bytes = await reader.readObject(part.path);
|
|
26
|
-
await requireDigest(bytes, part.digest, `compose slot ${slot.name}`);
|
|
27
|
-
parts.push(decoder.decode(bytes));
|
|
28
|
-
}
|
|
29
|
-
html = html.replace(slot.placeholder, parts.join(''));
|
|
30
|
-
}
|
|
31
|
-
if (html.includes('<!--ACI_SLOT:')) {
|
|
32
|
-
throw new Error('compose shell has unresolved slots');
|
|
33
|
-
}
|
|
34
|
-
return { html, contentType: 'text/html; charset=utf-8' };
|
|
35
|
-
}
|
|
36
|
-
export function validateEnvelope(envelope) {
|
|
37
|
-
if (!envelope || typeof envelope !== 'object') {
|
|
38
|
-
throw new Error('compose envelope is malformed');
|
|
39
|
-
}
|
|
40
|
-
const obj = envelope;
|
|
41
|
-
if (obj.schema !== 'gradial.compose-envelope.v1'
|
|
42
|
-
|| obj.algorithm !== 'aci-html-compose-v1'
|
|
43
|
-
|| !Array.isArray(obj.slots)) {
|
|
44
|
-
throw new Error('compose envelope is malformed');
|
|
45
|
-
}
|
|
46
|
-
const env = envelope;
|
|
47
|
-
validateRef(env.shell);
|
|
48
|
-
const names = new Set();
|
|
49
|
-
for (const slot of env.slots) {
|
|
50
|
-
if (!slot || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(slot.name) || names.has(slot.name)) {
|
|
51
|
-
throw new Error('compose slot is malformed');
|
|
52
|
-
}
|
|
53
|
-
names.add(slot.name);
|
|
54
|
-
if (slot.placeholder !== `<!--ACI_SLOT:${slot.name}-->` || !Array.isArray(slot.parts)) {
|
|
55
|
-
throw new Error(`compose slot ${JSON.stringify(slot.name)} is malformed`);
|
|
56
|
-
}
|
|
57
|
-
for (const part of slot.parts)
|
|
58
|
-
validateRef(part);
|
|
59
|
-
}
|
|
60
|
-
return env;
|
|
61
|
-
}
|
|
62
|
-
function validateRef(ref) {
|
|
63
|
-
const path = String(ref?.path || '');
|
|
64
|
-
if (!path || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..')) {
|
|
65
|
-
throw new Error('compose object path is invalid');
|
|
66
|
-
}
|
|
67
|
-
if (!/^[0-9a-f]{64}$/.test(String(ref.digest || '').toLowerCase())) {
|
|
68
|
-
throw new Error('compose object digest is invalid');
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async function requireDigest(bytes, want, label) {
|
|
72
|
-
const ab = new ArrayBuffer(bytes.byteLength);
|
|
73
|
-
new Uint8Array(ab).set(bytes);
|
|
74
|
-
const actual = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', ab)), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
75
|
-
if (actual !== want.toLowerCase())
|
|
76
|
-
throw new Error(`${label} digest mismatch`);
|
|
77
|
-
}
|
|
78
|
-
function occurrences(value, needle) {
|
|
79
|
-
let count = 0;
|
|
80
|
-
let offset = 0;
|
|
81
|
-
while (true) {
|
|
82
|
-
offset = value.indexOf(needle, offset);
|
|
83
|
-
if (offset < 0)
|
|
84
|
-
break;
|
|
85
|
-
count += 1;
|
|
86
|
-
offset += needle.length;
|
|
87
|
-
}
|
|
88
|
-
return count;
|
|
89
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { EdgeRouteEntry } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Unique sentinel object returned by expand functions on failure.
|
|
4
|
-
* Callers check `result === INVALID`. Never throws.
|
|
5
|
-
*/
|
|
6
|
-
export declare const INVALID: unique symbol;
|
|
7
|
-
export type Invalid = typeof INVALID;
|
|
8
|
-
interface CompactDispatch {
|
|
9
|
-
k: 's';
|
|
10
|
-
ap: string;
|
|
11
|
-
}
|
|
12
|
-
interface CompactCondition {
|
|
13
|
-
f?: string;
|
|
14
|
-
o?: string;
|
|
15
|
-
w?: unknown;
|
|
16
|
-
a?: CompactCondition[];
|
|
17
|
-
n?: CompactCondition[];
|
|
18
|
-
}
|
|
19
|
-
interface CompactRule {
|
|
20
|
-
a?: CompactCondition[];
|
|
21
|
-
n?: CompactCondition[];
|
|
22
|
-
}
|
|
23
|
-
export declare const COMPACT_MAX_DEPTH = 32;
|
|
24
|
-
interface CompactVariant {
|
|
25
|
-
pr?: number;
|
|
26
|
-
r?: CompactRule;
|
|
27
|
-
d?: CompactDispatch;
|
|
28
|
-
vk?: string;
|
|
29
|
-
vv?: Record<string, string>;
|
|
30
|
-
}
|
|
31
|
-
export interface CompactRouteRecord {
|
|
32
|
-
s?: number;
|
|
33
|
-
p: string;
|
|
34
|
-
d: CompactDispatch;
|
|
35
|
-
v?: Record<string, CompactVariant>;
|
|
36
|
-
x?: string[];
|
|
37
|
-
}
|
|
38
|
-
export declare function projectRouteRecord(entry: EdgeRouteEntry): CompactRouteRecord;
|
|
39
|
-
export declare function expandRouteRecord(raw: unknown): EdgeRouteEntry | Invalid;
|
|
40
|
-
export declare const MARKER = "{\"s\":1,\"e\":1}";
|
|
41
|
-
export declare function isMarker(raw: string): boolean;
|
|
42
|
-
export declare function canonicalSerialize(value: unknown): string;
|
|
43
|
-
export declare function variantValuesFromKey(key: string): Record<string, string>;
|
|
44
|
-
export declare function pathHash16(path: string): string;
|
|
45
|
-
export {};
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
// Compact JSON codec for edge-decisioning route records.
|
|
2
|
-
//
|
|
3
|
-
// project(): canonical → compact (short field names via alias table, omit defaults)
|
|
4
|
-
// expand(): compact → canonical (reverse, never throws — returns INVALID sentinel on failure)
|
|
5
|
-
//
|
|
6
|
-
// Laws (property-tested):
|
|
7
|
-
// expand(project(r)) ≡ normalize(r)
|
|
8
|
-
// expand() never throws — malformed input → INVALID
|
|
9
|
-
// Unknown field code on expand = invalid data (→ base + alarm)
|
|
10
|
-
//
|
|
11
|
-
// The alias table is the single source of truth (schema/alias-table.v1.json).
|
|
12
|
-
// These codecs are hand-written from it; cross-language round-trip tests
|
|
13
|
-
// enforce drift between the Go and TS implementations.
|
|
14
|
-
//
|
|
15
|
-
// Failure is represented by the exported INVALID sentinel (a unique object).
|
|
16
|
-
// Expand functions return the value directly on success, or INVALID on failure.
|
|
17
|
-
// Callers check `result === INVALID`. This avoids the {ok,value,reason}
|
|
18
|
-
// wrapper in the byte-constrained runtime.
|
|
19
|
-
// ── INVALID sentinel ─────────────────────────────────────────────────
|
|
20
|
-
/**
|
|
21
|
-
* Unique sentinel object returned by expand functions on failure.
|
|
22
|
-
* Callers check `result === INVALID`. Never throws.
|
|
23
|
-
*/
|
|
24
|
-
export const INVALID = Symbol();
|
|
25
|
-
const te = new TextEncoder();
|
|
26
|
-
// ── Alias table (hand-mirrored from schema/alias-table.v1.json) ──────
|
|
27
|
-
const FIELD_ALIASES = {
|
|
28
|
-
schema: 's', path: 'p', dispatch: 'd', variants: 'v', experiments: 'x',
|
|
29
|
-
priority: 'pr', kind: 'k', rule: 'r', all: 'a', any: 'n', field: 'f',
|
|
30
|
-
operator: 'o', value: 'w', audiences: 'au', segments: 'sg',
|
|
31
|
-
classification: 'c', arms: 'ar', id: 'id', status: 'st',
|
|
32
|
-
allocationVersion: 'av', salt: 'sl', allocationBasisPoints: 'ab',
|
|
33
|
-
variantKey: 'vk', variantValues: 'vv', artifactPath: 'ap',
|
|
34
|
-
displayName: 'dn', primaryGoalId: 'pg', goalIds: 'gl', role: 'ro',
|
|
35
|
-
};
|
|
36
|
-
const DISPATCH_KIND_TO_COMPACT = {
|
|
37
|
-
'static-direct': 's',
|
|
38
|
-
};
|
|
39
|
-
const DISPATCH_KIND_FROM_COMPACT = {
|
|
40
|
-
s: 'static-direct',
|
|
41
|
-
};
|
|
42
|
-
const OPERATOR_TO_COMPACT = {
|
|
43
|
-
equals: 'eq', contains: 'ct', startsWith: 'sw', exists: 'ex',
|
|
44
|
-
greaterThan: 'gt', greaterThanOrEqual: 'ge', lessThan: 'lt', lessThanOrEqual: 'le',
|
|
45
|
-
before: 'bf', after: 'af', between: 'bt', in: 'in',
|
|
46
|
-
};
|
|
47
|
-
const OPERATOR_FROM_COMPACT = {
|
|
48
|
-
eq: 'equals', ct: 'contains', sw: 'startsWith', ex: 'exists',
|
|
49
|
-
gt: 'greaterThan', ge: 'greaterThanOrEqual', lt: 'lessThan', le: 'lessThanOrEqual',
|
|
50
|
-
bf: 'before', af: 'after', bt: 'between', in: 'in',
|
|
51
|
-
};
|
|
52
|
-
// Compact type guard — reduces minified bytes vs repeated typeof+null+isArray checks
|
|
53
|
-
function isObj(v) {
|
|
54
|
-
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
55
|
-
}
|
|
56
|
-
function projectDispatch(dispatch) {
|
|
57
|
-
if (dispatch.kind !== 'static-direct' || !dispatch.artifactPath) {
|
|
58
|
-
throw new Error('edge dispatch must name one static artifact');
|
|
59
|
-
}
|
|
60
|
-
return { k: DISPATCH_KIND_TO_COMPACT[dispatch.kind], ap: dispatch.artifactPath };
|
|
61
|
-
}
|
|
62
|
-
function expandDispatch(raw) {
|
|
63
|
-
if (raw == null)
|
|
64
|
-
return undefined;
|
|
65
|
-
if (!isObj(raw))
|
|
66
|
-
return INVALID;
|
|
67
|
-
const kind = DISPATCH_KIND_FROM_COMPACT[raw.k];
|
|
68
|
-
const artifactPath = raw.ap;
|
|
69
|
-
if (kind !== 'static-direct' || typeof artifactPath !== 'string' || artifactPath.length === 0)
|
|
70
|
-
return INVALID;
|
|
71
|
-
return { kind, artifactPath };
|
|
72
|
-
}
|
|
73
|
-
// Structural recursion guard, deliberately looser than the authored MaxDepth
|
|
74
|
-
// (5) the validators enforce. The codec's job is to bound adversarial
|
|
75
|
-
// recursion in a byte-constrained runtime; the authored contract is
|
|
76
|
-
// validation's. Keeping it looser is what lets an over-depth expression
|
|
77
|
-
// round-trip and be rejected by validation identically in every runtime,
|
|
78
|
-
// rather than being erased by the codec first (ADR 0040).
|
|
79
|
-
export const COMPACT_MAX_DEPTH = 32;
|
|
80
|
-
// A member carrying all/any is an envelope, not a condition — the same
|
|
81
|
-
// presence rule the evaluators apply, so the codec cannot disagree with them
|
|
82
|
-
// about what a member is.
|
|
83
|
-
function isGroupMember(cond) {
|
|
84
|
-
return !!cond && (cond.all !== undefined || cond.any !== undefined);
|
|
85
|
-
}
|
|
86
|
-
function projectCondition(cond, depth) {
|
|
87
|
-
// Projection runs at publish time in a trusted context behind write-time
|
|
88
|
-
// validation: a malformed condition or an operator with no compact token is
|
|
89
|
-
// vocabulary/codec drift, and silently dropping the condition would ship a
|
|
90
|
-
// rule missing a leaf — MORE permissive than authored (ADR 0038). The same
|
|
91
|
-
// reasoning covers a group node: dropping one publishes a rule the author
|
|
92
|
-
// did not write. Expansion stays fail-closed for already-deployed bundles.
|
|
93
|
-
if (!cond) {
|
|
94
|
-
throw new Error('edge rule condition must name a field and operator');
|
|
95
|
-
}
|
|
96
|
-
if (isGroupMember(cond)) {
|
|
97
|
-
if (depth > COMPACT_MAX_DEPTH) {
|
|
98
|
-
throw new Error('edge rule nests deeper than the codec can project');
|
|
99
|
-
}
|
|
100
|
-
if (cond.field !== undefined || cond.operator !== undefined || cond.value !== undefined) {
|
|
101
|
-
throw new Error('edge rule member mixes group and leaf keys');
|
|
102
|
-
}
|
|
103
|
-
const group = {};
|
|
104
|
-
if (cond.all && cond.all.length > 0)
|
|
105
|
-
group.a = cond.all.map((member) => projectCondition(member, depth + 1));
|
|
106
|
-
if (cond.any && cond.any.length > 0)
|
|
107
|
-
group.n = cond.any.map((member) => projectCondition(member, depth + 1));
|
|
108
|
-
if (!group.a && !group.n) {
|
|
109
|
-
throw new Error('edge rule group must include at least one member');
|
|
110
|
-
}
|
|
111
|
-
return group;
|
|
112
|
-
}
|
|
113
|
-
if (!cond.field || !cond.operator) {
|
|
114
|
-
throw new Error('edge rule condition must name a field and operator');
|
|
115
|
-
}
|
|
116
|
-
const compactOp = OPERATOR_TO_COMPACT[cond.operator];
|
|
117
|
-
if (!compactOp) {
|
|
118
|
-
throw new Error(`edge rule operator has no compact token: ${cond.operator}`);
|
|
119
|
-
}
|
|
120
|
-
const result = { f: cond.field, o: compactOp };
|
|
121
|
-
if (cond.value !== undefined)
|
|
122
|
-
result.w = cond.value;
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
function expandCondition(raw, depth) {
|
|
126
|
-
if (!isObj(raw))
|
|
127
|
-
return INVALID;
|
|
128
|
-
if (raw.a !== undefined || raw.n !== undefined) {
|
|
129
|
-
if (depth > COMPACT_MAX_DEPTH)
|
|
130
|
-
return INVALID;
|
|
131
|
-
// A member cannot be both: expanding a mixed record would hand the
|
|
132
|
-
// evaluator something neither a group nor a condition.
|
|
133
|
-
if (raw.f !== undefined || raw.o !== undefined || raw.w !== undefined)
|
|
134
|
-
return INVALID;
|
|
135
|
-
const group = {};
|
|
136
|
-
for (const [compact, key] of [['a', 'all'], ['n', 'any']]) {
|
|
137
|
-
if (raw[compact] === undefined)
|
|
138
|
-
continue;
|
|
139
|
-
if (!Array.isArray(raw[compact]))
|
|
140
|
-
return INVALID;
|
|
141
|
-
const members = [];
|
|
142
|
-
for (const member of raw[compact]) {
|
|
143
|
-
const expanded = expandCondition(member, depth + 1);
|
|
144
|
-
if (expanded === INVALID)
|
|
145
|
-
return INVALID;
|
|
146
|
-
members.push(expanded);
|
|
147
|
-
}
|
|
148
|
-
group[key] = members;
|
|
149
|
-
}
|
|
150
|
-
// Length, not truthiness: [] is truthy, so a group whose only list is
|
|
151
|
-
// empty would otherwise expand into a member with no predicate at all.
|
|
152
|
-
if ((group.all?.length || 0) + (group.any?.length || 0) === 0)
|
|
153
|
-
return INVALID;
|
|
154
|
-
return group;
|
|
155
|
-
}
|
|
156
|
-
const f = raw.f;
|
|
157
|
-
const co = raw.o;
|
|
158
|
-
if (typeof f !== 'string')
|
|
159
|
-
return INVALID;
|
|
160
|
-
const op = OPERATOR_FROM_COMPACT[co];
|
|
161
|
-
if (!op)
|
|
162
|
-
return INVALID;
|
|
163
|
-
const c = { field: f, operator: op };
|
|
164
|
-
if (raw.w !== undefined)
|
|
165
|
-
c.value = raw.w;
|
|
166
|
-
return c;
|
|
167
|
-
}
|
|
168
|
-
function projectRule(rule) {
|
|
169
|
-
if (!rule)
|
|
170
|
-
return undefined;
|
|
171
|
-
const result = {};
|
|
172
|
-
if (rule.all && rule.all.length > 0)
|
|
173
|
-
result.a = rule.all.map((cond) => projectCondition(cond, 1));
|
|
174
|
-
if (rule.any && rule.any.length > 0)
|
|
175
|
-
result.n = rule.any.map((cond) => projectCondition(cond, 1));
|
|
176
|
-
if (!result.a && !result.n)
|
|
177
|
-
return undefined;
|
|
178
|
-
return result;
|
|
179
|
-
}
|
|
180
|
-
function expandRule(raw) {
|
|
181
|
-
if (raw == null)
|
|
182
|
-
return undefined;
|
|
183
|
-
if (!isObj(raw))
|
|
184
|
-
return INVALID;
|
|
185
|
-
const rule = {};
|
|
186
|
-
if (Array.isArray(raw.a))
|
|
187
|
-
for (const c of raw.a) {
|
|
188
|
-
const r = expandCondition(c, 1);
|
|
189
|
-
if (r === INVALID)
|
|
190
|
-
return INVALID;
|
|
191
|
-
if (r)
|
|
192
|
-
(rule.all = rule.all || []).push(r);
|
|
193
|
-
}
|
|
194
|
-
if (Array.isArray(raw.n))
|
|
195
|
-
for (const c of raw.n) {
|
|
196
|
-
const r = expandCondition(c, 1);
|
|
197
|
-
if (r === INVALID)
|
|
198
|
-
return INVALID;
|
|
199
|
-
if (r)
|
|
200
|
-
(rule.any = rule.any || []).push(r);
|
|
201
|
-
}
|
|
202
|
-
if (!rule.all && !rule.any)
|
|
203
|
-
return undefined;
|
|
204
|
-
return rule;
|
|
205
|
-
}
|
|
206
|
-
function projectVariant(variant) {
|
|
207
|
-
const result = {};
|
|
208
|
-
if (variant.priority)
|
|
209
|
-
result.pr = variant.priority;
|
|
210
|
-
if (variant.rule)
|
|
211
|
-
result.r = projectRule(variant.rule);
|
|
212
|
-
result.d = projectDispatch(variant.dispatch);
|
|
213
|
-
if (variant.variantValues && Object.keys(variant.variantValues).length > 0)
|
|
214
|
-
result.vv = variant.variantValues;
|
|
215
|
-
return result;
|
|
216
|
-
}
|
|
217
|
-
function expandVariant(raw) {
|
|
218
|
-
if (!isObj(raw))
|
|
219
|
-
return INVALID;
|
|
220
|
-
const obj = raw;
|
|
221
|
-
if (obj.d === undefined)
|
|
222
|
-
return INVALID;
|
|
223
|
-
const dispatchResult = expandDispatch(obj.d);
|
|
224
|
-
if (dispatchResult === INVALID || !dispatchResult)
|
|
225
|
-
return INVALID;
|
|
226
|
-
const variant = { variantKey: '', dispatch: dispatchResult };
|
|
227
|
-
if (typeof obj.pr === 'number')
|
|
228
|
-
variant.priority = obj.pr;
|
|
229
|
-
if (obj.r !== undefined) {
|
|
230
|
-
const ruleResult = expandRule(obj.r);
|
|
231
|
-
if (ruleResult === INVALID)
|
|
232
|
-
return INVALID;
|
|
233
|
-
if (ruleResult)
|
|
234
|
-
variant.rule = ruleResult;
|
|
235
|
-
}
|
|
236
|
-
if (isObj(obj.vv))
|
|
237
|
-
variant.variantValues = obj.vv;
|
|
238
|
-
return variant;
|
|
239
|
-
}
|
|
240
|
-
export function projectRouteRecord(entry) {
|
|
241
|
-
const result = { p: entry.pattern, d: projectDispatch(entry.dispatch) };
|
|
242
|
-
result.s = 1;
|
|
243
|
-
if (entry.variants && Object.keys(entry.variants).length > 0) {
|
|
244
|
-
result.v = {};
|
|
245
|
-
for (const [key, variant] of Object.entries(entry.variants))
|
|
246
|
-
result.v[key] = projectVariant(variant);
|
|
247
|
-
}
|
|
248
|
-
return result;
|
|
249
|
-
}
|
|
250
|
-
export function expandRouteRecord(raw) {
|
|
251
|
-
if (!isObj(raw))
|
|
252
|
-
return INVALID;
|
|
253
|
-
const obj = raw;
|
|
254
|
-
if (obj.s !== undefined && obj.s !== 1)
|
|
255
|
-
return INVALID;
|
|
256
|
-
const path = obj.p;
|
|
257
|
-
if (typeof path !== 'string')
|
|
258
|
-
return INVALID;
|
|
259
|
-
if (obj.d === undefined)
|
|
260
|
-
return INVALID;
|
|
261
|
-
const dispatchResult = expandDispatch(obj.d);
|
|
262
|
-
if (dispatchResult === INVALID || !dispatchResult)
|
|
263
|
-
return INVALID;
|
|
264
|
-
const entry = { pattern: path, priority: 0, dispatch: dispatchResult };
|
|
265
|
-
if (obj.v !== undefined) {
|
|
266
|
-
if (!isObj(obj.v))
|
|
267
|
-
return INVALID;
|
|
268
|
-
entry.variants = {};
|
|
269
|
-
for (const [key, variantRaw] of Object.entries(obj.v)) {
|
|
270
|
-
const variantResult = expandVariant(variantRaw);
|
|
271
|
-
if (variantResult === INVALID)
|
|
272
|
-
return INVALID;
|
|
273
|
-
const variant = variantResult;
|
|
274
|
-
variant.variantKey = key;
|
|
275
|
-
if (!variant.variantValues)
|
|
276
|
-
variant.variantValues = variantValuesFromKey(key);
|
|
277
|
-
entry.variants[key] = variant;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return entry;
|
|
281
|
-
}
|
|
282
|
-
// ── Marker (tri-state: record | marker | absent) ─────────────────────
|
|
283
|
-
export const MARKER = '{"s":1,"e":1}';
|
|
284
|
-
export function isMarker(raw) {
|
|
285
|
-
return raw === MARKER;
|
|
286
|
-
}
|
|
287
|
-
// ── Serialization helpers ────────────────────────────────────────────
|
|
288
|
-
export function canonicalSerialize(value) {
|
|
289
|
-
return JSON.stringify(sortKeys(value));
|
|
290
|
-
}
|
|
291
|
-
function sortKeys(value) {
|
|
292
|
-
if (value === null || typeof value !== 'object')
|
|
293
|
-
return value;
|
|
294
|
-
if (Array.isArray(value))
|
|
295
|
-
return value.map(sortKeys);
|
|
296
|
-
const sorted = {};
|
|
297
|
-
for (const key of Object.keys(value).sort())
|
|
298
|
-
sorted[key] = sortKeys(value[key]);
|
|
299
|
-
return sorted;
|
|
300
|
-
}
|
|
301
|
-
// ── VariantKey parsing ───────────────────────────────────────────────
|
|
302
|
-
export function variantValuesFromKey(key) {
|
|
303
|
-
const values = {};
|
|
304
|
-
for (const part of String(key || '').replace(/&/g, ';').split(';')) {
|
|
305
|
-
const sep = part.indexOf('=');
|
|
306
|
-
if (sep > 0)
|
|
307
|
-
values[part.slice(0, sep).trim()] = part.slice(sep + 1).trim();
|
|
308
|
-
}
|
|
309
|
-
return values;
|
|
310
|
-
}
|
|
311
|
-
// ── Path hashing (FNV-1a-32, hex 16 chars) ───────────────────────────
|
|
312
|
-
export function pathHash16(path) {
|
|
313
|
-
let hash = 0x811c9dc5;
|
|
314
|
-
for (const byte of te.encode(path)) {
|
|
315
|
-
hash ^= byte;
|
|
316
|
-
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
317
|
-
}
|
|
318
|
-
return hash.toString(16).padStart(8, '0') + '00000000';
|
|
319
|
-
}
|
package/dist/mimic/core.d.ts
DELETED
package/dist/mimic/core.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// View-as-audience mimic banner contract (specs/features/audience-mimic).
|
|
2
|
-
// The platform edge router (internal/domain/hosting/runtime/mimic.ts) owns
|
|
3
|
-
// the mimic session — token validation, cookies, redirects, and banner
|
|
4
|
-
// creation. On body-less SSR rewrites it forwards the rendered banner to the
|
|
5
|
-
// framework host via MIMIC_BANNER_HEADER; this module is the framework-side
|
|
6
|
-
// leg that decodes it (the MimicBanner component in next/page.tsx). The
|
|
7
|
-
// middleware strips any inbound copy of the header and sets it only for a
|
|
8
|
-
// validated session, so the decoded HTML is trusted as authored.
|
|
9
|
-
//
|
|
10
|
-
// The SDK-side token validator that used to live here served the retired
|
|
11
|
-
// Next personalization-host lane (ADR 0041); the Go/TS validators in
|
|
12
|
-
// internal/domain/edge remain pinned by the shared vector file.
|
|
13
|
-
export const MIMIC_BANNER_HEADER = 'x-aci-mimic-banner';
|
|
14
|
-
export function decodeMimicBanner(encoded) {
|
|
15
|
-
try {
|
|
16
|
-
return new TextDecoder().decode(base64URLToBytes(encoded));
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return '';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function base64URLToBytes(value) {
|
|
23
|
-
const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
24
|
-
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
25
|
-
const binary = atob(padded);
|
|
26
|
-
const bytes = new Uint8Array(binary.length);
|
|
27
|
-
for (let i = 0; i < binary.length; i++) {
|
|
28
|
-
bytes[i] = binary.charCodeAt(i);
|
|
29
|
-
}
|
|
30
|
-
return bytes;
|
|
31
|
-
}
|