@rift-finance/react 0.2.0 → 0.3.0
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/RiftProvider.d.ts +1 -1
- package/dist/RiftProvider.d.ts.map +1 -1
- package/dist/rift-react.cjs +1 -1
- package/dist/rift-react.cjs.map +1 -1
- package/dist/rift-react.js +170 -166
- package/dist/rift-react.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/RiftProvider.d.ts
CHANGED
|
@@ -29,6 +29,6 @@ interface RiftProviderProps extends RiftConfig {
|
|
|
29
29
|
autoOpen?: boolean;
|
|
30
30
|
persist?: boolean;
|
|
31
31
|
}
|
|
32
|
-
export declare function RiftProvider({ apiKey, widgetUrl, children, autoOpen, persist, }: RiftProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export declare function RiftProvider({ apiKey, environment, widgetUrl, children, autoOpen, persist, }: RiftProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
33
33
|
export {};
|
|
34
34
|
//# sourceMappingURL=RiftProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RiftProvider.d.ts","sourceRoot":"","sources":["../src/RiftProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,UAAU,EAAa,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"RiftProvider.d.ts","sourceRoot":"","sources":["../src/RiftProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,UAAU,EAAa,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAiCzE,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3C,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAID,wBAAgB,cAAc,IAAI,gBAAgB,CAQjD;AAED,UAAU,iBAAkB,SAAQ,UAAU;IAC5C,QAAQ,EAAE,SAAS,CAAC;IAGpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAInB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsBD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,WAAW,EACX,SAAS,EACT,QAAQ,EACR,QAAgB,EAChB,OAAc,GACf,EAAE,iBAAiB,2CAqRnB"}
|
package/dist/rift-react.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("react/jsx-runtime"),r=require("react");let
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("react/jsx-runtime"),r=require("react");let d=null,j=!1,D=[],x=null;const f=new Map;function Y(){return`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,9)}`}function G(e){if(typeof document>"u")return Promise.reject(new Error("Cannot mount refresh iframe outside the browser"));if(d&&j)return Promise.resolve();if(x=new URL(e.widgetUrl).origin,!d){d=document.createElement("iframe"),d.setAttribute("aria-hidden","true"),d.setAttribute("tabindex","-1"),d.title="Rift session refresh",d.style.cssText="position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;";const i=new URLSearchParams({key:e.apiKey,headless:"1"});d.src=`${e.widgetUrl.replace(/\/$/,"")}/?${i.toString()}`,document.body.appendChild(d),window.addEventListener("message",a=>{if(a.origin!==x)return;const t=a.data;if(!(!t||typeof t!="object"||typeof t.type!="string")&&t.type.startsWith("rift:")){if(t.type==="rift:ready"){j=!0,D.forEach(o=>o()),D=[];return}if(t.type==="rift:refresh-result"){const o=f.get(t.requestId);o&&(f.delete(t.requestId),o.resolve({accessToken:t.accessToken,expiresAt:t.expiresAt,expiresIn:t.expiresIn}));return}if(t.type==="rift:refresh-error"){const o=f.get(t.requestId);o&&(f.delete(t.requestId),o.reject(new Error(t.message||"Refresh failed")));return}if(t.type==="rift:logout-result"){const o=f.get(t.requestId);o&&(f.delete(t.requestId),o.resolve({accessToken:"",expiresAt:"",expiresIn:0}))}}})}return j?Promise.resolve():new Promise(i=>{D.push(i),setTimeout(()=>{if(!j){const a=D.shift();a&&a()}},8e3)})}async function z(e){if(await G(e),!d?.contentWindow||!x)throw new Error("Refresh iframe is not available");const i=Y();return new Promise((a,t)=>{f.set(i,{resolve:a,reject:t}),d.contentWindow.postMessage({type:"rift:refresh-request",requestId:i},x),setTimeout(()=>{const o=f.get(i);o&&(f.delete(i),o.reject(new Error("Refresh timed out")))},1e4)})}async function Z(e){try{if(await G(e),!d?.contentWindow||!x)return;const i=Y();await new Promise(a=>{f.set(i,{resolve:()=>a(),reject:()=>a()}),d.contentWindow.postMessage({type:"rift:logout-request",requestId:i},x),setTimeout(()=>{f.delete(i),a()},5e3)})}catch{}}const K={production:"https://widget.riftfi.xyz",sandbox:"https://widget.sandbox.riftfi.com"},W="rift:identity",ee=60,J=r.createContext(null);function V(){const e=r.useContext(J);if(!e)throw new Error("[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>");return e}function te(){if(typeof window>"u")return null;try{const e=localStorage.getItem(W);return e?JSON.parse(e):null}catch{return null}}function re(e){if(!(typeof window>"u"))try{e?localStorage.setItem(W,JSON.stringify(e)):localStorage.removeItem(W)}catch{}}function se({apiKey:e,environment:i,widgetUrl:a,children:t,autoOpen:o=!1,persist:h=!0}){const c=a||K[i??"production"],v=r.useMemo(()=>{try{return new URL(c).origin}catch{return c}},[c]),[y,R]=r.useState(null),[l,p]=r.useState(!1),[L,A]=r.useState("signin"),[_,b]=r.useState(!1),[C,U]=r.useState(null),[E,O]=r.useState(540),[S,M]=r.useState(0),k=r.useRef(null);k.current=y;const T=r.useRef(null),m=r.useCallback(s=>{R(s),h&&re(s?{user:s.user,address:s.address,btcAddress:s.btcAddress}:null)},[h]);r.useEffect(()=>{if(!h)return;const s=te();if(!s)return;let u=!0;return(async()=>{try{const n=await z({apiKey:e,widgetUrl:c});if(!u)return;m({user:s.user,address:s.address,btcAddress:s.btcAddress,accessToken:n.accessToken,expiresAt:n.expiresAt})}catch{if(!u)return;m(null)}})(),()=>{u=!1}},[]);const q=r.useCallback(s=>{A(s?.mode||"signin"),U(null),b(!1),M(u=>u+1),p(!0)},[]),P=r.useCallback(()=>{p(!1),b(!1)},[]),F=r.useCallback(async()=>{await Z({apiKey:e,widgetUrl:c}),m(null)},[e,c,m]),$=r.useCallback(async()=>{const s=k.current;if(!s)throw new Error("Not signed in");const u=s.expiresAt?new Date(s.expiresAt).getTime():null,n=Date.now();return!(!u||u-n<ee*1e3)&&s.accessToken?s.accessToken:(T.current||(T.current=(async()=>{try{const w=await z({apiKey:e,widgetUrl:c}),N=k.current;if(!N)throw new Error("Signed out during refresh");const Q={...N,accessToken:w.accessToken,expiresAt:w.expiresAt};return m(Q),w.accessToken}catch(w){throw m(null),w instanceof Error?w:new Error(String(w))}finally{T.current=null}})()),T.current)},[e,c,m]);r.useEffect(()=>{if(typeof window>"u")return;const s=u=>{if(u.origin!==v)return;const n=u.data;if(!(!n||typeof n!="object"||typeof n.type!="string")&&n.type.startsWith("rift:"))switch(n.type){case"rift:ready":l&&b(!0);break;case"rift:close":P();break;case"rift:resize":O(Math.max(360,Math.min(820,n.height+8)));break;case"rift:signin-success":{const g={user:n.user,address:n.address,btcAddress:n.btcAddress,accessToken:n.accessToken,expiresAt:n.expiresAt};m(g),p(!1);break}case"rift:signin-error":U(n.message);break}};return window.addEventListener("message",s),()=>window.removeEventListener("message",s)},[v,P,m,l]),r.useEffect(()=>{if(!(typeof document>"u")&&l){const s=document.documentElement.style.overflow;return document.documentElement.style.overflow="hidden",()=>{document.documentElement.style.overflow=s}}},[l]),r.useEffect(()=>{o&&!y&&q()},[]);const B=r.useMemo(()=>{const s=new URLSearchParams({key:e,mode:L,origin:typeof window<"u"?window.location.origin:"",t:String(S)});if(typeof document<"u"){const u=document.documentElement,n=u.getAttribute("data-theme");let g=null;n==="dark"||n==="light"?g=n:(u.classList.contains("dark")||document.body?.classList.contains("dark")||window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&(g="dark"),g&&s.set("theme",g)}return`${c.replace(/\/$/,"")}/?${s.toString()}`},[e,L,S,c]),H=r.useCallback(()=>{},[]),X=r.useMemo(()=>({apiKey:e,widgetUrl:c,user:y,isOpen:l,isReady:_,error:C,open:q,close:P,signOut:F,getAccessToken:$,_iframeSrc:B,_iframeHeight:E,_onIframeLoad:H}),[e,c,y,l,_,C,q,P,F,$,B,E,H]);return I.jsx(J.Provider,{value:X,children:t})}const ne="rgba(15,15,20,0.55)",ie=6,oe=480,ae=18;function ce({onSuccess:e,onError:i,onClose:a,maxHeight:t,maxWidth:o=oe,radius:h=ae,backdrop:c,backdropStyle:v,iframeStyle:y}){const{isOpen:R,isReady:l,error:p,close:L,user:A,_iframeSrc:_,_iframeHeight:b,_onIframeLoad:C}=V();if(r.useEffect(()=>{A&&e&&e(A)},[A,e]),r.useEffect(()=>{p&&i&&i(p)},[p,i]),r.useEffect(()=>{!R&&a&&a()},[R]),!R)return null;const U=c?.color??ne,E=c?.blur??ie,O=E>0?`blur(${E}px)`:void 0;let S=b,M;return typeof t=="number"?S=Math.min(b,t):typeof t=="string"&&(M=t),I.jsxs("div",{role:"dialog","aria-modal":"true","aria-label":"Sign in",onClick:k=>{k.target===k.currentTarget&&L()},style:{position:"fixed",inset:0,zIndex:2147483646,background:U,backdropFilter:O,WebkitBackdropFilter:O,display:"flex",alignItems:"center",justifyContent:"center",padding:16,animation:"rift-fade 180ms ease-out",...v},children:[I.jsx("style",{children:"@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }"}),!l&&I.jsx("div",{"aria-hidden":!0,style:{position:"absolute",color:"rgba(255,255,255,0.75)",fontSize:13,fontFamily:"Inter, ui-sans-serif, system-ui, sans-serif"},children:"Loading sign-in…"}),I.jsx("iframe",{src:_,onLoad:C,title:"Rift sign-in",allow:"publickey-credentials-get; identity-credentials-get",style:{border:0,background:"transparent",colorScheme:"light",width:"100%",maxWidth:o,height:S,maxHeight:M,borderRadius:h,boxShadow:"0 24px 60px -12px rgba(0,0,0,0.35)",transition:"height 200ms ease",opacity:l?1:0,...y}})]})}function ue(){const{user:e,isOpen:i,open:a,close:t,signOut:o,getAccessToken:h,error:c}=V();return{user:e,isAuthenticated:!!e,isOpen:i,open:a,close:t,signOut:o,getAccessToken:h,error:c}}exports.RiftAuth=ce;exports.RiftProvider=se;exports.useRift=ue;
|
|
2
2
|
//# sourceMappingURL=rift-react.cjs.map
|
package/dist/rift-react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rift-react.cjs","sources":["../src/silentRefresh.ts","../src/RiftProvider.tsx","../src/RiftAuth.tsx","../src/useRift.ts"],"sourcesContent":["/**\n * Silent-refresh bridge.\n *\n * The v2 backend session sits behind an httpOnly refresh cookie scoped\n * to the widget origin (widget.riftfi.xyz → service.riftfi.xyz). The\n * cookie cannot be read or sent from the merchant's own JS — only\n * widget-origin code can use it. So to refresh, we mount a HIDDEN\n * widget iframe in `?headless=1` mode and ask it (via postMessage) to\n * call /auth/refresh on our behalf. It posts the new access token back.\n *\n * This module owns that iframe as a singleton: we lazily create it on\n * the first refresh request, keep it alive across the page's lifetime,\n * and use a requestId-based pending map so concurrent refresh calls\n * dedupe to one network round trip.\n */\n\nlet iframe: HTMLIFrameElement | null = null;\nlet ready = false;\nlet readyResolvers: Array<() => void> = [];\nlet widgetOrigin: string | null = null;\n\ninterface Pending {\n resolve: (value: RefreshSuccess) => void;\n reject: (err: Error) => void;\n}\nconst pending = new Map<string, Pending>();\n\nexport interface RefreshSuccess {\n accessToken: string;\n expiresAt: string;\n expiresIn: number;\n}\n\nfunction uuid(): string {\n // Lightweight ID — doesn't need crypto strength, just unique per page.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction ensureMounted(opts: { apiKey: string; widgetUrl: string }): Promise<void> {\n if (typeof document === \"undefined\") {\n return Promise.reject(new Error(\"Cannot mount refresh iframe outside the browser\"));\n }\n if (iframe && ready) return Promise.resolve();\n\n widgetOrigin = new URL(opts.widgetUrl).origin;\n\n if (!iframe) {\n iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"aria-hidden\", \"true\");\n iframe.setAttribute(\"tabindex\", \"-1\");\n iframe.title = \"Rift session refresh\";\n iframe.style.cssText =\n \"position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;\";\n const params = new URLSearchParams({\n key: opts.apiKey,\n headless: \"1\",\n });\n iframe.src = `${opts.widgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n document.body.appendChild(iframe);\n\n window.addEventListener(\"message\", (e) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n if (data.type === \"rift:ready\") {\n ready = true;\n readyResolvers.forEach((r) => r());\n readyResolvers = [];\n return;\n }\n if (data.type === \"rift:refresh-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n expiresIn: data.expiresIn,\n });\n }\n return;\n }\n if (data.type === \"rift:refresh-error\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.reject(new Error(data.message || \"Refresh failed\"));\n }\n return;\n }\n if (data.type === \"rift:logout-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({ accessToken: \"\", expiresAt: \"\", expiresIn: 0 });\n }\n }\n });\n }\n\n if (ready) return Promise.resolve();\n return new Promise((resolve) => {\n readyResolvers.push(resolve);\n // Safety net: if the iframe somehow never posts ready (e.g. blocked\n // by browser privacy mode), reject after 8s so callers can surface\n // a useful error.\n setTimeout(() => {\n if (!ready) {\n const r = readyResolvers.shift();\n if (r) r();\n }\n }, 8000);\n });\n}\n\nexport async function silentRefresh(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<RefreshSuccess> {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) {\n throw new Error(\"Refresh iframe is not available\");\n }\n const requestId = uuid();\n return new Promise<RefreshSuccess>((resolve, reject) => {\n pending.set(requestId, { resolve, reject });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:refresh-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n const slot = pending.get(requestId);\n if (slot) {\n pending.delete(requestId);\n slot.reject(new Error(\"Refresh timed out\"));\n }\n }, 10000);\n });\n}\n\nexport async function silentLogout(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<void> {\n try {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) return;\n const requestId = uuid();\n await new Promise<void>((resolve) => {\n pending.set(requestId, {\n resolve: () => resolve(),\n reject: () => resolve(), // logout is idempotent — never reject\n });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:logout-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n pending.delete(requestId);\n resolve();\n }, 5000);\n });\n } catch {\n /* logout is best-effort */\n }\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport type { RiftConfig, RiftEvent, RiftMode, RiftUser } from \"./types\";\nimport { silentLogout, silentRefresh } from \"./silentRefresh\";\n\nconst DEFAULT_WIDGET_URL = \"https://widget.riftfi.xyz\";\n\n// v2 session-mode policy: the access token lives in memory only. We\n// persist a small \"identity hint\" (user id, address, btcAddress) so the\n// UI can render an authenticated state on hard reload, but the actual\n// access JWT is re-issued via the refresh cookie. Refresh tokens live\n// in an httpOnly cookie scoped to the widget origin — totally invisible\n// to this code, which is the whole point.\nconst IDENTITY_STORAGE_KEY = \"rift:identity\";\n\ninterface PersistedIdentity {\n user: string;\n address: string;\n btcAddress?: string;\n}\n\n// Refresh proactively this many seconds before the access token expires.\n// Keeps API calls from racing the actual expiry.\nconst REFRESH_LEEWAY_SECONDS = 60;\n\ninterface RiftContextValue {\n apiKey: string;\n widgetUrl: string;\n user: RiftUser | null;\n isOpen: boolean;\n isReady: boolean;\n error: string | null;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Returns a valid access token, refreshing silently if the current\n * one is missing or about to expire. Rejects if the user is signed\n * out or the refresh fails (in which case state is cleared and the\n * caller should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n _iframeSrc: string;\n _iframeHeight: number;\n _onIframeLoad: () => void;\n}\n\nconst RiftContext = createContext<RiftContextValue | null>(null);\n\nexport function useRiftContext(): RiftContextValue {\n const ctx = useContext(RiftContext);\n if (!ctx) {\n throw new Error(\n \"[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>\"\n );\n }\n return ctx;\n}\n\ninterface RiftProviderProps extends RiftConfig {\n children: ReactNode;\n // Auto-open the modal on mount. Most apps will leave this false and call\n // open() in response to a user clicking \"Sign in\".\n autoOpen?: boolean;\n // Restore the persisted identity (just the user id / address — never\n // the access token) on mount, then silently refresh to mint a token.\n // Default: true.\n persist?: boolean;\n}\n\nfunction loadIdentity(): PersistedIdentity | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(IDENTITY_STORAGE_KEY);\n return raw ? (JSON.parse(raw) as PersistedIdentity) : null;\n } catch {\n return null;\n }\n}\n\nfunction saveIdentity(id: PersistedIdentity | null) {\n if (typeof window === \"undefined\") return;\n try {\n if (id) localStorage.setItem(IDENTITY_STORAGE_KEY, JSON.stringify(id));\n else localStorage.removeItem(IDENTITY_STORAGE_KEY);\n } catch {\n /* private mode / quota — non-fatal */\n }\n}\n\nexport function RiftProvider({\n apiKey,\n widgetUrl,\n children,\n autoOpen = false,\n persist = true,\n}: RiftProviderProps) {\n const resolvedWidgetUrl = widgetUrl || DEFAULT_WIDGET_URL;\n const widgetOrigin = useMemo(() => {\n try {\n return new URL(resolvedWidgetUrl).origin;\n } catch {\n return resolvedWidgetUrl;\n }\n }, [resolvedWidgetUrl]);\n\n const [user, setUser] = useState<RiftUser | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [mode, setMode] = useState<RiftMode>(\"signin\");\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [iframeHeight, setIframeHeight] = useState(540);\n const [openToken, setOpenToken] = useState(0);\n\n // Hot ref to the current user — getAccessToken() reads from this so\n // it never closes over a stale React state snapshot.\n const userRef = useRef<RiftUser | null>(null);\n userRef.current = user;\n\n // Dedupe in-flight refreshes: if multiple API calls hit\n // getAccessToken() simultaneously and the token is stale, we only\n // want one network call.\n const refreshInFlight = useRef<Promise<string> | null>(null);\n\n const setAndPersist = useCallback(\n (next: RiftUser | null) => {\n setUser(next);\n if (persist) {\n saveIdentity(\n next\n ? {\n user: next.user,\n address: next.address,\n btcAddress: next.btcAddress,\n }\n : null\n );\n }\n },\n [persist]\n );\n\n // On mount, if we have a persisted identity, try a silent refresh to\n // rehydrate the access token. If it fails, drop the identity — the\n // user will be prompted to sign in again on first action.\n useEffect(() => {\n if (!persist) return;\n const identity = loadIdentity();\n if (!identity) return;\n let alive = true;\n (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n if (!alive) return;\n setAndPersist({\n user: identity.user,\n address: identity.address,\n btcAddress: identity.btcAddress,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n });\n } catch {\n if (!alive) return;\n // Refresh failed — likely cookie expired or revoked. Clear the\n // identity hint so the UI shows the signed-out state.\n setAndPersist(null);\n }\n })();\n return () => {\n alive = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const open = useCallback((opts?: { mode?: RiftMode }) => {\n setMode(opts?.mode || \"signin\");\n setError(null);\n setIsReady(false);\n setOpenToken((t) => t + 1);\n setIsOpen(true);\n }, []);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setIsReady(false);\n }, []);\n\n const signOut = useCallback(async () => {\n await silentLogout({ apiKey, widgetUrl: resolvedWidgetUrl });\n setAndPersist(null);\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n const getAccessToken = useCallback(async (): Promise<string> => {\n const current = userRef.current;\n if (!current) throw new Error(\"Not signed in\");\n\n const expiresAt = current.expiresAt\n ? new Date(current.expiresAt).getTime()\n : null;\n const now = Date.now();\n const needsRefresh =\n !expiresAt || expiresAt - now < REFRESH_LEEWAY_SECONDS * 1000;\n\n if (!needsRefresh && current.accessToken) {\n return current.accessToken;\n }\n\n if (refreshInFlight.current) {\n return refreshInFlight.current;\n }\n\n refreshInFlight.current = (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n const latest = userRef.current;\n if (!latest) throw new Error(\"Signed out during refresh\");\n const next: RiftUser = {\n ...latest,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n };\n setAndPersist(next);\n return result.accessToken;\n } catch (err: any) {\n // Refresh failed — wipe state so the host UI can prompt re-auth.\n setAndPersist(null);\n throw err instanceof Error ? err : new Error(String(err));\n } finally {\n refreshInFlight.current = null;\n }\n })();\n return refreshInFlight.current;\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n // Listen for messages from the VISIBLE login iframe (not the silent\n // refresh one — that one's events are handled inside silentRefresh.ts).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handler = (e: MessageEvent) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data as RiftEvent | undefined;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n switch (data.type) {\n case \"rift:ready\":\n // Only treat as \"modal ready\" while it's open — the silent\n // refresh iframe also emits ready, but we don't care here.\n if (isOpen) setIsReady(true);\n break;\n case \"rift:close\":\n close();\n break;\n case \"rift:resize\":\n setIframeHeight(Math.max(360, Math.min(820, data.height + 8)));\n break;\n case \"rift:signin-success\": {\n const next: RiftUser = {\n user: data.user,\n address: data.address,\n btcAddress: data.btcAddress,\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n };\n setAndPersist(next);\n setIsOpen(false);\n break;\n }\n case \"rift:signin-error\":\n setError(data.message);\n break;\n // refresh / logout result events belong to silentRefresh.ts —\n // ignore them here.\n }\n };\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n }, [widgetOrigin, close, setAndPersist, isOpen]);\n\n // Lock host page scroll while the modal is open.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n if (isOpen) {\n const prev = document.documentElement.style.overflow;\n document.documentElement.style.overflow = \"hidden\";\n return () => {\n document.documentElement.style.overflow = prev;\n };\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (autoOpen && !user) open();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const iframeSrc = useMemo(() => {\n const params = new URLSearchParams({\n key: apiKey,\n mode,\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n t: String(openToken),\n });\n\n // Best-effort: match the host page's theme so the modal blends in\n // instead of flashing white over a dark site. Checks data-theme,\n // the `dark` class convention, then system preference.\n if (typeof document !== \"undefined\") {\n const html = document.documentElement;\n const attr = html.getAttribute(\"data-theme\");\n let theme: string | null = null;\n if (attr === \"dark\" || attr === \"light\") theme = attr;\n else if (\n html.classList.contains(\"dark\") ||\n document.body?.classList.contains(\"dark\")\n )\n theme = \"dark\";\n else if (\n window.matchMedia &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n )\n theme = \"dark\";\n if (theme) params.set(\"theme\", theme);\n }\n\n return `${resolvedWidgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n }, [apiKey, mode, openToken, resolvedWidgetUrl]);\n\n const onIframeLoad = useCallback(() => {\n /* readiness is signalled via postMessage, not the load event */\n }, []);\n\n const value = useMemo<RiftContextValue>(\n () => ({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n _iframeSrc: iframeSrc,\n _iframeHeight: iframeHeight,\n _onIframeLoad: onIframeLoad,\n }),\n [\n apiKey,\n resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n iframeSrc,\n iframeHeight,\n onIframeLoad,\n ]\n );\n\n return <RiftContext.Provider value={value}>{children}</RiftContext.Provider>;\n}\n","import { useEffect, type CSSProperties } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\n\ninterface BackdropStyle {\n /** Backdrop fill colour. Default `rgba(15,15,20,0.55)` (dark scrim). */\n color?: string;\n /** CSS `backdrop-filter: blur(<px>)`. Default 6, set 0 to disable. */\n blur?: number;\n}\n\ninterface RiftAuthProps {\n // Optional event hooks so callers don't have to compose useEffect by hand.\n onSuccess?: (user: RiftUser) => void;\n onError?: (message: string) => void;\n onClose?: () => void;\n\n /**\n * Cap the modal height. Pass a number for px (e.g. `600`) or a CSS\n * string for viewport units (`\"70vh\"`). The iframe scrolls\n * internally if its content exceeds this. Defaults to no cap; the\n * widget reports its natural height via postMessage.\n */\n maxHeight?: number | string;\n\n /**\n * Cap the modal width. Defaults to 480 (px). Pass any CSS length.\n */\n maxWidth?: number | string;\n\n /**\n * Corner radius on the modal. Defaults to 18 (px).\n */\n radius?: number | string;\n\n /**\n * Backdrop styling. See `BackdropStyle`. Each field falls back to\n * the default if omitted.\n */\n backdrop?: BackdropStyle;\n\n /**\n * Extra style applied to the backdrop wrapper. Use for things outside\n * the typed `backdrop` knob (custom transitions, z-index, etc.).\n */\n backdropStyle?: CSSProperties;\n\n /**\n * Extra style applied to the iframe. Useful for borders, custom\n * shadows, or filters that the typed props don't cover.\n */\n iframeStyle?: CSSProperties;\n}\n\nconst DEFAULT_BACKDROP_COLOR = \"rgba(15,15,20,0.55)\";\nconst DEFAULT_BACKDROP_BLUR = 6;\nconst DEFAULT_MAX_WIDTH = 480;\nconst DEFAULT_RADIUS = 18;\n\n/**\n * Renders the modal backdrop + iframe whenever the provider's `isOpen` is\n * true. Place this once near the root of your app (typically just inside\n * <RiftProvider>); call `useRift().open()` to show it.\n *\n * All visual knobs are overridable from the host: see `maxHeight`,\n * `maxWidth`, `radius`, `backdrop`, plus escape hatches `backdropStyle`\n * and `iframeStyle` for anything else.\n */\nexport function RiftAuth({\n onSuccess,\n onError,\n onClose,\n maxHeight,\n maxWidth = DEFAULT_MAX_WIDTH,\n radius = DEFAULT_RADIUS,\n backdrop,\n backdropStyle,\n iframeStyle,\n}: RiftAuthProps) {\n const {\n isOpen,\n isReady,\n error,\n close,\n user,\n _iframeSrc,\n _iframeHeight,\n _onIframeLoad,\n } = useRiftContext();\n\n useEffect(() => {\n if (user && onSuccess) onSuccess(user);\n }, [user, onSuccess]);\n\n useEffect(() => {\n if (error && onError) onError(error);\n }, [error, onError]);\n\n useEffect(() => {\n if (!isOpen && onClose) onClose();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const backdropColor = backdrop?.color ?? DEFAULT_BACKDROP_COLOR;\n const backdropBlur = backdrop?.blur ?? DEFAULT_BACKDROP_BLUR;\n const blurCss = backdropBlur > 0 ? `blur(${backdropBlur}px)` : undefined;\n\n // Resolve the iframe's final height. The widget posts its desired\n // height via `rift:resize`; we honor it but clamp to `maxHeight` if\n // the host asked us to. For numeric maxHeight, we min() against the\n // reported height (so a fixed modal doesn't grow past it). For string\n // values like \"70vh\", we hand the limit to CSS via `maxHeight` and\n // let the browser do the math, but still cap our height attribute by\n // the reported natural height so we don't reserve unused space.\n let iframeHeight: number | string = _iframeHeight;\n let iframeMaxHeight: number | string | undefined;\n if (typeof maxHeight === \"number\") {\n iframeHeight = Math.min(_iframeHeight, maxHeight);\n } else if (typeof maxHeight === \"string\") {\n iframeMaxHeight = maxHeight;\n }\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Sign in\"\n onClick={(e) => {\n if (e.target === e.currentTarget) close();\n }}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 2147483646,\n background: backdropColor,\n backdropFilter: blurCss,\n WebkitBackdropFilter: blurCss,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\n ...backdropStyle,\n }}\n >\n <style>{`@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }`}</style>\n {!isReady && (\n <div\n aria-hidden\n style={{\n position: \"absolute\",\n color: \"rgba(255,255,255,0.75)\",\n fontSize: 13,\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n Loading sign-in…\n </div>\n )}\n <iframe\n src={_iframeSrc}\n onLoad={_onIframeLoad}\n title=\"Rift sign-in\"\n allow=\"publickey-credentials-get; identity-credentials-get\"\n style={{\n border: 0,\n background: \"transparent\",\n colorScheme: \"light\",\n width: \"100%\",\n maxWidth,\n height: iframeHeight,\n maxHeight: iframeMaxHeight,\n borderRadius: radius,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\n ...iframeStyle,\n }}\n />\n </div>\n );\n}\n","import { useRiftContext } from \"./RiftProvider\";\nimport type { RiftMode, RiftUser } from \"./types\";\n\ninterface UseRiftReturn {\n user: RiftUser | null;\n isAuthenticated: boolean;\n isOpen: boolean;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Async getter for a valid access token. Use this when calling Rift /\n * your backend — it returns the current token if fresh, or silently\n * refreshes via a hidden iframe if near expiry. Rejects when the user\n * isn't signed in or the refresh fails (in which case auth state is\n * cleared and the host should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n error: string | null;\n}\n\n/**\n * Read auth state and drive the widget from anywhere inside <RiftProvider>.\n *\n * const { user, isAuthenticated, open, signOut, getAccessToken } = useRift();\n * return isAuthenticated\n * ? <button onClick={signOut}>Sign out</button>\n * : <button onClick={() => open({ mode: 'signup' })}>Get started</button>;\n *\n * // When calling your backend with Rift's session JWT:\n * const token = await getAccessToken();\n * fetch('/api/my-thing', { headers: { Authorization: `Bearer ${token}` } });\n */\nexport function useRift(): UseRiftReturn {\n const { user, isOpen, open, close, signOut, getAccessToken, error } =\n useRiftContext();\n return {\n user,\n isAuthenticated: !!user,\n isOpen,\n open,\n close,\n signOut,\n getAccessToken,\n error,\n };\n}\n"],"names":["iframe","ready","readyResolvers","widgetOrigin","pending","uuid","ensureMounted","opts","params","e","data","r","slot","resolve","silentRefresh","requestId","reject","silentLogout","DEFAULT_WIDGET_URL","IDENTITY_STORAGE_KEY","REFRESH_LEEWAY_SECONDS","RiftContext","createContext","useRiftContext","ctx","useContext","loadIdentity","raw","saveIdentity","id","RiftProvider","apiKey","widgetUrl","children","autoOpen","persist","resolvedWidgetUrl","useMemo","user","setUser","useState","isOpen","setIsOpen","mode","setMode","isReady","setIsReady","error","setError","iframeHeight","setIframeHeight","openToken","setOpenToken","userRef","useRef","refreshInFlight","setAndPersist","useCallback","next","useEffect","identity","alive","result","open","t","close","signOut","getAccessToken","current","expiresAt","now","latest","err","handler","prev","iframeSrc","html","attr","theme","onIframeLoad","value","jsx","DEFAULT_BACKDROP_COLOR","DEFAULT_BACKDROP_BLUR","DEFAULT_MAX_WIDTH","DEFAULT_RADIUS","RiftAuth","onSuccess","onError","onClose","maxHeight","maxWidth","radius","backdrop","backdropStyle","iframeStyle","_iframeSrc","_iframeHeight","_onIframeLoad","backdropColor","backdropBlur","blurCss","iframeMaxHeight","jsxs","useRift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rift-react.cjs","sources":["../src/silentRefresh.ts","../src/RiftProvider.tsx","../src/RiftAuth.tsx","../src/useRift.ts"],"sourcesContent":["/**\n * Silent-refresh bridge.\n *\n * The v2 backend session sits behind an httpOnly refresh cookie scoped\n * to the widget origin (widget.riftfi.xyz → service.riftfi.xyz). The\n * cookie cannot be read or sent from the merchant's own JS — only\n * widget-origin code can use it. So to refresh, we mount a HIDDEN\n * widget iframe in `?headless=1` mode and ask it (via postMessage) to\n * call /auth/refresh on our behalf. It posts the new access token back.\n *\n * This module owns that iframe as a singleton: we lazily create it on\n * the first refresh request, keep it alive across the page's lifetime,\n * and use a requestId-based pending map so concurrent refresh calls\n * dedupe to one network round trip.\n */\n\nlet iframe: HTMLIFrameElement | null = null;\nlet ready = false;\nlet readyResolvers: Array<() => void> = [];\nlet widgetOrigin: string | null = null;\n\ninterface Pending {\n resolve: (value: RefreshSuccess) => void;\n reject: (err: Error) => void;\n}\nconst pending = new Map<string, Pending>();\n\nexport interface RefreshSuccess {\n accessToken: string;\n expiresAt: string;\n expiresIn: number;\n}\n\nfunction uuid(): string {\n // Lightweight ID — doesn't need crypto strength, just unique per page.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction ensureMounted(opts: { apiKey: string; widgetUrl: string }): Promise<void> {\n if (typeof document === \"undefined\") {\n return Promise.reject(new Error(\"Cannot mount refresh iframe outside the browser\"));\n }\n if (iframe && ready) return Promise.resolve();\n\n widgetOrigin = new URL(opts.widgetUrl).origin;\n\n if (!iframe) {\n iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"aria-hidden\", \"true\");\n iframe.setAttribute(\"tabindex\", \"-1\");\n iframe.title = \"Rift session refresh\";\n iframe.style.cssText =\n \"position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;\";\n const params = new URLSearchParams({\n key: opts.apiKey,\n headless: \"1\",\n });\n iframe.src = `${opts.widgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n document.body.appendChild(iframe);\n\n window.addEventListener(\"message\", (e) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n if (data.type === \"rift:ready\") {\n ready = true;\n readyResolvers.forEach((r) => r());\n readyResolvers = [];\n return;\n }\n if (data.type === \"rift:refresh-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n expiresIn: data.expiresIn,\n });\n }\n return;\n }\n if (data.type === \"rift:refresh-error\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.reject(new Error(data.message || \"Refresh failed\"));\n }\n return;\n }\n if (data.type === \"rift:logout-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({ accessToken: \"\", expiresAt: \"\", expiresIn: 0 });\n }\n }\n });\n }\n\n if (ready) return Promise.resolve();\n return new Promise((resolve) => {\n readyResolvers.push(resolve);\n // Safety net: if the iframe somehow never posts ready (e.g. blocked\n // by browser privacy mode), reject after 8s so callers can surface\n // a useful error.\n setTimeout(() => {\n if (!ready) {\n const r = readyResolvers.shift();\n if (r) r();\n }\n }, 8000);\n });\n}\n\nexport async function silentRefresh(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<RefreshSuccess> {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) {\n throw new Error(\"Refresh iframe is not available\");\n }\n const requestId = uuid();\n return new Promise<RefreshSuccess>((resolve, reject) => {\n pending.set(requestId, { resolve, reject });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:refresh-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n const slot = pending.get(requestId);\n if (slot) {\n pending.delete(requestId);\n slot.reject(new Error(\"Refresh timed out\"));\n }\n }, 10000);\n });\n}\n\nexport async function silentLogout(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<void> {\n try {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) return;\n const requestId = uuid();\n await new Promise<void>((resolve) => {\n pending.set(requestId, {\n resolve: () => resolve(),\n reject: () => resolve(), // logout is idempotent — never reject\n });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:logout-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n pending.delete(requestId);\n resolve();\n }, 5000);\n });\n } catch {\n /* logout is best-effort */\n }\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport type { RiftConfig, RiftEvent, RiftMode, RiftUser } from \"./types\";\nimport { silentLogout, silentRefresh } from \"./silentRefresh\";\n\n// Widget URL by environment. The widget is a separate Vite SPA that\n// gets built once per environment (the backend URL is baked into the\n// bundle), so picking the environment here is equivalent to picking\n// which deployed widget the iframe loads.\n//\n// Override either by passing `widgetUrl=` directly (wins over\n// `environment`), or self-host and point at your own URL.\nconst WIDGET_URL_BY_ENV: Record<\"production\" | \"sandbox\", string> = {\n production: \"https://widget.riftfi.xyz\",\n sandbox: \"https://widget.sandbox.riftfi.com\",\n};\n\n// v2 session-mode policy: the access token lives in memory only. We\n// persist a small \"identity hint\" (user id, address, btcAddress) so the\n// UI can render an authenticated state on hard reload, but the actual\n// access JWT is re-issued via the refresh cookie. Refresh tokens live\n// in an httpOnly cookie scoped to the widget origin — totally invisible\n// to this code, which is the whole point.\nconst IDENTITY_STORAGE_KEY = \"rift:identity\";\n\ninterface PersistedIdentity {\n user: string;\n address: string;\n btcAddress?: string;\n}\n\n// Refresh proactively this many seconds before the access token expires.\n// Keeps API calls from racing the actual expiry.\nconst REFRESH_LEEWAY_SECONDS = 60;\n\ninterface RiftContextValue {\n apiKey: string;\n widgetUrl: string;\n user: RiftUser | null;\n isOpen: boolean;\n isReady: boolean;\n error: string | null;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Returns a valid access token, refreshing silently if the current\n * one is missing or about to expire. Rejects if the user is signed\n * out or the refresh fails (in which case state is cleared and the\n * caller should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n _iframeSrc: string;\n _iframeHeight: number;\n _onIframeLoad: () => void;\n}\n\nconst RiftContext = createContext<RiftContextValue | null>(null);\n\nexport function useRiftContext(): RiftContextValue {\n const ctx = useContext(RiftContext);\n if (!ctx) {\n throw new Error(\n \"[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>\"\n );\n }\n return ctx;\n}\n\ninterface RiftProviderProps extends RiftConfig {\n children: ReactNode;\n // Auto-open the modal on mount. Most apps will leave this false and call\n // open() in response to a user clicking \"Sign in\".\n autoOpen?: boolean;\n // Restore the persisted identity (just the user id / address — never\n // the access token) on mount, then silently refresh to mint a token.\n // Default: true.\n persist?: boolean;\n}\n\nfunction loadIdentity(): PersistedIdentity | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(IDENTITY_STORAGE_KEY);\n return raw ? (JSON.parse(raw) as PersistedIdentity) : null;\n } catch {\n return null;\n }\n}\n\nfunction saveIdentity(id: PersistedIdentity | null) {\n if (typeof window === \"undefined\") return;\n try {\n if (id) localStorage.setItem(IDENTITY_STORAGE_KEY, JSON.stringify(id));\n else localStorage.removeItem(IDENTITY_STORAGE_KEY);\n } catch {\n /* private mode / quota — non-fatal */\n }\n}\n\nexport function RiftProvider({\n apiKey,\n environment,\n widgetUrl,\n children,\n autoOpen = false,\n persist = true,\n}: RiftProviderProps) {\n const resolvedWidgetUrl =\n widgetUrl || WIDGET_URL_BY_ENV[environment ?? \"production\"];\n const widgetOrigin = useMemo(() => {\n try {\n return new URL(resolvedWidgetUrl).origin;\n } catch {\n return resolvedWidgetUrl;\n }\n }, [resolvedWidgetUrl]);\n\n const [user, setUser] = useState<RiftUser | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [mode, setMode] = useState<RiftMode>(\"signin\");\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [iframeHeight, setIframeHeight] = useState(540);\n const [openToken, setOpenToken] = useState(0);\n\n // Hot ref to the current user — getAccessToken() reads from this so\n // it never closes over a stale React state snapshot.\n const userRef = useRef<RiftUser | null>(null);\n userRef.current = user;\n\n // Dedupe in-flight refreshes: if multiple API calls hit\n // getAccessToken() simultaneously and the token is stale, we only\n // want one network call.\n const refreshInFlight = useRef<Promise<string> | null>(null);\n\n const setAndPersist = useCallback(\n (next: RiftUser | null) => {\n setUser(next);\n if (persist) {\n saveIdentity(\n next\n ? {\n user: next.user,\n address: next.address,\n btcAddress: next.btcAddress,\n }\n : null\n );\n }\n },\n [persist]\n );\n\n // On mount, if we have a persisted identity, try a silent refresh to\n // rehydrate the access token. If it fails, drop the identity — the\n // user will be prompted to sign in again on first action.\n useEffect(() => {\n if (!persist) return;\n const identity = loadIdentity();\n if (!identity) return;\n let alive = true;\n (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n if (!alive) return;\n setAndPersist({\n user: identity.user,\n address: identity.address,\n btcAddress: identity.btcAddress,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n });\n } catch {\n if (!alive) return;\n // Refresh failed — likely cookie expired or revoked. Clear the\n // identity hint so the UI shows the signed-out state.\n setAndPersist(null);\n }\n })();\n return () => {\n alive = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const open = useCallback((opts?: { mode?: RiftMode }) => {\n setMode(opts?.mode || \"signin\");\n setError(null);\n setIsReady(false);\n setOpenToken((t) => t + 1);\n setIsOpen(true);\n }, []);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setIsReady(false);\n }, []);\n\n const signOut = useCallback(async () => {\n await silentLogout({ apiKey, widgetUrl: resolvedWidgetUrl });\n setAndPersist(null);\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n const getAccessToken = useCallback(async (): Promise<string> => {\n const current = userRef.current;\n if (!current) throw new Error(\"Not signed in\");\n\n const expiresAt = current.expiresAt\n ? new Date(current.expiresAt).getTime()\n : null;\n const now = Date.now();\n const needsRefresh =\n !expiresAt || expiresAt - now < REFRESH_LEEWAY_SECONDS * 1000;\n\n if (!needsRefresh && current.accessToken) {\n return current.accessToken;\n }\n\n if (refreshInFlight.current) {\n return refreshInFlight.current;\n }\n\n refreshInFlight.current = (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n const latest = userRef.current;\n if (!latest) throw new Error(\"Signed out during refresh\");\n const next: RiftUser = {\n ...latest,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n };\n setAndPersist(next);\n return result.accessToken;\n } catch (err: any) {\n // Refresh failed — wipe state so the host UI can prompt re-auth.\n setAndPersist(null);\n throw err instanceof Error ? err : new Error(String(err));\n } finally {\n refreshInFlight.current = null;\n }\n })();\n return refreshInFlight.current;\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n // Listen for messages from the VISIBLE login iframe (not the silent\n // refresh one — that one's events are handled inside silentRefresh.ts).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handler = (e: MessageEvent) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data as RiftEvent | undefined;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n switch (data.type) {\n case \"rift:ready\":\n // Only treat as \"modal ready\" while it's open — the silent\n // refresh iframe also emits ready, but we don't care here.\n if (isOpen) setIsReady(true);\n break;\n case \"rift:close\":\n close();\n break;\n case \"rift:resize\":\n setIframeHeight(Math.max(360, Math.min(820, data.height + 8)));\n break;\n case \"rift:signin-success\": {\n const next: RiftUser = {\n user: data.user,\n address: data.address,\n btcAddress: data.btcAddress,\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n };\n setAndPersist(next);\n setIsOpen(false);\n break;\n }\n case \"rift:signin-error\":\n setError(data.message);\n break;\n // refresh / logout result events belong to silentRefresh.ts —\n // ignore them here.\n }\n };\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n }, [widgetOrigin, close, setAndPersist, isOpen]);\n\n // Lock host page scroll while the modal is open.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n if (isOpen) {\n const prev = document.documentElement.style.overflow;\n document.documentElement.style.overflow = \"hidden\";\n return () => {\n document.documentElement.style.overflow = prev;\n };\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (autoOpen && !user) open();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const iframeSrc = useMemo(() => {\n const params = new URLSearchParams({\n key: apiKey,\n mode,\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n t: String(openToken),\n });\n\n // Best-effort: match the host page's theme so the modal blends in\n // instead of flashing white over a dark site. Checks data-theme,\n // the `dark` class convention, then system preference.\n if (typeof document !== \"undefined\") {\n const html = document.documentElement;\n const attr = html.getAttribute(\"data-theme\");\n let theme: string | null = null;\n if (attr === \"dark\" || attr === \"light\") theme = attr;\n else if (\n html.classList.contains(\"dark\") ||\n document.body?.classList.contains(\"dark\")\n )\n theme = \"dark\";\n else if (\n window.matchMedia &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n )\n theme = \"dark\";\n if (theme) params.set(\"theme\", theme);\n }\n\n return `${resolvedWidgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n }, [apiKey, mode, openToken, resolvedWidgetUrl]);\n\n const onIframeLoad = useCallback(() => {\n /* readiness is signalled via postMessage, not the load event */\n }, []);\n\n const value = useMemo<RiftContextValue>(\n () => ({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n _iframeSrc: iframeSrc,\n _iframeHeight: iframeHeight,\n _onIframeLoad: onIframeLoad,\n }),\n [\n apiKey,\n resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n iframeSrc,\n iframeHeight,\n onIframeLoad,\n ]\n );\n\n return <RiftContext.Provider value={value}>{children}</RiftContext.Provider>;\n}\n","import { useEffect, type CSSProperties } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\n\ninterface BackdropStyle {\n /** Backdrop fill colour. Default `rgba(15,15,20,0.55)` (dark scrim). */\n color?: string;\n /** CSS `backdrop-filter: blur(<px>)`. Default 6, set 0 to disable. */\n blur?: number;\n}\n\ninterface RiftAuthProps {\n // Optional event hooks so callers don't have to compose useEffect by hand.\n onSuccess?: (user: RiftUser) => void;\n onError?: (message: string) => void;\n onClose?: () => void;\n\n /**\n * Cap the modal height. Pass a number for px (e.g. `600`) or a CSS\n * string for viewport units (`\"70vh\"`). The iframe scrolls\n * internally if its content exceeds this. Defaults to no cap; the\n * widget reports its natural height via postMessage.\n */\n maxHeight?: number | string;\n\n /**\n * Cap the modal width. Defaults to 480 (px). Pass any CSS length.\n */\n maxWidth?: number | string;\n\n /**\n * Corner radius on the modal. Defaults to 18 (px).\n */\n radius?: number | string;\n\n /**\n * Backdrop styling. See `BackdropStyle`. Each field falls back to\n * the default if omitted.\n */\n backdrop?: BackdropStyle;\n\n /**\n * Extra style applied to the backdrop wrapper. Use for things outside\n * the typed `backdrop` knob (custom transitions, z-index, etc.).\n */\n backdropStyle?: CSSProperties;\n\n /**\n * Extra style applied to the iframe. Useful for borders, custom\n * shadows, or filters that the typed props don't cover.\n */\n iframeStyle?: CSSProperties;\n}\n\nconst DEFAULT_BACKDROP_COLOR = \"rgba(15,15,20,0.55)\";\nconst DEFAULT_BACKDROP_BLUR = 6;\nconst DEFAULT_MAX_WIDTH = 480;\nconst DEFAULT_RADIUS = 18;\n\n/**\n * Renders the modal backdrop + iframe whenever the provider's `isOpen` is\n * true. Place this once near the root of your app (typically just inside\n * <RiftProvider>); call `useRift().open()` to show it.\n *\n * All visual knobs are overridable from the host: see `maxHeight`,\n * `maxWidth`, `radius`, `backdrop`, plus escape hatches `backdropStyle`\n * and `iframeStyle` for anything else.\n */\nexport function RiftAuth({\n onSuccess,\n onError,\n onClose,\n maxHeight,\n maxWidth = DEFAULT_MAX_WIDTH,\n radius = DEFAULT_RADIUS,\n backdrop,\n backdropStyle,\n iframeStyle,\n}: RiftAuthProps) {\n const {\n isOpen,\n isReady,\n error,\n close,\n user,\n _iframeSrc,\n _iframeHeight,\n _onIframeLoad,\n } = useRiftContext();\n\n useEffect(() => {\n if (user && onSuccess) onSuccess(user);\n }, [user, onSuccess]);\n\n useEffect(() => {\n if (error && onError) onError(error);\n }, [error, onError]);\n\n useEffect(() => {\n if (!isOpen && onClose) onClose();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const backdropColor = backdrop?.color ?? DEFAULT_BACKDROP_COLOR;\n const backdropBlur = backdrop?.blur ?? DEFAULT_BACKDROP_BLUR;\n const blurCss = backdropBlur > 0 ? `blur(${backdropBlur}px)` : undefined;\n\n // Resolve the iframe's final height. The widget posts its desired\n // height via `rift:resize`; we honor it but clamp to `maxHeight` if\n // the host asked us to. For numeric maxHeight, we min() against the\n // reported height (so a fixed modal doesn't grow past it). For string\n // values like \"70vh\", we hand the limit to CSS via `maxHeight` and\n // let the browser do the math, but still cap our height attribute by\n // the reported natural height so we don't reserve unused space.\n let iframeHeight: number | string = _iframeHeight;\n let iframeMaxHeight: number | string | undefined;\n if (typeof maxHeight === \"number\") {\n iframeHeight = Math.min(_iframeHeight, maxHeight);\n } else if (typeof maxHeight === \"string\") {\n iframeMaxHeight = maxHeight;\n }\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Sign in\"\n onClick={(e) => {\n if (e.target === e.currentTarget) close();\n }}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 2147483646,\n background: backdropColor,\n backdropFilter: blurCss,\n WebkitBackdropFilter: blurCss,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\n ...backdropStyle,\n }}\n >\n <style>{`@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }`}</style>\n {!isReady && (\n <div\n aria-hidden\n style={{\n position: \"absolute\",\n color: \"rgba(255,255,255,0.75)\",\n fontSize: 13,\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n Loading sign-in…\n </div>\n )}\n <iframe\n src={_iframeSrc}\n onLoad={_onIframeLoad}\n title=\"Rift sign-in\"\n allow=\"publickey-credentials-get; identity-credentials-get\"\n style={{\n border: 0,\n background: \"transparent\",\n colorScheme: \"light\",\n width: \"100%\",\n maxWidth,\n height: iframeHeight,\n maxHeight: iframeMaxHeight,\n borderRadius: radius,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\n ...iframeStyle,\n }}\n />\n </div>\n );\n}\n","import { useRiftContext } from \"./RiftProvider\";\nimport type { RiftMode, RiftUser } from \"./types\";\n\ninterface UseRiftReturn {\n user: RiftUser | null;\n isAuthenticated: boolean;\n isOpen: boolean;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Async getter for a valid access token. Use this when calling Rift /\n * your backend — it returns the current token if fresh, or silently\n * refreshes via a hidden iframe if near expiry. Rejects when the user\n * isn't signed in or the refresh fails (in which case auth state is\n * cleared and the host should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n error: string | null;\n}\n\n/**\n * Read auth state and drive the widget from anywhere inside <RiftProvider>.\n *\n * const { user, isAuthenticated, open, signOut, getAccessToken } = useRift();\n * return isAuthenticated\n * ? <button onClick={signOut}>Sign out</button>\n * : <button onClick={() => open({ mode: 'signup' })}>Get started</button>;\n *\n * // When calling your backend with Rift's session JWT:\n * const token = await getAccessToken();\n * fetch('/api/my-thing', { headers: { Authorization: `Bearer ${token}` } });\n */\nexport function useRift(): UseRiftReturn {\n const { user, isOpen, open, close, signOut, getAccessToken, error } =\n useRiftContext();\n return {\n user,\n isAuthenticated: !!user,\n isOpen,\n open,\n close,\n signOut,\n getAccessToken,\n error,\n };\n}\n"],"names":["iframe","ready","readyResolvers","widgetOrigin","pending","uuid","ensureMounted","opts","params","e","data","r","slot","resolve","silentRefresh","requestId","reject","silentLogout","WIDGET_URL_BY_ENV","IDENTITY_STORAGE_KEY","REFRESH_LEEWAY_SECONDS","RiftContext","createContext","useRiftContext","ctx","useContext","loadIdentity","raw","saveIdentity","id","RiftProvider","apiKey","environment","widgetUrl","children","autoOpen","persist","resolvedWidgetUrl","useMemo","user","setUser","useState","isOpen","setIsOpen","mode","setMode","isReady","setIsReady","error","setError","iframeHeight","setIframeHeight","openToken","setOpenToken","userRef","useRef","refreshInFlight","setAndPersist","useCallback","next","useEffect","identity","alive","result","open","t","close","signOut","getAccessToken","current","expiresAt","now","latest","err","handler","prev","iframeSrc","html","attr","theme","onIframeLoad","value","jsx","DEFAULT_BACKDROP_COLOR","DEFAULT_BACKDROP_BLUR","DEFAULT_MAX_WIDTH","DEFAULT_RADIUS","RiftAuth","onSuccess","onError","onClose","maxHeight","maxWidth","radius","backdrop","backdropStyle","iframeStyle","_iframeSrc","_iframeHeight","_onIframeLoad","backdropColor","backdropBlur","blurCss","iframeMaxHeight","jsxs","useRift"],"mappings":"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"}
|
package/dist/rift-react.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
let d = null,
|
|
1
|
+
import { jsx as j, jsxs as re } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as ne, useMemo as $, useState as w, useRef as J, useCallback as A, useEffect as y, useContext as se } from "react";
|
|
3
|
+
let d = null, W = !1, q = [], R = null;
|
|
4
4
|
const u = /* @__PURE__ */ new Map();
|
|
5
5
|
function X() {
|
|
6
6
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -8,24 +8,24 @@ function X() {
|
|
|
8
8
|
function Q(e) {
|
|
9
9
|
if (typeof document > "u")
|
|
10
10
|
return Promise.reject(new Error("Cannot mount refresh iframe outside the browser"));
|
|
11
|
-
if (d &&
|
|
12
|
-
if (
|
|
11
|
+
if (d && W) return Promise.resolve();
|
|
12
|
+
if (R = new URL(e.widgetUrl).origin, !d) {
|
|
13
13
|
d = document.createElement("iframe"), d.setAttribute("aria-hidden", "true"), d.setAttribute("tabindex", "-1"), d.title = "Rift session refresh", d.style.cssText = "position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;";
|
|
14
|
-
const
|
|
14
|
+
const s = new URLSearchParams({
|
|
15
15
|
key: e.apiKey,
|
|
16
16
|
headless: "1"
|
|
17
17
|
});
|
|
18
|
-
d.src = `${e.widgetUrl.replace(/\/$/, "")}/?${
|
|
19
|
-
if (o.origin !==
|
|
18
|
+
d.src = `${e.widgetUrl.replace(/\/$/, "")}/?${s.toString()}`, document.body.appendChild(d), window.addEventListener("message", (o) => {
|
|
19
|
+
if (o.origin !== R) return;
|
|
20
20
|
const t = o.data;
|
|
21
21
|
if (!(!t || typeof t != "object" || typeof t.type != "string") && t.type.startsWith("rift:")) {
|
|
22
22
|
if (t.type === "rift:ready") {
|
|
23
|
-
|
|
23
|
+
W = !0, q.forEach((i) => i()), q = [];
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
if (t.type === "rift:refresh-result") {
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const i = u.get(t.requestId);
|
|
28
|
+
i && (u.delete(t.requestId), i.resolve({
|
|
29
29
|
accessToken: t.accessToken,
|
|
30
30
|
expiresAt: t.expiresAt,
|
|
31
31
|
expiresIn: t.expiresIn
|
|
@@ -33,102 +33,106 @@ function Q(e) {
|
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
if (t.type === "rift:refresh-error") {
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
const i = u.get(t.requestId);
|
|
37
|
+
i && (u.delete(t.requestId), i.reject(new Error(t.message || "Refresh failed")));
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
if (t.type === "rift:logout-result") {
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
const i = u.get(t.requestId);
|
|
42
|
+
i && (u.delete(t.requestId), i.resolve({ accessToken: "", expiresAt: "", expiresIn: 0 }));
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
if (!
|
|
50
|
-
const o =
|
|
47
|
+
return W ? Promise.resolve() : new Promise((s) => {
|
|
48
|
+
q.push(s), setTimeout(() => {
|
|
49
|
+
if (!W) {
|
|
50
|
+
const o = q.shift();
|
|
51
51
|
o && o();
|
|
52
52
|
}
|
|
53
53
|
}, 8e3);
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
async function
|
|
57
|
-
if (await Q(e), !d?.contentWindow || !
|
|
56
|
+
async function V(e) {
|
|
57
|
+
if (await Q(e), !d?.contentWindow || !R)
|
|
58
58
|
throw new Error("Refresh iframe is not available");
|
|
59
|
-
const
|
|
59
|
+
const s = X();
|
|
60
60
|
return new Promise((o, t) => {
|
|
61
|
-
u.set(
|
|
62
|
-
{ type: "rift:refresh-request", requestId:
|
|
63
|
-
|
|
61
|
+
u.set(s, { resolve: o, reject: t }), d.contentWindow.postMessage(
|
|
62
|
+
{ type: "rift:refresh-request", requestId: s },
|
|
63
|
+
R
|
|
64
64
|
), setTimeout(() => {
|
|
65
|
-
const
|
|
66
|
-
|
|
65
|
+
const i = u.get(s);
|
|
66
|
+
i && (u.delete(s), i.reject(new Error("Refresh timed out")));
|
|
67
67
|
}, 1e4);
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
-
async function
|
|
70
|
+
async function ie(e) {
|
|
71
71
|
try {
|
|
72
|
-
if (await Q(e), !d?.contentWindow || !
|
|
73
|
-
const
|
|
72
|
+
if (await Q(e), !d?.contentWindow || !R) return;
|
|
73
|
+
const s = X();
|
|
74
74
|
await new Promise((o) => {
|
|
75
|
-
u.set(
|
|
75
|
+
u.set(s, {
|
|
76
76
|
resolve: () => o(),
|
|
77
77
|
reject: () => o()
|
|
78
78
|
// logout is idempotent — never reject
|
|
79
79
|
}), d.contentWindow.postMessage(
|
|
80
|
-
{ type: "rift:logout-request", requestId:
|
|
81
|
-
|
|
80
|
+
{ type: "rift:logout-request", requestId: s },
|
|
81
|
+
R
|
|
82
82
|
), setTimeout(() => {
|
|
83
|
-
u.delete(
|
|
83
|
+
u.delete(s), o();
|
|
84
84
|
}, 5e3);
|
|
85
85
|
});
|
|
86
86
|
} catch {
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
const oe = {
|
|
90
|
+
production: "https://widget.riftfi.xyz",
|
|
91
|
+
sandbox: "https://widget.sandbox.riftfi.com"
|
|
92
|
+
}, B = "rift:identity", ae = 60, Z = ne(null);
|
|
93
|
+
function K() {
|
|
94
|
+
const e = se(Z);
|
|
92
95
|
if (!e)
|
|
93
96
|
throw new Error(
|
|
94
97
|
"[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>"
|
|
95
98
|
);
|
|
96
99
|
return e;
|
|
97
100
|
}
|
|
98
|
-
function
|
|
101
|
+
function ce() {
|
|
99
102
|
if (typeof window > "u") return null;
|
|
100
103
|
try {
|
|
101
|
-
const e = localStorage.getItem(
|
|
104
|
+
const e = localStorage.getItem(B);
|
|
102
105
|
return e ? JSON.parse(e) : null;
|
|
103
106
|
} catch {
|
|
104
107
|
return null;
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
|
-
function
|
|
110
|
+
function de(e) {
|
|
108
111
|
if (!(typeof window > "u"))
|
|
109
112
|
try {
|
|
110
|
-
e ? localStorage.setItem(
|
|
113
|
+
e ? localStorage.setItem(B, JSON.stringify(e)) : localStorage.removeItem(B);
|
|
111
114
|
} catch {
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
|
-
function
|
|
117
|
+
function he({
|
|
115
118
|
apiKey: e,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
environment: s,
|
|
120
|
+
widgetUrl: o,
|
|
121
|
+
children: t,
|
|
122
|
+
autoOpen: i = !1,
|
|
123
|
+
persist: g = !0
|
|
120
124
|
}) {
|
|
121
|
-
const a =
|
|
125
|
+
const a = o || oe[s ?? "production"], L = $(() => {
|
|
122
126
|
try {
|
|
123
127
|
return new URL(a).origin;
|
|
124
128
|
} catch {
|
|
125
129
|
return a;
|
|
126
130
|
}
|
|
127
|
-
}, [a]), [b,
|
|
128
|
-
|
|
129
|
-
const
|
|
131
|
+
}, [a]), [b, E] = w(null), [f, p] = w(!1), [_, I] = w("signin"), [U, k] = w(!1), [O, C] = w(null), [T, D] = w(540), [v, M] = w(0), x = J(null);
|
|
132
|
+
x.current = b;
|
|
133
|
+
const S = J(null), l = A(
|
|
130
134
|
(r) => {
|
|
131
|
-
|
|
135
|
+
E(r), g && de(
|
|
132
136
|
r ? {
|
|
133
137
|
user: r.user,
|
|
134
138
|
address: r.address,
|
|
@@ -136,16 +140,16 @@ function pe({
|
|
|
136
140
|
} : null
|
|
137
141
|
);
|
|
138
142
|
},
|
|
139
|
-
[
|
|
143
|
+
[g]
|
|
140
144
|
);
|
|
141
145
|
y(() => {
|
|
142
|
-
if (!
|
|
143
|
-
const r =
|
|
146
|
+
if (!g) return;
|
|
147
|
+
const r = ce();
|
|
144
148
|
if (!r) return;
|
|
145
149
|
let c = !0;
|
|
146
150
|
return (async () => {
|
|
147
151
|
try {
|
|
148
|
-
const
|
|
152
|
+
const n = await V({
|
|
149
153
|
apiKey: e,
|
|
150
154
|
widgetUrl: a
|
|
151
155
|
});
|
|
@@ -154,8 +158,8 @@ function pe({
|
|
|
154
158
|
user: r.user,
|
|
155
159
|
address: r.address,
|
|
156
160
|
btcAddress: r.btcAddress,
|
|
157
|
-
accessToken:
|
|
158
|
-
expiresAt:
|
|
161
|
+
accessToken: n.accessToken,
|
|
162
|
+
expiresAt: n.expiresAt
|
|
159
163
|
});
|
|
160
164
|
} catch {
|
|
161
165
|
if (!c) return;
|
|
@@ -165,70 +169,70 @@ function pe({
|
|
|
165
169
|
c = !1;
|
|
166
170
|
};
|
|
167
171
|
}, []);
|
|
168
|
-
const F =
|
|
169
|
-
|
|
170
|
-
}, []),
|
|
171
|
-
|
|
172
|
-
}, []), H =
|
|
173
|
-
await
|
|
174
|
-
}, [e, a, l]),
|
|
175
|
-
const r =
|
|
172
|
+
const F = A((r) => {
|
|
173
|
+
I(r?.mode || "signin"), C(null), k(!1), M((c) => c + 1), p(!0);
|
|
174
|
+
}, []), P = A(() => {
|
|
175
|
+
p(!1), k(!1);
|
|
176
|
+
}, []), H = A(async () => {
|
|
177
|
+
await ie({ apiKey: e, widgetUrl: a }), l(null);
|
|
178
|
+
}, [e, a, l]), N = A(async () => {
|
|
179
|
+
const r = x.current;
|
|
176
180
|
if (!r) throw new Error("Not signed in");
|
|
177
|
-
const c = r.expiresAt ? new Date(r.expiresAt).getTime() : null,
|
|
178
|
-
return !(!c || c -
|
|
181
|
+
const c = r.expiresAt ? new Date(r.expiresAt).getTime() : null, n = Date.now();
|
|
182
|
+
return !(!c || c - n < ae * 1e3) && r.accessToken ? r.accessToken : (S.current || (S.current = (async () => {
|
|
179
183
|
try {
|
|
180
|
-
const h = await
|
|
184
|
+
const h = await V({
|
|
181
185
|
apiKey: e,
|
|
182
186
|
widgetUrl: a
|
|
183
|
-
}),
|
|
184
|
-
if (!
|
|
185
|
-
const
|
|
186
|
-
...
|
|
187
|
+
}), G = x.current;
|
|
188
|
+
if (!G) throw new Error("Signed out during refresh");
|
|
189
|
+
const te = {
|
|
190
|
+
...G,
|
|
187
191
|
accessToken: h.accessToken,
|
|
188
192
|
expiresAt: h.expiresAt
|
|
189
193
|
};
|
|
190
|
-
return l(
|
|
194
|
+
return l(te), h.accessToken;
|
|
191
195
|
} catch (h) {
|
|
192
196
|
throw l(null), h instanceof Error ? h : new Error(String(h));
|
|
193
197
|
} finally {
|
|
194
|
-
|
|
198
|
+
S.current = null;
|
|
195
199
|
}
|
|
196
|
-
})()),
|
|
200
|
+
})()), S.current);
|
|
197
201
|
}, [e, a, l]);
|
|
198
202
|
y(() => {
|
|
199
203
|
if (typeof window > "u") return;
|
|
200
204
|
const r = (c) => {
|
|
201
|
-
if (c.origin !==
|
|
202
|
-
const
|
|
203
|
-
if (!(!
|
|
204
|
-
switch (
|
|
205
|
+
if (c.origin !== L) return;
|
|
206
|
+
const n = c.data;
|
|
207
|
+
if (!(!n || typeof n != "object" || typeof n.type != "string") && n.type.startsWith("rift:"))
|
|
208
|
+
switch (n.type) {
|
|
205
209
|
case "rift:ready":
|
|
206
|
-
f &&
|
|
210
|
+
f && k(!0);
|
|
207
211
|
break;
|
|
208
212
|
case "rift:close":
|
|
209
|
-
|
|
213
|
+
P();
|
|
210
214
|
break;
|
|
211
215
|
case "rift:resize":
|
|
212
|
-
|
|
216
|
+
D(Math.max(360, Math.min(820, n.height + 8)));
|
|
213
217
|
break;
|
|
214
218
|
case "rift:signin-success": {
|
|
215
|
-
const
|
|
216
|
-
user:
|
|
217
|
-
address:
|
|
218
|
-
btcAddress:
|
|
219
|
-
accessToken:
|
|
220
|
-
expiresAt:
|
|
219
|
+
const m = {
|
|
220
|
+
user: n.user,
|
|
221
|
+
address: n.address,
|
|
222
|
+
btcAddress: n.btcAddress,
|
|
223
|
+
accessToken: n.accessToken,
|
|
224
|
+
expiresAt: n.expiresAt
|
|
221
225
|
};
|
|
222
|
-
l(
|
|
226
|
+
l(m), p(!1);
|
|
223
227
|
break;
|
|
224
228
|
}
|
|
225
229
|
case "rift:signin-error":
|
|
226
|
-
|
|
230
|
+
C(n.message);
|
|
227
231
|
break;
|
|
228
232
|
}
|
|
229
233
|
};
|
|
230
234
|
return window.addEventListener("message", r), () => window.removeEventListener("message", r);
|
|
231
|
-
}, [
|
|
235
|
+
}, [L, P, l, f]), y(() => {
|
|
232
236
|
if (!(typeof document > "u") && f) {
|
|
233
237
|
const r = document.documentElement.style.overflow;
|
|
234
238
|
return document.documentElement.style.overflow = "hidden", () => {
|
|
@@ -236,113 +240,113 @@ function pe({
|
|
|
236
240
|
};
|
|
237
241
|
}
|
|
238
242
|
}, [f]), y(() => {
|
|
239
|
-
|
|
243
|
+
i && !b && F();
|
|
240
244
|
}, []);
|
|
241
|
-
const
|
|
245
|
+
const z = $(() => {
|
|
242
246
|
const r = new URLSearchParams({
|
|
243
247
|
key: e,
|
|
244
|
-
mode:
|
|
248
|
+
mode: _,
|
|
245
249
|
origin: typeof window < "u" ? window.location.origin : "",
|
|
246
|
-
t: String(
|
|
250
|
+
t: String(v)
|
|
247
251
|
});
|
|
248
252
|
if (typeof document < "u") {
|
|
249
|
-
const c = document.documentElement,
|
|
250
|
-
let
|
|
251
|
-
|
|
253
|
+
const c = document.documentElement, n = c.getAttribute("data-theme");
|
|
254
|
+
let m = null;
|
|
255
|
+
n === "dark" || n === "light" ? m = n : (c.classList.contains("dark") || document.body?.classList.contains("dark") || window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) && (m = "dark"), m && r.set("theme", m);
|
|
252
256
|
}
|
|
253
257
|
return `${a.replace(/\/$/, "")}/?${r.toString()}`;
|
|
254
|
-
}, [e,
|
|
255
|
-
}, []),
|
|
258
|
+
}, [e, _, v, a]), Y = A(() => {
|
|
259
|
+
}, []), ee = $(
|
|
256
260
|
() => ({
|
|
257
261
|
apiKey: e,
|
|
258
262
|
widgetUrl: a,
|
|
259
263
|
user: b,
|
|
260
264
|
isOpen: f,
|
|
261
|
-
isReady:
|
|
262
|
-
error:
|
|
265
|
+
isReady: U,
|
|
266
|
+
error: O,
|
|
263
267
|
open: F,
|
|
264
|
-
close:
|
|
268
|
+
close: P,
|
|
265
269
|
signOut: H,
|
|
266
|
-
getAccessToken:
|
|
267
|
-
_iframeSrc:
|
|
268
|
-
_iframeHeight:
|
|
269
|
-
_onIframeLoad:
|
|
270
|
+
getAccessToken: N,
|
|
271
|
+
_iframeSrc: z,
|
|
272
|
+
_iframeHeight: T,
|
|
273
|
+
_onIframeLoad: Y
|
|
270
274
|
}),
|
|
271
275
|
[
|
|
272
276
|
e,
|
|
273
277
|
a,
|
|
274
278
|
b,
|
|
275
279
|
f,
|
|
276
|
-
|
|
277
|
-
|
|
280
|
+
U,
|
|
281
|
+
O,
|
|
278
282
|
F,
|
|
279
|
-
|
|
283
|
+
P,
|
|
280
284
|
H,
|
|
281
|
-
B,
|
|
282
285
|
N,
|
|
283
|
-
|
|
284
|
-
|
|
286
|
+
z,
|
|
287
|
+
T,
|
|
288
|
+
Y
|
|
285
289
|
]
|
|
286
290
|
);
|
|
287
|
-
return /* @__PURE__ */
|
|
291
|
+
return /* @__PURE__ */ j(Z.Provider, { value: ee, children: t });
|
|
288
292
|
}
|
|
289
|
-
const
|
|
290
|
-
function
|
|
293
|
+
const ue = "rgba(15,15,20,0.55)", fe = 6, le = 480, me = 18;
|
|
294
|
+
function we({
|
|
291
295
|
onSuccess: e,
|
|
292
|
-
onError:
|
|
296
|
+
onError: s,
|
|
293
297
|
onClose: o,
|
|
294
298
|
maxHeight: t,
|
|
295
|
-
maxWidth:
|
|
296
|
-
radius:
|
|
297
|
-
backdrop:
|
|
298
|
-
backdropStyle:
|
|
299
|
-
iframeStyle:
|
|
299
|
+
maxWidth: i = le,
|
|
300
|
+
radius: g = me,
|
|
301
|
+
backdrop: a,
|
|
302
|
+
backdropStyle: L,
|
|
303
|
+
iframeStyle: b
|
|
300
304
|
}) {
|
|
301
305
|
const {
|
|
302
|
-
isOpen:
|
|
303
|
-
isReady:
|
|
304
|
-
error:
|
|
305
|
-
close:
|
|
306
|
-
user:
|
|
307
|
-
_iframeSrc:
|
|
308
|
-
_iframeHeight:
|
|
309
|
-
_onIframeLoad:
|
|
310
|
-
} =
|
|
306
|
+
isOpen: E,
|
|
307
|
+
isReady: f,
|
|
308
|
+
error: p,
|
|
309
|
+
close: _,
|
|
310
|
+
user: I,
|
|
311
|
+
_iframeSrc: U,
|
|
312
|
+
_iframeHeight: k,
|
|
313
|
+
_onIframeLoad: O
|
|
314
|
+
} = K();
|
|
311
315
|
if (y(() => {
|
|
312
|
-
|
|
313
|
-
}, [
|
|
314
|
-
|
|
315
|
-
}, [
|
|
316
|
-
!
|
|
317
|
-
}, [
|
|
318
|
-
const
|
|
319
|
-
let
|
|
320
|
-
return typeof t == "number" ?
|
|
316
|
+
I && e && e(I);
|
|
317
|
+
}, [I, e]), y(() => {
|
|
318
|
+
p && s && s(p);
|
|
319
|
+
}, [p, s]), y(() => {
|
|
320
|
+
!E && o && o();
|
|
321
|
+
}, [E]), !E) return null;
|
|
322
|
+
const C = a?.color ?? ue, T = a?.blur ?? fe, D = T > 0 ? `blur(${T}px)` : void 0;
|
|
323
|
+
let v = k, M;
|
|
324
|
+
return typeof t == "number" ? v = Math.min(k, t) : typeof t == "string" && (M = t), /* @__PURE__ */ re(
|
|
321
325
|
"div",
|
|
322
326
|
{
|
|
323
327
|
role: "dialog",
|
|
324
328
|
"aria-modal": "true",
|
|
325
329
|
"aria-label": "Sign in",
|
|
326
|
-
onClick: (
|
|
327
|
-
|
|
330
|
+
onClick: (x) => {
|
|
331
|
+
x.target === x.currentTarget && _();
|
|
328
332
|
},
|
|
329
333
|
style: {
|
|
330
334
|
position: "fixed",
|
|
331
335
|
inset: 0,
|
|
332
336
|
zIndex: 2147483646,
|
|
333
|
-
background:
|
|
334
|
-
backdropFilter:
|
|
335
|
-
WebkitBackdropFilter:
|
|
337
|
+
background: C,
|
|
338
|
+
backdropFilter: D,
|
|
339
|
+
WebkitBackdropFilter: D,
|
|
336
340
|
display: "flex",
|
|
337
341
|
alignItems: "center",
|
|
338
342
|
justifyContent: "center",
|
|
339
343
|
padding: 16,
|
|
340
344
|
animation: "rift-fade 180ms ease-out",
|
|
341
|
-
...
|
|
345
|
+
...L
|
|
342
346
|
},
|
|
343
347
|
children: [
|
|
344
|
-
/* @__PURE__ */
|
|
345
|
-
!
|
|
348
|
+
/* @__PURE__ */ j("style", { children: "@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }" }),
|
|
349
|
+
!f && /* @__PURE__ */ j(
|
|
346
350
|
"div",
|
|
347
351
|
{
|
|
348
352
|
"aria-hidden": !0,
|
|
@@ -355,11 +359,11 @@ function he({
|
|
|
355
359
|
children: "Loading sign-in…"
|
|
356
360
|
}
|
|
357
361
|
),
|
|
358
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ j(
|
|
359
363
|
"iframe",
|
|
360
364
|
{
|
|
361
|
-
src:
|
|
362
|
-
onLoad:
|
|
365
|
+
src: U,
|
|
366
|
+
onLoad: O,
|
|
363
367
|
title: "Rift sign-in",
|
|
364
368
|
allow: "publickey-credentials-get; identity-credentials-get",
|
|
365
369
|
style: {
|
|
@@ -367,14 +371,14 @@ function he({
|
|
|
367
371
|
background: "transparent",
|
|
368
372
|
colorScheme: "light",
|
|
369
373
|
width: "100%",
|
|
370
|
-
maxWidth:
|
|
371
|
-
height:
|
|
372
|
-
maxHeight:
|
|
373
|
-
borderRadius:
|
|
374
|
+
maxWidth: i,
|
|
375
|
+
height: v,
|
|
376
|
+
maxHeight: M,
|
|
377
|
+
borderRadius: g,
|
|
374
378
|
boxShadow: "0 24px 60px -12px rgba(0,0,0,0.35)",
|
|
375
379
|
transition: "height 200ms ease",
|
|
376
|
-
opacity:
|
|
377
|
-
...
|
|
380
|
+
opacity: f ? 1 : 0,
|
|
381
|
+
...b
|
|
378
382
|
}
|
|
379
383
|
}
|
|
380
384
|
)
|
|
@@ -382,22 +386,22 @@ function he({
|
|
|
382
386
|
}
|
|
383
387
|
);
|
|
384
388
|
}
|
|
385
|
-
function
|
|
386
|
-
const { user: e, isOpen:
|
|
389
|
+
function ye() {
|
|
390
|
+
const { user: e, isOpen: s, open: o, close: t, signOut: i, getAccessToken: g, error: a } = K();
|
|
387
391
|
return {
|
|
388
392
|
user: e,
|
|
389
393
|
isAuthenticated: !!e,
|
|
390
|
-
isOpen:
|
|
394
|
+
isOpen: s,
|
|
391
395
|
open: o,
|
|
392
396
|
close: t,
|
|
393
|
-
signOut:
|
|
394
|
-
getAccessToken:
|
|
395
|
-
error:
|
|
397
|
+
signOut: i,
|
|
398
|
+
getAccessToken: g,
|
|
399
|
+
error: a
|
|
396
400
|
};
|
|
397
401
|
}
|
|
398
402
|
export {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
403
|
+
we as RiftAuth,
|
|
404
|
+
he as RiftProvider,
|
|
405
|
+
ye as useRift
|
|
402
406
|
};
|
|
403
407
|
//# sourceMappingURL=rift-react.js.map
|
package/dist/rift-react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rift-react.js","sources":["../src/silentRefresh.ts","../src/RiftProvider.tsx","../src/RiftAuth.tsx","../src/useRift.ts"],"sourcesContent":["/**\n * Silent-refresh bridge.\n *\n * The v2 backend session sits behind an httpOnly refresh cookie scoped\n * to the widget origin (widget.riftfi.xyz → service.riftfi.xyz). The\n * cookie cannot be read or sent from the merchant's own JS — only\n * widget-origin code can use it. So to refresh, we mount a HIDDEN\n * widget iframe in `?headless=1` mode and ask it (via postMessage) to\n * call /auth/refresh on our behalf. It posts the new access token back.\n *\n * This module owns that iframe as a singleton: we lazily create it on\n * the first refresh request, keep it alive across the page's lifetime,\n * and use a requestId-based pending map so concurrent refresh calls\n * dedupe to one network round trip.\n */\n\nlet iframe: HTMLIFrameElement | null = null;\nlet ready = false;\nlet readyResolvers: Array<() => void> = [];\nlet widgetOrigin: string | null = null;\n\ninterface Pending {\n resolve: (value: RefreshSuccess) => void;\n reject: (err: Error) => void;\n}\nconst pending = new Map<string, Pending>();\n\nexport interface RefreshSuccess {\n accessToken: string;\n expiresAt: string;\n expiresIn: number;\n}\n\nfunction uuid(): string {\n // Lightweight ID — doesn't need crypto strength, just unique per page.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction ensureMounted(opts: { apiKey: string; widgetUrl: string }): Promise<void> {\n if (typeof document === \"undefined\") {\n return Promise.reject(new Error(\"Cannot mount refresh iframe outside the browser\"));\n }\n if (iframe && ready) return Promise.resolve();\n\n widgetOrigin = new URL(opts.widgetUrl).origin;\n\n if (!iframe) {\n iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"aria-hidden\", \"true\");\n iframe.setAttribute(\"tabindex\", \"-1\");\n iframe.title = \"Rift session refresh\";\n iframe.style.cssText =\n \"position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;\";\n const params = new URLSearchParams({\n key: opts.apiKey,\n headless: \"1\",\n });\n iframe.src = `${opts.widgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n document.body.appendChild(iframe);\n\n window.addEventListener(\"message\", (e) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n if (data.type === \"rift:ready\") {\n ready = true;\n readyResolvers.forEach((r) => r());\n readyResolvers = [];\n return;\n }\n if (data.type === \"rift:refresh-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n expiresIn: data.expiresIn,\n });\n }\n return;\n }\n if (data.type === \"rift:refresh-error\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.reject(new Error(data.message || \"Refresh failed\"));\n }\n return;\n }\n if (data.type === \"rift:logout-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({ accessToken: \"\", expiresAt: \"\", expiresIn: 0 });\n }\n }\n });\n }\n\n if (ready) return Promise.resolve();\n return new Promise((resolve) => {\n readyResolvers.push(resolve);\n // Safety net: if the iframe somehow never posts ready (e.g. blocked\n // by browser privacy mode), reject after 8s so callers can surface\n // a useful error.\n setTimeout(() => {\n if (!ready) {\n const r = readyResolvers.shift();\n if (r) r();\n }\n }, 8000);\n });\n}\n\nexport async function silentRefresh(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<RefreshSuccess> {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) {\n throw new Error(\"Refresh iframe is not available\");\n }\n const requestId = uuid();\n return new Promise<RefreshSuccess>((resolve, reject) => {\n pending.set(requestId, { resolve, reject });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:refresh-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n const slot = pending.get(requestId);\n if (slot) {\n pending.delete(requestId);\n slot.reject(new Error(\"Refresh timed out\"));\n }\n }, 10000);\n });\n}\n\nexport async function silentLogout(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<void> {\n try {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) return;\n const requestId = uuid();\n await new Promise<void>((resolve) => {\n pending.set(requestId, {\n resolve: () => resolve(),\n reject: () => resolve(), // logout is idempotent — never reject\n });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:logout-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n pending.delete(requestId);\n resolve();\n }, 5000);\n });\n } catch {\n /* logout is best-effort */\n }\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport type { RiftConfig, RiftEvent, RiftMode, RiftUser } from \"./types\";\nimport { silentLogout, silentRefresh } from \"./silentRefresh\";\n\nconst DEFAULT_WIDGET_URL = \"https://widget.riftfi.xyz\";\n\n// v2 session-mode policy: the access token lives in memory only. We\n// persist a small \"identity hint\" (user id, address, btcAddress) so the\n// UI can render an authenticated state on hard reload, but the actual\n// access JWT is re-issued via the refresh cookie. Refresh tokens live\n// in an httpOnly cookie scoped to the widget origin — totally invisible\n// to this code, which is the whole point.\nconst IDENTITY_STORAGE_KEY = \"rift:identity\";\n\ninterface PersistedIdentity {\n user: string;\n address: string;\n btcAddress?: string;\n}\n\n// Refresh proactively this many seconds before the access token expires.\n// Keeps API calls from racing the actual expiry.\nconst REFRESH_LEEWAY_SECONDS = 60;\n\ninterface RiftContextValue {\n apiKey: string;\n widgetUrl: string;\n user: RiftUser | null;\n isOpen: boolean;\n isReady: boolean;\n error: string | null;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Returns a valid access token, refreshing silently if the current\n * one is missing or about to expire. Rejects if the user is signed\n * out or the refresh fails (in which case state is cleared and the\n * caller should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n _iframeSrc: string;\n _iframeHeight: number;\n _onIframeLoad: () => void;\n}\n\nconst RiftContext = createContext<RiftContextValue | null>(null);\n\nexport function useRiftContext(): RiftContextValue {\n const ctx = useContext(RiftContext);\n if (!ctx) {\n throw new Error(\n \"[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>\"\n );\n }\n return ctx;\n}\n\ninterface RiftProviderProps extends RiftConfig {\n children: ReactNode;\n // Auto-open the modal on mount. Most apps will leave this false and call\n // open() in response to a user clicking \"Sign in\".\n autoOpen?: boolean;\n // Restore the persisted identity (just the user id / address — never\n // the access token) on mount, then silently refresh to mint a token.\n // Default: true.\n persist?: boolean;\n}\n\nfunction loadIdentity(): PersistedIdentity | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(IDENTITY_STORAGE_KEY);\n return raw ? (JSON.parse(raw) as PersistedIdentity) : null;\n } catch {\n return null;\n }\n}\n\nfunction saveIdentity(id: PersistedIdentity | null) {\n if (typeof window === \"undefined\") return;\n try {\n if (id) localStorage.setItem(IDENTITY_STORAGE_KEY, JSON.stringify(id));\n else localStorage.removeItem(IDENTITY_STORAGE_KEY);\n } catch {\n /* private mode / quota — non-fatal */\n }\n}\n\nexport function RiftProvider({\n apiKey,\n widgetUrl,\n children,\n autoOpen = false,\n persist = true,\n}: RiftProviderProps) {\n const resolvedWidgetUrl = widgetUrl || DEFAULT_WIDGET_URL;\n const widgetOrigin = useMemo(() => {\n try {\n return new URL(resolvedWidgetUrl).origin;\n } catch {\n return resolvedWidgetUrl;\n }\n }, [resolvedWidgetUrl]);\n\n const [user, setUser] = useState<RiftUser | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [mode, setMode] = useState<RiftMode>(\"signin\");\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [iframeHeight, setIframeHeight] = useState(540);\n const [openToken, setOpenToken] = useState(0);\n\n // Hot ref to the current user — getAccessToken() reads from this so\n // it never closes over a stale React state snapshot.\n const userRef = useRef<RiftUser | null>(null);\n userRef.current = user;\n\n // Dedupe in-flight refreshes: if multiple API calls hit\n // getAccessToken() simultaneously and the token is stale, we only\n // want one network call.\n const refreshInFlight = useRef<Promise<string> | null>(null);\n\n const setAndPersist = useCallback(\n (next: RiftUser | null) => {\n setUser(next);\n if (persist) {\n saveIdentity(\n next\n ? {\n user: next.user,\n address: next.address,\n btcAddress: next.btcAddress,\n }\n : null\n );\n }\n },\n [persist]\n );\n\n // On mount, if we have a persisted identity, try a silent refresh to\n // rehydrate the access token. If it fails, drop the identity — the\n // user will be prompted to sign in again on first action.\n useEffect(() => {\n if (!persist) return;\n const identity = loadIdentity();\n if (!identity) return;\n let alive = true;\n (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n if (!alive) return;\n setAndPersist({\n user: identity.user,\n address: identity.address,\n btcAddress: identity.btcAddress,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n });\n } catch {\n if (!alive) return;\n // Refresh failed — likely cookie expired or revoked. Clear the\n // identity hint so the UI shows the signed-out state.\n setAndPersist(null);\n }\n })();\n return () => {\n alive = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const open = useCallback((opts?: { mode?: RiftMode }) => {\n setMode(opts?.mode || \"signin\");\n setError(null);\n setIsReady(false);\n setOpenToken((t) => t + 1);\n setIsOpen(true);\n }, []);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setIsReady(false);\n }, []);\n\n const signOut = useCallback(async () => {\n await silentLogout({ apiKey, widgetUrl: resolvedWidgetUrl });\n setAndPersist(null);\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n const getAccessToken = useCallback(async (): Promise<string> => {\n const current = userRef.current;\n if (!current) throw new Error(\"Not signed in\");\n\n const expiresAt = current.expiresAt\n ? new Date(current.expiresAt).getTime()\n : null;\n const now = Date.now();\n const needsRefresh =\n !expiresAt || expiresAt - now < REFRESH_LEEWAY_SECONDS * 1000;\n\n if (!needsRefresh && current.accessToken) {\n return current.accessToken;\n }\n\n if (refreshInFlight.current) {\n return refreshInFlight.current;\n }\n\n refreshInFlight.current = (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n const latest = userRef.current;\n if (!latest) throw new Error(\"Signed out during refresh\");\n const next: RiftUser = {\n ...latest,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n };\n setAndPersist(next);\n return result.accessToken;\n } catch (err: any) {\n // Refresh failed — wipe state so the host UI can prompt re-auth.\n setAndPersist(null);\n throw err instanceof Error ? err : new Error(String(err));\n } finally {\n refreshInFlight.current = null;\n }\n })();\n return refreshInFlight.current;\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n // Listen for messages from the VISIBLE login iframe (not the silent\n // refresh one — that one's events are handled inside silentRefresh.ts).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handler = (e: MessageEvent) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data as RiftEvent | undefined;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n switch (data.type) {\n case \"rift:ready\":\n // Only treat as \"modal ready\" while it's open — the silent\n // refresh iframe also emits ready, but we don't care here.\n if (isOpen) setIsReady(true);\n break;\n case \"rift:close\":\n close();\n break;\n case \"rift:resize\":\n setIframeHeight(Math.max(360, Math.min(820, data.height + 8)));\n break;\n case \"rift:signin-success\": {\n const next: RiftUser = {\n user: data.user,\n address: data.address,\n btcAddress: data.btcAddress,\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n };\n setAndPersist(next);\n setIsOpen(false);\n break;\n }\n case \"rift:signin-error\":\n setError(data.message);\n break;\n // refresh / logout result events belong to silentRefresh.ts —\n // ignore them here.\n }\n };\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n }, [widgetOrigin, close, setAndPersist, isOpen]);\n\n // Lock host page scroll while the modal is open.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n if (isOpen) {\n const prev = document.documentElement.style.overflow;\n document.documentElement.style.overflow = \"hidden\";\n return () => {\n document.documentElement.style.overflow = prev;\n };\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (autoOpen && !user) open();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const iframeSrc = useMemo(() => {\n const params = new URLSearchParams({\n key: apiKey,\n mode,\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n t: String(openToken),\n });\n\n // Best-effort: match the host page's theme so the modal blends in\n // instead of flashing white over a dark site. Checks data-theme,\n // the `dark` class convention, then system preference.\n if (typeof document !== \"undefined\") {\n const html = document.documentElement;\n const attr = html.getAttribute(\"data-theme\");\n let theme: string | null = null;\n if (attr === \"dark\" || attr === \"light\") theme = attr;\n else if (\n html.classList.contains(\"dark\") ||\n document.body?.classList.contains(\"dark\")\n )\n theme = \"dark\";\n else if (\n window.matchMedia &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n )\n theme = \"dark\";\n if (theme) params.set(\"theme\", theme);\n }\n\n return `${resolvedWidgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n }, [apiKey, mode, openToken, resolvedWidgetUrl]);\n\n const onIframeLoad = useCallback(() => {\n /* readiness is signalled via postMessage, not the load event */\n }, []);\n\n const value = useMemo<RiftContextValue>(\n () => ({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n _iframeSrc: iframeSrc,\n _iframeHeight: iframeHeight,\n _onIframeLoad: onIframeLoad,\n }),\n [\n apiKey,\n resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n iframeSrc,\n iframeHeight,\n onIframeLoad,\n ]\n );\n\n return <RiftContext.Provider value={value}>{children}</RiftContext.Provider>;\n}\n","import { useEffect, type CSSProperties } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\n\ninterface BackdropStyle {\n /** Backdrop fill colour. Default `rgba(15,15,20,0.55)` (dark scrim). */\n color?: string;\n /** CSS `backdrop-filter: blur(<px>)`. Default 6, set 0 to disable. */\n blur?: number;\n}\n\ninterface RiftAuthProps {\n // Optional event hooks so callers don't have to compose useEffect by hand.\n onSuccess?: (user: RiftUser) => void;\n onError?: (message: string) => void;\n onClose?: () => void;\n\n /**\n * Cap the modal height. Pass a number for px (e.g. `600`) or a CSS\n * string for viewport units (`\"70vh\"`). The iframe scrolls\n * internally if its content exceeds this. Defaults to no cap; the\n * widget reports its natural height via postMessage.\n */\n maxHeight?: number | string;\n\n /**\n * Cap the modal width. Defaults to 480 (px). Pass any CSS length.\n */\n maxWidth?: number | string;\n\n /**\n * Corner radius on the modal. Defaults to 18 (px).\n */\n radius?: number | string;\n\n /**\n * Backdrop styling. See `BackdropStyle`. Each field falls back to\n * the default if omitted.\n */\n backdrop?: BackdropStyle;\n\n /**\n * Extra style applied to the backdrop wrapper. Use for things outside\n * the typed `backdrop` knob (custom transitions, z-index, etc.).\n */\n backdropStyle?: CSSProperties;\n\n /**\n * Extra style applied to the iframe. Useful for borders, custom\n * shadows, or filters that the typed props don't cover.\n */\n iframeStyle?: CSSProperties;\n}\n\nconst DEFAULT_BACKDROP_COLOR = \"rgba(15,15,20,0.55)\";\nconst DEFAULT_BACKDROP_BLUR = 6;\nconst DEFAULT_MAX_WIDTH = 480;\nconst DEFAULT_RADIUS = 18;\n\n/**\n * Renders the modal backdrop + iframe whenever the provider's `isOpen` is\n * true. Place this once near the root of your app (typically just inside\n * <RiftProvider>); call `useRift().open()` to show it.\n *\n * All visual knobs are overridable from the host: see `maxHeight`,\n * `maxWidth`, `radius`, `backdrop`, plus escape hatches `backdropStyle`\n * and `iframeStyle` for anything else.\n */\nexport function RiftAuth({\n onSuccess,\n onError,\n onClose,\n maxHeight,\n maxWidth = DEFAULT_MAX_WIDTH,\n radius = DEFAULT_RADIUS,\n backdrop,\n backdropStyle,\n iframeStyle,\n}: RiftAuthProps) {\n const {\n isOpen,\n isReady,\n error,\n close,\n user,\n _iframeSrc,\n _iframeHeight,\n _onIframeLoad,\n } = useRiftContext();\n\n useEffect(() => {\n if (user && onSuccess) onSuccess(user);\n }, [user, onSuccess]);\n\n useEffect(() => {\n if (error && onError) onError(error);\n }, [error, onError]);\n\n useEffect(() => {\n if (!isOpen && onClose) onClose();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const backdropColor = backdrop?.color ?? DEFAULT_BACKDROP_COLOR;\n const backdropBlur = backdrop?.blur ?? DEFAULT_BACKDROP_BLUR;\n const blurCss = backdropBlur > 0 ? `blur(${backdropBlur}px)` : undefined;\n\n // Resolve the iframe's final height. The widget posts its desired\n // height via `rift:resize`; we honor it but clamp to `maxHeight` if\n // the host asked us to. For numeric maxHeight, we min() against the\n // reported height (so a fixed modal doesn't grow past it). For string\n // values like \"70vh\", we hand the limit to CSS via `maxHeight` and\n // let the browser do the math, but still cap our height attribute by\n // the reported natural height so we don't reserve unused space.\n let iframeHeight: number | string = _iframeHeight;\n let iframeMaxHeight: number | string | undefined;\n if (typeof maxHeight === \"number\") {\n iframeHeight = Math.min(_iframeHeight, maxHeight);\n } else if (typeof maxHeight === \"string\") {\n iframeMaxHeight = maxHeight;\n }\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Sign in\"\n onClick={(e) => {\n if (e.target === e.currentTarget) close();\n }}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 2147483646,\n background: backdropColor,\n backdropFilter: blurCss,\n WebkitBackdropFilter: blurCss,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\n ...backdropStyle,\n }}\n >\n <style>{`@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }`}</style>\n {!isReady && (\n <div\n aria-hidden\n style={{\n position: \"absolute\",\n color: \"rgba(255,255,255,0.75)\",\n fontSize: 13,\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n Loading sign-in…\n </div>\n )}\n <iframe\n src={_iframeSrc}\n onLoad={_onIframeLoad}\n title=\"Rift sign-in\"\n allow=\"publickey-credentials-get; identity-credentials-get\"\n style={{\n border: 0,\n background: \"transparent\",\n colorScheme: \"light\",\n width: \"100%\",\n maxWidth,\n height: iframeHeight,\n maxHeight: iframeMaxHeight,\n borderRadius: radius,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\n ...iframeStyle,\n }}\n />\n </div>\n );\n}\n","import { useRiftContext } from \"./RiftProvider\";\nimport type { RiftMode, RiftUser } from \"./types\";\n\ninterface UseRiftReturn {\n user: RiftUser | null;\n isAuthenticated: boolean;\n isOpen: boolean;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Async getter for a valid access token. Use this when calling Rift /\n * your backend — it returns the current token if fresh, or silently\n * refreshes via a hidden iframe if near expiry. Rejects when the user\n * isn't signed in or the refresh fails (in which case auth state is\n * cleared and the host should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n error: string | null;\n}\n\n/**\n * Read auth state and drive the widget from anywhere inside <RiftProvider>.\n *\n * const { user, isAuthenticated, open, signOut, getAccessToken } = useRift();\n * return isAuthenticated\n * ? <button onClick={signOut}>Sign out</button>\n * : <button onClick={() => open({ mode: 'signup' })}>Get started</button>;\n *\n * // When calling your backend with Rift's session JWT:\n * const token = await getAccessToken();\n * fetch('/api/my-thing', { headers: { Authorization: `Bearer ${token}` } });\n */\nexport function useRift(): UseRiftReturn {\n const { user, isOpen, open, close, signOut, getAccessToken, error } =\n useRiftContext();\n return {\n user,\n isAuthenticated: !!user,\n isOpen,\n open,\n close,\n signOut,\n getAccessToken,\n error,\n };\n}\n"],"names":["iframe","ready","readyResolvers","widgetOrigin","pending","uuid","ensureMounted","opts","params","e","data","r","slot","resolve","silentRefresh","requestId","reject","silentLogout","DEFAULT_WIDGET_URL","IDENTITY_STORAGE_KEY","REFRESH_LEEWAY_SECONDS","RiftContext","createContext","useRiftContext","ctx","useContext","loadIdentity","raw","saveIdentity","id","RiftProvider","apiKey","widgetUrl","children","autoOpen","persist","resolvedWidgetUrl","useMemo","user","setUser","useState","isOpen","setIsOpen","mode","setMode","isReady","setIsReady","error","setError","iframeHeight","setIframeHeight","openToken","setOpenToken","userRef","useRef","refreshInFlight","setAndPersist","useCallback","next","useEffect","identity","alive","result","open","t","close","signOut","getAccessToken","current","expiresAt","now","latest","err","handler","prev","iframeSrc","html","attr","theme","onIframeLoad","value","jsx","DEFAULT_BACKDROP_COLOR","DEFAULT_BACKDROP_BLUR","DEFAULT_MAX_WIDTH","DEFAULT_RADIUS","RiftAuth","onSuccess","onError","onClose","maxHeight","maxWidth","radius","backdrop","backdropStyle","iframeStyle","_iframeSrc","_iframeHeight","_onIframeLoad","backdropColor","backdropBlur","blurCss","iframeMaxHeight","jsxs","useRift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rift-react.js","sources":["../src/silentRefresh.ts","../src/RiftProvider.tsx","../src/RiftAuth.tsx","../src/useRift.ts"],"sourcesContent":["/**\n * Silent-refresh bridge.\n *\n * The v2 backend session sits behind an httpOnly refresh cookie scoped\n * to the widget origin (widget.riftfi.xyz → service.riftfi.xyz). The\n * cookie cannot be read or sent from the merchant's own JS — only\n * widget-origin code can use it. So to refresh, we mount a HIDDEN\n * widget iframe in `?headless=1` mode and ask it (via postMessage) to\n * call /auth/refresh on our behalf. It posts the new access token back.\n *\n * This module owns that iframe as a singleton: we lazily create it on\n * the first refresh request, keep it alive across the page's lifetime,\n * and use a requestId-based pending map so concurrent refresh calls\n * dedupe to one network round trip.\n */\n\nlet iframe: HTMLIFrameElement | null = null;\nlet ready = false;\nlet readyResolvers: Array<() => void> = [];\nlet widgetOrigin: string | null = null;\n\ninterface Pending {\n resolve: (value: RefreshSuccess) => void;\n reject: (err: Error) => void;\n}\nconst pending = new Map<string, Pending>();\n\nexport interface RefreshSuccess {\n accessToken: string;\n expiresAt: string;\n expiresIn: number;\n}\n\nfunction uuid(): string {\n // Lightweight ID — doesn't need crypto strength, just unique per page.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction ensureMounted(opts: { apiKey: string; widgetUrl: string }): Promise<void> {\n if (typeof document === \"undefined\") {\n return Promise.reject(new Error(\"Cannot mount refresh iframe outside the browser\"));\n }\n if (iframe && ready) return Promise.resolve();\n\n widgetOrigin = new URL(opts.widgetUrl).origin;\n\n if (!iframe) {\n iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"aria-hidden\", \"true\");\n iframe.setAttribute(\"tabindex\", \"-1\");\n iframe.title = \"Rift session refresh\";\n iframe.style.cssText =\n \"position:absolute;width:1px;height:1px;border:0;opacity:0;pointer-events:none;left:-9999px;top:-9999px;\";\n const params = new URLSearchParams({\n key: opts.apiKey,\n headless: \"1\",\n });\n iframe.src = `${opts.widgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n document.body.appendChild(iframe);\n\n window.addEventListener(\"message\", (e) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n if (data.type === \"rift:ready\") {\n ready = true;\n readyResolvers.forEach((r) => r());\n readyResolvers = [];\n return;\n }\n if (data.type === \"rift:refresh-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n expiresIn: data.expiresIn,\n });\n }\n return;\n }\n if (data.type === \"rift:refresh-error\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.reject(new Error(data.message || \"Refresh failed\"));\n }\n return;\n }\n if (data.type === \"rift:logout-result\") {\n const slot = pending.get(data.requestId);\n if (slot) {\n pending.delete(data.requestId);\n slot.resolve({ accessToken: \"\", expiresAt: \"\", expiresIn: 0 });\n }\n }\n });\n }\n\n if (ready) return Promise.resolve();\n return new Promise((resolve) => {\n readyResolvers.push(resolve);\n // Safety net: if the iframe somehow never posts ready (e.g. blocked\n // by browser privacy mode), reject after 8s so callers can surface\n // a useful error.\n setTimeout(() => {\n if (!ready) {\n const r = readyResolvers.shift();\n if (r) r();\n }\n }, 8000);\n });\n}\n\nexport async function silentRefresh(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<RefreshSuccess> {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) {\n throw new Error(\"Refresh iframe is not available\");\n }\n const requestId = uuid();\n return new Promise<RefreshSuccess>((resolve, reject) => {\n pending.set(requestId, { resolve, reject });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:refresh-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n const slot = pending.get(requestId);\n if (slot) {\n pending.delete(requestId);\n slot.reject(new Error(\"Refresh timed out\"));\n }\n }, 10000);\n });\n}\n\nexport async function silentLogout(opts: {\n apiKey: string;\n widgetUrl: string;\n}): Promise<void> {\n try {\n await ensureMounted(opts);\n if (!iframe?.contentWindow || !widgetOrigin) return;\n const requestId = uuid();\n await new Promise<void>((resolve) => {\n pending.set(requestId, {\n resolve: () => resolve(),\n reject: () => resolve(), // logout is idempotent — never reject\n });\n iframe!.contentWindow!.postMessage(\n { type: \"rift:logout-request\", requestId },\n widgetOrigin!\n );\n setTimeout(() => {\n pending.delete(requestId);\n resolve();\n }, 5000);\n });\n } catch {\n /* logout is best-effort */\n }\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport type { RiftConfig, RiftEvent, RiftMode, RiftUser } from \"./types\";\nimport { silentLogout, silentRefresh } from \"./silentRefresh\";\n\n// Widget URL by environment. The widget is a separate Vite SPA that\n// gets built once per environment (the backend URL is baked into the\n// bundle), so picking the environment here is equivalent to picking\n// which deployed widget the iframe loads.\n//\n// Override either by passing `widgetUrl=` directly (wins over\n// `environment`), or self-host and point at your own URL.\nconst WIDGET_URL_BY_ENV: Record<\"production\" | \"sandbox\", string> = {\n production: \"https://widget.riftfi.xyz\",\n sandbox: \"https://widget.sandbox.riftfi.com\",\n};\n\n// v2 session-mode policy: the access token lives in memory only. We\n// persist a small \"identity hint\" (user id, address, btcAddress) so the\n// UI can render an authenticated state on hard reload, but the actual\n// access JWT is re-issued via the refresh cookie. Refresh tokens live\n// in an httpOnly cookie scoped to the widget origin — totally invisible\n// to this code, which is the whole point.\nconst IDENTITY_STORAGE_KEY = \"rift:identity\";\n\ninterface PersistedIdentity {\n user: string;\n address: string;\n btcAddress?: string;\n}\n\n// Refresh proactively this many seconds before the access token expires.\n// Keeps API calls from racing the actual expiry.\nconst REFRESH_LEEWAY_SECONDS = 60;\n\ninterface RiftContextValue {\n apiKey: string;\n widgetUrl: string;\n user: RiftUser | null;\n isOpen: boolean;\n isReady: boolean;\n error: string | null;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Returns a valid access token, refreshing silently if the current\n * one is missing or about to expire. Rejects if the user is signed\n * out or the refresh fails (in which case state is cleared and the\n * caller should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n _iframeSrc: string;\n _iframeHeight: number;\n _onIframeLoad: () => void;\n}\n\nconst RiftContext = createContext<RiftContextValue | null>(null);\n\nexport function useRiftContext(): RiftContextValue {\n const ctx = useContext(RiftContext);\n if (!ctx) {\n throw new Error(\n \"[@rift/react] useRift() / <RiftAuth> must be used inside <RiftProvider>\"\n );\n }\n return ctx;\n}\n\ninterface RiftProviderProps extends RiftConfig {\n children: ReactNode;\n // Auto-open the modal on mount. Most apps will leave this false and call\n // open() in response to a user clicking \"Sign in\".\n autoOpen?: boolean;\n // Restore the persisted identity (just the user id / address — never\n // the access token) on mount, then silently refresh to mint a token.\n // Default: true.\n persist?: boolean;\n}\n\nfunction loadIdentity(): PersistedIdentity | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(IDENTITY_STORAGE_KEY);\n return raw ? (JSON.parse(raw) as PersistedIdentity) : null;\n } catch {\n return null;\n }\n}\n\nfunction saveIdentity(id: PersistedIdentity | null) {\n if (typeof window === \"undefined\") return;\n try {\n if (id) localStorage.setItem(IDENTITY_STORAGE_KEY, JSON.stringify(id));\n else localStorage.removeItem(IDENTITY_STORAGE_KEY);\n } catch {\n /* private mode / quota — non-fatal */\n }\n}\n\nexport function RiftProvider({\n apiKey,\n environment,\n widgetUrl,\n children,\n autoOpen = false,\n persist = true,\n}: RiftProviderProps) {\n const resolvedWidgetUrl =\n widgetUrl || WIDGET_URL_BY_ENV[environment ?? \"production\"];\n const widgetOrigin = useMemo(() => {\n try {\n return new URL(resolvedWidgetUrl).origin;\n } catch {\n return resolvedWidgetUrl;\n }\n }, [resolvedWidgetUrl]);\n\n const [user, setUser] = useState<RiftUser | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [mode, setMode] = useState<RiftMode>(\"signin\");\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [iframeHeight, setIframeHeight] = useState(540);\n const [openToken, setOpenToken] = useState(0);\n\n // Hot ref to the current user — getAccessToken() reads from this so\n // it never closes over a stale React state snapshot.\n const userRef = useRef<RiftUser | null>(null);\n userRef.current = user;\n\n // Dedupe in-flight refreshes: if multiple API calls hit\n // getAccessToken() simultaneously and the token is stale, we only\n // want one network call.\n const refreshInFlight = useRef<Promise<string> | null>(null);\n\n const setAndPersist = useCallback(\n (next: RiftUser | null) => {\n setUser(next);\n if (persist) {\n saveIdentity(\n next\n ? {\n user: next.user,\n address: next.address,\n btcAddress: next.btcAddress,\n }\n : null\n );\n }\n },\n [persist]\n );\n\n // On mount, if we have a persisted identity, try a silent refresh to\n // rehydrate the access token. If it fails, drop the identity — the\n // user will be prompted to sign in again on first action.\n useEffect(() => {\n if (!persist) return;\n const identity = loadIdentity();\n if (!identity) return;\n let alive = true;\n (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n if (!alive) return;\n setAndPersist({\n user: identity.user,\n address: identity.address,\n btcAddress: identity.btcAddress,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n });\n } catch {\n if (!alive) return;\n // Refresh failed — likely cookie expired or revoked. Clear the\n // identity hint so the UI shows the signed-out state.\n setAndPersist(null);\n }\n })();\n return () => {\n alive = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const open = useCallback((opts?: { mode?: RiftMode }) => {\n setMode(opts?.mode || \"signin\");\n setError(null);\n setIsReady(false);\n setOpenToken((t) => t + 1);\n setIsOpen(true);\n }, []);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setIsReady(false);\n }, []);\n\n const signOut = useCallback(async () => {\n await silentLogout({ apiKey, widgetUrl: resolvedWidgetUrl });\n setAndPersist(null);\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n const getAccessToken = useCallback(async (): Promise<string> => {\n const current = userRef.current;\n if (!current) throw new Error(\"Not signed in\");\n\n const expiresAt = current.expiresAt\n ? new Date(current.expiresAt).getTime()\n : null;\n const now = Date.now();\n const needsRefresh =\n !expiresAt || expiresAt - now < REFRESH_LEEWAY_SECONDS * 1000;\n\n if (!needsRefresh && current.accessToken) {\n return current.accessToken;\n }\n\n if (refreshInFlight.current) {\n return refreshInFlight.current;\n }\n\n refreshInFlight.current = (async () => {\n try {\n const result = await silentRefresh({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n });\n const latest = userRef.current;\n if (!latest) throw new Error(\"Signed out during refresh\");\n const next: RiftUser = {\n ...latest,\n accessToken: result.accessToken,\n expiresAt: result.expiresAt,\n };\n setAndPersist(next);\n return result.accessToken;\n } catch (err: any) {\n // Refresh failed — wipe state so the host UI can prompt re-auth.\n setAndPersist(null);\n throw err instanceof Error ? err : new Error(String(err));\n } finally {\n refreshInFlight.current = null;\n }\n })();\n return refreshInFlight.current;\n }, [apiKey, resolvedWidgetUrl, setAndPersist]);\n\n // Listen for messages from the VISIBLE login iframe (not the silent\n // refresh one — that one's events are handled inside silentRefresh.ts).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handler = (e: MessageEvent) => {\n if (e.origin !== widgetOrigin) return;\n const data = e.data as RiftEvent | undefined;\n if (!data || typeof data !== \"object\" || typeof data.type !== \"string\") return;\n if (!data.type.startsWith(\"rift:\")) return;\n\n switch (data.type) {\n case \"rift:ready\":\n // Only treat as \"modal ready\" while it's open — the silent\n // refresh iframe also emits ready, but we don't care here.\n if (isOpen) setIsReady(true);\n break;\n case \"rift:close\":\n close();\n break;\n case \"rift:resize\":\n setIframeHeight(Math.max(360, Math.min(820, data.height + 8)));\n break;\n case \"rift:signin-success\": {\n const next: RiftUser = {\n user: data.user,\n address: data.address,\n btcAddress: data.btcAddress,\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n };\n setAndPersist(next);\n setIsOpen(false);\n break;\n }\n case \"rift:signin-error\":\n setError(data.message);\n break;\n // refresh / logout result events belong to silentRefresh.ts —\n // ignore them here.\n }\n };\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n }, [widgetOrigin, close, setAndPersist, isOpen]);\n\n // Lock host page scroll while the modal is open.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n if (isOpen) {\n const prev = document.documentElement.style.overflow;\n document.documentElement.style.overflow = \"hidden\";\n return () => {\n document.documentElement.style.overflow = prev;\n };\n }\n }, [isOpen]);\n\n useEffect(() => {\n if (autoOpen && !user) open();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const iframeSrc = useMemo(() => {\n const params = new URLSearchParams({\n key: apiKey,\n mode,\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n t: String(openToken),\n });\n\n // Best-effort: match the host page's theme so the modal blends in\n // instead of flashing white over a dark site. Checks data-theme,\n // the `dark` class convention, then system preference.\n if (typeof document !== \"undefined\") {\n const html = document.documentElement;\n const attr = html.getAttribute(\"data-theme\");\n let theme: string | null = null;\n if (attr === \"dark\" || attr === \"light\") theme = attr;\n else if (\n html.classList.contains(\"dark\") ||\n document.body?.classList.contains(\"dark\")\n )\n theme = \"dark\";\n else if (\n window.matchMedia &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n )\n theme = \"dark\";\n if (theme) params.set(\"theme\", theme);\n }\n\n return `${resolvedWidgetUrl.replace(/\\/$/, \"\")}/?${params.toString()}`;\n }, [apiKey, mode, openToken, resolvedWidgetUrl]);\n\n const onIframeLoad = useCallback(() => {\n /* readiness is signalled via postMessage, not the load event */\n }, []);\n\n const value = useMemo<RiftContextValue>(\n () => ({\n apiKey,\n widgetUrl: resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n _iframeSrc: iframeSrc,\n _iframeHeight: iframeHeight,\n _onIframeLoad: onIframeLoad,\n }),\n [\n apiKey,\n resolvedWidgetUrl,\n user,\n isOpen,\n isReady,\n error,\n open,\n close,\n signOut,\n getAccessToken,\n iframeSrc,\n iframeHeight,\n onIframeLoad,\n ]\n );\n\n return <RiftContext.Provider value={value}>{children}</RiftContext.Provider>;\n}\n","import { useEffect, type CSSProperties } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\n\ninterface BackdropStyle {\n /** Backdrop fill colour. Default `rgba(15,15,20,0.55)` (dark scrim). */\n color?: string;\n /** CSS `backdrop-filter: blur(<px>)`. Default 6, set 0 to disable. */\n blur?: number;\n}\n\ninterface RiftAuthProps {\n // Optional event hooks so callers don't have to compose useEffect by hand.\n onSuccess?: (user: RiftUser) => void;\n onError?: (message: string) => void;\n onClose?: () => void;\n\n /**\n * Cap the modal height. Pass a number for px (e.g. `600`) or a CSS\n * string for viewport units (`\"70vh\"`). The iframe scrolls\n * internally if its content exceeds this. Defaults to no cap; the\n * widget reports its natural height via postMessage.\n */\n maxHeight?: number | string;\n\n /**\n * Cap the modal width. Defaults to 480 (px). Pass any CSS length.\n */\n maxWidth?: number | string;\n\n /**\n * Corner radius on the modal. Defaults to 18 (px).\n */\n radius?: number | string;\n\n /**\n * Backdrop styling. See `BackdropStyle`. Each field falls back to\n * the default if omitted.\n */\n backdrop?: BackdropStyle;\n\n /**\n * Extra style applied to the backdrop wrapper. Use for things outside\n * the typed `backdrop` knob (custom transitions, z-index, etc.).\n */\n backdropStyle?: CSSProperties;\n\n /**\n * Extra style applied to the iframe. Useful for borders, custom\n * shadows, or filters that the typed props don't cover.\n */\n iframeStyle?: CSSProperties;\n}\n\nconst DEFAULT_BACKDROP_COLOR = \"rgba(15,15,20,0.55)\";\nconst DEFAULT_BACKDROP_BLUR = 6;\nconst DEFAULT_MAX_WIDTH = 480;\nconst DEFAULT_RADIUS = 18;\n\n/**\n * Renders the modal backdrop + iframe whenever the provider's `isOpen` is\n * true. Place this once near the root of your app (typically just inside\n * <RiftProvider>); call `useRift().open()` to show it.\n *\n * All visual knobs are overridable from the host: see `maxHeight`,\n * `maxWidth`, `radius`, `backdrop`, plus escape hatches `backdropStyle`\n * and `iframeStyle` for anything else.\n */\nexport function RiftAuth({\n onSuccess,\n onError,\n onClose,\n maxHeight,\n maxWidth = DEFAULT_MAX_WIDTH,\n radius = DEFAULT_RADIUS,\n backdrop,\n backdropStyle,\n iframeStyle,\n}: RiftAuthProps) {\n const {\n isOpen,\n isReady,\n error,\n close,\n user,\n _iframeSrc,\n _iframeHeight,\n _onIframeLoad,\n } = useRiftContext();\n\n useEffect(() => {\n if (user && onSuccess) onSuccess(user);\n }, [user, onSuccess]);\n\n useEffect(() => {\n if (error && onError) onError(error);\n }, [error, onError]);\n\n useEffect(() => {\n if (!isOpen && onClose) onClose();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const backdropColor = backdrop?.color ?? DEFAULT_BACKDROP_COLOR;\n const backdropBlur = backdrop?.blur ?? DEFAULT_BACKDROP_BLUR;\n const blurCss = backdropBlur > 0 ? `blur(${backdropBlur}px)` : undefined;\n\n // Resolve the iframe's final height. The widget posts its desired\n // height via `rift:resize`; we honor it but clamp to `maxHeight` if\n // the host asked us to. For numeric maxHeight, we min() against the\n // reported height (so a fixed modal doesn't grow past it). For string\n // values like \"70vh\", we hand the limit to CSS via `maxHeight` and\n // let the browser do the math, but still cap our height attribute by\n // the reported natural height so we don't reserve unused space.\n let iframeHeight: number | string = _iframeHeight;\n let iframeMaxHeight: number | string | undefined;\n if (typeof maxHeight === \"number\") {\n iframeHeight = Math.min(_iframeHeight, maxHeight);\n } else if (typeof maxHeight === \"string\") {\n iframeMaxHeight = maxHeight;\n }\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Sign in\"\n onClick={(e) => {\n if (e.target === e.currentTarget) close();\n }}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 2147483646,\n background: backdropColor,\n backdropFilter: blurCss,\n WebkitBackdropFilter: blurCss,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\n ...backdropStyle,\n }}\n >\n <style>{`@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }`}</style>\n {!isReady && (\n <div\n aria-hidden\n style={{\n position: \"absolute\",\n color: \"rgba(255,255,255,0.75)\",\n fontSize: 13,\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n Loading sign-in…\n </div>\n )}\n <iframe\n src={_iframeSrc}\n onLoad={_onIframeLoad}\n title=\"Rift sign-in\"\n allow=\"publickey-credentials-get; identity-credentials-get\"\n style={{\n border: 0,\n background: \"transparent\",\n colorScheme: \"light\",\n width: \"100%\",\n maxWidth,\n height: iframeHeight,\n maxHeight: iframeMaxHeight,\n borderRadius: radius,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\n ...iframeStyle,\n }}\n />\n </div>\n );\n}\n","import { useRiftContext } from \"./RiftProvider\";\nimport type { RiftMode, RiftUser } from \"./types\";\n\ninterface UseRiftReturn {\n user: RiftUser | null;\n isAuthenticated: boolean;\n isOpen: boolean;\n open: (opts?: { mode?: RiftMode }) => void;\n close: () => void;\n signOut: () => Promise<void>;\n /**\n * Async getter for a valid access token. Use this when calling Rift /\n * your backend — it returns the current token if fresh, or silently\n * refreshes via a hidden iframe if near expiry. Rejects when the user\n * isn't signed in or the refresh fails (in which case auth state is\n * cleared and the host should prompt re-auth).\n */\n getAccessToken: () => Promise<string>;\n error: string | null;\n}\n\n/**\n * Read auth state and drive the widget from anywhere inside <RiftProvider>.\n *\n * const { user, isAuthenticated, open, signOut, getAccessToken } = useRift();\n * return isAuthenticated\n * ? <button onClick={signOut}>Sign out</button>\n * : <button onClick={() => open({ mode: 'signup' })}>Get started</button>;\n *\n * // When calling your backend with Rift's session JWT:\n * const token = await getAccessToken();\n * fetch('/api/my-thing', { headers: { Authorization: `Bearer ${token}` } });\n */\nexport function useRift(): UseRiftReturn {\n const { user, isOpen, open, close, signOut, getAccessToken, error } =\n useRiftContext();\n return {\n user,\n isAuthenticated: !!user,\n isOpen,\n open,\n close,\n signOut,\n getAccessToken,\n error,\n };\n}\n"],"names":["iframe","ready","readyResolvers","widgetOrigin","pending","uuid","ensureMounted","opts","params","e","data","r","slot","resolve","silentRefresh","requestId","reject","silentLogout","WIDGET_URL_BY_ENV","IDENTITY_STORAGE_KEY","REFRESH_LEEWAY_SECONDS","RiftContext","createContext","useRiftContext","ctx","useContext","loadIdentity","raw","saveIdentity","id","RiftProvider","apiKey","environment","widgetUrl","children","autoOpen","persist","resolvedWidgetUrl","useMemo","user","setUser","useState","isOpen","setIsOpen","mode","setMode","isReady","setIsReady","error","setError","iframeHeight","setIframeHeight","openToken","setOpenToken","userRef","useRef","refreshInFlight","setAndPersist","useCallback","next","useEffect","identity","alive","result","open","t","close","signOut","getAccessToken","current","expiresAt","now","latest","err","handler","prev","iframeSrc","html","attr","theme","onIframeLoad","value","jsx","DEFAULT_BACKDROP_COLOR","DEFAULT_BACKDROP_BLUR","DEFAULT_MAX_WIDTH","DEFAULT_RADIUS","RiftAuth","onSuccess","onError","onClose","maxHeight","maxWidth","radius","backdrop","backdropStyle","iframeStyle","_iframeSrc","_iframeHeight","_onIframeLoad","backdropColor","backdropBlur","blurCss","iframeMaxHeight","jsxs","useRift"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,WAAW,EAAE,MAAM,CAAC;IAGpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAEzB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,WAAW,EAAE,MAAM,CAAC;IAGpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAEzB,MAAM,EAAE,MAAM,CAAC;IAcf,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAGvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC9C;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC"}
|
package/package.json
CHANGED