@chaindaddy/js 0.3.0 → 0.3.2

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/dist/cd.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var S="0.3.0";function v(t){let e="";for(let n of t)e+=String.fromCharCode(n);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function f(){return v(crypto.getRandomValues(new Uint8Array(32)))}function R(t,e,n,s){let a=o=>{if(o&&typeof o=="object"&&o.type===t)try{n(o)}catch{}},d=o=>{o.origin===e&&a(o.data)};window.addEventListener("message",d);let u=null;if(s&&typeof BroadcastChannel=="function"&&e===location.origin)try{u=new BroadcastChannel(s),u.onmessage=o=>a(o.data)}catch{u=null}return()=>{window.removeEventListener("message",d),u?.close()}}var m={get(t){try{return sessionStorage.getItem(t)}catch{return null}},set(t,e){try{sessionStorage.setItem(t,e)}catch{}},remove(t){try{sessionStorage.removeItem(t)}catch{}}};var q="https://api.chaindaddy.io",V={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},w="cd-signin",T="cd:signin",y="cd.signin";async function F(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return v(new Uint8Array(e))}function G(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function K(t,e,n){let s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:G(t.scope),state:e.state,nonce:e.nonce,code_challenge:await F(e.verifier),code_challenge_method:"S256"});return t.claims&&s.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${n}?${s}`}function _(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}function b(t){let e=(t.issuer??q).replace(/\/$/,""),n=t.authorizeUrl??V[e];if(!n)return Promise.reject(new Error("authorizeUrl is required for this issuer"));if(!t.clientId||!t.redirectUri)return Promise.reject(new Error("clientId and redirectUri are required"));let s={state:f(),nonce:f(),verifier:f(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};m.set(y,JSON.stringify(s));let a=window.open("",w,"popup,width=480,height=720");return(async()=>{let d=await K(t,s,n);if(!a)return location.assign(d),null;a.location.href=d;let u=new URL(t.redirectUri,location.href).origin;return new Promise((o,i)=>{let c=R(T,u,r=>{r.state===s.state&&(c(),m.remove(y),typeof r.error=="string"&&r.error?i(new Error(r.error)):r.iss!==e?i(new Error("the answer came from another issuer")):typeof r.code!="string"||!r.code?i(new Error("the answer carried no code")):o(_(s,r.code)))},w);t.signal?.addEventListener("abort",()=>{c(),i(new Error("aborted"))})})})()}async function U(){let t=new URLSearchParams(location.search),e={type:T,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===w){try{window.opener?.postMessage(e,location.origin)}catch{}try{let a=new BroadcastChannel(w);a.postMessage(e),a.close()}catch{}return window.close(),null}let n=m.get(y);m.remove(y);let s=n?JSON.parse(n):null;if(!s||e.state!==s.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==s.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return _(s,e.code)}async function A(t){let e=await fetch(`${t.issuer}/api/v2/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:t.codeVerifier})}),n=await e.json();if(!e.ok)throw new Error(`${n.error??e.status}: ${n.error_description??""}`.trim());return n}var W='<svg viewBox="0 0 24 24" fill="#FFD700" width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5z" stroke="#B8860B" stroke-width="0.5"/><path d="M5 18h14v2H5v-2z" stroke="#B8860B" stroke-width="0.5"/></svg>',Y=`
1
+ "use strict";(()=>{var S="0.3.2";function k(t){let e="";for(let i of t)e+=String.fromCharCode(i);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function f(){return k(crypto.getRandomValues(new Uint8Array(32)))}function R(t,e,i,s,o){let u=n=>{if(n&&typeof n=="object"&&n.type===t)try{i(n)}catch{}},l=n=>{n.origin===e&&(!o||n.source===o)&&u(n.data)};window.addEventListener("message",l);let a=null;if(s&&typeof BroadcastChannel=="function"&&e===location.origin)try{a=new BroadcastChannel(s),a.onmessage=n=>u(n.data)}catch{a=null}return()=>{window.removeEventListener("message",l),a?.close()}}var m={get(t){try{return sessionStorage.getItem(t)}catch{return null}},set(t,e){try{sessionStorage.setItem(t,e)}catch{}},remove(t){try{sessionStorage.removeItem(t)}catch{}}};var q="https://api.chaindaddy.io",V={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},w="cd-signin",T="cd:signin",y="cd.signin";async function F(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return k(new Uint8Array(e))}function G(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function K(t,e,i){let s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:G(t.scope),state:e.state,nonce:e.nonce,code_challenge:await F(e.verifier),code_challenge_method:"S256"});return t.claims&&s.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${i}?${s}`}function U(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}function b(t){let e=(t.issuer??q).replace(/\/$/,""),i=t.authorizeUrl??V[e];if(!i)return Promise.reject(new Error("authorizeUrl is required for this issuer"));if(!t.clientId||!t.redirectUri)return Promise.reject(new Error("clientId and redirectUri are required"));let s={state:f(),nonce:f(),verifier:f(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};m.set(y,JSON.stringify(s));let o=window.open("",w,"popup,width=480,height=720");return(async()=>{let u=await K(t,s,i);if(!o)return location.assign(u),null;o.location.href=u;let l=new URL(t.redirectUri,location.href).origin;return new Promise((a,n)=>{let c=R(T,l,r=>{r.state===s.state&&(c(),m.remove(y),typeof r.error=="string"&&r.error?n(new Error(r.error)):r.iss!==e?n(new Error("the answer came from another issuer")):typeof r.code!="string"||!r.code?n(new Error("the answer carried no code")):a(U(s,r.code)))},w,o);t.signal?.addEventListener("abort",()=>{c(),n(new Error("aborted"))})})})()}async function _(){let t=new URLSearchParams(location.search),e={type:T,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===w){try{window.opener?.postMessage(e,location.origin)}catch{}try{let o=new BroadcastChannel(w);o.postMessage(e),o.close()}catch{}return window.close(),null}let i=m.get(y);m.remove(y);let s=i?JSON.parse(i):null;if(!s||e.state!==s.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==s.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return U(s,e.code)}async function A(t){let e=await fetch(`${t.issuer}/api/v2/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:t.codeVerifier})}),i=await e.json();if(!e.ok)throw new Error(`${i.error??e.status}: ${i.error_description??""}`.trim());return i}var W='<svg viewBox="0 0 24 24" fill="#FFD700" width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5z" stroke="#B8860B" stroke-width="0.5"/><path d="M5 18h14v2H5v-2z" stroke="#B8860B" stroke-width="0.5"/></svg>',Y=`
2
2
  :host { display: inline-block; }
3
3
  button { display: inline-flex; align-items: center; justify-content: center; gap: 10px; box-sizing: border-box;
4
4
  min-height: 40px; padding: 0 16px; border-radius: 10px; font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
@@ -7,16 +7,16 @@ button[data-theme="dark"] { background: #101828; color: #ffffff; border-color: #
7
7
  button:focus-visible { outline: 2px solid #f59e0b; outline-offset: 2px; }
8
8
  button:disabled { opacity: .6; cursor: default; }
9
9
  svg { flex: none; }
10
- `;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let n=this.getAttribute("text")==="continue"?"Continue with Chain Daddy":"Sign in with Chain Daddy",s=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${Y}</style><button type="button" part="button" data-theme="${s}">${W}<span></span></button>`;let a=this.root.querySelector("button");a.querySelector("span").textContent=n,a.addEventListener("click",()=>this.start())}start(){let n=s=>this.getAttribute(s)||void 0;b({clientId:n("client-id")??"",redirectUri:n("redirect-uri")??"",scope:n("scope"),claims:n("claims"),issuer:n("issuer"),authorizeUrl:n("authorize-url")}).then(s=>s&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:s,bubbles:!0,composed:!0})),s=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(s?.message??s)},bubbles:!0,composed:!0})))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var M="https://embed.chaindaddy.io",k="https://embed-staging.chaindaddy.io";var J=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function P(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return k}catch{}return M}var X=`
10
+ `;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let i=this.getAttribute("text")==="continue"?"Continue with Chain Daddy":"Sign in with Chain Daddy",s=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${Y}</style><button type="button" part="button" data-theme="${s}">${W}<span></span></button>`;let o=this.root.querySelector("button");o.querySelector("span").textContent=i,o.addEventListener("click",()=>this.start())}start(){let i=s=>this.getAttribute(s)||void 0;b({clientId:i("client-id")??"",redirectUri:i("redirect-uri")??"",scope:i("scope"),claims:i("claims"),issuer:i("issuer"),authorizeUrl:i("authorize-url")}).then(s=>s&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:s,bubbles:!0,composed:!0})),s=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(s?.message??s)},bubbles:!0,composed:!0})))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var M="https://embed.chaindaddy.io",v="https://embed-staging.chaindaddy.io";var J="1500px 0px",X="docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";var Q=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function O(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return v}catch{}return M}var Z=`
11
11
  :host { display: block; }
