@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/api.d.ts +626 -52
- package/dist/cd.js +4 -4
- package/dist/cd.js.sri +1 -1
- package/dist/core.d.ts +3 -2
- package/dist/embed.js +36 -5
- package/dist/index.js +40 -9
- package/dist/signin.js +3 -3
- package/package.json +2 -1
package/dist/cd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var S="0.3.
|
|
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
|
|
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
|
-
`,
|
|
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
|
|
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-
|
|
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).
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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.
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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.
|
|
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
|
},
|