@certrev/cert-block 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -0
- package/README.md +25 -6
- package/dist/builder/cert-chrome-data.d.ts +26 -1
- package/dist/builder/cert-chrome-data.d.ts.map +1 -1
- package/dist/builder/cert-chrome-data.js +1 -0
- package/dist/builder/cert-chrome-data.js.map +1 -1
- package/dist/builder/cert-review-card.d.ts +35 -2
- package/dist/builder/cert-review-card.d.ts.map +1 -1
- package/dist/builder/cert-review-card.js +112 -2
- package/dist/builder/cert-review-card.js.map +1 -1
- package/dist/builder/delivery-runtime.d.ts +23 -0
- package/dist/builder/delivery-runtime.d.ts.map +1 -0
- package/dist/builder/delivery-runtime.js +38 -0
- package/dist/builder/delivery-runtime.js.map +1 -0
- package/dist/builder/envelope-face.d.ts +63 -0
- package/dist/builder/envelope-face.d.ts.map +1 -0
- package/dist/builder/envelope-face.js +151 -0
- package/dist/builder/envelope-face.js.map +1 -0
- package/dist/builder/index.d.ts +15 -3
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +14 -2
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/issuer-key.d.ts +12 -0
- package/dist/builder/issuer-key.d.ts.map +1 -0
- package/dist/builder/issuer-key.js +14 -0
- package/dist/builder/issuer-key.js.map +1 -0
- package/dist/components/memo-toggle.d.ts +42 -0
- package/dist/components/memo-toggle.d.ts.map +1 -0
- package/dist/components/memo-toggle.js +60 -0
- package/dist/components/memo-toggle.js.map +1 -0
- package/dist/components/render-cert-block.d.ts +9 -0
- package/dist/components/render-cert-block.d.ts.map +1 -1
- package/dist/components/render-cert-block.js +83 -25
- package/dist/components/render-cert-block.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/modal/certrev-cert-modal.d.ts +34 -1
- package/dist/modal/certrev-cert-modal.d.ts.map +1 -1
- package/dist/modal/certrev-cert-modal.js +40 -2
- package/dist/modal/certrev-cert-modal.js.map +1 -1
- package/dist/modal/certrev-cert.entry.d.ts.map +1 -1
- package/dist/modal/certrev-cert.entry.js +2 -4
- package/dist/modal/certrev-cert.entry.js.map +1 -1
- package/dist/modal/certrev-cert.js +4 -4
- package/dist/modal/deprecated.d.ts +15 -0
- package/dist/modal/deprecated.d.ts.map +1 -0
- package/dist/modal/deprecated.js +22 -0
- package/dist/modal/deprecated.js.map +1 -0
- package/dist/modal/ftc-disclosure.d.ts +7 -3
- package/dist/modal/ftc-disclosure.d.ts.map +1 -1
- package/dist/modal/ftc-disclosure.js +7 -3
- package/dist/modal/ftc-disclosure.js.map +1 -1
- package/dist/modal/index.d.ts +4 -3
- package/dist/modal/index.d.ts.map +1 -1
- package/dist/modal/index.js +4 -3
- package/dist/modal/index.js.map +1 -1
- package/dist/modal/interactions.d.ts +20 -4
- package/dist/modal/interactions.d.ts.map +1 -1
- package/dist/modal/interactions.js +34 -7
- package/dist/modal/interactions.js.map +1 -1
- package/dist/verify/get-verified-delivery.d.ts +91 -0
- package/dist/verify/get-verified-delivery.d.ts.map +1 -0
- package/dist/verify/get-verified-delivery.js +157 -0
- package/dist/verify/get-verified-delivery.js.map +1 -0
- package/package.json +5 -1
- package/src/builder/cert-chrome-data.ts +27 -0
- package/src/builder/cert-review-card.tsx +143 -3
- package/src/builder/delivery-runtime.ts +54 -0
- package/src/builder/envelope-face.ts +173 -0
- package/src/builder/index.tsx +23 -1
- package/src/builder/issuer-key.ts +13 -0
- package/src/components/memo-toggle.ts +63 -0
- package/src/components/render-cert-block.ts +124 -28
- package/src/index.ts +28 -0
- package/src/modal/certrev-cert-modal.ts +45 -2
- package/src/modal/certrev-cert.entry.ts +2 -4
- package/src/modal/deprecated.ts +25 -0
- package/src/modal/ftc-disclosure.ts +7 -3
- package/src/modal/index.ts +4 -3
- package/src/modal/interactions.ts +37 -7
- package/src/verify/get-verified-delivery.ts +211 -0
- package/dist/modal/ftc-guard.d.ts +0 -85
- package/dist/modal/ftc-guard.d.ts.map +0 -1
- package/dist/modal/ftc-guard.js +0 -150
- package/dist/modal/ftc-guard.js.map +0 -1
- package/src/modal/ftc-guard.ts +0 -174
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* biome-ignore-all format: generated cert-badge bundle, see @certrev/cert-block src/modal/ */ /* biome-ignore-all lint: generated cert-badge bundle, see @certrev/cert-block src/modal/ */
|
|
2
|
-
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
2
|
+
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",Be=(()=>{let e=new Int8Array(128).fill(-1);for(let t=0;t<H.length;t++)e[H.charCodeAt(t)]=t;return e[45]=62,e[95]=63,e})();function g(e){let t=[];for(let i=0;i<e.length;i++){let c=e.charCodeAt(i),d=c<128?Be[c]:-1;d>=0&&t.push(d)}let r=Math.floor(t.length*6/8),n=new Uint8Array(r),o=0,a=0,s=0;for(let i=0;i<t.length;i++)o=o<<6|t[i],a+=6,a>=8&&(a-=8,n[s++]=o>>a&255);return n}function C(e){return g(e)}function p(e,t=new Set){if(typeof e=="number"&&isNaN(e))throw new Error("NaN is not allowed");if(typeof e=="number"&&!isFinite(e))throw new Error("Infinity is not allowed");if(e===null||typeof e!="object")return JSON.stringify(e);if(typeof e.toJSON=="function"){if(t.has(e))throw new Error("Circular reference detected");t.add(e);let n=p(e.toJSON(),t);return t.delete(e),n}if(t.has(e))throw new Error("Circular reference detected");t.add(e);let r;if(Array.isArray(e))r=`[${e.map(o=>p(o===void 0||typeof o=="symbol"?null:o,t)).join(",")}]`;else{let n=[];for(let o of Object.keys(e).sort())e[o]===void 0||typeof e[o]=="symbol"||n.push(`${p(o)}:${p(e[o],t)}`);r=`{${n.join(",")}}`}return t.delete(e),r}var Ie=typeof p=="function"?p:p.default;function K(e){let t=Ie(e);if(typeof t!="string")throw new Error("canonicalize: value is not JSON-serializable");return t}function F(e){return new TextEncoder().encode(K(e))}function b(e,t="$"){if(typeof e=="number"){if(!Number.isInteger(e)||!Number.isSafeInteger(e))throw new Error(`canonicalPayloadBytes: number at ${t} (${e}) is not a safe integer. Signed-payload numbers must be safe integers so PHP/Go/Java verifiers reproduce identical canonical bytes`);return}if(Array.isArray(e)){e.forEach((r,n)=>b(r,`${t}[${n}]`));return}if(e&&typeof e=="object")for(let[r,n]of Object.entries(e))b(n,`${t}.${r}`)}function T(e){return b(e),F(e)}function R(e){return b(e),F(e)}var U="ed25519";var m={name:"Ed25519"};function Oe(e){return e.replace(/-----BEGIN [^-]+-----/g,"").replace(/-----END [^-]+-----/g,"").replace(/\s+/g,"")}async function q(e){switch(e.format){case"pem":return crypto.subtle.importKey("spki",g(Oe(e.pem)),m,!1,["verify"]);case"spki-base64":return crypto.subtle.importKey("spki",g(e.base64),m,!1,["verify"]);case"spki-der":return crypto.subtle.importKey("spki",e.bytes,m,!1,["verify"]);case"raw":{if(e.bytes.length!==32)throw new Error(`ed25519 raw public key must be 32 bytes, got ${e.bytes.length}`);return crypto.subtle.importKey("raw",e.bytes,m,!1,["verify"])}}}function Ne(e){if("format"in e)return!1;let t=e;return typeof t.type=="string"&&typeof t.algorithm=="object"&&Array.isArray(t.usages)}async function j(e){return Ne(e)?e:q(e)}async function G(e,t,r){let n=T(e),o=C(t);try{return await crypto.subtle.verify(m,r,o,n)}catch{return!1}}async function Pe(e,t){let{payload:r,signature:n}=e??{};if(!r||!n||r.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(n.alg!==U)return{ok:!1,reason:"unsupported_alg"};let o=await t(n.kid);if(!o)return{ok:!1,reason:"unknown_key"};let a;try{a=await j(o)}catch{return{ok:!1,reason:"unknown_key"}}return await G(r,n.sig,a)?{ok:!0,key:a}:{ok:!1,reason:"invalid_signature"}}function J(e,t){let r=t.now??new Date;if(e.subject.platform!==t.platform)return{decision:"suppress",reason:"platform_mismatch"};if(e.subject.externalId!==t.externalId)return{decision:"suppress",reason:"subject_mismatch"};if(e.lifecycle.revokedAt!==null)return{decision:"suppress",reason:"revoked"};if(r.getTime()>=Date.parse(e.lifecycle.expiresAt))return{decision:"suppress",reason:"expired"};let n=e.subject.contentDigest,o=t.liveContentHash;return n!==null&&o!=null&&n!==o?{decision:"suppress",reason:"content_drift"}:{decision:"render",payload:e}}async function Z(e,t,r){let n;try{n=await Pe(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?J(e.payload,r):{decision:"suppress",reason:n.reason}}function v(e){return e?.kind==="tombstone"}async function ze(e,t){let{signature:r}=e??{};if(!e?.subject||!r||e.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(r.alg!==U)return{ok:!1,reason:"unsupported_alg"};let n=await t(r.kid);if(!n)return{ok:!1,reason:"unknown_key"};let o;try{o=await j(n)}catch{return{ok:!1,reason:"unknown_key"}}let{signature:a,...s}=e,i=C(r.sig),c=!1;try{c=await crypto.subtle.verify(m,o,i,R(s))}catch{c=!1}return c?{ok:!0,key:o}:{ok:!1,reason:"invalid_signature"}}async function W(e,t,r){let n;try{n=await ze(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?e.subject.platform!==r.platform?{decision:"suppress",reason:"platform_mismatch"}:e.subject.externalId!==r.externalId?{decision:"suppress",reason:"subject_mismatch"}:{decision:"suppress",reason:"revoked"}:{decision:"suppress",reason:n.reason}}async function L(e,t,r){return v(e)?W(e,t,r):Z(e,t,r)}var Y=`
|
|
3
3
|
@font-face {
|
|
4
4
|
font-family: "DM Sans";
|
|
5
5
|
font-style: normal;
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
unicode-range:
|
|
120
120
|
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
121
121
|
}
|
|
122
|
-
`,X="https://assets.certrev.com";function f(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):""}function l(e){return f(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var
|
|
122
|
+
`,X="https://assets.certrev.com";function f(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):""}function l(e){return f(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var Ve=["assets.certrev.com","staging-assets.certrev.com"];function He(e){return`width=${e},height=${e},fit=cover,format=auto,onerror=redirect`}function Ke(e,t){let r=(e??"").trim();if(!/^https:\/\//i.test(r))return r;let n;try{n=new URL(r)}catch{return r}if(!Ve.includes(n.hostname)||n.pathname.startsWith("/cdn-cgi/image/"))return r;let o=n.pathname.replace(/^\//,"");return`${n.origin}/cdn-cgi/image/${He(t)}/${o}`}function Q(e,t){return Ke(e,t)}var ee="Independent editorial review of this article\u2019s text and sources. Not a product endorsement, nor medical advice.";var te=ee;function M(e){if(!e||typeof e!="string"&&!(e instanceof Date))return null;let t=e instanceof Date?e:new Date(e);return Number.isNaN(t.getTime())?null:t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric",timeZone:"UTC"})}var B=["M67.59 221.77L138.77 292.95C145.8 299.98 145.8 311.44 138.77 318.47L123.02 334.22C116 341.25 104.54 341.25 97.5 334.22L26.32 263.04C19.29 256.01 19.29 244.55 26.32 237.52L42.07 221.77C49.1 214.74 60.56 214.74 67.59 221.77Z","M96.79 335.08L81.12 319.42C73.8 312.1 73.8 300.23 81.12 292.92L315.88 58.16C323.2 50.85 335.06 50.85 342.38 58.16L358.04 73.83C365.36 81.14 365.36 93.01 358.04 100.33L123.29 335.08C115.97 342.4 104.1 342.4 96.79 335.08Z","M203.5 335.14L187.83 319.48C180.52 312.16 180.52 300.29 187.83 292.98L315.88 164.93C323.2 157.62 335.06 157.62 342.38 164.93L358.04 180.6C365.36 187.92 365.36 199.78 358.04 207.1L230 335.14C222.68 342.46 210.82 342.46 203.5 335.14Z","M310.21 335.2L294.54 319.54C287.23 312.22 287.23 300.36 294.54 293.04L315.88 271.71C323.2 264.39 335.06 264.39 342.38 271.71L358.04 287.37C365.36 294.69 365.36 306.55 358.04 313.87L336.71 335.2C329.39 342.52 317.53 342.52 310.21 335.2Z"];function re(e,t){let r=B.map(n=>`<path fill="${t}" d="${n}"/>`).join("");return`<svg width="${e}" height="${e}" viewBox="0 0 375 375" aria-hidden="true" style="display:block;flex-shrink:0">${r}</svg>`}function ne(e=8){return`<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`}function oe(e="crm"){let t=`crm-stamp-arc-${e}`,r=B.map(n=>`<path fill="#0a1b3f" d="${n}"/>`).join("");return`<span class="crm-stamp"><svg viewBox="0 0 128 128" width="74" height="74" role="img" aria-label="CertREV verified stamp" style="display:block"><defs><path id="${t}" d="M64 18 A46 46 0 1 1 64 110 A46 46 0 1 1 64 18"/></defs><circle cx="64" cy="64" r="58" fill="none" stroke="#0a1b3f" stroke-width="1.3"/><circle cx="64" cy="64" r="51.5" fill="none" stroke="#0a1b3f" stroke-width="0.7" stroke-opacity="0.4"/><text font-family="'JetBrains Mono',ui-monospace,monospace" font-size="7.8" font-weight="600" letter-spacing="1.3" fill="#0a1b3f"><textPath href="#${t}" startOffset="1%">CERTREV VERIFIED \xB7 CERTIFIED \xB7 </textPath></text><svg x="49" y="50" width="30" height="28" viewBox="0 0 375 375" preserveAspectRatio="xMidYMid meet">${r}</svg></svg></span>`}var I=`
|
|
123
123
|
:host {
|
|
124
124
|
--cr-navy: #0a1b3f;
|
|
125
125
|
--cr-lime: #d4e157;
|
|
@@ -552,6 +552,6 @@
|
|
|
552
552
|
padding: 18px 18px 20px;
|
|
553
553
|
}
|
|
554
554
|
}
|
|
555
|
-
`;var oe="nofollow sponsored noopener noreferrer",je=/^(dr|prof|professor|mr|mrs|ms|mx|rev|sir|dame)\.?\s+/i;function ie(e){let t=f(e).replace(je,"").trim(),r=t.indexOf(",");return(r===-1?t:t.slice(0,r)).trim()}function Je(e){let t=ie(e).split(/\s+/).filter(Boolean);return t.length===0?"":t.length===1?t[0].slice(0,2).toUpperCase():(t[0][0]+t[t.length-1][0]).toUpperCase()}function We(e){return ie(e)}function Ze(e){return f(e).replace(/^https?:\/\//i,"").replace(/\/$/,"")}function ae(e){let t=f(e).trim();return t&&/^https?:\/\//i.test(t)?t:"#"}function Ye(e){return(f(e?.cardLabel)||f(e?.fullName)||f(e?.abbreviation)).trim()}function se(e){let t=[],r=Array.isArray(e?.credentials)?e.credentials:[];for(let n of r){let o=Ye(n);o&&!t.includes(o)&&t.push(o)}return t}function Xe(e,t){return t?.showBio===!1?"":(f(e?.background)||f(e?.bio)).trim()}function ce(e,t,r,n){let o=f(e?.photoUrl).trim();if(n&&/^https?:\/\//i.test(o)){let i=r.includes("--64")?64:56,s=Q(o,i===64?128:112);return`<img class="crm-avatar ${r}" src="${l(s)}" alt="" width="${i}" height="${i}" decoding="async">`}return`<span class="crm-avatar ${r}">${l(Je(t))}</span>`}var le='<button type="button" class="crm-close" data-certrev-modal-close aria-label="Close">×</button>';function fe(e){return`<div class="crm-head">${te(16,"#ffffff")}<span class="crm-head__eyebrow">${l(e)}</span><span class="crm-head__wordmark">CertREV</span></div>`}function de(e){return`<span class="${e}">${re(8)}Certified</span>`}function pe(e){let t=f(e?.compensationCue).trim();return`<div class="crm-disclosure">${t?`<span class="crm-disclosure__cue">${l(t)}</span> `:""}${l(ee)}</div>`}function ue(e){let t=e.expert??null,r=f(t?.displayName).trim();if(!r)return null;let n=e.display?.showExpertPhoto!==!1,o=se(t).join(" \xB7 "),i=M(e.certifiedAt)??"",s=f(e.displayCertId).trim(),a=f(e.articleTitle).trim(),c=f(e.verifyUrl).trim(),d=We(r),h=a?`<div class="crm-certifies"><div class="crm-certifies__eyebrow">Certifies the article</div><div class="crm-certifies__title">${l(a)}</div></div>`:"",x=s?`<div class="crm-meta__col"><div class="crm-meta__label">Certificate ID</div><div class="crm-meta__value crm-meta__value--mono">${l(s)}</div></div>`:"",Be=c?`<div class="crm-sig__url">${l(Ze(c))}</div>`:"";return`${le}${fe("Certificate of Expert Review")}<div class="crm-body--cert"><div class="crm-expert-row">${ce(t,r,"crm-avatar--56",n)}<div class="crm-expert-row__info"><div class="crm-expert-row__eyebrow">Reviewing Expert</div><div class="crm-expert-row__name">${l(r)}</div>${o?`<div class="crm-expert-row__creds">${l(o)}</div>`:""}</div></div>${h}<div class="crm-meta"><div class="crm-meta__col"><div class="crm-meta__label">Issued</div><div class="crm-meta__value">${l(i)}</div></div>${x}<div class="crm-meta__col"><div class="crm-meta__label">Status</div><div class="crm-meta__status">${de("crm-chip")}</div></div></div><div class="crm-sig"><div><div class="crm-sig__name">${l(d)}</div><div class="crm-sig__rule"></div>${Be}</div>${ne("cert")}</div>${pe(t)}<div class="crm-actions"><a class="crm-cta" href="${l(ae(c))}" target="_blank" rel="${oe}">View full certificate</a><button type="button" class="crm-ghost" data-certrev-modal-close>Close</button></div></div>`}function me(e){let t=e.expert??null,r=f(t?.displayName).trim();if(!r)return null;let n=e.display?.showExpertPhoto!==!1,o=se(t),i=Xe(t,e.display),s=M(e.certifiedAt),a=o.length?`<div class="crm-chips">${o.map(h=>`<span class="crm-chips__chip">${l(h)}</span>`).join("")}</div>`:"",c=i?`<p class="crm-bio">${l(i)}</p>`:"",d=s?`Independently reviewed \xB7 Certified ${l(s)}`:"Independently reviewed";return`${le}${fe("Verified reviewer")}<div class="crm-body--expert"><div class="crm-id">${ce(t,r,"crm-avatar--64",n)}<div class="crm-id__info"><div class="crm-id__name">${l(r)}</div></div></div>${a}${c}<div class="crm-trust">${de("crm-trust__chip")}<span class="crm-trust__text">${d}</span></div>${pe(t)}<div class="crm-actions"><a class="crm-cta" href="${l(ae(t?.profileUrl))}" target="_blank" rel="${oe}">View full profile on CertREV</a><button type="button" class="crm-ghost" data-certrev-modal-close>Close</button></div></div>`}var A="certrev-cert-modal",w=null;function Qe(){return typeof CSSStyleSheet<"u"&&"replaceSync"in CSSStyleSheet.prototype&&"adoptedStyleSheets"in Document.prototype}function et(){return Qe()?(w||(w=new CSSStyleSheet,w.replaceSync($)),w):null}var ye=!1;function tt(e){if(ye||(ye=!0,e.querySelector("style[data-certrev-modal-fonts]")))return;if(!e.querySelector("link[data-certrev-modal-preconnect]")){let r=e.createElement("link");r.rel="preconnect",r.href=X,r.crossOrigin="",r.setAttribute("data-certrev-modal-preconnect",""),e.head.appendChild(r)}let t=e.createElement("style");t.textContent=Y,t.setAttribute("data-certrev-modal-fonts",""),e.head.appendChild(t)}var O=class extends HTMLElement{constructor(){super();this.rendered=!1;this.content=null;this.fetchInFlight=null;this.root=this.attachShadow({mode:"open"});let r=et();if(r)this.root.adoptedStyleSheets=[r];else{let n=document.createElement("style");n.textContent=$,this.root.appendChild(n)}this.root.addEventListener("click",n=>this.onShadowClick(n))}open(r){return this.getAttribute("data-fonts")!=="host"&&tt(document),this.ensureRendered()?(this.show(r),!0):this.hasFetchSource()?(this.fetchThenShow(r),!0):!1}ensureRendered(){if(this.rendered)return this.content!=null;let r=this.readSyncContent();return r?(this.content=r,this.renderDialogs(r),this.rendered=!0,!0):!1}readSyncContent(){let r=this.getAttribute("data-envelope");if(r)return ge(r);let n=this.querySelector("script[data-certrev-envelope]");return n?.textContent?ge(n.textContent):null}hasFetchSource(){return!!(this.getAttribute("data-delivery-api")&&this.getAttribute("data-platform")&&this.getAttribute("data-external-id"))}async fetchThenShow(r){if(!this.fetchInFlight){let n=(this.getAttribute("data-delivery-api")||"").replace(/\/$/,""),o=this.getAttribute("data-platform")||"",i=this.getAttribute("data-external-id")||"";this.fetchInFlight=(async()=>{try{let s=await fetch(`${n}/api/cert/v1/delivery/${encodeURIComponent(o)}/${encodeURIComponent(i)}`,{cache:"no-store",credentials:"omit"});if(!s.ok)return;let a=await s.json();if(rt(a)){this.markSuppressed();return}let c=v(a)?void 0:a.payload?.content;c&&(this.content=c,this.renderDialogs(c),this.rendered=!0)}catch{}})()}await this.fetchInFlight,this.rendered&&this.show(r)}renderDialogs(r){let n=ue(r),o=me(r),i=document.createElement("div");i.innerHTML=`${n?`<dialog id="cert" class="crm-dialog crm-dialog--cert" aria-label="CertREV certificate">${n}</dialog>`:""}${o?`<dialog id="expert" class="crm-dialog crm-dialog--expert" aria-label="Verified reviewer profile">${o}</dialog>`:""}`;for(let s of Array.from(i.childNodes))this.root.appendChild(s)}show(r){let n=this.root.getElementById(r);n&&typeof n.showModal=="function"&&!n.open&&n.showModal()}markSuppressed(){this.setAttribute("data-certrev-suppressed","");for(let r of Array.from(document.querySelectorAll("[data-certrev-modal-open]")))r.setAttribute("hidden",""),r.setAttribute("aria-hidden","true")}onShadowClick(r){let n=r.target;if(!n)return;if(n instanceof HTMLDialogElement&&n.classList.contains("crm-dialog")){n.close();return}let o=n.closest("[data-certrev-modal-close]");o&&(r.preventDefault(),o.closest("dialog")?.close())}};function ge(e){try{let t=JSON.parse(e);return t&&typeof t=="object"?t:null}catch{return null}}function rt(e){if(!e)return!1;if(v(e))return!0;let t=e.payload?.lifecycle;return t?!!(t.revokedAt!=null||t.expiresAt&&Date.now()>=new Date(t.expiresAt).getTime()):!1}function nt(){typeof customElements>"u"||customElements.get(A)||customElements.define(A,O)}nt();var ot="[data-ftc-line]",xe=".certrev-memo",ve=".certrev-memo-wrap",it="data-certrev-ftc-neutralized",at="nofollow sponsored noopener noreferrer",he=new WeakSet;function st(e){let t=e.ownerDocument.defaultView;if(!t)return!0;let r=t.getComputedStyle(e);if(r.display==="none"||r.visibility==="hidden"||Number.parseFloat(r.opacity||"1")===0)return!1;let n=e.getBoundingClientRect();return n.width>0&&n.height>0}function ct(e){let t=e.querySelector(ot);return!t||(t.textContent||"").trim()!==U?!1:st(t)}function lt(e){let r=e.ownerDocument.createElement("div");r.setAttribute(it,""),r.style.cssText="font-family:system-ui,-apple-system,sans-serif;font-size:12px;color:#555;padding:12px;line-height:1.4",r.innerHTML=`This CertREV verification cannot be displayed because its required disclosure was not shown. <a href="https://certrev.com" style="color:#555" target="_blank" rel="${at}">Learn more</a>.`,e.replaceWith(r)}function ft(e){let t=Array.from(e.querySelectorAll(xe));if(!t.length)return"unguarded";let r="intact";for(let n of t)ct(n)||(r="tampered",lt(n));return r}function Ce(e){if(he.has(e))return;he.add(e);let t=()=>{ft(e)},r=()=>{t();let n=e.defaultView?.MutationObserver;if(!n)return;let o=Array.from(e.querySelectorAll(ve)),i=Array.from(e.querySelectorAll(xe)).filter(s=>!s.closest(ve));for(let s of[...o,...i])new n(t).observe(s,{childList:!0,subtree:!0,characterData:!0,attributes:!0})};e.readyState==="loading"?e.addEventListener("DOMContentLoaded",r,{once:!0}):r()}var be="data-certrev-acc",_="data-certrev-modal-open",N="data-certrev-initials";var dt=["avatar","certrev-memo__avatar"],Ee=new WeakSet;function Ue(e){let t=e.classList.toggle("open");e.setAttribute("aria-expanded",t?"true":"false")}function we(e){return e==="expert"?"expert":"cert"}function Ae(e,t){let r=e.querySelector(A);return r&&typeof r.open=="function"?r.open(t):!1}function _e(e){return e&&e.closest?e:null}function Se(e){let t=e.getAttribute(N);if(t==null)return;let r=dt.find(o=>e.classList.contains(o));if(!r)return;let n=e.ownerDocument.createElement("span");n.className=`${r} ${r}--initials`,n.setAttribute("aria-hidden","true"),n.textContent=t,e.replaceWith(n)}function pt(e){for(let t of Array.from(e.querySelectorAll(`img[${N}]`)))t.complete&&t.naturalWidth===0&&Se(t)}function ut(e){Ee.has(e)||(Ee.add(e),e.addEventListener("click",t=>{let r=_e(t.target);if(!r)return;let n=r.closest(`[${_}]`);if(n){Ae(e,we(n.getAttribute(_)))&&t.preventDefault();return}let o=r.closest(`[${be}]`);o&&!r.closest("a, button")&&Ue(o)}),e.addEventListener("error",t=>{let r=t.target;r&&r.tagName==="IMG"&&r.hasAttribute(N)&&Se(r)},!0),pt(e),e.addEventListener("keydown",t=>{if(t.key!=="Enter"&&t.key!==" "&&t.key!=="Spacebar")return;let r=_e(t.target);if(!r||r.closest("a, button"))return;let n=r.closest(`[${_}]`);if(n&&Ae(e,we(n.getAttribute(_)))){t.preventDefault();return}let o=r.closest(`[${be}]`);o&&(t.preventDefault(),Ue(o))}))}function De(e){let t=()=>ut(e);e.readyState==="loading"?e.addEventListener("DOMContentLoaded",t,{once:!0}):t()}var mt=[{selector:"main article",tier:"article"},{selector:"article",tier:"article"},{selector:"main",tier:"main"},{selector:".article-template__content",tier:"theme-class"},{selector:'[id^="ArticleContent"]',tier:"theme-class"},{selector:".rte",tier:"theme-class"}],y=[{selector:".article-template__content",tier:"theme-class"},{selector:'[id^="ArticleContent"]',tier:"theme-class"},{selector:".rte",tier:"theme-class"},{selector:"main article",tier:"article"},{selector:"article",tier:"article"},{selector:"main",tier:"main"}],P=y.map(e=>e.selector).join(", "),yt='header, [role="banner"], [class*="announcement"], footer, [role="contentinfo"], nav, [role="navigation"], cart-drawer, .cart-drawer, .drawer, .mini-cart, .modal, [role="dialog"]',u="data-certrev-placement",Te="data-certrev-anchor",gt='script[type="application/ld+json"][data-certrev-graph]',vt=".certrev-card-wrap",Re=".certrev-memo-wrap",ht=".certrev-pill-wrap",ke=384,xt=24,S="#references";function D(e){for(let t=e;t;t=t.parentElement){if(t.hasAttribute("hidden")||t.getAttribute("aria-hidden")==="true")return!1;let r=getComputedStyle(t);if(r.display==="none"||r.visibility==="hidden")return!1}return!0}function z(e){return e.closest(yt)!==null}function Fe(e,t){for(let{selector:r,tier:n}of t)if(e.matches(r))return n;return"article"}function F(e,t,r){for(let{selector:n,tier:o}of r)for(let i of Array.from(e.querySelectorAll(n)))if(i!==t&&!i.contains(t)&&!t.contains(i)&&D(i)&&!z(i))return{el:i,tier:o};return null}function V(e,t){let r=t.getAttribute(u);if(r)return{anchored:r!=="body-fallback",tier:r};let n=t.getAttribute(Te);if(n==="rail"){let a=e.querySelector(S)?.closest(P)??F(e,t,y)?.el??null,c=e.defaultView;if(a&&c&&D(a)&&!z(a)){let d=a.getBoundingClientRect();if(c.innerWidth-d.right>=ke)return t.style.setProperty("--certrev-rail-left",`${Math.round(d.right+xt)}px`),t.setAttribute(u,"rail"),{anchored:!0,tier:"rail"};a.insertAdjacentElement("afterbegin",t);let x=Fe(a,y);return t.setAttribute(u,x),{anchored:!0,tier:x}}return t.setAttribute(u,"body-fallback"),{anchored:!1,tier:"body-fallback"}}let o=n==="bottom"?"bottom":"top",i="body-fallback";if(o==="top"){let a=e.querySelector(S)?.closest(P)??null;if(a&&a!==t&&!a.contains(t)&&!t.contains(a)&&D(a)&&!z(a))a.insertAdjacentElement("afterbegin",t),i=Fe(a,y);else{let c=F(e,t,y);c&&(c.el.insertAdjacentElement("afterbegin",t),i=c.tier)}}else{let s=e.querySelector(S);if(s&&s!==t&&!t.contains(s)&&!s.contains(t)&&D(s))s.insertAdjacentElement("beforebegin",t),i="references";else{let a=F(e,t,mt);a&&(a.el.insertAdjacentElement("afterend",t),i=a.tier)}}return t.setAttribute(u,i),{anchored:i!=="body-fallback",tier:i}}function Ct(e){let t=e.querySelector(gt),r=!1;t&&t.parentNode!==e.head&&(e.head.appendChild(t),r=!0);let n=e.querySelector(vt),o=e.querySelector(Re);return e.querySelector(ht)?.setAttribute(u,"pill-fixed"),{graph:r,card:n?V(e,n):null,memo:o?V(e,o):null}}function bt(e){let t=e.defaultView;if(!t)return;let r=e.querySelector(`${Re}[${Te}="rail"]`);if(!r||r.getAttribute(u)!=="rail")return;let n,o=()=>{n&&clearTimeout(n),n=setTimeout(()=>{if(r.getAttribute(u)!=="rail"){t.removeEventListener("resize",o);return}let s=e.querySelector(S)?.closest(P)??F(e,r,y)?.el??null;(s?t.innerWidth-s.getBoundingClientRect().right:0)<ke&&(r.style.removeProperty("--certrev-rail-left"),r.removeAttribute(u),V(e,r),t.removeEventListener("resize",o))},150)};t.addEventListener("resize",o)}function Le(e){let t=()=>{Ct(e),bt(e)};e.readyState==="loading"?e.addEventListener("DOMContentLoaded",t,{once:!0}):t()}async function Et(e,t){let r=e.querySelector(".certrev-badge");if(!r)return;let n=e.getAttribute("data-delivery-api"),o=e.getAttribute("data-external-id"),i=e.getAttribute("data-platform")??"shopify";if(!n||!o)return;let s;try{s=await t.verify({baseUrl:n,platform:i,externalId:o})}catch{return}s.decision!=="render"&&(r.setAttribute("hidden",""),r.setAttribute("data-certrev-suppressed",s.reason))}function Ie(e,t){let r=()=>{for(let n of Array.from(e.querySelectorAll("certrev-badge[revalidate]")))Et(n,t)};e.readyState==="loading"?e.addEventListener("DOMContentLoaded",r,{once:!0}):r()}function Me(e,t=fetch){return async r=>{let o=`${r.baseUrl.replace(/\/+$/,"")}/api/cert/v1/delivery/${encodeURIComponent(r.platform)}/${encodeURIComponent(r.externalId)}`,i=await t(o,{headers:{accept:"application/json"}});if(!i.ok)throw new Error(`cert delivery ${i.status}`);let s=await i.json();return I(s,e,{platform:r.platform,externalId:r.externalId,liveContentHash:null,now:new Date})}}var Ut="cert-issuer-1",wt=`-----BEGIN PUBLIC KEY-----
|
|
555
|
+
`;var ae="nofollow sponsored noopener noreferrer",qe=/^(dr|prof|professor|mr|mrs|ms|mx|rev|sir|dame)\.?\s+/i;function ie(e){let t=f(e).replace(qe,"").trim(),r=t.indexOf(",");return(r===-1?t:t.slice(0,r)).trim()}function je(e){let t=ie(e).split(/\s+/).filter(Boolean);return t.length===0?"":t.length===1?t[0].slice(0,2).toUpperCase():(t[0][0]+t[t.length-1][0]).toUpperCase()}function Ge(e){return ie(e)}function Je(e){return f(e).replace(/^https?:\/\//i,"").replace(/\/$/,"")}function se(e){let t=f(e).trim();return t&&/^https?:\/\//i.test(t)?t:"#"}function Ze(e){return(f(e?.cardLabel)||f(e?.fullName)||f(e?.abbreviation)).trim()}function ce(e){let t=[],r=Array.isArray(e?.credentials)?e.credentials:[];for(let n of r){let o=Ze(n);o&&!t.includes(o)&&t.push(o)}return t}function We(e,t){return t?.showBio===!1?"":(f(e?.background)||f(e?.bio)).trim()}function le(e,t,r,n){let o=f(e?.photoUrl).trim();if(n&&/^https?:\/\//i.test(o)){let a=r.includes("--64")?64:56,s=Q(o,a===64?128:112);return`<img class="crm-avatar ${r}" src="${l(s)}" alt="" width="${a}" height="${a}" decoding="async">`}return`<span class="crm-avatar ${r}">${l(je(t))}</span>`}var fe='<button type="button" class="crm-close" data-certrev-modal-close aria-label="Close">×</button>';function de(e){return`<div class="crm-head">${re(16,"#ffffff")}<span class="crm-head__eyebrow">${l(e)}</span><span class="crm-head__wordmark">CertREV</span></div>`}function pe(e){return`<span class="${e}">${ne(8)}Certified</span>`}function ue(e){let t=f(e?.compensationCue).trim();return`<div class="crm-disclosure">${t?`<span class="crm-disclosure__cue">${l(t)}</span> `:""}${l(te)}</div>`}function me(e){let t=e.expert??null,r=f(t?.displayName).trim();if(!r)return null;let n=e.display?.showExpertPhoto!==!1,o=ce(t).join(" \xB7 "),a=M(e.certifiedAt)??"",s=f(e.displayCertId).trim(),i=f(e.articleTitle).trim(),c=f(e.verifyUrl).trim(),d=Ge(r),h=i?`<div class="crm-certifies"><div class="crm-certifies__eyebrow">Certifies the article</div><div class="crm-certifies__title">${l(i)}</div></div>`:"",x=s?`<div class="crm-meta__col"><div class="crm-meta__label">Certificate ID</div><div class="crm-meta__value crm-meta__value--mono">${l(s)}</div></div>`:"",Me=c?`<div class="crm-sig__url">${l(Je(c))}</div>`:"";return`${fe}${de("Certificate of Expert Review")}<div class="crm-body--cert"><div class="crm-expert-row">${le(t,r,"crm-avatar--56",n)}<div class="crm-expert-row__info"><div class="crm-expert-row__eyebrow">Reviewing Expert</div><div class="crm-expert-row__name">${l(r)}</div>${o?`<div class="crm-expert-row__creds">${l(o)}</div>`:""}</div></div>${h}<div class="crm-meta"><div class="crm-meta__col"><div class="crm-meta__label">Issued</div><div class="crm-meta__value">${l(a)}</div></div>${x}<div class="crm-meta__col"><div class="crm-meta__label">Status</div><div class="crm-meta__status">${pe("crm-chip")}</div></div></div><div class="crm-sig"><div><div class="crm-sig__name">${l(d)}</div><div class="crm-sig__rule"></div>${Me}</div>${oe("cert")}</div>${ue(t)}<div class="crm-actions"><a class="crm-cta" href="${l(se(c))}" target="_blank" rel="${ae}">View full certificate</a><button type="button" class="crm-ghost" data-certrev-modal-close>Close</button></div></div>`}function ye(e){let t=e.expert??null,r=f(t?.displayName).trim();if(!r)return null;let n=e.display?.showExpertPhoto!==!1,o=ce(t),a=We(t,e.display),s=M(e.certifiedAt),i=o.length?`<div class="crm-chips">${o.map(h=>`<span class="crm-chips__chip">${l(h)}</span>`).join("")}</div>`:"",c=a?`<p class="crm-bio">${l(a)}</p>`:"",d=s?`Independently reviewed \xB7 Certified ${l(s)}`:"Independently reviewed";return`${fe}${de("Verified reviewer")}<div class="crm-body--expert"><div class="crm-id">${le(t,r,"crm-avatar--64",n)}<div class="crm-id__info"><div class="crm-id__name">${l(r)}</div></div></div>${i}${c}<div class="crm-trust">${pe("crm-trust__chip")}<span class="crm-trust__text">${d}</span></div>${ue(t)}<div class="crm-actions"><a class="crm-cta" href="${l(se(t?.profileUrl))}" target="_blank" rel="${ae}">View full profile on CertREV</a><button type="button" class="crm-ghost" data-certrev-modal-close>Close</button></div></div>`}var w="certrev-cert-modal",ge="[data-certrev-cert]";function O(e){let t=e.closest(ge);for(let r=e;r;r=r.parentElement){let n=Array.from(r.querySelectorAll(w)).filter(o=>o.closest(ge)===t);if(n.length===1)return{modal:n[0],reason:null};if(n.length>1)return{modal:null,reason:"ambiguous",count:n.length};if(r===t)break}return{modal:null,reason:"none",count:0}}var E=null;function Ye(){return typeof CSSStyleSheet<"u"&&"replaceSync"in CSSStyleSheet.prototype&&"adoptedStyleSheets"in Document.prototype}function Xe(){return Ye()?(E||(E=new CSSStyleSheet,E.replaceSync(I)),E):null}var ve=!1;function Qe(e){if(ve||(ve=!0,e.querySelector("style[data-certrev-modal-fonts]")))return;if(!e.querySelector("link[data-certrev-modal-preconnect]")){let r=e.createElement("link");r.rel="preconnect",r.href=X,r.crossOrigin="",r.setAttribute("data-certrev-modal-preconnect",""),e.head.appendChild(r)}let t=e.createElement("style");t.textContent=Y,t.setAttribute("data-certrev-modal-fonts",""),e.head.appendChild(t)}var $=class extends HTMLElement{constructor(){super();this.rendered=!1;this.content=null;this.fetchInFlight=null;this.root=this.attachShadow({mode:"open"});let r=Xe();if(r)this.root.adoptedStyleSheets=[r];else{let n=document.createElement("style");n.textContent=I,this.root.appendChild(n)}this.root.addEventListener("click",n=>this.onShadowClick(n))}open(r){return this.getAttribute("data-fonts")!=="host"&&Qe(document),this.ensureRendered()?(this.show(r),!0):this.hasFetchSource()?(this.fetchThenShow(r),!0):!1}ensureRendered(){if(this.rendered)return this.content!=null;let r=this.readSyncContent();return r?(this.content=r,this.renderDialogs(r),this.rendered=!0,!0):!1}readSyncContent(){let r=this.getAttribute("data-envelope");if(r)return he(r);let n=this.querySelector("script[data-certrev-envelope]");return n?.textContent?he(n.textContent):null}hasFetchSource(){return!!(this.getAttribute("data-delivery-api")&&this.getAttribute("data-platform")&&this.getAttribute("data-external-id"))}async fetchThenShow(r){if(!this.fetchInFlight){let n=(this.getAttribute("data-delivery-api")||"").replace(/\/$/,""),o=this.getAttribute("data-platform")||"",a=this.getAttribute("data-external-id")||"";this.fetchInFlight=(async()=>{try{let s=await fetch(`${n}/api/cert/v1/delivery/${encodeURIComponent(o)}/${encodeURIComponent(a)}`,{cache:"no-store",credentials:"omit"});if(!s.ok)return;let i=await s.json();if(et(i)){this.markSuppressed();return}let c=v(i)?void 0:i.payload?.content;c&&(this.content=c,this.renderDialogs(c),this.rendered=!0)}catch{}})()}await this.fetchInFlight,this.rendered&&this.show(r)}renderDialogs(r){let n=me(r),o=ye(r),a=document.createElement("div");a.innerHTML=`${n?`<dialog id="cert" class="crm-dialog crm-dialog--cert" aria-label="CertREV certificate">${n}</dialog>`:""}${o?`<dialog id="expert" class="crm-dialog crm-dialog--expert" aria-label="Verified reviewer profile">${o}</dialog>`:""}`;for(let s of Array.from(a.childNodes))this.root.appendChild(s)}show(r){let n=this.root.getElementById(r);n&&typeof n.showModal=="function"&&!n.open&&n.showModal()}markSuppressed(){this.setAttribute("data-certrev-suppressed","");for(let r of Array.from(this.ownerDocument.querySelectorAll("[data-certrev-modal-open]")))O(r).modal===this&&(r.setAttribute("hidden",""),r.setAttribute("aria-hidden","true"))}onShadowClick(r){let n=r.target;if(!n)return;if(n instanceof HTMLDialogElement&&n.classList.contains("crm-dialog")){n.close();return}let o=n.closest("[data-certrev-modal-close]");o&&(r.preventDefault(),o.closest("dialog")?.close())}};function he(e){try{let t=JSON.parse(e);return t&&typeof t=="object"?t:null}catch{return null}}function et(e){if(!e)return!1;if(v(e))return!0;let t=e.payload?.lifecycle;return t?!!(t.revokedAt!=null||t.expiresAt&&Date.now()>=new Date(t.expiresAt).getTime()):!1}function tt(){typeof customElements>"u"||customElements.get(w)||customElements.define(w,$)}tt();var xe="data-certrev-acc",A="data-certrev-modal-open",N="data-certrev-initials";var rt="data-certrev-modal-unresolved",nt="certrev:modal-unresolved",ot=["avatar","certrev-memo__avatar"],Ce=new WeakSet;function be(e){let t=e.classList.toggle("open");e.setAttribute("aria-expanded",t?"true":"false")}function Ue(e){return e==="expert"?"expert":"cert"}function Ee(e,t){let r=O(e);if(r.modal){let n=r.modal;return typeof n.open=="function"?n.open(t):!1}return r.reason==="ambiguous"&&(e.setAttribute(rt,r.reason),e.dispatchEvent(new CustomEvent(nt,{bubbles:!0,detail:{reason:r.reason,modals:r.count}}))),!1}function we(e){return e&&e.closest?e:null}function Ae(e){let t=e.getAttribute(N);if(t==null)return;let r=ot.find(o=>e.classList.contains(o));if(!r)return;let n=e.ownerDocument.createElement("span");n.className=`${r} ${r}--initials`,n.setAttribute("aria-hidden","true"),n.textContent=t,e.replaceWith(n)}function at(e){for(let t of Array.from(e.querySelectorAll(`img[${N}]`)))t.complete&&t.naturalWidth===0&&Ae(t)}function it(e){Ce.has(e)||(Ce.add(e),e.addEventListener("click",t=>{let r=we(t.target);if(!r)return;let n=r.closest(`[${A}]`);if(n){Ee(n,Ue(n.getAttribute(A)))&&t.preventDefault();return}let o=r.closest(`[${xe}]`);o&&!r.closest("a, button")&&be(o)}),e.addEventListener("error",t=>{let r=t.target;r&&r.tagName==="IMG"&&r.hasAttribute(N)&&Ae(r)},!0),at(e),e.addEventListener("keydown",t=>{if(t.key!=="Enter"&&t.key!==" "&&t.key!=="Spacebar")return;let r=we(t.target);if(!r||r.closest("a, button"))return;let n=r.closest(`[${A}]`);if(n&&Ee(n,Ue(n.getAttribute(A)))){t.preventDefault();return}let o=r.closest(`[${xe}]`);o&&(t.preventDefault(),be(o))}))}function _e(e){let t=()=>it(e);e.readyState==="loading"?e.addEventListener("DOMContentLoaded",t,{once:!0}):t()}var st=[{selector:"main article",tier:"article"},{selector:"article",tier:"article"},{selector:"main",tier:"main"},{selector:".article-template__content",tier:"theme-class"},{selector:'[id^="ArticleContent"]',tier:"theme-class"},{selector:".rte",tier:"theme-class"}],y=[{selector:".article-template__content",tier:"theme-class"},{selector:'[id^="ArticleContent"]',tier:"theme-class"},{selector:".rte",tier:"theme-class"},{selector:"main article",tier:"article"},{selector:"article",tier:"article"},{selector:"main",tier:"main"}],P=y.map(e=>e.selector).join(", "),ct='header, [role="banner"], [class*="announcement"], footer, [role="contentinfo"], nav, [role="navigation"], cart-drawer, .cart-drawer, .drawer, .mini-cart, .modal, [role="dialog"]',u="data-certrev-placement",De="data-certrev-anchor",lt='script[type="application/ld+json"][data-certrev-graph]',ft=".certrev-card-wrap",Fe=".certrev-memo-wrap",dt=".certrev-pill-wrap",Te=384,pt=24,_="#references";function S(e){for(let t=e;t;t=t.parentElement){if(t.hasAttribute("hidden")||t.getAttribute("aria-hidden")==="true")return!1;let r=getComputedStyle(t);if(r.display==="none"||r.visibility==="hidden")return!1}return!0}function z(e){return e.closest(ct)!==null}function Se(e,t){for(let{selector:r,tier:n}of t)if(e.matches(r))return n;return"article"}function D(e,t,r){for(let{selector:n,tier:o}of r)for(let a of Array.from(e.querySelectorAll(n)))if(a!==t&&!a.contains(t)&&!t.contains(a)&&S(a)&&!z(a))return{el:a,tier:o};return null}function V(e,t){let r=t.getAttribute(u);if(r)return{anchored:r!=="body-fallback",tier:r};let n=t.getAttribute(De);if(n==="rail"){let i=e.querySelector(_)?.closest(P)??D(e,t,y)?.el??null,c=e.defaultView;if(i&&c&&S(i)&&!z(i)){let d=i.getBoundingClientRect();if(c.innerWidth-d.right>=Te)return t.style.setProperty("--certrev-rail-left",`${Math.round(d.right+pt)}px`),t.setAttribute(u,"rail"),{anchored:!0,tier:"rail"};i.insertAdjacentElement("afterbegin",t);let x=Se(i,y);return t.setAttribute(u,x),{anchored:!0,tier:x}}return t.setAttribute(u,"body-fallback"),{anchored:!1,tier:"body-fallback"}}let o=n==="bottom"?"bottom":"top",a="body-fallback";if(o==="top"){let i=e.querySelector(_)?.closest(P)??null;if(i&&i!==t&&!i.contains(t)&&!t.contains(i)&&S(i)&&!z(i))i.insertAdjacentElement("afterbegin",t),a=Se(i,y);else{let c=D(e,t,y);c&&(c.el.insertAdjacentElement("afterbegin",t),a=c.tier)}}else{let s=e.querySelector(_);if(s&&s!==t&&!t.contains(s)&&!s.contains(t)&&S(s))s.insertAdjacentElement("beforebegin",t),a="references";else{let i=D(e,t,st);i&&(i.el.insertAdjacentElement("afterend",t),a=i.tier)}}return t.setAttribute(u,a),{anchored:a!=="body-fallback",tier:a}}function ut(e){let t=e.querySelector(lt),r=!1;t&&t.parentNode!==e.head&&(e.head.appendChild(t),r=!0);let n=e.querySelector(ft),o=e.querySelector(Fe);return e.querySelector(dt)?.setAttribute(u,"pill-fixed"),{graph:r,card:n?V(e,n):null,memo:o?V(e,o):null}}function mt(e){let t=e.defaultView;if(!t)return;let r=e.querySelector(`${Fe}[${De}="rail"]`);if(!r||r.getAttribute(u)!=="rail")return;let n,o=()=>{n&&clearTimeout(n),n=setTimeout(()=>{if(r.getAttribute(u)!=="rail"){t.removeEventListener("resize",o);return}let s=e.querySelector(_)?.closest(P)??D(e,r,y)?.el??null;(s?t.innerWidth-s.getBoundingClientRect().right:0)<Te&&(r.style.removeProperty("--certrev-rail-left"),r.removeAttribute(u),V(e,r),t.removeEventListener("resize",o))},150)};t.addEventListener("resize",o)}function Re(e){let t=()=>{ut(e),mt(e)};e.readyState==="loading"?e.addEventListener("DOMContentLoaded",t,{once:!0}):t()}async function yt(e,t){let r=e.querySelector(".certrev-badge");if(!r)return;let n=e.getAttribute("data-delivery-api"),o=e.getAttribute("data-external-id"),a=e.getAttribute("data-platform")??"shopify";if(!n||!o)return;let s;try{s=await t.verify({baseUrl:n,platform:a,externalId:o})}catch{return}s.decision!=="render"&&(r.setAttribute("hidden",""),r.setAttribute("data-certrev-suppressed",s.reason))}function ke(e,t){let r=()=>{for(let n of Array.from(e.querySelectorAll("certrev-badge[revalidate]")))yt(n,t)};e.readyState==="loading"?e.addEventListener("DOMContentLoaded",r,{once:!0}):r()}function Le(e,t=fetch){return async r=>{let o=`${r.baseUrl.replace(/\/+$/,"")}/api/cert/v1/delivery/${encodeURIComponent(r.platform)}/${encodeURIComponent(r.externalId)}`,a=await t(o,{headers:{accept:"application/json"}});if(!a.ok)throw new Error(`cert delivery ${a.status}`);let s=await a.json();return L(s,e,{platform:r.platform,externalId:r.externalId,liveContentHash:null,now:new Date})}}var gt="cert-issuer-1",vt=`-----BEGIN PUBLIC KEY-----
|
|
556
556
|
MCowBQYDK2VwAyEAMWN956IOPjpAq900dL428VzA28TO/pVXnq3brwqUmwM=
|
|
557
|
-
-----END PUBLIC KEY-----`,
|
|
557
|
+
-----END PUBLIC KEY-----`,ht=e=>e===gt?{format:"pem",pem:vt}:null;typeof document<"u"&&(Re(document),_e(document),ke(document,{verify:Le(ht)}));})();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public names kept only so a patch release never breaks a compiling integration (the semver
|
|
3
|
+
* contract at the head of CHANGELOG.md). Each one does nothing it used to do; removal is for 2.0.
|
|
4
|
+
*/
|
|
5
|
+
/** Dispatched on the document, once per document, on the first call of a removed API, so a caller
|
|
6
|
+
* that still relies on it can see that it does nothing. `detail: { api, removedIn }`. */
|
|
7
|
+
export declare const DEPRECATED_API_EVENT = "certrev:deprecated-api";
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Removed in 1.1.1: the runtime FTC disclosure guard is gone. This installs nothing,
|
|
10
|
+
* checks nothing and never hides or replaces a face; whether a face shows its disclosure is settled
|
|
11
|
+
* where the face is configured. The first call per document dispatches {@link DEPRECATED_API_EVENT}.
|
|
12
|
+
* Will be deleted in 2.0.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installFtcGuard(doc: Document): void;
|
|
15
|
+
//# sourceMappingURL=deprecated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.d.ts","sourceRoot":"","sources":["../../src/modal/deprecated.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;0FAC0F;AAC1F,eAAO,MAAM,oBAAoB,2BAA2B,CAAA;AAK5D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAMnD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public names kept only so a patch release never breaks a compiling integration (the semver
|
|
3
|
+
* contract at the head of CHANGELOG.md). Each one does nothing it used to do; removal is for 2.0.
|
|
4
|
+
*/
|
|
5
|
+
/** Dispatched on the document, once per document, on the first call of a removed API, so a caller
|
|
6
|
+
* that still relies on it can see that it does nothing. `detail: { api, removedIn }`. */
|
|
7
|
+
export const DEPRECATED_API_EVENT = 'certrev:deprecated-api';
|
|
8
|
+
/** Documents that have already been told, so repeated calls dispatch once. */
|
|
9
|
+
const announcedDocs = new WeakSet();
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Removed in 1.1.1: the runtime FTC disclosure guard is gone. This installs nothing,
|
|
12
|
+
* checks nothing and never hides or replaces a face; whether a face shows its disclosure is settled
|
|
13
|
+
* where the face is configured. The first call per document dispatches {@link DEPRECATED_API_EVENT}.
|
|
14
|
+
* Will be deleted in 2.0.
|
|
15
|
+
*/
|
|
16
|
+
export function installFtcGuard(doc) {
|
|
17
|
+
if (announcedDocs.has(doc))
|
|
18
|
+
return;
|
|
19
|
+
announcedDocs.add(doc);
|
|
20
|
+
doc.dispatchEvent(new CustomEvent(DEPRECATED_API_EVENT, { detail: { api: 'installFtcGuard', removedIn: '1.1.1' } }));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=deprecated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.js","sourceRoot":"","sources":["../../src/modal/deprecated.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;0FAC0F;AAC1F,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAA;AAE5D,8EAA8E;AAC9E,MAAM,aAAa,GAAG,IAAI,OAAO,EAAY,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAa;IAC5C,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAM;IAClC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACtB,GAAG,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CACjG,CAAA;AACF,CAAC"}
|
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* module. Legally load-bearing: the curly apostrophe (U+2019) in "article’s"
|
|
5
5
|
* is byte-exact; a reword is a contract divergence.
|
|
6
6
|
*
|
|
7
|
-
* Kept as its OWN module (imported by
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Kept as its OWN module (imported by display-strings, exactly as in portal) so the browser bundle
|
|
8
|
+
* emits the same `var` structure as the deployed certrev-cert.js — do not merge it into
|
|
9
|
+
* display-strings.ts (that consolidation drops one `var` and breaks byte-parity).
|
|
10
|
+
*
|
|
11
|
+
* It is the text the faces RENDER, nothing more. Since 1.1.1 nothing in this package checks at
|
|
12
|
+
* runtime that it is present or visible (the `ftc-guard` kill switch is gone): whether a face shows
|
|
13
|
+
* its disclosure is settled where the face is configured, not by hiding or replacing it in the page.
|
|
10
14
|
*/
|
|
11
15
|
export declare const FTC_DISCLOSURE_LINE: "Independent editorial review of this article\u2019s text and sources. Not a product endorsement, nor medical advice.";
|
|
12
16
|
//# sourceMappingURL=ftc-disclosure.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ftc-disclosure.d.ts","sourceRoot":"","sources":["../../src/modal/ftc-disclosure.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ftc-disclosure.d.ts","sourceRoot":"","sources":["../../src/modal/ftc-disclosure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,mBAAmB,EAC/B,sHAA0H,CAAC"}
|
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* module. Legally load-bearing: the curly apostrophe (U+2019) in "article’s"
|
|
5
5
|
* is byte-exact; a reword is a contract divergence.
|
|
6
6
|
*
|
|
7
|
-
* Kept as its OWN module (imported by
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Kept as its OWN module (imported by display-strings, exactly as in portal) so the browser bundle
|
|
8
|
+
* emits the same `var` structure as the deployed certrev-cert.js — do not merge it into
|
|
9
|
+
* display-strings.ts (that consolidation drops one `var` and breaks byte-parity).
|
|
10
|
+
*
|
|
11
|
+
* It is the text the faces RENDER, nothing more. Since 1.1.1 nothing in this package checks at
|
|
12
|
+
* runtime that it is present or visible (the `ftc-guard` kill switch is gone): whether a face shows
|
|
13
|
+
* its disclosure is settled where the face is configured, not by hiding or replacing it in the page.
|
|
10
14
|
*/
|
|
11
15
|
export const FTC_DISCLOSURE_LINE = "Independent editorial review of this article’s text and sources. Not a product endorsement, nor medical advice.";
|
|
12
16
|
//# sourceMappingURL=ftc-disclosure.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ftc-disclosure.js","sourceRoot":"","sources":["../../src/modal/ftc-disclosure.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ftc-disclosure.js","sourceRoot":"","sources":["../../src/modal/ftc-disclosure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAC/B,iHAA0H,CAAC"}
|
package/dist/modal/index.d.ts
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* storefront (`<script src=".../dist/modal/certrev-cert.js">`) instead of vendoring a copy.
|
|
7
7
|
*
|
|
8
8
|
* Importing this module as ESM has the SAME side effect as loading the IIFE: it registers the
|
|
9
|
-
* `<certrev-cert-modal>` element and (in a browser) runs placement + interactions +
|
|
10
|
-
*
|
|
9
|
+
* `<certrev-cert-modal>` element and (in a browser) runs placement + interactions + badge
|
|
10
|
+
* revalidation (no FTC guard since 1.1.1). Bundler users: `import '@certrev/cert-block/modal'`.
|
|
11
11
|
*/
|
|
12
12
|
import "./certrev-cert.entry.js";
|
|
13
13
|
export { CERT_ISSUER_KID, CERT_ISSUER_PUBLIC_KEY_PEM, } from "./certrev-cert.entry.js";
|
|
14
14
|
export { CERT_MODAL_TAG } from "./certrev-cert-modal.js";
|
|
15
|
-
|
|
15
|
+
/** @deprecated Since 1.1.1 a no-op (the runtime FTC guard was removed); deleted in 2.0. */
|
|
16
|
+
export { installFtcGuard } from "./deprecated.js";
|
|
16
17
|
export { initCertInteractionsOnReady } from "./interactions.js";
|
|
17
18
|
export { initCertPlacement } from "./placement.js";
|
|
18
19
|
export { initBadgeRevalidation, type RevalidateDeps, revalidateBadge, } from "./revalidate.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,yBAAyB,CAAC;AAKjC,OAAO,EACN,eAAe,EACf,0BAA0B,GAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,yBAAyB,CAAC;AAKjC,OAAO,EACN,eAAe,EACf,0BAA0B,GAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,2FAA2F;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EACN,qBAAqB,EACrB,KAAK,cAAc,EACnB,eAAe,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/modal/index.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* storefront (`<script src=".../dist/modal/certrev-cert.js">`) instead of vendoring a copy.
|
|
7
7
|
*
|
|
8
8
|
* Importing this module as ESM has the SAME side effect as loading the IIFE: it registers the
|
|
9
|
-
* `<certrev-cert-modal>` element and (in a browser) runs placement + interactions +
|
|
10
|
-
*
|
|
9
|
+
* `<certrev-cert-modal>` element and (in a browser) runs placement + interactions + badge
|
|
10
|
+
* revalidation (no FTC guard since 1.1.1). Bundler users: `import '@certrev/cert-block/modal'`.
|
|
11
11
|
*/
|
|
12
12
|
// Side-effect: register the element + (in a browser) kick off the DOM passes.
|
|
13
13
|
import "./certrev-cert.entry.js";
|
|
@@ -17,7 +17,8 @@ import "./certrev-cert.entry.js";
|
|
|
17
17
|
export { CERT_ISSUER_KID, CERT_ISSUER_PUBLIC_KEY_PEM, } from "./certrev-cert.entry.js";
|
|
18
18
|
// Composable entry points for advanced consumers (the IIFE wires these itself).
|
|
19
19
|
export { CERT_MODAL_TAG } from "./certrev-cert-modal.js";
|
|
20
|
-
|
|
20
|
+
/** @deprecated Since 1.1.1 a no-op (the runtime FTC guard was removed); deleted in 2.0. */
|
|
21
|
+
export { installFtcGuard } from "./deprecated.js";
|
|
21
22
|
export { initCertInteractionsOnReady } from "./interactions.js";
|
|
22
23
|
export { initCertPlacement } from "./placement.js";
|
|
23
24
|
export { initBadgeRevalidation, revalidateBadge, } from "./revalidate.js";
|
package/dist/modal/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8EAA8E;AAC9E,OAAO,yBAAyB,CAAC;AAEjC,+FAA+F;AAC/F,iGAAiG;AACjG,0FAA0F;AAC1F,OAAO,EACN,eAAe,EACf,0BAA0B,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/modal/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8EAA8E;AAC9E,OAAO,yBAAyB,CAAC;AAEjC,+FAA+F;AAC/F,iGAAiG;AACjG,0FAA0F;AAC1F,OAAO,EACN,eAAe,EACf,0BAA0B,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,2FAA2F;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EACN,qBAAqB,EAErB,eAAe,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* (revealing the row's `.acc-bio` in place; the name caret rotates via CSS) and mirrors the
|
|
9
9
|
* state on `aria-expanded`. A click on a link inside the row (e.g. the "Compensated expert"
|
|
10
10
|
* cue) is NOT a toggle. Keyboard: Enter/Space on the role=button row.
|
|
11
|
-
* • In-page modals: a `[data-certrev-modal-open]` control opens
|
|
12
|
-
* `<certrev-cert-modal>` element via its `.open('cert'|'expert')` method. Bare /
|
|
11
|
+
* • In-page modals: a `[data-certrev-modal-open]` control opens its OWN face's
|
|
12
|
+
* `<certrev-cert-modal>` element (`ownCertModal`) via its `.open('cert'|'expert')` method. Bare /
|
|
13
13
|
* `"cert"` opens the certificate dialog; `"expert"` opens the reviewer dialog. With no JS / no
|
|
14
14
|
* element / no envelope a link control falls back to its href (the certificate or the CertREV
|
|
15
15
|
* profile page). The element owns its OWN close plumbing (× / Close / backdrop / Esc) inside its
|
|
@@ -21,6 +21,12 @@ export declare const ACC_ATTR = "data-certrev-acc";
|
|
|
21
21
|
export declare const MODAL_OPEN_ATTR = "data-certrev-modal-open";
|
|
22
22
|
export declare const INITIALS_ATTR = "data-certrev-initials";
|
|
23
23
|
export declare const CARD_SELECTOR = ".certrev-card";
|
|
24
|
+
/** Stamped on a control that could not tell which modal is its own (value: the reason), beside the
|
|
25
|
+
* bubbling {@link MODAL_UNRESOLVED_EVENT}, so the miss is inspectable rather than a silent link. */
|
|
26
|
+
export declare const MODAL_UNRESOLVED_ATTR = "data-certrev-modal-unresolved";
|
|
27
|
+
/** Dispatched on a control whose face has no modal of its own but sits between other faces'
|
|
28
|
+
* (`ownCertModal` → `ambiguous`). `detail: { reason: 'ambiguous', modals }`. */
|
|
29
|
+
export declare const MODAL_UNRESOLVED_EVENT = "certrev:modal-unresolved";
|
|
24
30
|
/** The dialog a `data-certrev-modal-open` control targets. */
|
|
25
31
|
export type ModalKind = 'cert' | 'expert';
|
|
26
32
|
/** Toggle a contributor row's accordion bio open/closed (the `.open` class drives the CSS reveal +
|
|
@@ -29,11 +35,21 @@ export declare function toggleAccordion(row: Element): void;
|
|
|
29
35
|
/** Resolve a `data-certrev-modal-open` value to the dialog it opens. `"expert"` opens the reviewer
|
|
30
36
|
* modal; anything else (the bare attribute, or `"cert"`) opens the certificate modal. */
|
|
31
37
|
export declare function modalTargetKind(value: string | null): ModalKind;
|
|
32
|
-
/** Open a cert dialog via the `<certrev-cert-modal>` element
|
|
33
|
-
*
|
|
38
|
+
/** Open a cert dialog via the document's FIRST `<certrev-cert-modal>` element: a page-level helper
|
|
39
|
+
* for a page with one face. Controls go through `openOwnModal`, which finds their own face's element.
|
|
40
|
+
* Returns false (so the caller lets an href fall back) when the element is absent or declines (no
|
|
41
|
+
* envelope + no fetch source). */
|
|
34
42
|
export declare function openModal(doc: Document, kind: ModalKind): boolean;
|
|
35
43
|
/** Open the page's certificate modal (backward-compatible wrapper over openModal). */
|
|
36
44
|
export declare function openCertModal(doc: Document): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Open a dialog of `control`'s OWN face's `<certrev-cert-modal>` (see `ownCertModal`), which is what
|
|
47
|
+
* the delegated click / key handlers do. Returns false (so a link control follows its own href) when
|
|
48
|
+
* the face has no modal, the element declines, or the control cannot tell which modal is its own; in
|
|
49
|
+
* that last case it is stamped `data-certrev-modal-unresolved="ambiguous"` and receives
|
|
50
|
+
* `certrev:modal-unresolved`, and another face's certificate is never opened in its place.
|
|
51
|
+
*/
|
|
52
|
+
export declare function openOwnModal(control: Element, kind: ModalKind): boolean;
|
|
37
53
|
/**
|
|
38
54
|
* Replace a broken avatar `<img>` with its initials placeholder — the SAME markup the Liquid emits
|
|
39
55
|
* when no photo URL is present. The SSR can't know a present URL will 404 (the canary's expert photo
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../src/modal/interactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,eAAO,MAAM,QAAQ,qBAAqB,CAAA;AAC1C,eAAO,MAAM,eAAe,4BAA4B,CAAA;AACxD,eAAO,MAAM,aAAa,0BAA0B,CAAA;AACpD,eAAO,MAAM,aAAa,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../src/modal/interactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,eAAO,MAAM,QAAQ,qBAAqB,CAAA;AAC1C,eAAO,MAAM,eAAe,4BAA4B,CAAA;AACxD,eAAO,MAAM,aAAa,0BAA0B,CAAA;AACpD,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAC5C;qGACqG;AACrG,eAAO,MAAM,qBAAqB,kCAAkC,CAAA;AACpE;iFACiF;AACjF,eAAO,MAAM,sBAAsB,6BAA6B,CAAA;AAEhE,8DAA8D;AAC9D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAczC;6DAC6D;AAC7D,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAGlD;AAED;0FAC0F;AAC1F,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE/D;AAED;;;mCAGmC;AACnC,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAIjE;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAavE;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAU5D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAItD;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAyDxD;AAED,qDAAqD;AACrD,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAI/D"}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* (revealing the row's `.acc-bio` in place; the name caret rotates via CSS) and mirrors the
|
|
9
9
|
* state on `aria-expanded`. A click on a link inside the row (e.g. the "Compensated expert"
|
|
10
10
|
* cue) is NOT a toggle. Keyboard: Enter/Space on the role=button row.
|
|
11
|
-
* • In-page modals: a `[data-certrev-modal-open]` control opens
|
|
12
|
-
* `<certrev-cert-modal>` element via its `.open('cert'|'expert')` method. Bare /
|
|
11
|
+
* • In-page modals: a `[data-certrev-modal-open]` control opens its OWN face's
|
|
12
|
+
* `<certrev-cert-modal>` element (`ownCertModal`) via its `.open('cert'|'expert')` method. Bare /
|
|
13
13
|
* `"cert"` opens the certificate dialog; `"expert"` opens the reviewer dialog. With no JS / no
|
|
14
14
|
* element / no envelope a link control falls back to its href (the certificate or the CertREV
|
|
15
15
|
* profile page). The element owns its OWN close plumbing (× / Close / backdrop / Esc) inside its
|
|
@@ -17,11 +17,17 @@
|
|
|
17
17
|
*
|
|
18
18
|
* No crypto, no network — pure DOM. Idempotent install (guarded so a double-load can't double-bind).
|
|
19
19
|
*/
|
|
20
|
-
import { CERT_MODAL_TAG } from './certrev-cert-modal.js';
|
|
20
|
+
import { CERT_MODAL_TAG, ownCertModal } from './certrev-cert-modal.js';
|
|
21
21
|
export const ACC_ATTR = 'data-certrev-acc';
|
|
22
22
|
export const MODAL_OPEN_ATTR = 'data-certrev-modal-open';
|
|
23
23
|
export const INITIALS_ATTR = 'data-certrev-initials';
|
|
24
24
|
export const CARD_SELECTOR = '.certrev-card';
|
|
25
|
+
/** Stamped on a control that could not tell which modal is its own (value: the reason), beside the
|
|
26
|
+
* bubbling {@link MODAL_UNRESOLVED_EVENT}, so the miss is inspectable rather than a silent link. */
|
|
27
|
+
export const MODAL_UNRESOLVED_ATTR = 'data-certrev-modal-unresolved';
|
|
28
|
+
/** Dispatched on a control whose face has no modal of its own but sits between other faces'
|
|
29
|
+
* (`ownCertModal` → `ambiguous`). `detail: { reason: 'ambiguous', modals }`. */
|
|
30
|
+
export const MODAL_UNRESOLVED_EVENT = 'certrev:modal-unresolved';
|
|
25
31
|
/** Avatar base classes whose `--initials` variant is the SSR no-photo fallback (kept in lockstep
|
|
26
32
|
* with certrev-badge.liquid / certrev-memo.liquid). A broken <img> is swapped to `{base} {base}--initials`. */
|
|
27
33
|
const AVATAR_BASE_CLASSES = ['avatar', 'certrev-memo__avatar'];
|
|
@@ -38,8 +44,10 @@ export function toggleAccordion(row) {
|
|
|
38
44
|
export function modalTargetKind(value) {
|
|
39
45
|
return value === 'expert' ? 'expert' : 'cert';
|
|
40
46
|
}
|
|
41
|
-
/** Open a cert dialog via the `<certrev-cert-modal>` element
|
|
42
|
-
*
|
|
47
|
+
/** Open a cert dialog via the document's FIRST `<certrev-cert-modal>` element: a page-level helper
|
|
48
|
+
* for a page with one face. Controls go through `openOwnModal`, which finds their own face's element.
|
|
49
|
+
* Returns false (so the caller lets an href fall back) when the element is absent or declines (no
|
|
50
|
+
* envelope + no fetch source). */
|
|
43
51
|
export function openModal(doc, kind) {
|
|
44
52
|
const el = doc.querySelector(CERT_MODAL_TAG);
|
|
45
53
|
if (el && typeof el.open === 'function')
|
|
@@ -50,6 +58,25 @@ export function openModal(doc, kind) {
|
|
|
50
58
|
export function openCertModal(doc) {
|
|
51
59
|
return openModal(doc, 'cert');
|
|
52
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Open a dialog of `control`'s OWN face's `<certrev-cert-modal>` (see `ownCertModal`), which is what
|
|
63
|
+
* the delegated click / key handlers do. Returns false (so a link control follows its own href) when
|
|
64
|
+
* the face has no modal, the element declines, or the control cannot tell which modal is its own; in
|
|
65
|
+
* that last case it is stamped `data-certrev-modal-unresolved="ambiguous"` and receives
|
|
66
|
+
* `certrev:modal-unresolved`, and another face's certificate is never opened in its place.
|
|
67
|
+
*/
|
|
68
|
+
export function openOwnModal(control, kind) {
|
|
69
|
+
const own = ownCertModal(control);
|
|
70
|
+
if (own.modal) {
|
|
71
|
+
const el = own.modal;
|
|
72
|
+
return typeof el.open === 'function' ? el.open(kind) : false;
|
|
73
|
+
}
|
|
74
|
+
if (own.reason === 'ambiguous') {
|
|
75
|
+
control.setAttribute(MODAL_UNRESOLVED_ATTR, own.reason);
|
|
76
|
+
control.dispatchEvent(new CustomEvent(MODAL_UNRESOLVED_EVENT, { bubbles: true, detail: { reason: own.reason, modals: own.count } }));
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
53
80
|
function asElement(target) {
|
|
54
81
|
return target && target.closest ? target : null;
|
|
55
82
|
}
|
|
@@ -95,7 +122,7 @@ export function initCertInteractions(doc) {
|
|
|
95
122
|
// (Close is owned by the <certrev-cert-modal> element inside its shadow root.)
|
|
96
123
|
const opener = el.closest(`[${MODAL_OPEN_ATTR}]`);
|
|
97
124
|
if (opener) {
|
|
98
|
-
if (
|
|
125
|
+
if (openOwnModal(opener, modalTargetKind(opener.getAttribute(MODAL_OPEN_ATTR))))
|
|
99
126
|
e.preventDefault();
|
|
100
127
|
return;
|
|
101
128
|
}
|
|
@@ -124,7 +151,7 @@ export function initCertInteractions(doc) {
|
|
|
124
151
|
return;
|
|
125
152
|
const opener = el.closest(`[${MODAL_OPEN_ATTR}]`);
|
|
126
153
|
if (opener) {
|
|
127
|
-
if (
|
|
154
|
+
if (openOwnModal(opener, modalTargetKind(opener.getAttribute(MODAL_OPEN_ATTR)))) {
|
|
128
155
|
e.preventDefault();
|
|
129
156
|
return;
|
|
130
157
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../src/modal/interactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../src/modal/interactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAEtE,MAAM,CAAC,MAAM,QAAQ,GAAG,kBAAkB,CAAA;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAA;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,uBAAuB,CAAA;AACpD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAA;AAC5C;qGACqG;AACrG,MAAM,CAAC,MAAM,qBAAqB,GAAG,+BAA+B,CAAA;AACpE;iFACiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAA;AAUhE;gHACgH;AAChH,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAU,CAAA;AAEvE,qGAAqG;AACrG,MAAM,aAAa,GAAG,IAAI,OAAO,EAAY,CAAA;AAE7C;6DAC6D;AAC7D,MAAM,UAAU,eAAe,CAAC,GAAY;IAC3C,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,GAAG,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAC3D,CAAC;AAED;0FAC0F;AAC1F,MAAM,UAAU,eAAe,CAAC,KAAoB;IACnD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAA;AAC9C,CAAC;AAED;;;mCAGmC;AACnC,MAAM,UAAU,SAAS,CAAC,GAAa,EAAE,IAAe;IACvD,MAAM,EAAE,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAA4B,CAAA;IACvE,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,KAAK,CAAA;AACb,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,GAAa;IAC1C,OAAO,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,IAAe;IAC7D,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,GAAG,GAAG,CAAC,KAAyB,CAAA;QACxC,OAAO,OAAO,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC7D,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,CAAC,YAAY,CAAC,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;QACvD,OAAO,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,sBAAsB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAC7G,CAAA;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED,SAAS,SAAS,CAAC,MAA0B;IAC5C,OAAO,MAAM,IAAK,MAAkB,CAAC,OAAO,CAAC,CAAC,CAAE,MAAkB,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;IAChD,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAM;IAC5B,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IACvE,IAAI,CAAC,IAAI;QAAE,OAAM;IACjB,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,SAAS,GAAG,GAAG,IAAI,IAAI,IAAI,YAAY,CAAA;IAC5C,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAA;IAC3B,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAa;IAC/C,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAmB,OAAO,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC;QAC/F,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,YAAY,KAAK,CAAC;YAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAClE,CAAC;AACF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,GAAa;IACjD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAM;IAClC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEtB,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACnC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE;YAAE,OAAM;QAEf,sGAAsG;QACtG,+EAA+E;QAC/E,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,eAAe,GAAG,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC;gBAAE,CAAC,CAAC,cAAc,EAAE,CAAA;YACnG,OAAM;QACP,CAAC;QAED,wFAAwF;QACxF,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAA;QACvC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,eAAe,CAAC,GAAG,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,6FAA6F;IAC7F,iFAAiF;IACjF,GAAG,CAAC,gBAAgB,CACnB,OAAO,EACP,CAAC,CAAC,EAAE,EAAE;QACL,MAAM,CAAC,GAAG,CAAC,CAAC,MAAwB,CAAA;QACpC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC;YAAE,gBAAgB,CAAC,CAAqB,CAAC,CAAA;IACvG,CAAC,EACD,IAAI,CACJ,CAAA;IAED,4FAA4F;IAC5F,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAEvB,kGAAkG;IAClG,4FAA4F;IAC5F,4FAA4F;IAC5F,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QACrC,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,UAAU;YAAE,OAAM;QACtE,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAM;QAE1C,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,eAAe,GAAG,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,OAAM;YACP,CAAC;QACF,CAAC;QAED,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAA;QACvC,IAAI,GAAG,EAAE,CAAC;YACT,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,eAAe,CAAC,GAAG,CAAC,CAAA;QACrB,CAAC;IACF,CAAC,CAAC,CAAA;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,2BAA2B,CAAC,GAAa;IACxD,MAAM,GAAG,GAAG,GAAS,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAA;IACjD,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS;QAAE,GAAG,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;;QAC1F,GAAG,EAAE,CAAA;AACX,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
* getVerifiedDelivery — the envelope PLUS the brand's render def, from the Delivery API
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* 1.1.0. The Delivery API answers a Builder placement with the signed artifact and, beside it,
|
|
7
|
+
* the brand's resolved render-def projection as a top-level `renderDef` sibling (the same shape a
|
|
8
|
+
* Shopify store carries in its `render_def` metafield). The envelope's signature covers `payload`
|
|
9
|
+
* only, so the sibling rides the response without touching the signed bytes, and a tombstone
|
|
10
|
+
* carries none. `getVerifiedEnvelope` keeps discarding it; this reads it.
|
|
11
|
+
*
|
|
12
|
+
* The same fetch, deadline, kernel and fail-closed rules as `getVerifiedEnvelope` (every sourcing
|
|
13
|
+
* failure reports the reason it actually is; nothing throws into the render path). Two things are
|
|
14
|
+
* different, and both are the point:
|
|
15
|
+
*
|
|
16
|
+
* 1. WHAT IS CACHED is the cryptographically verified PAYLOAD, not the verdict. The lifecycle
|
|
17
|
+
* policy (revoked, expired) and the subject match are re-run by `renderVerdict` on EVERY call,
|
|
18
|
+
* with the caller's clock. So a payload that was valid when fetched and expires while cached
|
|
19
|
+
* stops rendering at its `expiresAt`, not at the next refetch; and a refetch after the TTL
|
|
20
|
+
* picks up a tombstone (revocation) or a changed def with no write on the brand's side.
|
|
21
|
+
* 2. The CACHE KEY is the Delivery URL plus the rendering placement, so a staging and a
|
|
22
|
+
* production origin never share an entry, and neither do two placements.
|
|
23
|
+
*
|
|
24
|
+
* `peekVerifiedDelivery` is the synchronous read of the same cache: a React component suspends on
|
|
25
|
+
* `getVerifiedDelivery` once, then reads the settled value without a second promise (which is what
|
|
26
|
+
* keeps a streaming SSR render from suspending forever on a fresh promise every retry).
|
|
27
|
+
*/
|
|
28
|
+
import type { CertChromeRenderDef } from '../builder/cert-chrome-data.js';
|
|
29
|
+
import type { CertPayload, CertSuppressReason, CertVerdict, RenderContext, ResolvePublicKeyByKid } from '../contract/kernel.js';
|
|
30
|
+
import { TtlCache } from './cache.js';
|
|
31
|
+
/** What one Delivery read settles to, as cached: a verified payload + its def, or the reason not. */
|
|
32
|
+
export type CachedDelivery = {
|
|
33
|
+
readonly kind: 'verified';
|
|
34
|
+
readonly payload: CertPayload;
|
|
35
|
+
readonly renderDef: CertChromeRenderDef | null;
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: 'suppressed';
|
|
38
|
+
readonly reason: CertSuppressReason;
|
|
39
|
+
};
|
|
40
|
+
/** The answer for one render: the verdict AT THIS RENDER'S CLOCK, and the def to paint it with. */
|
|
41
|
+
export interface VerifiedDelivery {
|
|
42
|
+
readonly verdict: CertVerdict;
|
|
43
|
+
/** The brand's render def, or null (no def resolved, a malformed one, or a suppress verdict). */
|
|
44
|
+
readonly renderDef: CertChromeRenderDef | null;
|
|
45
|
+
}
|
|
46
|
+
export interface GetVerifiedDeliveryOptions {
|
|
47
|
+
/** Base URL of the CertREV app that serves the Delivery API, e.g. 'https://portal.certrev.com'. */
|
|
48
|
+
readonly baseUrl: string;
|
|
49
|
+
readonly platform: string;
|
|
50
|
+
readonly externalId: string;
|
|
51
|
+
/** kid → public-key resolver. Required: no verify without keys. */
|
|
52
|
+
readonly resolveKid: ResolvePublicKeyByKid;
|
|
53
|
+
/** Render context: the platform + externalId this edge IS, and the render's clock (`now`). */
|
|
54
|
+
readonly context: RenderContext;
|
|
55
|
+
readonly fetchImpl?: typeof fetch;
|
|
56
|
+
/** Cache override. Defaults to the module-level shared delivery cache. */
|
|
57
|
+
readonly cache?: TtlCache<CachedDelivery>;
|
|
58
|
+
/** TTL in ms for a VERIFIED read (default: the cache's positive TTL, 60_000 on the shared one). */
|
|
59
|
+
readonly ttlMs?: number;
|
|
60
|
+
/** Deadline in ms for the fetch (default `DEFAULT_FETCH_TIMEOUT_MS`, 3s). */
|
|
61
|
+
readonly timeoutMs?: number;
|
|
62
|
+
}
|
|
63
|
+
/** Shared per-process cache: 60s for a verified read, 5s for a suppressed one. */
|
|
64
|
+
declare const sharedDeliveryCache: TtlCache<CachedDelivery>;
|
|
65
|
+
export declare function deliveryUrl(baseUrl: string, platform: string, externalId: string): string;
|
|
66
|
+
/** Cache key: the URL (origin included) × the placement rendering it. No clock: the TTL bounds time. */
|
|
67
|
+
export declare function deliveryCacheKey(opts: Pick<GetVerifiedDeliveryOptions, 'baseUrl' | 'platform' | 'externalId' | 'context'>): string;
|
|
68
|
+
/**
|
|
69
|
+
* The def sibling, shape-checked. Anything that is not the metafield shape is null, which paints
|
|
70
|
+
* the preset default (the same thing an absent def paints): a def is presentation, never a claim,
|
|
71
|
+
* so a malformed one degrades the styling and nothing else. The TOKEN VALUES are not judged here;
|
|
72
|
+
* `resolveBlockTheme` re-validates each at paint time and drops what is not a safe value.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseRenderDef(value: unknown): CertChromeRenderDef | null;
|
|
75
|
+
/** Apply the render-time policy to a cached read: lifecycle + subject at THIS render's clock. */
|
|
76
|
+
export declare function settleDelivery(cached: CachedDelivery, context: RenderContext): VerifiedDelivery;
|
|
77
|
+
/**
|
|
78
|
+
* Fetch (if needed), verify, cache, and settle one Delivery read for one placement. FAIL-CLOSED:
|
|
79
|
+
* the verdict is `render` only when the artifact verified against `resolveKid` AND, at
|
|
80
|
+
* `context.now`, the payload is unrevoked, unexpired and for this placement.
|
|
81
|
+
*/
|
|
82
|
+
export declare function getVerifiedDelivery(opts: GetVerifiedDeliveryOptions): Promise<VerifiedDelivery>;
|
|
83
|
+
/**
|
|
84
|
+
* The synchronous read: the settled answer when a fresh read is cached, else `undefined` (the
|
|
85
|
+
* caller then awaits `getVerifiedDelivery`). Never fetches.
|
|
86
|
+
*/
|
|
87
|
+
export declare function peekVerifiedDelivery(opts: Pick<GetVerifiedDeliveryOptions, 'baseUrl' | 'platform' | 'externalId' | 'context' | 'cache'>): VerifiedDelivery | undefined;
|
|
88
|
+
/** Drop every cached read for one placement (e.g. from a revocation webhook). */
|
|
89
|
+
export declare function invalidateDelivery(opts: Pick<GetVerifiedDeliveryOptions, 'baseUrl' | 'platform' | 'externalId' | 'context'>, cache?: TtlCache<CachedDelivery>): void;
|
|
90
|
+
export { sharedDeliveryCache };
|
|
91
|
+
//# sourceMappingURL=get-verified-delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-verified-delivery.d.ts","sourceRoot":"","sources":["../../src/verify/get-verified-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,KAAK,EAEX,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGrC,qGAAqG;AACrG,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAEvE,mGAAmG;AACnG,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAC9C;AAED,MAAM,WAAW,0BAA0B;IAC1C,mGAAmG;IACnG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAA;IAC1C,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAA;IACzC,mGAAmG;IACnG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,kFAAkF;AAClF,QAAA,MAAM,mBAAmB,0BAAwE,CAAA;AAMjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC,GACvF,MAAM,CAER;AAKD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CA4BzE;AAyCD,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,GAAG,gBAAgB,CAI/F;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CASrG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC,GACjG,gBAAgB,GAAG,SAAS,CAG9B;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC,EACzF,KAAK,GAAE,QAAQ,CAAC,cAAc,CAAuB,GACnD,IAAI,CAEN;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAA"}
|