12
12
  iframe { display: block; width: 100%; border: 0; height: 1px; opacity: 0; color-scheme: normal; }
13
13
  :host([data-cd-ready]) iframe { opacity: 1; }
14
14
  :host([data-cd-ready]) slot { display: none; }
15
15
  :host([data-cd-unavailable]) iframe { display: none; }
16
- `,Q=`
16
+ `,tt=`
17
17
  :host { display: inline-block; }
18
18
  button { box-sizing: border-box; min-height: 40px; padding: 0 16px; border-radius: 10px; cursor: pointer;
19
19
  font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
20
20
  border: 1px solid var(--cd-border, #d0d5dd); background: var(--cd-accent, #f59e0b); color: var(--cd-text, #101828); }
21
21
  button:focus-visible { outline: 2px solid var(--cd-accent, #f59e0b); outline-offset: 2px; }
22
- `;function O(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===k?k:M;class n extends HTMLElement{constructor(){super(...arguments);this.kind="app";this.frame=null;this.onMessage=i=>this.receive(i)}connectedCallback(){try{this.shadowRoot||this.attachShadow({mode:"open"}),this.mount()}catch{this.unavailable()}}disconnectedCallback(){try{window.removeEventListener("message",this.onMessage),clearTimeout(this.timer)}catch{}}attributeChangedCallback(){try{this.isConnected&&this.shadowRoot&&this.mount()}catch{this.unavailable()}}src(){let i=(this.getAttribute("crown")??"").trim(),c=(this.getAttribute("chain")??"").trim(),r=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(i)||!c||this.kind==="app"&&!r)return null;let l=new URLSearchParams({crown:i,chain:c});return this.kind==="app"?l.set("app",r):l.set("kind","coin"),l.set("mode",this.mode()),l.set("v","1"),`${e}/v1/frame?${l.toString()}`}mode(){let i=this.getAttribute("mode");if(i==="light"||i==="dark")return i;try{return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}mount(){let i=this.shadowRoot;window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.removeAttribute("data-cd-unavailable");let c=this.src();if(!c){i.innerHTML="<slot></slot>",this.frame=null;return}i.innerHTML=`<style>${X}</style><slot></slot>`;let r=document.createElement("iframe");r.setAttribute("loading","lazy"),r.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),r.setAttribute("allow","clipboard-write; fullscreen"),r.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),r.src=c,i.appendChild(r),this.frame=r,window.addEventListener("message",this.onMessage),this.timer=setTimeout(()=>{this.hasAttribute("data-cd-ready")||this.unavailable()},8e3)}receive(i){try{if(i.origin!==e||!this.frame||!i.source||i.source!==this.frame.contentWindow)return;let c=i.data;if(!c||typeof c!="object")return;if(c.type==="cd:ready")clearTimeout(this.timer),this.removeAttribute("data-cd-unavailable"),this.setAttribute("data-cd-ready",""),this.sendTheme(),this.dispatchEvent(new CustomEvent("cd:ready"));else if(c.type==="cd:resize"){let r=Number(c.height);Number.isFinite(r)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(r)))}px`)}else c.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let i={},c=getComputedStyle(this);for(let r of J){let l=c.getPropertyValue(r).trim();l&&(i[r]=l)}this.frame?.contentWindow?.postMessage({type:"cd:theme",tokens:i},e)}unavailable(){try{if(clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.hasAttribute("data-cd-unavailable"))return;this.setAttribute("data-cd-unavailable",""),this.dispatchEvent(new CustomEvent("cd:unavailable"))}catch{}}}n.observedAttributes=["crown","chain","app","mode"];class s extends n{}class a extends n{constructor(){super(...arguments);this.kind="coin"}}class d extends HTMLElement{connectedCallback(){try{let o=this.querySelector("a[href]"),i=o?new URL(o.getAttribute("href")??"",document.baseURI):null;if(!i||i.protocol!=="https:")return;let c=this.shadowRoot??this.attachShadow({mode:"open"});c.innerHTML=`<style>${Q}</style><button type="button" part="button"></button>`;let r=c.querySelector("button");r.textContent=(o.textContent??"").trim()||"Buy on Chain Daddy",r.addEventListener("click",()=>{try{window.open(i.toString(),"cd-buy","popup,width=460,height=760")||window.location.assign(i.toString())}catch{}})}catch{}}}for(let[u,o]of[["cd-app",s],["cd-coin",a],["cd-buy",d]])customElements.get(u)||customElements.define(u,o)}catch{}}var Z=["https://chaindaddy.io","https://staging.chaindaddy.io"],D=()=>"cd-checkout-"+f(),H="popup,width=480,height=820",$=300,tt=7;function E(t){if(typeof t!="string")return null;let e;try{e=new URL(t)}catch{return null}let n=/^\/_pay\/([\w-]+)$/.exec(e.pathname);return n&&Z.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:n[1]}:null}function C(t,e){if(typeof t!="string")return null;let n;try{n=new URL(t,location.href)}catch{return null}if(!/^https?:$/.test(n.protocol)||n.username||n.password)return null;let s=d=>{let u=d.split("=")[0];try{return decodeURIComponent(u.replace(/\+/g," "))}catch{return u}},a=n.search.slice(1).split("&").filter(d=>d&&s(d)!=="session_id");return a.push("session_id="+encodeURIComponent(e)),n.origin+n.pathname+"?"+a.join("&")+n.hash}var p;function N(t,e,n){return new Promise(s=>{let a=!1,d=0,u,o=l=>{clearInterval(r),clearTimeout(u),window.removeEventListener("message",i),n&&l.status==="completed"&&location.assign(n),s(l)},i=l=>{if(l.origin!==e.origin||l.source!==t)return;let g=l.data;if(!g||typeof g!="object")return;g.type==="cd:checkout.ready"&&(a=!0);let h=g.session;if(!(!h||typeof h!="object"||h.id!==e.id)){if(g.type==="cd:checkout.cancelled")o({status:"cancelled",session:{id:e.id}});else if(g.type==="cd:checkout.completed"&&(h.status==="complete"||h.status==="paid")){let I={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(I.orderId=h.orderId),o({status:"completed",session:I})}}},c=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},r=setInterval(()=>{t.closed?(clearInterval(r),u=setTimeout(()=>o({status:"closed"}),$)):(!a||++d%tt===0)&&c()},$);window.addEventListener("message",i),c()})}function x(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),n=(a,d,u)=>{p={popup:a,result:d,href:u};let o=()=>{p&&p.popup===a&&(p=void 0)};return d.then(o,o),d},s=a=>(location.assign(a.href),{status:"redirected"});try{let{url:a,successUrl:d}=t;if(d&&!C(d,""))throw new Error("checkout: successUrl is not a web address");let u=r=>d?C(d,r.id):null,o=typeof a=="function"?null:E(a);if(typeof a!="function"&&!o)throw e();if(p&&!p.popup.closed&&(!o||p.href===o.href))return p.popup.focus(),p.result;if(o){let r=window.open(o.href,D(),H);return r?n(r,N(r,o,u(o)),o.href):Promise.resolve(s(o))}let i=window.open("",D(),H),c=new Promise(r=>r(a())).then(r=>{let l=E(r);if(!l)throw i?.close(),e();return i?(i.location.href=l.href,N(i,l,u(l))):s(l)},r=>{throw i?.close(),r});return i?n(i,c):c}catch(a){return Promise.reject(a)}}var B=!1,z;function j(){B||typeof document>"u"||(B=!0,document.addEventListener("click",t=>{let e=t.target instanceof Element?t.target.closest("[data-cd-checkout]"):null;if(!e||t.defaultPrevented||t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let n=e.getAttribute("data-cd-checkout")||e.href,s=e.getAttribute("data-cd-success");if(!E(n)||s&&!C(s,""))return;t.preventDefault();let a=x({url:n,successUrl:s});a!==z&&(z=a,a.then(d=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:d,bubbles:!0})),()=>{}))}))}var et={version:S,signIn:b,handleCallback:U,exchangeCode:A,checkout:x};window.ChainDaddy=Object.assign(window.ChainDaddy??{},et);L();O({origin:P(document.currentScript?.src)});j();})();
22
+ `;function P(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===v?v:M;class i extends HTMLElement{constructor(){super(...arguments);this.kind="app";this.frame=null;this.onMessage=n=>this.receive(n)}connectedCallback(){try{this.shadowRoot||this.attachShadow({mode:"open"}),this.mount()}catch{this.unavailable()}}disconnectedCallback(){try{window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.watch?.disconnect()}catch{}}attributeChangedCallback(){try{this.isConnected&&this.shadowRoot&&this.mount()}catch{this.unavailable()}}src(){let n=(this.getAttribute("crown")??"").trim(),c=(this.getAttribute("chain")??"").trim(),r=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(n)||!c||this.kind==="app"&&!r)return null;let d=new URLSearchParams({crown:n,chain:c});return this.kind==="app"?d.set("app",r):d.set("kind","coin"),d.set("mode",this.mode()),d.set("v","1"),`${e}/v1/frame?${d.toString()}`}mode(){let n=this.getAttribute("mode");if(n==="light"||n==="dark")return n;try{return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}mount(){let n=this.shadowRoot;window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.watch?.disconnect(),this.removeAttribute("data-cd-ready"),this.removeAttribute("data-cd-unavailable");let c=this.src();if(!c){n.innerHTML="<slot></slot>",this.frame=null;return}n.innerHTML=`<style>${Z}</style><slot></slot>`;let r=document.createElement("iframe");r.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),r.setAttribute("allow","clipboard-write; fullscreen"),r.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),n.appendChild(r),this.frame=r,window.addEventListener("message",this.onMessage);let d=()=>{r.src=c,this.timer=setTimeout(()=>{this.hasAttribute("data-cd-ready")||this.unavailable()},8e3)};if(typeof IntersectionObserver>"u")return d();this.watch=new IntersectionObserver(g=>{g.some(h=>h.isIntersecting)&&(this.watch?.disconnect(),d())},{rootMargin:J}),this.watch.observe(this)}why(){try{let n=new URL(this.frame.src).searchParams,c=n.get("app");n.delete("mode"),n.delete("v"),fetch(`https://${e===v?"staging-api":"api"}.chaindaddy.io/api/v2/embed/status?${n}`,{credentials:"omit"}).then(r=>r.json()).then(r=>{let d=r.embeddable?r.interactive?"not a verified site of the coin":"not https, or the app did not start":r.reason;console.info(`[Chain Daddy] cd-${this.kind} ${c??""} cannot show here: ${d}. ${X}`)}).catch(()=>{})}catch{}}receive(n){try{if(n.origin!==e||!this.frame||!n.source||n.source!==this.frame.contentWindow)return;let c=n.data;if(!c||typeof c!="object")return;if(c.type==="cd:ready")clearTimeout(this.timer),this.removeAttribute("data-cd-unavailable"),this.setAttribute("data-cd-ready",""),this.sendTheme(),this.dispatchEvent(new CustomEvent("cd:ready"));else if(c.type==="cd:resize"){let r=Number(c.height);Number.isFinite(r)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(r)))}px`)}else c.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let n={},c=getComputedStyle(this);for(let r of Q){let d=c.getPropertyValue(r).trim();d&&(n[r]=d)}this.frame?.contentWindow?.postMessage({type:"cd:theme",tokens:n},e)}unavailable(){try{if(clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.hasAttribute("data-cd-unavailable"))return;this.setAttribute("data-cd-unavailable",""),this.dispatchEvent(new CustomEvent("cd:unavailable")),this.why()}catch{}}}i.observedAttributes=["crown","chain","app","mode"];class s extends i{}class o extends i{constructor(){super(...arguments);this.kind="coin"}}class u extends HTMLElement{connectedCallback(){try{let a=this.querySelector("a[href]"),n=a?new URL(a.getAttribute("href")??"",document.baseURI):null;if(!n||n.protocol!=="https:")return;let c=this.shadowRoot??this.attachShadow({mode:"open"});c.innerHTML=`<style>${tt}</style><button type="button" part="button"></button>`;let r=c.querySelector("button");r.textContent=(a.textContent??"").trim()||"Buy on Chain Daddy",r.addEventListener("click",()=>{try{window.open(n.toString(),"cd-buy","popup,width=460,height=760")||window.location.assign(n.toString())}catch{}})}catch{}}}for(let[l,a]of[["cd-app",s],["cd-coin",o],["cd-buy",u]])customElements.get(l)||customElements.define(l,a)}catch{}}var et=["https://chaindaddy.io","https://staging.chaindaddy.io"],$=()=>"cd-checkout-"+f(),D="popup,width=480,height=820",H=300,nt=7;function E(t){if(typeof t!="string")return null;let e;try{e=new URL(t)}catch{return null}let i=/^\/_pay\/([\w-]+)$/.exec(e.pathname);return i&&et.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:i[1]}:null}function C(t,e){if(typeof t!="string")return null;let i;try{i=new URL(t,location.href)}catch{return null}if(!/^https?:$/.test(i.protocol)||i.username||i.password)return null;let s=u=>{let l=u.split("=")[0];try{return decodeURIComponent(l.replace(/\+/g," "))}catch{return l}},o=i.search.slice(1).split("&").filter(u=>u&&s(u)!=="session_id");return o.push("session_id="+encodeURIComponent(e)),i.origin+i.pathname+"?"+o.join("&")+i.hash}var p;function N(t,e,i){return new Promise(s=>{let o=!1,u=0,l,a=d=>{clearInterval(r),clearTimeout(l),window.removeEventListener("message",n),i&&d.status==="completed"&&location.assign(i),s(d)},n=d=>{if(d.origin!==e.origin||d.source!==t)return;let g=d.data;if(!g||typeof g!="object")return;g.type==="cd:checkout.ready"&&(o=!0);let h=g.session;if(!(!h||typeof h!="object"||h.id!==e.id)){if(g.type==="cd:checkout.cancelled")a({status:"cancelled",session:{id:e.id}});else if(g.type==="cd:checkout.completed"&&(h.status==="complete"||h.status==="paid")){let x={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(x.orderId=h.orderId),a({status:"completed",session:x})}}},c=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},r=setInterval(()=>{t.closed?(clearInterval(r),l=setTimeout(()=>a({status:"closed"}),H)):(!o||++u%nt===0)&&c()},H);window.addEventListener("message",n),c()})}function I(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),i=(o,u,l)=>{p={popup:o,result:u,href:l};let a=()=>{p&&p.popup===o&&(p=void 0)};return u.then(a,a),u},s=o=>(location.assign(o.href),{status:"redirected"});try{let{url:o,successUrl:u}=t;if(u&&!C(u,""))throw new Error("checkout: successUrl is not a web address");let l=r=>u?C(u,r.id):null,a=typeof o=="function"?null:E(o);if(typeof o!="function"&&!a)throw e();if(p&&!p.popup.closed&&(!a||p.href===a.href))return p.popup.focus(),p.result;if(a){let r=window.open(a.href,$(),D);return r?i(r,N(r,a,l(a)),a.href):Promise.resolve(s(a))}let n=window.open("",$(),D),c=new Promise(r=>r(o())).then(r=>{let d=E(r);if(!d)throw n?.close(),e();return n?(n.location.href=d.href,N(n,d,l(d))):s(d)},r=>{throw n?.close(),r});return n?i(n,c):c}catch(o){return Promise.reject(o)}}var B=!1,z;function j(){B||typeof document>"u"||(B=!0,document.addEventListener("click",t=>{let e=t.target instanceof Element?t.target.closest("[data-cd-checkout]"):null;if(!e||t.defaultPrevented||t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let i=e.getAttribute("data-cd-checkout")||e.href,s=e.getAttribute("data-cd-success");if(!E(i)||s&&!C(s,""))return;t.preventDefault();let o=I({url:i,successUrl:s});o!==z&&(z=o,o.then(u=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:u,bubbles:!0})),()=>{}))}))}var it={version:S,signIn:b,handleCallback:_,exchangeCode:A,checkout:I};window.ChainDaddy=Object.assign(window.ChainDaddy??{},it);L();P({origin:O(document.currentScript?.src)});j();})();
package/dist/cd.js.sri CHANGED
@@ -1 +1 @@
1
- sha384-oAzz7JY6TsxFo7MvOAoI5wWjgXMDQsM27deR2Rr9F1DCHjZMz+Tb0oN9LT46GSnm
1
+ sha384-fJEKHZn2Glw6r/O/phO2IcSENkiPJsnY5wmjK3psuPFMg86hzPzk7/Mvq07tNiGP
package/dist/core.d.ts CHANGED
@@ -6,9 +6,10 @@ export declare function randomToken(): string;
6
6
  /**
7
7
  * Listens for messages of `type` from exactly `origin`, on window and on a
8
8
  * same-origin BroadcastChannel (which still works when a COOP header has
9
- * severed window.opener). Returns a function that stops listening.
9
+ * severed window.opener). With `from`, a window message counts only when
10
+ * that window sent it. Returns a function that stops listening.
10
11
  */
11
- export declare function listen(type: string, origin: string, onMessage: (data: Record<string, unknown>) => void, channel?: string): () => void;
12
+ export declare function listen(type: string, origin: string, onMessage: (data: Record<string, unknown>) => void, channel?: string, from?: Window | null): () => void;
12
13
  /** sessionStorage that never throws (private windows, blocked storage). */
13
14
  export declare const store: {
14
15
  get(key: string): string | null;
package/dist/embed.js CHANGED
@@ -2,6 +2,8 @@
2
2
  var PROD_ORIGIN = "https://embed.chaindaddy.io";
3
3
  var STAGING_ORIGIN = "https://embed-staging.chaindaddy.io";
4
4
  var READY_TIMEOUT_MS = 8e3;
5
+ var NEAR = "1500px 0px";
6
+ var DOCS = "docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";
5
7
  var MAX_HEIGHT = 4e3;
6
8
  var THEME_TOKENS = ["--cd-accent", "--cd-surface", "--cd-text", "--cd-text-muted", "--cd-border"];
7
9
  function embedOriginFor(scriptSrc) {
@@ -48,6 +50,7 @@ function defineEmbedElements(options = {}) {
48
50
  try {
49
51
  window.removeEventListener("message", this.onMessage);
50
52
  clearTimeout(this.timer);
53
+ this.watch?.disconnect();
51
54
  } catch {
52
55
  }
53
56
  }
@@ -84,6 +87,7 @@ function defineEmbedElements(options = {}) {
84
87
  const root = this.shadowRoot;
85
88
  window.removeEventListener("message", this.onMessage);
86
89
  clearTimeout(this.timer);
90
+ this.watch?.disconnect();
87
91
  this.removeAttribute("data-cd-ready");
88
92
  this.removeAttribute("data-cd-unavailable");
89
93
  const src = this.src();
@@ -94,17 +98,43 @@ function defineEmbedElements(options = {}) {
94
98
  }
95
99
  root.innerHTML = `<style>${STYLE}</style><slot></slot>`;
96
100
  const frame = document.createElement("iframe");
97
- frame.setAttribute("loading", "lazy");
98
101
  frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
99
102
  frame.setAttribute("allow", "clipboard-write; fullscreen");
100
103
  frame.setAttribute("title", this.kind === "coin" ? "Coin on Chain Daddy" : "App on Chain Daddy");
101
- frame.src = src;
102
104
  root.appendChild(frame);
103
105
  this.frame = frame;
104
106
  window.addEventListener("message", this.onMessage);
105
- this.timer = setTimeout(() => {
106
- if (!this.hasAttribute("data-cd-ready")) this.unavailable();
107
- }, READY_TIMEOUT_MS);
107
+ const load = () => {
108
+ frame.src = src;
109
+ this.timer = setTimeout(() => {
110
+ if (!this.hasAttribute("data-cd-ready")) this.unavailable();
111
+ }, READY_TIMEOUT_MS);
112
+ };
113
+ if (typeof IntersectionObserver === "undefined") return load();
114
+ this.watch = new IntersectionObserver(
115
+ (entries) => {
116
+ if (!entries.some((e) => e.isIntersecting)) return;
117
+ this.watch?.disconnect();
118
+ load();
119
+ },
120
+ { rootMargin: NEAR }
121
+ );
122
+ this.watch.observe(this);
123
+ }
124
+ /** Says in the console why the link stayed: the page sees only that it did. */
125
+ why() {
126
+ try {
127
+ const q = new URL(this.frame.src).searchParams;
128
+ const app = q.get("app");
129
+ q.delete("mode");
130
+ q.delete("v");
131
+ fetch(`https://${origin === STAGING_ORIGIN ? "staging-api" : "api"}.chaindaddy.io/api/v2/embed/status?${q}`, { credentials: "omit" }).then((r) => r.json()).then((s) => {
132
+ const reason = !s.embeddable ? s.reason : s.interactive ? "not a verified site of the coin" : "not https, or the app did not start";
133
+ console.info(`[Chain Daddy] cd-${this.kind} ${app ?? ""} cannot show here: ${reason}. ${DOCS}`);
134
+ }).catch(() => {
135
+ });
136
+ } catch {
137
+ }
108
138
  }
109
139
  receive(event) {
110
140
  try {
@@ -143,6 +173,7 @@ function defineEmbedElements(options = {}) {
143
173
  if (this.hasAttribute("data-cd-unavailable")) return;
144
174
  this.setAttribute("data-cd-unavailable", "");
145
175
  this.dispatchEvent(new CustomEvent("cd:unavailable"));
176
+ this.why();
146
177
  } catch {
147
178
  }
148
179
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/core.ts
2
- var VERSION = true ? "0.3.0" : "0.0.0";
2
+ var VERSION = true ? "0.3.2" : "0.0.0";
3
3
  function b64url(bytes) {
4
4
  let s = "";
5
5
  for (const b of bytes) s += String.fromCharCode(b);
@@ -8,7 +8,7 @@ function b64url(bytes) {
8
8
  function randomToken() {
9
9
  return b64url(crypto.getRandomValues(new Uint8Array(32)));
10
10
  }
11
- function listen(type, origin, onMessage, channel) {
11
+ function listen(type, origin, onMessage, channel, from) {
12
12
  const accept = (data) => {
13
13
  if (data && typeof data === "object" && data.type === type) {
14
14
  try {
@@ -18,7 +18,7 @@ function listen(type, origin, onMessage, channel) {
18
18
  }
19
19
  };
20
20
  const onWindow = (e) => {
21
- if (e.origin === origin) accept(e.data);
21
+ if (e.origin === origin && (!from || e.source === from)) accept(e.data);
22
22
  };
23
23
  window.addEventListener("message", onWindow);
24
24
  let bc = null;
@@ -116,7 +116,7 @@ function signIn(o) {
116
116
  else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
117
117
  else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
118
118
  else resolve(result(p, m.code));
119
- }, POPUP_NAME);
119
+ }, POPUP_NAME, popup);
120
120
  o.signal?.addEventListener("abort", () => {
121
121
  stop();
122
122
  reject(new Error("aborted"));
@@ -217,6 +217,8 @@ function defineSignInButton() {
217
217
  var PROD_ORIGIN = "https://embed.chaindaddy.io";
218
218
  var STAGING_ORIGIN = "https://embed-staging.chaindaddy.io";
219
219
  var READY_TIMEOUT_MS = 8e3;
220
+ var NEAR = "1500px 0px";
221
+ var DOCS = "docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";
220
222
  var MAX_HEIGHT = 4e3;
221
223
  var THEME_TOKENS = ["--cd-accent", "--cd-surface", "--cd-text", "--cd-text-muted", "--cd-border"];
222
224
  function embedOriginFor(scriptSrc) {
@@ -263,6 +265,7 @@ function defineEmbedElements(options = {}) {
263
265
  try {
264
266
  window.removeEventListener("message", this.onMessage);
265
267
  clearTimeout(this.timer);
268
+ this.watch?.disconnect();
266
269
  } catch {
267
270
  }
268
271
  }
@@ -299,6 +302,7 @@ function defineEmbedElements(options = {}) {
299
302
  const root = this.shadowRoot;
300
303
  window.removeEventListener("message", this.onMessage);
301
304
  clearTimeout(this.timer);
305
+ this.watch?.disconnect();
302
306
  this.removeAttribute("data-cd-ready");
303
307
  this.removeAttribute("data-cd-unavailable");
304
308
  const src = this.src();
@@ -309,17 +313,43 @@ function defineEmbedElements(options = {}) {
309
313
  }
310
314
  root.innerHTML = `<style>${STYLE2}</style><slot></slot>`;
311
315
  const frame = document.createElement("iframe");
312
- frame.setAttribute("loading", "lazy");
313
316
  frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
314
317
  frame.setAttribute("allow", "clipboard-write; fullscreen");
315
318
  frame.setAttribute("title", this.kind === "coin" ? "Coin on Chain Daddy" : "App on Chain Daddy");
316
- frame.src = src;
317
319
  root.appendChild(frame);
318
320
  this.frame = frame;
319
321
  window.addEventListener("message", this.onMessage);
320
- this.timer = setTimeout(() => {
321
- if (!this.hasAttribute("data-cd-ready")) this.unavailable();
322
- }, READY_TIMEOUT_MS);
322
+ const load = () => {
323
+ frame.src = src;
324
+ this.timer = setTimeout(() => {
325
+ if (!this.hasAttribute("data-cd-ready")) this.unavailable();
326
+ }, READY_TIMEOUT_MS);
327
+ };
328
+ if (typeof IntersectionObserver === "undefined") return load();
329
+ this.watch = new IntersectionObserver(
330
+ (entries) => {
331
+ if (!entries.some((e) => e.isIntersecting)) return;
332
+ this.watch?.disconnect();
333
+ load();
334
+ },
335
+ { rootMargin: NEAR }
336
+ );
337
+ this.watch.observe(this);
338
+ }
339
+ /** Says in the console why the link stayed: the page sees only that it did. */
340
+ why() {
341
+ try {
342
+ const q = new URL(this.frame.src).searchParams;
343
+ const app = q.get("app");
344
+ q.delete("mode");
345
+ q.delete("v");
346
+ fetch(`https://${origin === STAGING_ORIGIN ? "staging-api" : "api"}.chaindaddy.io/api/v2/embed/status?${q}`, { credentials: "omit" }).then((r) => r.json()).then((s) => {
347
+ const reason = !s.embeddable ? s.reason : s.interactive ? "not a verified site of the coin" : "not https, or the app did not start";
348
+ console.info(`[Chain Daddy] cd-${this.kind} ${app ?? ""} cannot show here: ${reason}. ${DOCS}`);
349
+ }).catch(() => {
350
+ });
351
+ } catch {
352
+ }
323
353
  }
324
354
  receive(event) {
325
355
  try {
@@ -358,6 +388,7 @@ function defineEmbedElements(options = {}) {
358
388
  if (this.hasAttribute("data-cd-unavailable")) return;
359
389
  this.setAttribute("data-cd-unavailable", "");
360
390
  this.dispatchEvent(new CustomEvent("cd:unavailable"));
391
+ this.why();
361
392
  } catch {
362
393
  }
363
394
  }
package/dist/signin.js CHANGED
@@ -7,7 +7,7 @@ function b64url(bytes) {
7
7
  function randomToken() {
8
8
  return b64url(crypto.getRandomValues(new Uint8Array(32)));
9
9
  }
10
- function listen(type, origin, onMessage, channel) {
10
+ function listen(type, origin, onMessage, channel, from) {
11
11
  const accept = (data) => {
12
12
  if (data && typeof data === "object" && data.type === type) {
13
13
  try {
@@ -17,7 +17,7 @@ function listen(type, origin, onMessage, channel) {
17
17
  }
18
18
  };
19
19
  const onWindow = (e) => {
20
- if (e.origin === origin) accept(e.data);
20
+ if (e.origin === origin && (!from || e.source === from)) accept(e.data);
21
21
  };
22
22
  window.addEventListener("message", onWindow);
23
23
  let bc = null;
@@ -115,7 +115,7 @@ function signIn(o) {
115
115
  else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
116
116
  else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
117
117
  else resolve(result(p, m.code));
118
- }, POPUP_NAME);
118
+ }, POPUP_NAME, popup);
119
119
  o.signal?.addEventListener("abort", () => {
120
120
  stop();
121
121
  reject(new Error("aborted"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaindaddy/js",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Chain Daddy for any website: Sign in with Chain Daddy (a popup and a button), a coin's apps on its own site (<cd-app>), checkout for a coin's store in a popup, and a server-side check that a store order was paid on chain",
5
5
  "homepage": "https://docs.chaindaddy.io/developer/javascript-sdk",
6
6
  "repository": {
@@ -60,6 +60,7 @@
60
60
  "devDependencies": {
61
61
  "esbuild": "^0.25.0",
62
62
  "jsdom": "^26.0.0",
63
+ "openapi-typescript": "7.13.0",
63
64
  "typescript": "^5.7.0",
64
65
  "vitest": "^3.2.0"
65
66
  },