@rift-finance/react 0.1.3 → 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/README.md +83 -13
- package/dist/RiftAuth.d.ts +42 -1
- package/dist/RiftAuth.d.ts.map +1 -1
- 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 +218 -192
- 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/README.md
CHANGED
|
@@ -79,14 +79,28 @@ async function send() {
|
|
|
79
79
|
|
|
80
80
|
### `<RiftAuth />`
|
|
81
81
|
|
|
82
|
-
| Prop | Type | Description |
|
|
83
|
-
|
|
84
|
-
| `onSuccess` | `(user) => void` | Fires when sign-in completes. |
|
|
85
|
-
| `onError` | `(message) => void` | Fires on auth failure. |
|
|
86
|
-
| `onClose` | `() => void` | Fires when the modal closes. |
|
|
82
|
+
| Prop | Type | Default | Description |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| `onSuccess` | `(user) => void` | — | Fires when sign-in completes. |
|
|
85
|
+
| `onError` | `(message) => void` | — | Fires on auth failure. |
|
|
86
|
+
| `onClose` | `() => void` | — | Fires when the modal closes. |
|
|
87
|
+
| `maxHeight` | `number \| string` | unset | Cap the modal height. Pass `600` (px) or `"70vh"`. iframe scrolls internally if content exceeds this. |
|
|
88
|
+
| `maxWidth` | `number \| string` | `480` | Cap the modal width. Any CSS length. |
|
|
89
|
+
| `radius` | `number \| string` | `18` | Modal corner radius. |
|
|
90
|
+
| `backdrop` | `{ color?: string; blur?: number }` | `{ color: "rgba(15,15,20,0.55)", blur: 6 }` | Backdrop fill and CSS `backdrop-filter` blur in px. Set `blur: 0` to disable. |
|
|
91
|
+
| `backdropStyle` | `CSSProperties` | — | Escape hatch for anything `backdrop` doesn't cover (custom transitions, z-index, etc.). |
|
|
92
|
+
| `iframeStyle` | `CSSProperties` | — | Escape hatch for the iframe (borders, custom shadows, filters). |
|
|
87
93
|
|
|
88
94
|
Mount this once near the root. It renders nothing until `useRift().open()` is called.
|
|
89
95
|
|
|
96
|
+
```tsx
|
|
97
|
+
<RiftAuth
|
|
98
|
+
maxHeight={600}
|
|
99
|
+
radius={22}
|
|
100
|
+
backdrop={{ color: "rgba(255,255,255,0.5)", blur: 10 }}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
90
104
|
### `useRift()`
|
|
91
105
|
|
|
92
106
|
| Field | Type | Description |
|
|
@@ -114,16 +128,72 @@ interface RiftUser {
|
|
|
114
128
|
|
|
115
129
|
## Auth methods
|
|
116
130
|
|
|
117
|
-
|
|
131
|
+
Google, Apple, email OTP, and phone OTP all surface automatically in the widget — zero setup. You don't register anything with Google or Apple; Rift handles all that under the hood. Drop `<RiftProvider>` + `<RiftAuth />` and the modal renders every available method.
|
|
118
132
|
|
|
133
|
+
## Theming the modal
|
|
134
|
+
|
|
135
|
+
The modal's chrome — backdrop colour, blur, modal width, height cap, corner radius — is controllable from the host via props on `<RiftAuth>`. The widget's content area is iframed and brand-locked to Rift's visual identity (this is intentional — it's a trust signal so users know they're signing into Rift, not a phishing page that just looks like it). But the **frame around the iframe** is yours to style.
|
|
136
|
+
|
|
137
|
+
### Why this exists
|
|
138
|
+
|
|
139
|
+
On large viewports the modal renders quite tall (~750px+ for the full email/phone/Google flow). Hosts with compact layouts or non-dark designs needed a way to:
|
|
140
|
+
|
|
141
|
+
- Cap the height so the modal doesn't dwarf the page.
|
|
142
|
+
- Swap the dark scrim for a light backdrop that matches their brand.
|
|
143
|
+
- Soften the blur (or disable it entirely on low-end devices).
|
|
144
|
+
- Round the modal more or less to match the rest of their UI.
|
|
145
|
+
|
|
146
|
+
Before 0.2.0, none of this was reachable — the styles were inline and the iframe ignored host CSS. Now they're typed props.
|
|
147
|
+
|
|
148
|
+
### The props
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<RiftAuth
|
|
152
|
+
// Numeric (px) or any CSS length. Iframe scrolls internally
|
|
153
|
+
// if widget content exceeds this. Default: no cap.
|
|
154
|
+
maxHeight={600} // or "70vh"
|
|
155
|
+
|
|
156
|
+
// Default 480 (px). Any CSS length works.
|
|
157
|
+
maxWidth={520}
|
|
158
|
+
|
|
159
|
+
// Default 18. Match the rest of your UI's roundness.
|
|
160
|
+
radius={22}
|
|
161
|
+
|
|
162
|
+
// Defaults: dark scrim with 6px blur.
|
|
163
|
+
backdrop={{
|
|
164
|
+
color: "rgba(255,255,255,0.5)",
|
|
165
|
+
blur: 10, // 0 disables backdrop-filter entirely
|
|
166
|
+
}}
|
|
167
|
+
|
|
168
|
+
// Escape hatches — spread last over the typed props above:
|
|
169
|
+
backdropStyle={{ animation: "myFadeIn 220ms ease" }}
|
|
170
|
+
iframeStyle={{ boxShadow: "0 4px 12px rgba(0,0,0,0.1)" }}
|
|
171
|
+
/>
|
|
119
172
|
```
|
|
120
|
-
1. Google Cloud Console → APIs & Services → Credentials → Create OAuth 2.0 Client ID
|
|
121
|
-
2. Authorized origins: https://yoursite.com
|
|
122
|
-
3. Copy the Client ID
|
|
123
|
-
4. Rift dashboard → your project → Auth → paste it → Save
|
|
124
|
-
```
|
|
125
173
|
|
|
126
|
-
|
|
174
|
+
### How `maxHeight` actually works
|
|
175
|
+
|
|
176
|
+
The widget posts its desired height to the SDK via `postMessage` (`rift:resize` events) whenever its content reflows — toggling between email and phone, showing an error, etc. Without `maxHeight`, the SDK sets the iframe to that reported height verbatim. With `maxHeight`, the SDK clamps:
|
|
177
|
+
|
|
178
|
+
- **Numeric `maxHeight`**: iframe `height` is `min(widget.reported, maxHeight)` — never grows past your cap.
|
|
179
|
+
- **String `maxHeight`** (e.g. `"70vh"`): iframe `height` stays at the widget's reported value but with `max-height` set in CSS so the browser does the clamp. Same effective behavior.
|
|
180
|
+
|
|
181
|
+
Either way, if the widget's content exceeds the cap, the iframe's native scrollbar takes over — no clipping, no layout breakage.
|
|
182
|
+
|
|
183
|
+
### Anything not exposed as a typed prop
|
|
184
|
+
|
|
185
|
+
Use `backdropStyle` and `iframeStyle`. They're spread **after** the typed props, so they override anything that conflicts. Useful for:
|
|
186
|
+
|
|
187
|
+
- Custom enter/exit animations (the default is a 180ms opacity fade)
|
|
188
|
+
- A z-index lower than the default `2147483646` if you're nesting under another high-z portal
|
|
189
|
+
- Borders, rings, gradient shadows on the modal that the `radius` prop alone can't express
|
|
190
|
+
- `transition` overrides on the iframe height — the default is `200ms ease`
|
|
191
|
+
|
|
192
|
+
These escape hatches keep the surface tight (no need to add a prop for every possible CSS knob) while letting any unusual host design ship without forking the SDK.
|
|
193
|
+
|
|
194
|
+
### Backwards compatibility
|
|
195
|
+
|
|
196
|
+
Every new prop is optional with the previous default. Apps on 0.1.x can upgrade to 0.2.0 with no code change and get the same modal they had before.
|
|
127
197
|
|
|
128
198
|
## Self-hosting the widget
|
|
129
199
|
|
|
@@ -138,7 +208,7 @@ The default widget URL is `https://widget.riftfi.xyz`. If you need to self-host
|
|
|
138
208
|
Integration walkthrough, OpenAPI spec, transaction signing flows: **https://service.riftfi.xyz/docs**
|
|
139
209
|
|
|
140
210
|
- Live API explorer: https://developers.riftfi.xyz
|
|
141
|
-
- OpenAPI spec: https://
|
|
211
|
+
- OpenAPI spec: https://github.com/Rift-FI/Rift-Sdk-Wrapper/blob/main/docs.json
|
|
142
212
|
- Source: https://github.com/Rift-FI/rift-react
|
|
143
213
|
|
|
144
214
|
## License
|
package/dist/RiftAuth.d.ts
CHANGED
|
@@ -1,14 +1,55 @@
|
|
|
1
|
+
import { CSSProperties } from 'react';
|
|
1
2
|
import { RiftUser } from './types';
|
|
3
|
+
interface BackdropStyle {
|
|
4
|
+
/** Backdrop fill colour. Default `rgba(15,15,20,0.55)` (dark scrim). */
|
|
5
|
+
color?: string;
|
|
6
|
+
/** CSS `backdrop-filter: blur(<px>)`. Default 6, set 0 to disable. */
|
|
7
|
+
blur?: number;
|
|
8
|
+
}
|
|
2
9
|
interface RiftAuthProps {
|
|
3
10
|
onSuccess?: (user: RiftUser) => void;
|
|
4
11
|
onError?: (message: string) => void;
|
|
5
12
|
onClose?: () => void;
|
|
13
|
+
/**
|
|
14
|
+
* Cap the modal height. Pass a number for px (e.g. `600`) or a CSS
|
|
15
|
+
* string for viewport units (`"70vh"`). The iframe scrolls
|
|
16
|
+
* internally if its content exceeds this. Defaults to no cap; the
|
|
17
|
+
* widget reports its natural height via postMessage.
|
|
18
|
+
*/
|
|
19
|
+
maxHeight?: number | string;
|
|
20
|
+
/**
|
|
21
|
+
* Cap the modal width. Defaults to 480 (px). Pass any CSS length.
|
|
22
|
+
*/
|
|
23
|
+
maxWidth?: number | string;
|
|
24
|
+
/**
|
|
25
|
+
* Corner radius on the modal. Defaults to 18 (px).
|
|
26
|
+
*/
|
|
27
|
+
radius?: number | string;
|
|
28
|
+
/**
|
|
29
|
+
* Backdrop styling. See `BackdropStyle`. Each field falls back to
|
|
30
|
+
* the default if omitted.
|
|
31
|
+
*/
|
|
32
|
+
backdrop?: BackdropStyle;
|
|
33
|
+
/**
|
|
34
|
+
* Extra style applied to the backdrop wrapper. Use for things outside
|
|
35
|
+
* the typed `backdrop` knob (custom transitions, z-index, etc.).
|
|
36
|
+
*/
|
|
37
|
+
backdropStyle?: CSSProperties;
|
|
38
|
+
/**
|
|
39
|
+
* Extra style applied to the iframe. Useful for borders, custom
|
|
40
|
+
* shadows, or filters that the typed props don't cover.
|
|
41
|
+
*/
|
|
42
|
+
iframeStyle?: CSSProperties;
|
|
6
43
|
}
|
|
7
44
|
/**
|
|
8
45
|
* Renders the modal backdrop + iframe whenever the provider's `isOpen` is
|
|
9
46
|
* true. Place this once near the root of your app (typically just inside
|
|
10
47
|
* <RiftProvider>); call `useRift().open()` to show it.
|
|
48
|
+
*
|
|
49
|
+
* All visual knobs are overridable from the host: see `maxHeight`,
|
|
50
|
+
* `maxWidth`, `radius`, `backdrop`, plus escape hatches `backdropStyle`
|
|
51
|
+
* and `iframeStyle` for anything else.
|
|
11
52
|
*/
|
|
12
|
-
export declare function RiftAuth({ onSuccess, onError, onClose }: RiftAuthProps): import("react/jsx-runtime").JSX.Element | null;
|
|
53
|
+
export declare function RiftAuth({ onSuccess, onError, onClose, maxHeight, maxWidth, radius, backdrop, backdropStyle, iframeStyle, }: RiftAuthProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
54
|
export {};
|
|
14
55
|
//# sourceMappingURL=RiftAuth.d.ts.map
|
package/dist/RiftAuth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RiftAuth.d.ts","sourceRoot":"","sources":["../src/RiftAuth.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RiftAuth.d.ts","sourceRoot":"","sources":["../src/RiftAuth.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,UAAU,aAAa;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,aAAa;IAErB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IAEzB;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;CAC7B;AAOD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAA4B,EAC5B,MAAuB,EACvB,QAAQ,EACR,aAAa,EACb,WAAW,GACZ,EAAE,aAAa,kDA0Gf"}
|
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
|
|
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 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 } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\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/**\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 */\nexport function RiftAuth({ onSuccess, onError, onClose }: 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 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: \"rgba(15,15,20,0.55)\",\n backdropFilter: \"blur(6px)\",\n WebkitBackdropFilter: \"blur(6px)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\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: 480,\n height: _iframeHeight,\n borderRadius: 18,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\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","onIframeLoad","value","jsx","RiftAuth","onSuccess","onError","onClose","_iframeSrc","_iframeHeight","_onIframeLoad","jsxs","useRift"],"mappings":"wIAgBA,IAAIA,EAAmC,KACnCC,EAAQ,GACRC,EAAoC,CAAA,EACpCC,EAA8B,KAMlC,MAAMC,MAAc,IAQpB,SAASC,GAAe,CAEtB,MAAO,GAAG,KAAK,IAAA,EAAM,SAAS,EAAE,CAAC,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,EAC7E,CAEA,SAASC,EAAcC,EAA4D,CACjF,GAAI,OAAO,SAAa,IACtB,OAAO,QAAQ,OAAO,IAAI,MAAM,iDAAiD,CAAC,EAEpF,GAAIP,GAAUC,EAAO,OAAO,QAAQ,QAAA,EAIpC,GAFAE,EAAe,IAAI,IAAII,EAAK,SAAS,EAAE,OAEnC,CAACP,EAAQ,CACXA,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,aAAa,cAAe,MAAM,EACzCA,EAAO,aAAa,WAAY,IAAI,EACpCA,EAAO,MAAQ,uBACfA,EAAO,MAAM,QACX,0GACF,MAAMQ,EAAS,IAAI,gBAAgB,CACjC,IAAKD,EAAK,OACV,SAAU,GAAA,CACX,EACDP,EAAO,IAAM,GAAGO,EAAK,UAAU,QAAQ,MAAO,EAAE,CAAC,KAAKC,EAAO,SAAA,CAAU,GACvE,SAAS,KAAK,YAAYR,CAAM,EAEhC,OAAO,iBAAiB,UAAYS,GAAM,CACxC,GAAIA,EAAE,SAAWN,EAAc,OAC/B,MAAMO,EAAOD,EAAE,KACf,GAAI,GAACC,GAAQ,OAAOA,GAAS,UAAY,OAAOA,EAAK,MAAS,WACzDA,EAAK,KAAK,WAAW,OAAO,EAEjC,IAAIA,EAAK,OAAS,aAAc,CAC9BT,EAAQ,GACRC,EAAe,QAASS,GAAMA,EAAA,CAAG,EACjCT,EAAiB,CAAA,EACjB,MACF,CACA,GAAIQ,EAAK,OAAS,sBAAuB,CACvC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,QAAQ,CACX,YAAaF,EAAK,YAClB,UAAWA,EAAK,UAChB,UAAWA,EAAK,SAAA,CACjB,GAEH,MACF,CACA,GAAIA,EAAK,OAAS,qBAAsB,CACtC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,OAAO,IAAI,MAAMF,EAAK,SAAW,gBAAgB,CAAC,GAEzD,MACF,CACA,GAAIA,EAAK,OAAS,qBAAsB,CACtC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,QAAQ,CAAE,YAAa,GAAI,UAAW,GAAI,UAAW,EAAG,EAEjE,EACF,CAAC,CACH,CAEA,OAAIX,EAAc,QAAQ,QAAA,EACnB,IAAI,QAASY,GAAY,CAC9BX,EAAe,KAAKW,CAAO,EAI3B,WAAW,IAAM,CACf,GAAI,CAACZ,EAAO,CACV,MAAMU,EAAIT,EAAe,MAAA,EACrBS,GAAGA,EAAA,CACT,CACF,EAAG,GAAI,CACT,CAAC,CACH,CAEA,eAAsBG,EAAcP,EAGR,CAE1B,GADA,MAAMD,EAAcC,CAAI,EACpB,CAACP,GAAQ,eAAiB,CAACG,EAC7B,MAAM,IAAI,MAAM,iCAAiC,EAEnD,MAAMY,EAAYV,EAAA,EAClB,OAAO,IAAI,QAAwB,CAACQ,EAASG,IAAW,CACtDZ,EAAQ,IAAIW,EAAW,CAAE,QAAAF,EAAS,OAAAG,EAAQ,EAC1ChB,EAAQ,cAAe,YACrB,CAAE,KAAM,uBAAwB,UAAAe,CAAA,EAChCZ,CAAA,EAEF,WAAW,IAAM,CACf,MAAMS,EAAOR,EAAQ,IAAIW,CAAS,EAC9BH,IACFR,EAAQ,OAAOW,CAAS,EACxBH,EAAK,OAAO,IAAI,MAAM,mBAAmB,CAAC,EAE9C,EAAG,GAAK,CACV,CAAC,CACH,CAEA,eAAsBK,EAAaV,EAGjB,CAChB,GAAI,CAEF,GADA,MAAMD,EAAcC,CAAI,EACpB,CAACP,GAAQ,eAAiB,CAACG,EAAc,OAC7C,MAAMY,EAAYV,EAAA,EAClB,MAAM,IAAI,QAAeQ,GAAY,CACnCT,EAAQ,IAAIW,EAAW,CACrB,QAAS,IAAMF,EAAA,EACf,OAAQ,IAAMA,EAAA,CAAQ,CACvB,EACDb,EAAQ,cAAe,YACrB,CAAE,KAAM,sBAAuB,UAAAe,CAAA,EAC/BZ,CAAA,EAEF,WAAW,IAAM,CACfC,EAAQ,OAAOW,CAAS,EACxBF,EAAA,CACF,EAAG,GAAI,CACT,CAAC,CACH,MAAQ,CAER,CACF,CC1JA,MAAMK,EAAqB,4BAQrBC,EAAuB,gBAUvBC,EAAyB,GAwBzBC,EAAcC,EAAAA,cAAuC,IAAI,EAExD,SAASC,GAAmC,CACjD,MAAMC,EAAMC,EAAAA,WAAWJ,CAAW,EAClC,GAAI,CAACG,EACH,MAAM,IAAI,MACR,yEAAA,EAGJ,OAAOA,CACT,CAaA,SAASE,IAAyC,CAChD,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,CACF,MAAMC,EAAM,aAAa,QAAQR,CAAoB,EACrD,OAAOQ,EAAO,KAAK,MAAMA,CAAG,EAA0B,IACxD,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASC,GAAaC,EAA8B,CAClD,GAAI,SAAO,OAAW,KACtB,GAAI,CACEA,EAAI,aAAa,QAAQV,EAAsB,KAAK,UAAUU,CAAE,CAAC,EAChE,aAAa,WAAWV,CAAoB,CACnD,MAAQ,CAER,CACF,CAEO,SAASW,GAAa,CAC3B,OAAAC,EACA,UAAAC,EACA,SAAAC,EACA,SAAAC,EAAW,GACX,QAAAC,EAAU,EACZ,EAAsB,CACpB,MAAMC,EAAoBJ,GAAad,EACjCf,EAAekC,EAAAA,QAAQ,IAAM,CACjC,GAAI,CACF,OAAO,IAAI,IAAID,CAAiB,EAAE,MACpC,MAAQ,CACN,OAAOA,CACT,CACF,EAAG,CAACA,CAAiB,CAAC,EAEhB,CAACE,EAAMC,CAAO,EAAIC,EAAAA,SAA0B,IAAI,EAChD,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EACpC,CAACG,EAAMC,CAAO,EAAIJ,EAAAA,SAAmB,QAAQ,EAC7C,CAACK,EAASC,CAAU,EAAIN,EAAAA,SAAS,EAAK,EACtC,CAACO,EAAOC,CAAQ,EAAIR,EAAAA,SAAwB,IAAI,EAChD,CAACS,EAAcC,CAAe,EAAIV,EAAAA,SAAS,GAAG,EAC9C,CAACW,EAAWC,CAAY,EAAIZ,EAAAA,SAAS,CAAC,EAItCa,EAAUC,EAAAA,OAAwB,IAAI,EAC5CD,EAAQ,QAAUf,EAKlB,MAAMiB,EAAkBD,EAAAA,OAA+B,IAAI,EAErDE,EAAgBC,EAAAA,YACnBC,GAA0B,CACzBnB,EAAQmB,CAAI,EACRvB,GACFP,GACE8B,EACI,CACE,KAAMA,EAAK,KACX,QAASA,EAAK,QACd,WAAYA,EAAK,UAAA,EAEnB,IAAA,CAGV,EACA,CAACvB,CAAO,CAAA,EAMVwB,EAAAA,UAAU,IAAM,CACd,GAAI,CAACxB,EAAS,OACd,MAAMyB,EAAWlC,GAAA,EACjB,GAAI,CAACkC,EAAU,OACf,IAAIC,EAAQ,GACZ,OAAC,SAAY,CACX,GAAI,CACF,MAAMC,EAAS,MAAMhD,EAAc,CACjC,OAAAiB,EACA,UAAWK,CAAA,CACZ,EACD,GAAI,CAACyB,EAAO,OACZL,EAAc,CACZ,KAAMI,EAAS,KACf,QAASA,EAAS,QAClB,WAAYA,EAAS,WACrB,YAAaE,EAAO,YACpB,UAAWA,EAAO,SAAA,CACnB,CACH,MAAQ,CACN,GAAI,CAACD,EAAO,OAGZL,EAAc,IAAI,CACpB,CACF,GAAA,EACO,IAAM,CACXK,EAAQ,EACV,CAEF,EAAG,CAAA,CAAE,EAEL,MAAME,EAAON,cAAalD,GAA+B,CACvDqC,EAAQrC,GAAM,MAAQ,QAAQ,EAC9ByC,EAAS,IAAI,EACbF,EAAW,EAAK,EAChBM,EAAcY,GAAMA,EAAI,CAAC,EACzBtB,EAAU,EAAI,CAChB,EAAG,CAAA,CAAE,EAECuB,EAAQR,EAAAA,YAAY,IAAM,CAC9Bf,EAAU,EAAK,EACfI,EAAW,EAAK,CAClB,EAAG,CAAA,CAAE,EAECoB,EAAUT,EAAAA,YAAY,SAAY,CACtC,MAAMxC,EAAa,CAAE,OAAAc,EAAQ,UAAWK,EAAmB,EAC3DoB,EAAc,IAAI,CACpB,EAAG,CAACzB,EAAQK,EAAmBoB,CAAa,CAAC,EAEvCW,EAAiBV,EAAAA,YAAY,SAA6B,CAC9D,MAAMW,EAAUf,EAAQ,QACxB,GAAI,CAACe,EAAS,MAAM,IAAI,MAAM,eAAe,EAE7C,MAAMC,EAAYD,EAAQ,UACtB,IAAI,KAAKA,EAAQ,SAAS,EAAE,QAAA,EAC5B,KACEE,EAAM,KAAK,IAAA,EAIjB,MAAI,EAFF,CAACD,GAAaA,EAAYC,EAAMlD,EAAyB,MAEtCgD,EAAQ,YACpBA,EAAQ,aAGbb,EAAgB,UAIpBA,EAAgB,SAAW,SAAY,CACrC,GAAI,CACF,MAAMO,EAAS,MAAMhD,EAAc,CACjC,OAAAiB,EACA,UAAWK,CAAA,CACZ,EACKmC,EAASlB,EAAQ,QACvB,GAAI,CAACkB,EAAQ,MAAM,IAAI,MAAM,2BAA2B,EACxD,MAAMb,EAAiB,CACrB,GAAGa,EACH,YAAaT,EAAO,YACpB,UAAWA,EAAO,SAAA,EAEpB,OAAAN,EAAcE,CAAI,EACXI,EAAO,WAChB,OAASU,EAAU,CAEjB,MAAAhB,EAAc,IAAI,EACZgB,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CAC1D,QAAA,CACEjB,EAAgB,QAAU,IAC5B,CACF,GAAA,GACOA,EAAgB,QACzB,EAAG,CAACxB,EAAQK,EAAmBoB,CAAa,CAAC,EAI7CG,EAAAA,UAAU,IAAM,CACd,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMc,EAAWhE,GAAoB,CACnC,GAAIA,EAAE,SAAWN,EAAc,OAC/B,MAAMO,EAAOD,EAAE,KACf,GAAI,GAACC,GAAQ,OAAOA,GAAS,UAAY,OAAOA,EAAK,MAAS,WACzDA,EAAK,KAAK,WAAW,OAAO,EAEjC,OAAQA,EAAK,KAAA,CACX,IAAK,aAGC+B,KAAmB,EAAI,EAC3B,MACF,IAAK,aACHwB,EAAA,EACA,MACF,IAAK,cACHf,EAAgB,KAAK,IAAI,IAAK,KAAK,IAAI,IAAKxC,EAAK,OAAS,CAAC,CAAC,CAAC,EAC7D,MACF,IAAK,sBAAuB,CAC1B,MAAMgD,EAAiB,CACrB,KAAMhD,EAAK,KACX,QAASA,EAAK,QACd,WAAYA,EAAK,WACjB,YAAaA,EAAK,YAClB,UAAWA,EAAK,SAAA,EAElB8C,EAAcE,CAAI,EAClBhB,EAAU,EAAK,EACf,KACF,CACA,IAAK,oBACHM,EAAStC,EAAK,OAAO,EACrB,KAAA,CAIN,EACA,cAAO,iBAAiB,UAAW+D,CAAO,EACnC,IAAM,OAAO,oBAAoB,UAAWA,CAAO,CAC5D,EAAG,CAACtE,EAAc8D,EAAOT,EAAef,CAAM,CAAC,EAG/CkB,EAAAA,UAAU,IAAM,CACd,GAAI,SAAO,SAAa,MACpBlB,EAAQ,CACV,MAAMiC,EAAO,SAAS,gBAAgB,MAAM,SAC5C,gBAAS,gBAAgB,MAAM,SAAW,SACnC,IAAM,CACX,SAAS,gBAAgB,MAAM,SAAWA,CAC5C,CACF,CACF,EAAG,CAACjC,CAAM,CAAC,EAEXkB,EAAAA,UAAU,IAAM,CACVzB,GAAY,CAACI,GAAMyB,EAAA,CAEzB,EAAG,CAAA,CAAE,EAEL,MAAMY,EAAYtC,EAAAA,QAAQ,IAAM,CAC9B,MAAM7B,EAAS,IAAI,gBAAgB,CACjC,IAAKuB,EACL,KAAAY,EACA,OAAQ,OAAO,OAAW,IAAc,OAAO,SAAS,OAAS,GACjE,EAAG,OAAOQ,CAAS,CAAA,CACpB,EACD,MAAO,GAAGf,EAAkB,QAAQ,MAAO,EAAE,CAAC,KAAK5B,EAAO,SAAA,CAAU,EACtE,EAAG,CAACuB,EAAQY,EAAMQ,EAAWf,CAAiB,CAAC,EAEzCwC,EAAenB,EAAAA,YAAY,IAAM,CAEvC,EAAG,CAAA,CAAE,EAECoB,EAAQxC,EAAAA,QACZ,KAAO,CACL,OAAAN,EACA,UAAWK,EACX,KAAAE,EACA,OAAAG,EACA,QAAAI,EACA,MAAAE,EACA,KAAAgB,EACA,MAAAE,EACA,QAAAC,EACA,eAAAC,EACA,WAAYQ,EACZ,cAAe1B,EACf,cAAe2B,CAAA,GAEjB,CACE7C,EACAK,EACAE,EACAG,EACAI,EACAE,EACAgB,EACAE,EACAC,EACAC,EACAQ,EACA1B,EACA2B,CAAA,CACF,EAGF,OAAOE,EAAAA,IAACzD,EAAY,SAAZ,CAAqB,MAAAwD,EAAe,SAAA5C,CAAA,CAAS,CACvD,CCtVO,SAAS8C,GAAS,CAAE,UAAAC,EAAW,QAAAC,EAAS,QAAAC,GAA0B,CACvE,KAAM,CACJ,OAAAzC,EACA,QAAAI,EACA,MAAAE,EACA,MAAAkB,EACA,KAAA3B,EACA,WAAA6C,EACA,cAAAC,EACA,cAAAC,CAAA,EACE9D,EAAA,EAeJ,OAbAoC,EAAAA,UAAU,IAAM,CACVrB,GAAQ0C,GAAWA,EAAU1C,CAAI,CACvC,EAAG,CAACA,EAAM0C,CAAS,CAAC,EAEpBrB,EAAAA,UAAU,IAAM,CACVZ,GAASkC,GAASA,EAAQlC,CAAK,CACrC,EAAG,CAACA,EAAOkC,CAAO,CAAC,EAEnBtB,EAAAA,UAAU,IAAM,CACV,CAAClB,GAAUyC,GAASA,EAAA,CAE1B,EAAG,CAACzC,CAAM,CAAC,EAENA,EAGH6C,EAAAA,KAAC,MAAA,CACC,KAAK,SACL,aAAW,OACX,aAAW,UACX,QAAU7E,GAAM,CACVA,EAAE,SAAWA,EAAE,eAAewD,EAAA,CACpC,EACA,MAAO,CACL,SAAU,QACV,MAAO,EACP,OAAQ,WACR,WAAY,sBACZ,eAAgB,YAChB,qBAAsB,YACtB,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,QAAS,GACT,UAAW,0BAAA,EAGb,SAAA,CAAAa,EAAAA,IAAC,SAAO,SAAA,gEAAA,CAAiE,EACxE,CAACjC,GACAiC,EAAAA,IAAC,MAAA,CACC,cAAW,GACX,MAAO,CACL,SAAU,WACV,MAAO,yBACP,SAAU,GACV,WACE,6CAAA,EAEL,SAAA,kBAAA,CAAA,EAIHA,EAAAA,IAAC,SAAA,CACC,IAAKK,EACL,OAAQE,EACR,MAAM,eACN,MAAM,sDACN,MAAO,CACL,OAAQ,EACR,WAAY,cACZ,YAAa,QACb,MAAO,OACP,SAAU,IACV,OAAQD,EACR,aAAc,GACd,UAAW,qCACX,WAAY,oBACZ,QAASvC,EAAU,EAAI,CAAA,CACzB,CAAA,CACF,CAAA,CAAA,EAxDgB,IA2DtB,CCnEO,SAAS0C,IAAyB,CACvC,KAAM,CAAE,KAAAjD,EAAM,OAAAG,EAAQ,KAAAsB,EAAM,MAAAE,EAAO,QAAAC,EAAS,eAAAC,EAAgB,MAAApB,CAAA,EAC1DxB,EAAA,EACF,MAAO,CACL,KAAAe,EACA,gBAAiB,CAAC,CAACA,EACnB,OAAAG,EACA,KAAAsB,EACA,MAAAE,EACA,QAAAC,EACA,eAAAC,EACA,MAAApB,CAAA,CAEJ"}
|
|
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":"wIAgBA,IAAIA,EAAmC,KACnCC,EAAQ,GACRC,EAAoC,CAAA,EACpCC,EAA8B,KAMlC,MAAMC,MAAc,IAQpB,SAASC,GAAe,CAEtB,MAAO,GAAG,KAAK,IAAA,EAAM,SAAS,EAAE,CAAC,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,EAC7E,CAEA,SAASC,EAAcC,EAA4D,CACjF,GAAI,OAAO,SAAa,IACtB,OAAO,QAAQ,OAAO,IAAI,MAAM,iDAAiD,CAAC,EAEpF,GAAIP,GAAUC,EAAO,OAAO,QAAQ,QAAA,EAIpC,GAFAE,EAAe,IAAI,IAAII,EAAK,SAAS,EAAE,OAEnC,CAACP,EAAQ,CACXA,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,aAAa,cAAe,MAAM,EACzCA,EAAO,aAAa,WAAY,IAAI,EACpCA,EAAO,MAAQ,uBACfA,EAAO,MAAM,QACX,0GACF,MAAMQ,EAAS,IAAI,gBAAgB,CACjC,IAAKD,EAAK,OACV,SAAU,GAAA,CACX,EACDP,EAAO,IAAM,GAAGO,EAAK,UAAU,QAAQ,MAAO,EAAE,CAAC,KAAKC,EAAO,SAAA,CAAU,GACvE,SAAS,KAAK,YAAYR,CAAM,EAEhC,OAAO,iBAAiB,UAAYS,GAAM,CACxC,GAAIA,EAAE,SAAWN,EAAc,OAC/B,MAAMO,EAAOD,EAAE,KACf,GAAI,GAACC,GAAQ,OAAOA,GAAS,UAAY,OAAOA,EAAK,MAAS,WACzDA,EAAK,KAAK,WAAW,OAAO,EAEjC,IAAIA,EAAK,OAAS,aAAc,CAC9BT,EAAQ,GACRC,EAAe,QAASS,GAAMA,EAAA,CAAG,EACjCT,EAAiB,CAAA,EACjB,MACF,CACA,GAAIQ,EAAK,OAAS,sBAAuB,CACvC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,QAAQ,CACX,YAAaF,EAAK,YAClB,UAAWA,EAAK,UAChB,UAAWA,EAAK,SAAA,CACjB,GAEH,MACF,CACA,GAAIA,EAAK,OAAS,qBAAsB,CACtC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,OAAO,IAAI,MAAMF,EAAK,SAAW,gBAAgB,CAAC,GAEzD,MACF,CACA,GAAIA,EAAK,OAAS,qBAAsB,CACtC,MAAME,EAAOR,EAAQ,IAAIM,EAAK,SAAS,EACnCE,IACFR,EAAQ,OAAOM,EAAK,SAAS,EAC7BE,EAAK,QAAQ,CAAE,YAAa,GAAI,UAAW,GAAI,UAAW,EAAG,EAEjE,EACF,CAAC,CACH,CAEA,OAAIX,EAAc,QAAQ,QAAA,EACnB,IAAI,QAASY,GAAY,CAC9BX,EAAe,KAAKW,CAAO,EAI3B,WAAW,IAAM,CACf,GAAI,CAACZ,EAAO,CACV,MAAMU,EAAIT,EAAe,MAAA,EACrBS,GAAGA,EAAA,CACT,CACF,EAAG,GAAI,CACT,CAAC,CACH,CAEA,eAAsBG,EAAcP,EAGR,CAE1B,GADA,MAAMD,EAAcC,CAAI,EACpB,CAACP,GAAQ,eAAiB,CAACG,EAC7B,MAAM,IAAI,MAAM,iCAAiC,EAEnD,MAAMY,EAAYV,EAAA,EAClB,OAAO,IAAI,QAAwB,CAACQ,EAASG,IAAW,CACtDZ,EAAQ,IAAIW,EAAW,CAAE,QAAAF,EAAS,OAAAG,EAAQ,EAC1ChB,EAAQ,cAAe,YACrB,CAAE,KAAM,uBAAwB,UAAAe,CAAA,EAChCZ,CAAA,EAEF,WAAW,IAAM,CACf,MAAMS,EAAOR,EAAQ,IAAIW,CAAS,EAC9BH,IACFR,EAAQ,OAAOW,CAAS,EACxBH,EAAK,OAAO,IAAI,MAAM,mBAAmB,CAAC,EAE9C,EAAG,GAAK,CACV,CAAC,CACH,CAEA,eAAsBK,EAAaV,EAGjB,CAChB,GAAI,CAEF,GADA,MAAMD,EAAcC,CAAI,EACpB,CAACP,GAAQ,eAAiB,CAACG,EAAc,OAC7C,MAAMY,EAAYV,EAAA,EAClB,MAAM,IAAI,QAAeQ,GAAY,CACnCT,EAAQ,IAAIW,EAAW,CACrB,QAAS,IAAMF,EAAA,EACf,OAAQ,IAAMA,EAAA,CAAQ,CACvB,EACDb,EAAQ,cAAe,YACrB,CAAE,KAAM,sBAAuB,UAAAe,CAAA,EAC/BZ,CAAA,EAEF,WAAW,IAAM,CACfC,EAAQ,OAAOW,CAAS,EACxBF,EAAA,CACF,EAAG,GAAI,CACT,CAAC,CACH,MAAQ,CAER,CACF,CCnJA,MAAMK,EAA8D,CAClE,WAAY,4BACZ,QAAS,mCACX,EAQMC,EAAuB,gBAUvBC,GAAyB,GAwBzBC,EAAcC,EAAAA,cAAuC,IAAI,EAExD,SAASC,GAAmC,CACjD,MAAMC,EAAMC,EAAAA,WAAWJ,CAAW,EAClC,GAAI,CAACG,EACH,MAAM,IAAI,MACR,yEAAA,EAGJ,OAAOA,CACT,CAaA,SAASE,IAAyC,CAChD,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,CACF,MAAMC,EAAM,aAAa,QAAQR,CAAoB,EACrD,OAAOQ,EAAO,KAAK,MAAMA,CAAG,EAA0B,IACxD,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASC,GAAaC,EAA8B,CAClD,GAAI,SAAO,OAAW,KACtB,GAAI,CACEA,EAAI,aAAa,QAAQV,EAAsB,KAAK,UAAUU,CAAE,CAAC,EAChE,aAAa,WAAWV,CAAoB,CACnD,MAAQ,CAER,CACF,CAEO,SAASW,GAAa,CAC3B,OAAAC,EACA,YAAAC,EACA,UAAAC,EACA,SAAAC,EACA,SAAAC,EAAW,GACX,QAAAC,EAAU,EACZ,EAAsB,CACpB,MAAMC,EACJJ,GAAaf,EAAkBc,GAAe,YAAY,EACtD7B,EAAemC,EAAAA,QAAQ,IAAM,CACjC,GAAI,CACF,OAAO,IAAI,IAAID,CAAiB,EAAE,MACpC,MAAQ,CACN,OAAOA,CACT,CACF,EAAG,CAACA,CAAiB,CAAC,EAEhB,CAACE,EAAMC,CAAO,EAAIC,EAAAA,SAA0B,IAAI,EAChD,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EACpC,CAACG,EAAMC,CAAO,EAAIJ,EAAAA,SAAmB,QAAQ,EAC7C,CAACK,EAASC,CAAU,EAAIN,EAAAA,SAAS,EAAK,EACtC,CAACO,EAAOC,CAAQ,EAAIR,EAAAA,SAAwB,IAAI,EAChD,CAACS,EAAcC,CAAe,EAAIV,EAAAA,SAAS,GAAG,EAC9C,CAACW,EAAWC,CAAY,EAAIZ,EAAAA,SAAS,CAAC,EAItCa,EAAUC,EAAAA,OAAwB,IAAI,EAC5CD,EAAQ,QAAUf,EAKlB,MAAMiB,EAAkBD,EAAAA,OAA+B,IAAI,EAErDE,EAAgBC,EAAAA,YACnBC,GAA0B,CACzBnB,EAAQmB,CAAI,EACRvB,GACFR,GACE+B,EACI,CACE,KAAMA,EAAK,KACX,QAASA,EAAK,QACd,WAAYA,EAAK,UAAA,EAEnB,IAAA,CAGV,EACA,CAACvB,CAAO,CAAA,EAMVwB,EAAAA,UAAU,IAAM,CACd,GAAI,CAACxB,EAAS,OACd,MAAMyB,EAAWnC,GAAA,EACjB,GAAI,CAACmC,EAAU,OACf,IAAIC,EAAQ,GACZ,OAAC,SAAY,CACX,GAAI,CACF,MAAMC,EAAS,MAAMjD,EAAc,CACjC,OAAAiB,EACA,UAAWM,CAAA,CACZ,EACD,GAAI,CAACyB,EAAO,OACZL,EAAc,CACZ,KAAMI,EAAS,KACf,QAASA,EAAS,QAClB,WAAYA,EAAS,WACrB,YAAaE,EAAO,YACpB,UAAWA,EAAO,SAAA,CACnB,CACH,MAAQ,CACN,GAAI,CAACD,EAAO,OAGZL,EAAc,IAAI,CACpB,CACF,GAAA,EACO,IAAM,CACXK,EAAQ,EACV,CAEF,EAAG,CAAA,CAAE,EAEL,MAAME,EAAON,cAAanD,GAA+B,CACvDsC,EAAQtC,GAAM,MAAQ,QAAQ,EAC9B0C,EAAS,IAAI,EACbF,EAAW,EAAK,EAChBM,EAAcY,GAAMA,EAAI,CAAC,EACzBtB,EAAU,EAAI,CAChB,EAAG,CAAA,CAAE,EAECuB,EAAQR,EAAAA,YAAY,IAAM,CAC9Bf,EAAU,EAAK,EACfI,EAAW,EAAK,CAClB,EAAG,CAAA,CAAE,EAECoB,EAAUT,EAAAA,YAAY,SAAY,CACtC,MAAMzC,EAAa,CAAE,OAAAc,EAAQ,UAAWM,EAAmB,EAC3DoB,EAAc,IAAI,CACpB,EAAG,CAAC1B,EAAQM,EAAmBoB,CAAa,CAAC,EAEvCW,EAAiBV,EAAAA,YAAY,SAA6B,CAC9D,MAAMW,EAAUf,EAAQ,QACxB,GAAI,CAACe,EAAS,MAAM,IAAI,MAAM,eAAe,EAE7C,MAAMC,EAAYD,EAAQ,UACtB,IAAI,KAAKA,EAAQ,SAAS,EAAE,QAAA,EAC5B,KACEE,EAAM,KAAK,IAAA,EAIjB,MAAI,EAFF,CAACD,GAAaA,EAAYC,EAAMnD,GAAyB,MAEtCiD,EAAQ,YACpBA,EAAQ,aAGbb,EAAgB,UAIpBA,EAAgB,SAAW,SAAY,CACrC,GAAI,CACF,MAAMO,EAAS,MAAMjD,EAAc,CACjC,OAAAiB,EACA,UAAWM,CAAA,CACZ,EACKmC,EAASlB,EAAQ,QACvB,GAAI,CAACkB,EAAQ,MAAM,IAAI,MAAM,2BAA2B,EACxD,MAAMb,EAAiB,CACrB,GAAGa,EACH,YAAaT,EAAO,YACpB,UAAWA,EAAO,SAAA,EAEpB,OAAAN,EAAcE,CAAI,EACXI,EAAO,WAChB,OAASU,EAAU,CAEjB,MAAAhB,EAAc,IAAI,EACZgB,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CAC1D,QAAA,CACEjB,EAAgB,QAAU,IAC5B,CACF,GAAA,GACOA,EAAgB,QACzB,EAAG,CAACzB,EAAQM,EAAmBoB,CAAa,CAAC,EAI7CG,EAAAA,UAAU,IAAM,CACd,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMc,EAAWjE,GAAoB,CACnC,GAAIA,EAAE,SAAWN,EAAc,OAC/B,MAAMO,EAAOD,EAAE,KACf,GAAI,GAACC,GAAQ,OAAOA,GAAS,UAAY,OAAOA,EAAK,MAAS,WACzDA,EAAK,KAAK,WAAW,OAAO,EAEjC,OAAQA,EAAK,KAAA,CACX,IAAK,aAGCgC,KAAmB,EAAI,EAC3B,MACF,IAAK,aACHwB,EAAA,EACA,MACF,IAAK,cACHf,EAAgB,KAAK,IAAI,IAAK,KAAK,IAAI,IAAKzC,EAAK,OAAS,CAAC,CAAC,CAAC,EAC7D,MACF,IAAK,sBAAuB,CAC1B,MAAMiD,EAAiB,CACrB,KAAMjD,EAAK,KACX,QAASA,EAAK,QACd,WAAYA,EAAK,WACjB,YAAaA,EAAK,YAClB,UAAWA,EAAK,SAAA,EAElB+C,EAAcE,CAAI,EAClBhB,EAAU,EAAK,EACf,KACF,CACA,IAAK,oBACHM,EAASvC,EAAK,OAAO,EACrB,KAAA,CAIN,EACA,cAAO,iBAAiB,UAAWgE,CAAO,EACnC,IAAM,OAAO,oBAAoB,UAAWA,CAAO,CAC5D,EAAG,CAACvE,EAAc+D,EAAOT,EAAef,CAAM,CAAC,EAG/CkB,EAAAA,UAAU,IAAM,CACd,GAAI,SAAO,SAAa,MACpBlB,EAAQ,CACV,MAAMiC,EAAO,SAAS,gBAAgB,MAAM,SAC5C,gBAAS,gBAAgB,MAAM,SAAW,SACnC,IAAM,CACX,SAAS,gBAAgB,MAAM,SAAWA,CAC5C,CACF,CACF,EAAG,CAACjC,CAAM,CAAC,EAEXkB,EAAAA,UAAU,IAAM,CACVzB,GAAY,CAACI,GAAMyB,EAAA,CAEzB,EAAG,CAAA,CAAE,EAEL,MAAMY,EAAYtC,EAAAA,QAAQ,IAAM,CAC9B,MAAM9B,EAAS,IAAI,gBAAgB,CACjC,IAAKuB,EACL,KAAAa,EACA,OAAQ,OAAO,OAAW,IAAc,OAAO,SAAS,OAAS,GACjE,EAAG,OAAOQ,CAAS,CAAA,CACpB,EAKD,GAAI,OAAO,SAAa,IAAa,CACnC,MAAMyB,EAAO,SAAS,gBAChBC,EAAOD,EAAK,aAAa,YAAY,EAC3C,IAAIE,EAAuB,KACvBD,IAAS,QAAUA,IAAS,QAASC,EAAQD,GAE/CD,EAAK,UAAU,SAAS,MAAM,GAC9B,SAAS,MAAM,UAAU,SAAS,MAAM,GAIxC,OAAO,YACP,OAAO,WAAW,8BAA8B,EAAE,WAElDE,EAAQ,QACNA,GAAOvE,EAAO,IAAI,QAASuE,CAAK,CACtC,CAEA,MAAO,GAAG1C,EAAkB,QAAQ,MAAO,EAAE,CAAC,KAAK7B,EAAO,SAAA,CAAU,EACtE,EAAG,CAACuB,EAAQa,EAAMQ,EAAWf,CAAiB,CAAC,EAEzC2C,EAAetB,EAAAA,YAAY,IAAM,CAEvC,EAAG,CAAA,CAAE,EAECuB,EAAQ3C,EAAAA,QACZ,KAAO,CACL,OAAAP,EACA,UAAWM,EACX,KAAAE,EACA,OAAAG,EACA,QAAAI,EACA,MAAAE,EACA,KAAAgB,EACA,MAAAE,EACA,QAAAC,EACA,eAAAC,EACA,WAAYQ,EACZ,cAAe1B,EACf,cAAe8B,CAAA,GAEjB,CACEjD,EACAM,EACAE,EACAG,EACAI,EACAE,EACAgB,EACAE,EACAC,EACAC,EACAQ,EACA1B,EACA8B,CAAA,CACF,EAGF,OAAOE,EAAAA,IAAC7D,EAAY,SAAZ,CAAqB,MAAA4D,EAAe,SAAA/C,CAAA,CAAS,CACvD,CClVA,MAAMiD,GAAyB,sBACzBC,GAAwB,EACxBC,GAAoB,IACpBC,GAAiB,GAWhB,SAASC,GAAS,CACvB,UAAAC,EACA,QAAAC,EACA,QAAAC,EACA,UAAAC,EACA,SAAAC,EAAWP,GACX,OAAAQ,EAASP,GACT,SAAAQ,EACA,cAAAC,EACA,YAAAC,CACF,EAAkB,CAChB,KAAM,CACJ,OAAAtD,EACA,QAAAI,EACA,MAAAE,EACA,MAAAkB,EACA,KAAA3B,EACA,WAAA0D,EACA,cAAAC,EACA,cAAAC,CAAA,EACE5E,EAAA,EAeJ,GAbAqC,EAAAA,UAAU,IAAM,CACVrB,GAAQiD,GAAWA,EAAUjD,CAAI,CACvC,EAAG,CAACA,EAAMiD,CAAS,CAAC,EAEpB5B,EAAAA,UAAU,IAAM,CACVZ,GAASyC,GAASA,EAAQzC,CAAK,CACrC,EAAG,CAACA,EAAOyC,CAAO,CAAC,EAEnB7B,EAAAA,UAAU,IAAM,CACV,CAAClB,GAAUgD,GAASA,EAAA,CAE1B,EAAG,CAAChD,CAAM,CAAC,EAEP,CAACA,EAAQ,OAAO,KAEpB,MAAM0D,EAAgBN,GAAU,OAASX,GACnCkB,EAAeP,GAAU,MAAQV,GACjCkB,EAAUD,EAAe,EAAI,QAAQA,CAAY,MAAQ,OAS/D,IAAInD,EAAgCgD,EAChCK,EACJ,OAAI,OAAOZ,GAAc,SACvBzC,EAAe,KAAK,IAAIgD,EAAeP,CAAS,EACvC,OAAOA,GAAc,WAC9BY,EAAkBZ,GAIlBa,EAAAA,KAAC,MAAA,CACC,KAAK,SACL,aAAW,OACX,aAAW,UACX,QAAU/F,GAAM,CACVA,EAAE,SAAWA,EAAE,eAAeyD,EAAA,CACpC,EACA,MAAO,CACL,SAAU,QACV,MAAO,EACP,OAAQ,WACR,WAAYkC,EACZ,eAAgBE,EAChB,qBAAsBA,EACtB,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,QAAS,GACT,UAAW,2BACX,GAAGP,CAAA,EAGL,SAAA,CAAAb,EAAAA,IAAC,SAAO,SAAA,gEAAA,CAAiE,EACxE,CAACpC,GACAoC,EAAAA,IAAC,MAAA,CACC,cAAW,GACX,MAAO,CACL,SAAU,WACV,MAAO,yBACP,SAAU,GACV,WACE,6CAAA,EAEL,SAAA,kBAAA,CAAA,EAIHA,EAAAA,IAAC,SAAA,CACC,IAAKe,EACL,OAAQE,EACR,MAAM,eACN,MAAM,sDACN,MAAO,CACL,OAAQ,EACR,WAAY,cACZ,YAAa,QACb,MAAO,OACP,SAAAP,EACA,OAAQ1C,EACR,UAAWqD,EACX,aAAcV,EACd,UAAW,qCACX,WAAY,oBACZ,QAAS/C,EAAU,EAAI,EACvB,GAAGkD,CAAA,CACL,CAAA,CACF,CAAA,CAAA,CAGN,CCvJO,SAASS,IAAyB,CACvC,KAAM,CAAE,KAAAlE,EAAM,OAAAG,EAAQ,KAAAsB,EAAM,MAAAE,EAAO,QAAAC,EAAS,eAAAC,EAAgB,MAAApB,CAAA,EAC1DzB,EAAA,EACF,MAAO,CACL,KAAAgB,EACA,gBAAiB,CAAC,CAACA,EACnB,OAAAG,EACA,KAAAsB,EACA,MAAAE,EACA,QAAAC,EACA,eAAAC,EACA,MAAApB,CAAA,CAEJ"}
|
package/dist/rift-react.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
let
|
|
4
|
-
const
|
|
5
|
-
function
|
|
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
|
+
const u = /* @__PURE__ */ new Map();
|
|
5
|
+
function X() {
|
|
6
6
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
7
7
|
}
|
|
8
|
-
function
|
|
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 (
|
|
12
|
-
if (
|
|
13
|
-
|
|
11
|
+
if (d && W) return Promise.resolve();
|
|
12
|
+
if (R = new URL(e.widgetUrl).origin, !d) {
|
|
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
14
|
const s = new URLSearchParams({
|
|
15
15
|
key: e.apiKey,
|
|
16
16
|
headless: "1"
|
|
17
17
|
});
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
const t =
|
|
18
|
+
d.src = `${e.widgetUrl.replace(/\/$/, "")}/?${s.toString()}`, document.body.appendChild(d), window.addEventListener("message", (o) => {
|
|
19
|
+
if (o.origin !== R) return;
|
|
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 J(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
|
|
51
|
-
|
|
47
|
+
return W ? Promise.resolve() : new Promise((s) => {
|
|
48
|
+
q.push(s), setTimeout(() => {
|
|
49
|
+
if (!W) {
|
|
50
|
+
const o = q.shift();
|
|
51
|
+
o && o();
|
|
52
52
|
}
|
|
53
53
|
}, 8e3);
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
async function
|
|
57
|
-
if (await
|
|
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 s =
|
|
60
|
-
return new Promise((
|
|
61
|
-
|
|
59
|
+
const s = X();
|
|
60
|
+
return new Promise((o, t) => {
|
|
61
|
+
u.set(s, { resolve: o, reject: t }), d.contentWindow.postMessage(
|
|
62
62
|
{ type: "rift:refresh-request", requestId: s },
|
|
63
|
-
|
|
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
|
|
73
|
-
const s =
|
|
74
|
-
await new Promise((
|
|
75
|
-
|
|
76
|
-
resolve: () =>
|
|
77
|
-
reject: () =>
|
|
72
|
+
if (await Q(e), !d?.contentWindow || !R) return;
|
|
73
|
+
const s = X();
|
|
74
|
+
await new Promise((o) => {
|
|
75
|
+
u.set(s, {
|
|
76
|
+
resolve: () => o(),
|
|
77
|
+
reject: () => o()
|
|
78
78
|
// logout is idempotent — never reject
|
|
79
|
-
}),
|
|
79
|
+
}), d.contentWindow.postMessage(
|
|
80
80
|
{ type: "rift:logout-request", requestId: s },
|
|
81
|
-
|
|
81
|
+
R
|
|
82
82
|
), setTimeout(() => {
|
|
83
|
-
|
|
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
|
|
125
|
+
const a = o || oe[s ?? "production"], L = $(() => {
|
|
122
126
|
try {
|
|
123
|
-
return new URL(
|
|
127
|
+
return new URL(a).origin;
|
|
124
128
|
} catch {
|
|
125
|
-
return
|
|
129
|
+
return a;
|
|
126
130
|
}
|
|
127
|
-
}, [
|
|
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,193 +140,213 @@ function fe({
|
|
|
136
140
|
} : null
|
|
137
141
|
);
|
|
138
142
|
},
|
|
139
|
-
[
|
|
143
|
+
[g]
|
|
140
144
|
);
|
|
141
|
-
|
|
142
|
-
if (!
|
|
143
|
-
const r =
|
|
145
|
+
y(() => {
|
|
146
|
+
if (!g) return;
|
|
147
|
+
const r = ce();
|
|
144
148
|
if (!r) return;
|
|
145
|
-
let
|
|
149
|
+
let c = !0;
|
|
146
150
|
return (async () => {
|
|
147
151
|
try {
|
|
148
|
-
const
|
|
152
|
+
const n = await V({
|
|
149
153
|
apiKey: e,
|
|
150
|
-
widgetUrl:
|
|
154
|
+
widgetUrl: a
|
|
151
155
|
});
|
|
152
|
-
if (!
|
|
153
|
-
|
|
156
|
+
if (!c) return;
|
|
157
|
+
l({
|
|
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
|
-
if (!
|
|
162
|
-
|
|
165
|
+
if (!c) return;
|
|
166
|
+
l(null);
|
|
163
167
|
}
|
|
164
168
|
})(), () => {
|
|
165
|
-
|
|
169
|
+
c = !1;
|
|
166
170
|
};
|
|
167
171
|
}, []);
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
}, []),
|
|
171
|
-
|
|
172
|
-
}, []),
|
|
173
|
-
await
|
|
174
|
-
}, [e,
|
|
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
|
|
178
|
-
return !(!
|
|
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
|
|
184
|
+
const h = await V({
|
|
181
185
|
apiKey: e,
|
|
182
|
-
widgetUrl:
|
|
183
|
-
}),
|
|
184
|
-
if (!
|
|
185
|
-
const
|
|
186
|
-
...
|
|
187
|
-
accessToken:
|
|
188
|
-
expiresAt:
|
|
186
|
+
widgetUrl: a
|
|
187
|
+
}), G = x.current;
|
|
188
|
+
if (!G) throw new Error("Signed out during refresh");
|
|
189
|
+
const te = {
|
|
190
|
+
...G,
|
|
191
|
+
accessToken: h.accessToken,
|
|
192
|
+
expiresAt: h.expiresAt
|
|
189
193
|
};
|
|
190
|
-
return
|
|
191
|
-
} catch (
|
|
192
|
-
throw
|
|
194
|
+
return l(te), h.accessToken;
|
|
195
|
+
} catch (h) {
|
|
196
|
+
throw l(null), h instanceof Error ? h : new Error(String(h));
|
|
193
197
|
} finally {
|
|
194
|
-
|
|
198
|
+
S.current = null;
|
|
195
199
|
}
|
|
196
|
-
})()),
|
|
197
|
-
}, [e,
|
|
198
|
-
|
|
200
|
+
})()), S.current);
|
|
201
|
+
}, [e, a, l]);
|
|
202
|
+
y(() => {
|
|
199
203
|
if (typeof window > "u") return;
|
|
200
|
-
const r = (
|
|
201
|
-
if (
|
|
202
|
-
const
|
|
203
|
-
if (!(!
|
|
204
|
-
switch (
|
|
204
|
+
const r = (c) => {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
232
|
-
if (!(typeof document > "u") &&
|
|
235
|
+
}, [L, P, l, f]), y(() => {
|
|
236
|
+
if (!(typeof document > "u") && f) {
|
|
233
237
|
const r = document.documentElement.style.overflow;
|
|
234
238
|
return document.documentElement.style.overflow = "hidden", () => {
|
|
235
239
|
document.documentElement.style.overflow = r;
|
|
236
240
|
};
|
|
237
241
|
}
|
|
238
|
-
}, [
|
|
239
|
-
|
|
242
|
+
}, [f]), y(() => {
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
252
|
+
if (typeof document < "u") {
|
|
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);
|
|
256
|
+
}
|
|
257
|
+
return `${a.replace(/\/$/, "")}/?${r.toString()}`;
|
|
258
|
+
}, [e, _, v, a]), Y = A(() => {
|
|
259
|
+
}, []), ee = $(
|
|
251
260
|
() => ({
|
|
252
261
|
apiKey: e,
|
|
253
|
-
widgetUrl:
|
|
254
|
-
user:
|
|
255
|
-
isOpen:
|
|
256
|
-
isReady:
|
|
257
|
-
error:
|
|
258
|
-
open:
|
|
259
|
-
close:
|
|
260
|
-
signOut:
|
|
261
|
-
getAccessToken:
|
|
262
|
-
_iframeSrc:
|
|
263
|
-
_iframeHeight:
|
|
264
|
-
_onIframeLoad:
|
|
262
|
+
widgetUrl: a,
|
|
263
|
+
user: b,
|
|
264
|
+
isOpen: f,
|
|
265
|
+
isReady: U,
|
|
266
|
+
error: O,
|
|
267
|
+
open: F,
|
|
268
|
+
close: P,
|
|
269
|
+
signOut: H,
|
|
270
|
+
getAccessToken: N,
|
|
271
|
+
_iframeSrc: z,
|
|
272
|
+
_iframeHeight: T,
|
|
273
|
+
_onIframeLoad: Y
|
|
265
274
|
}),
|
|
266
275
|
[
|
|
267
276
|
e,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
_,
|
|
272
|
-
W,
|
|
277
|
+
a,
|
|
278
|
+
b,
|
|
279
|
+
f,
|
|
273
280
|
U,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
281
|
+
O,
|
|
282
|
+
F,
|
|
283
|
+
P,
|
|
284
|
+
H,
|
|
285
|
+
N,
|
|
286
|
+
z,
|
|
287
|
+
T,
|
|
288
|
+
Y
|
|
280
289
|
]
|
|
281
290
|
);
|
|
282
|
-
return /* @__PURE__ */
|
|
291
|
+
return /* @__PURE__ */ j(Z.Provider, { value: ee, children: t });
|
|
283
292
|
}
|
|
284
|
-
|
|
293
|
+
const ue = "rgba(15,15,20,0.55)", fe = 6, le = 480, me = 18;
|
|
294
|
+
function we({
|
|
295
|
+
onSuccess: e,
|
|
296
|
+
onError: s,
|
|
297
|
+
onClose: o,
|
|
298
|
+
maxHeight: t,
|
|
299
|
+
maxWidth: i = le,
|
|
300
|
+
radius: g = me,
|
|
301
|
+
backdrop: a,
|
|
302
|
+
backdropStyle: L,
|
|
303
|
+
iframeStyle: b
|
|
304
|
+
}) {
|
|
285
305
|
const {
|
|
286
|
-
isOpen:
|
|
287
|
-
isReady:
|
|
288
|
-
error:
|
|
289
|
-
close:
|
|
290
|
-
user:
|
|
291
|
-
_iframeSrc:
|
|
292
|
-
_iframeHeight:
|
|
293
|
-
_onIframeLoad:
|
|
294
|
-
} =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}, [
|
|
298
|
-
|
|
299
|
-
}, [
|
|
300
|
-
!
|
|
301
|
-
}, [
|
|
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();
|
|
315
|
+
if (y(() => {
|
|
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(
|
|
302
325
|
"div",
|
|
303
326
|
{
|
|
304
327
|
role: "dialog",
|
|
305
328
|
"aria-modal": "true",
|
|
306
329
|
"aria-label": "Sign in",
|
|
307
|
-
onClick: (
|
|
308
|
-
|
|
330
|
+
onClick: (x) => {
|
|
331
|
+
x.target === x.currentTarget && _();
|
|
309
332
|
},
|
|
310
333
|
style: {
|
|
311
334
|
position: "fixed",
|
|
312
335
|
inset: 0,
|
|
313
336
|
zIndex: 2147483646,
|
|
314
|
-
background:
|
|
315
|
-
backdropFilter:
|
|
316
|
-
WebkitBackdropFilter:
|
|
337
|
+
background: C,
|
|
338
|
+
backdropFilter: D,
|
|
339
|
+
WebkitBackdropFilter: D,
|
|
317
340
|
display: "flex",
|
|
318
341
|
alignItems: "center",
|
|
319
342
|
justifyContent: "center",
|
|
320
343
|
padding: 16,
|
|
321
|
-
animation: "rift-fade 180ms ease-out"
|
|
344
|
+
animation: "rift-fade 180ms ease-out",
|
|
345
|
+
...L
|
|
322
346
|
},
|
|
323
347
|
children: [
|
|
324
|
-
/* @__PURE__ */
|
|
325
|
-
!
|
|
348
|
+
/* @__PURE__ */ j("style", { children: "@keyframes rift-fade { from { opacity: 0 } to { opacity: 1 } }" }),
|
|
349
|
+
!f && /* @__PURE__ */ j(
|
|
326
350
|
"div",
|
|
327
351
|
{
|
|
328
352
|
"aria-hidden": !0,
|
|
@@ -335,11 +359,11 @@ function le({ onSuccess: e, onError: s, onClose: i }) {
|
|
|
335
359
|
children: "Loading sign-in…"
|
|
336
360
|
}
|
|
337
361
|
),
|
|
338
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ j(
|
|
339
363
|
"iframe",
|
|
340
364
|
{
|
|
341
|
-
src:
|
|
342
|
-
onLoad:
|
|
365
|
+
src: U,
|
|
366
|
+
onLoad: O,
|
|
343
367
|
title: "Rift sign-in",
|
|
344
368
|
allow: "publickey-credentials-get; identity-credentials-get",
|
|
345
369
|
style: {
|
|
@@ -347,35 +371,37 @@ function le({ onSuccess: e, onError: s, onClose: i }) {
|
|
|
347
371
|
background: "transparent",
|
|
348
372
|
colorScheme: "light",
|
|
349
373
|
width: "100%",
|
|
350
|
-
maxWidth:
|
|
351
|
-
height:
|
|
352
|
-
|
|
374
|
+
maxWidth: i,
|
|
375
|
+
height: v,
|
|
376
|
+
maxHeight: M,
|
|
377
|
+
borderRadius: g,
|
|
353
378
|
boxShadow: "0 24px 60px -12px rgba(0,0,0,0.35)",
|
|
354
379
|
transition: "height 200ms ease",
|
|
355
|
-
opacity:
|
|
380
|
+
opacity: f ? 1 : 0,
|
|
381
|
+
...b
|
|
356
382
|
}
|
|
357
383
|
}
|
|
358
384
|
)
|
|
359
385
|
]
|
|
360
386
|
}
|
|
361
|
-
)
|
|
387
|
+
);
|
|
362
388
|
}
|
|
363
|
-
function
|
|
364
|
-
const { user: e, isOpen: s, open:
|
|
389
|
+
function ye() {
|
|
390
|
+
const { user: e, isOpen: s, open: o, close: t, signOut: i, getAccessToken: g, error: a } = K();
|
|
365
391
|
return {
|
|
366
392
|
user: e,
|
|
367
393
|
isAuthenticated: !!e,
|
|
368
394
|
isOpen: s,
|
|
369
|
-
open:
|
|
395
|
+
open: o,
|
|
370
396
|
close: t,
|
|
371
|
-
signOut:
|
|
372
|
-
getAccessToken:
|
|
373
|
-
error:
|
|
397
|
+
signOut: i,
|
|
398
|
+
getAccessToken: g,
|
|
399
|
+
error: a
|
|
374
400
|
};
|
|
375
401
|
}
|
|
376
402
|
export {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
403
|
+
we as RiftAuth,
|
|
404
|
+
he as RiftProvider,
|
|
405
|
+
ye as useRift
|
|
380
406
|
};
|
|
381
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 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 } from \"react\";\nimport { useRiftContext } from \"./RiftProvider\";\nimport type { RiftUser } from \"./types\";\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/**\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 */\nexport function RiftAuth({ onSuccess, onError, onClose }: 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 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: \"rgba(15,15,20,0.55)\",\n backdropFilter: \"blur(6px)\",\n WebkitBackdropFilter: \"blur(6px)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n animation: \"rift-fade 180ms ease-out\",\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: 480,\n height: _iframeHeight,\n borderRadius: 18,\n boxShadow: \"0 24px 60px -12px rgba(0,0,0,0.35)\",\n transition: \"height 200ms ease\",\n opacity: isReady ? 1 : 0,\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","onIframeLoad","value","jsx","RiftAuth","onSuccess","onError","onClose","_iframeSrc","_iframeHeight","_onIframeLoad","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":";;AAgBA,IAAIA,IAAmC,MACnCC,IAAQ,IACRC,IAAoC,CAAA,GACpCC,IAA8B;AAMlC,MAAMC,wBAAc,IAAA;AAQpB,SAASC,IAAe;AAEtB,SAAO,GAAG,KAAK,IAAA,EAAM,SAAS,EAAE,CAAC,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC7E;AAEA,SAASC,EAAcC,GAA4D;AACjF,MAAI,OAAO,WAAa;AACtB,WAAO,QAAQ,OAAO,IAAI,MAAM,iDAAiD,CAAC;AAEpF,MAAIP,KAAUC,EAAO,QAAO,QAAQ,QAAA;AAIpC,MAFAE,IAAe,IAAI,IAAII,EAAK,SAAS,EAAE,QAEnC,CAACP,GAAQ;AACX,IAAAA,IAAS,SAAS,cAAc,QAAQ,GACxCA,EAAO,aAAa,eAAe,MAAM,GACzCA,EAAO,aAAa,YAAY,IAAI,GACpCA,EAAO,QAAQ,wBACfA,EAAO,MAAM,UACX;AACF,UAAMQ,IAAS,IAAI,gBAAgB;AAAA,MACjC,KAAKD,EAAK;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AACD,IAAAP,EAAO,MAAM,GAAGO,EAAK,UAAU,QAAQ,OAAO,EAAE,CAAC,KAAKC,EAAO,SAAA,CAAU,IACvE,SAAS,KAAK,YAAYR,CAAM,GAEhC,OAAO,iBAAiB,WAAW,CAACS,MAAM;AACxC,UAAIA,EAAE,WAAWN,EAAc;AAC/B,YAAMO,IAAOD,EAAE;AACf,UAAI,GAACC,KAAQ,OAAOA,KAAS,YAAY,OAAOA,EAAK,QAAS,aACzDA,EAAK,KAAK,WAAW,OAAO,GAEjC;AAAA,YAAIA,EAAK,SAAS,cAAc;AAC9B,UAAAT,IAAQ,IACRC,EAAe,QAAQ,CAACS,MAAMA,EAAA,CAAG,GACjCT,IAAiB,CAAA;AACjB;AAAA,QACF;AACA,YAAIQ,EAAK,SAAS,uBAAuB;AACvC,gBAAME,IAAOR,EAAQ,IAAIM,EAAK,SAAS;AACvC,UAAIE,MACFR,EAAQ,OAAOM,EAAK,SAAS,GAC7BE,EAAK,QAAQ;AAAA,YACX,aAAaF,EAAK;AAAA,YAClB,WAAWA,EAAK;AAAA,YAChB,WAAWA,EAAK;AAAA,UAAA,CACjB;AAEH;AAAA,QACF;AACA,YAAIA,EAAK,SAAS,sBAAsB;AACtC,gBAAME,IAAOR,EAAQ,IAAIM,EAAK,SAAS;AACvC,UAAIE,MACFR,EAAQ,OAAOM,EAAK,SAAS,GAC7BE,EAAK,OAAO,IAAI,MAAMF,EAAK,WAAW,gBAAgB,CAAC;AAEzD;AAAA,QACF;AACA,YAAIA,EAAK,SAAS,sBAAsB;AACtC,gBAAME,IAAOR,EAAQ,IAAIM,EAAK,SAAS;AACvC,UAAIE,MACFR,EAAQ,OAAOM,EAAK,SAAS,GAC7BE,EAAK,QAAQ,EAAE,aAAa,IAAI,WAAW,IAAI,WAAW,GAAG;AAAA,QAEjE;AAAA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAIX,IAAc,QAAQ,QAAA,IACnB,IAAI,QAAQ,CAACY,MAAY;AAC9B,IAAAX,EAAe,KAAKW,CAAO,GAI3B,WAAW,MAAM;AACf,UAAI,CAACZ,GAAO;AACV,cAAMU,IAAIT,EAAe,MAAA;AACzB,QAAIS,KAAGA,EAAA;AAAA,MACT;AAAA,IACF,GAAG,GAAI;AAAA,EACT,CAAC;AACH;AAEA,eAAsBG,EAAcP,GAGR;AAE1B,MADA,MAAMD,EAAcC,CAAI,GACpB,CAACP,GAAQ,iBAAiB,CAACG;AAC7B,UAAM,IAAI,MAAM,iCAAiC;AAEnD,QAAMY,IAAYV,EAAA;AAClB,SAAO,IAAI,QAAwB,CAACQ,GAASG,MAAW;AACtD,IAAAZ,EAAQ,IAAIW,GAAW,EAAE,SAAAF,GAAS,QAAAG,GAAQ,GAC1ChB,EAAQ,cAAe;AAAA,MACrB,EAAE,MAAM,wBAAwB,WAAAe,EAAA;AAAA,MAChCZ;AAAA,IAAA,GAEF,WAAW,MAAM;AACf,YAAMS,IAAOR,EAAQ,IAAIW,CAAS;AAClC,MAAIH,MACFR,EAAQ,OAAOW,CAAS,GACxBH,EAAK,OAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,IAE9C,GAAG,GAAK;AAAA,EACV,CAAC;AACH;AAEA,eAAsBK,GAAaV,GAGjB;AAChB,MAAI;AAEF,QADA,MAAMD,EAAcC,CAAI,GACpB,CAACP,GAAQ,iBAAiB,CAACG,EAAc;AAC7C,UAAMY,IAAYV,EAAA;AAClB,UAAM,IAAI,QAAc,CAACQ,MAAY;AACnC,MAAAT,EAAQ,IAAIW,GAAW;AAAA,QACrB,SAAS,MAAMF,EAAA;AAAA,QACf,QAAQ,MAAMA,EAAA;AAAA;AAAA,MAAQ,CACvB,GACDb,EAAQ,cAAe;AAAA,QACrB,EAAE,MAAM,uBAAuB,WAAAe,EAAA;AAAA,QAC/BZ;AAAA,MAAA,GAEF,WAAW,MAAM;AACf,QAAAC,EAAQ,OAAOW,CAAS,GACxBF,EAAA;AAAA,MACF,GAAG,GAAI;AAAA,IACT,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;ACnJA,MAAMK,KAA8D;AAAA,EAClE,YAAY;AAAA,EACZ,SAAS;AACX,GAQMC,IAAuB,iBAUvBC,KAAyB,IAwBzBC,IAAcC,GAAuC,IAAI;AAExD,SAASC,IAAmC;AACjD,QAAMC,IAAMC,GAAWJ,CAAW;AAClC,MAAI,CAACG;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGJ,SAAOA;AACT;AAaA,SAASE,KAAyC;AAChD,MAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,MAAI;AACF,UAAMC,IAAM,aAAa,QAAQR,CAAoB;AACrD,WAAOQ,IAAO,KAAK,MAAMA,CAAG,IAA0B;AAAA,EACxD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASC,GAAaC,GAA8B;AAClD,MAAI,SAAO,SAAW;AACtB,QAAI;AACF,MAAIA,IAAI,aAAa,QAAQV,GAAsB,KAAK,UAAUU,CAAE,CAAC,IAChE,aAAa,WAAWV,CAAoB;AAAA,IACnD,QAAQ;AAAA,IAER;AACF;AAEO,SAASW,GAAa;AAAA,EAC3B,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,SAAAC,IAAU;AACZ,GAAsB;AACpB,QAAMC,IACJJ,KAAaf,GAAkBc,KAAe,YAAY,GACtD7B,IAAemC,EAAQ,MAAM;AACjC,QAAI;AACF,aAAO,IAAI,IAAID,CAAiB,EAAE;AAAA,IACpC,QAAQ;AACN,aAAOA;AAAA,IACT;AAAA,EACF,GAAG,CAACA,CAAiB,CAAC,GAEhB,CAACE,GAAMC,CAAO,IAAIC,EAA0B,IAAI,GAChD,CAACC,GAAQC,CAAS,IAAIF,EAAS,EAAK,GACpC,CAACG,GAAMC,CAAO,IAAIJ,EAAmB,QAAQ,GAC7C,CAACK,GAASC,CAAU,IAAIN,EAAS,EAAK,GACtC,CAACO,GAAOC,CAAQ,IAAIR,EAAwB,IAAI,GAChD,CAACS,GAAcC,CAAe,IAAIV,EAAS,GAAG,GAC9C,CAACW,GAAWC,CAAY,IAAIZ,EAAS,CAAC,GAItCa,IAAUC,EAAwB,IAAI;AAC5C,EAAAD,EAAQ,UAAUf;AAKlB,QAAMiB,IAAkBD,EAA+B,IAAI,GAErDE,IAAgBC;AAAA,IACpB,CAACC,MAA0B;AACzB,MAAAnB,EAAQmB,CAAI,GACRvB,KACFR;AAAA,QACE+B,IACI;AAAA,UACE,MAAMA,EAAK;AAAA,UACX,SAASA,EAAK;AAAA,UACd,YAAYA,EAAK;AAAA,QAAA,IAEnB;AAAA,MAAA;AAAA,IAGV;AAAA,IACA,CAACvB,CAAO;AAAA,EAAA;AAMV,EAAAwB,EAAU,MAAM;AACd,QAAI,CAACxB,EAAS;AACd,UAAMyB,IAAWnC,GAAA;AACjB,QAAI,CAACmC,EAAU;AACf,QAAIC,IAAQ;AACZ,YAAC,YAAY;AACX,UAAI;AACF,cAAMC,IAAS,MAAMjD,EAAc;AAAA,UACjC,QAAAiB;AAAA,UACA,WAAWM;AAAA,QAAA,CACZ;AACD,YAAI,CAACyB,EAAO;AACZ,QAAAL,EAAc;AAAA,UACZ,MAAMI,EAAS;AAAA,UACf,SAASA,EAAS;AAAA,UAClB,YAAYA,EAAS;AAAA,UACrB,aAAaE,EAAO;AAAA,UACpB,WAAWA,EAAO;AAAA,QAAA,CACnB;AAAA,MACH,QAAQ;AACN,YAAI,CAACD,EAAO;AAGZ,QAAAL,EAAc,IAAI;AAAA,MACpB;AAAA,IACF,GAAA,GACO,MAAM;AACX,MAAAK,IAAQ;AAAA,IACV;AAAA,EAEF,GAAG,CAAA,CAAE;AAEL,QAAME,IAAON,EAAY,CAACnD,MAA+B;AACvD,IAAAsC,EAAQtC,GAAM,QAAQ,QAAQ,GAC9B0C,EAAS,IAAI,GACbF,EAAW,EAAK,GAChBM,EAAa,CAACY,MAAMA,IAAI,CAAC,GACzBtB,EAAU,EAAI;AAAA,EAChB,GAAG,CAAA,CAAE,GAECuB,IAAQR,EAAY,MAAM;AAC9B,IAAAf,EAAU,EAAK,GACfI,EAAW,EAAK;AAAA,EAClB,GAAG,CAAA,CAAE,GAECoB,IAAUT,EAAY,YAAY;AACtC,UAAMzC,GAAa,EAAE,QAAAc,GAAQ,WAAWM,GAAmB,GAC3DoB,EAAc,IAAI;AAAA,EACpB,GAAG,CAAC1B,GAAQM,GAAmBoB,CAAa,CAAC,GAEvCW,IAAiBV,EAAY,YAA6B;AAC9D,UAAMW,IAAUf,EAAQ;AACxB,QAAI,CAACe,EAAS,OAAM,IAAI,MAAM,eAAe;AAE7C,UAAMC,IAAYD,EAAQ,YACtB,IAAI,KAAKA,EAAQ,SAAS,EAAE,QAAA,IAC5B,MACEE,IAAM,KAAK,IAAA;AAIjB,WAAI,EAFF,CAACD,KAAaA,IAAYC,IAAMnD,KAAyB,QAEtCiD,EAAQ,cACpBA,EAAQ,eAGbb,EAAgB,YAIpBA,EAAgB,WAAW,YAAY;AACrC,UAAI;AACF,cAAMO,IAAS,MAAMjD,EAAc;AAAA,UACjC,QAAAiB;AAAA,UACA,WAAWM;AAAA,QAAA,CACZ,GACKmC,IAASlB,EAAQ;AACvB,YAAI,CAACkB,EAAQ,OAAM,IAAI,MAAM,2BAA2B;AACxD,cAAMb,KAAiB;AAAA,UACrB,GAAGa;AAAA,UACH,aAAaT,EAAO;AAAA,UACpB,WAAWA,EAAO;AAAA,QAAA;AAEpB,eAAAN,EAAcE,EAAI,GACXI,EAAO;AAAA,MAChB,SAASU,GAAU;AAEjB,cAAAhB,EAAc,IAAI,GACZgB,aAAe,QAAQA,IAAM,IAAI,MAAM,OAAOA,CAAG,CAAC;AAAA,MAC1D,UAAA;AACE,QAAAjB,EAAgB,UAAU;AAAA,MAC5B;AAAA,IACF,GAAA,IACOA,EAAgB;AAAA,EACzB,GAAG,CAACzB,GAAQM,GAAmBoB,CAAa,CAAC;AAI7C,EAAAG,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AACnC,UAAMc,IAAU,CAACjE,MAAoB;AACnC,UAAIA,EAAE,WAAWN,EAAc;AAC/B,YAAMO,IAAOD,EAAE;AACf,UAAI,GAACC,KAAQ,OAAOA,KAAS,YAAY,OAAOA,EAAK,QAAS,aACzDA,EAAK,KAAK,WAAW,OAAO;AAEjC,gBAAQA,EAAK,MAAA;AAAA,UACX,KAAK;AAGH,YAAIgC,OAAmB,EAAI;AAC3B;AAAA,UACF,KAAK;AACH,YAAAwB,EAAA;AACA;AAAA,UACF,KAAK;AACH,YAAAf,EAAgB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAKzC,EAAK,SAAS,CAAC,CAAC,CAAC;AAC7D;AAAA,UACF,KAAK,uBAAuB;AAC1B,kBAAMiD,IAAiB;AAAA,cACrB,MAAMjD,EAAK;AAAA,cACX,SAASA,EAAK;AAAA,cACd,YAAYA,EAAK;AAAA,cACjB,aAAaA,EAAK;AAAA,cAClB,WAAWA,EAAK;AAAA,YAAA;AAElB,YAAA+C,EAAcE,CAAI,GAClBhB,EAAU,EAAK;AACf;AAAA,UACF;AAAA,UACA,KAAK;AACH,YAAAM,EAASvC,EAAK,OAAO;AACrB;AAAA,QAAA;AAAA,IAIN;AACA,kBAAO,iBAAiB,WAAWgE,CAAO,GACnC,MAAM,OAAO,oBAAoB,WAAWA,CAAO;AAAA,EAC5D,GAAG,CAACvE,GAAc+D,GAAOT,GAAef,CAAM,CAAC,GAG/CkB,EAAU,MAAM;AACd,QAAI,SAAO,WAAa,QACpBlB,GAAQ;AACV,YAAMiC,IAAO,SAAS,gBAAgB,MAAM;AAC5C,sBAAS,gBAAgB,MAAM,WAAW,UACnC,MAAM;AACX,iBAAS,gBAAgB,MAAM,WAAWA;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,GAAG,CAACjC,CAAM,CAAC,GAEXkB,EAAU,MAAM;AACd,IAAIzB,KAAY,CAACI,KAAMyB,EAAA;AAAA,EAEzB,GAAG,CAAA,CAAE;AAEL,QAAMY,IAAYtC,EAAQ,MAAM;AAC9B,UAAM9B,IAAS,IAAI,gBAAgB;AAAA,MACjC,KAAKuB;AAAA,MACL,MAAAa;AAAA,MACA,QAAQ,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS;AAAA,MACjE,GAAG,OAAOQ,CAAS;AAAA,IAAA,CACpB;AAKD,QAAI,OAAO,WAAa,KAAa;AACnC,YAAMyB,IAAO,SAAS,iBAChBC,IAAOD,EAAK,aAAa,YAAY;AAC3C,UAAIE,IAAuB;AAC3B,MAAID,MAAS,UAAUA,MAAS,UAASC,IAAQD,KAE/CD,EAAK,UAAU,SAAS,MAAM,KAC9B,SAAS,MAAM,UAAU,SAAS,MAAM,KAIxC,OAAO,cACP,OAAO,WAAW,8BAA8B,EAAE,aAElDE,IAAQ,SACNA,KAAOvE,EAAO,IAAI,SAASuE,CAAK;AAAA,IACtC;AAEA,WAAO,GAAG1C,EAAkB,QAAQ,OAAO,EAAE,CAAC,KAAK7B,EAAO,SAAA,CAAU;AAAA,EACtE,GAAG,CAACuB,GAAQa,GAAMQ,GAAWf,CAAiB,CAAC,GAEzC2C,IAAetB,EAAY,MAAM;AAAA,EAEvC,GAAG,CAAA,CAAE,GAECuB,KAAQ3C;AAAA,IACZ,OAAO;AAAA,MACL,QAAAP;AAAA,MACA,WAAWM;AAAA,MACX,MAAAE;AAAA,MACA,QAAAG;AAAA,MACA,SAAAI;AAAA,MACA,OAAAE;AAAA,MACA,MAAAgB;AAAA,MACA,OAAAE;AAAA,MACA,SAAAC;AAAA,MACA,gBAAAC;AAAA,MACA,YAAYQ;AAAA,MACZ,eAAe1B;AAAA,MACf,eAAe8B;AAAA,IAAA;AAAA,IAEjB;AAAA,MACEjD;AAAA,MACAM;AAAA,MACAE;AAAA,MACAG;AAAA,MACAI;AAAA,MACAE;AAAA,MACAgB;AAAA,MACAE;AAAA,MACAC;AAAA,MACAC;AAAA,MACAQ;AAAA,MACA1B;AAAA,MACA8B;AAAA,IAAA;AAAA,EACF;AAGF,SAAO,gBAAAE,EAAC7D,EAAY,UAAZ,EAAqB,OAAA4D,IAAe,UAAA/C,EAAA,CAAS;AACvD;AClVA,MAAMiD,KAAyB,uBACzBC,KAAwB,GACxBC,KAAoB,KACpBC,KAAiB;AAWhB,SAASC,GAAS;AAAA,EACvB,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC,IAAWP;AAAA,EACX,QAAAQ,IAASP;AAAA,EACT,UAAAQ;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AACF,GAAkB;AAChB,QAAM;AAAA,IACJ,QAAAtD;AAAA,IACA,SAAAI;AAAA,IACA,OAAAE;AAAA,IACA,OAAAkB;AAAA,IACA,MAAA3B;AAAA,IACA,YAAA0D;AAAA,IACA,eAAAC;AAAA,IACA,eAAAC;AAAA,EAAA,IACE5E,EAAA;AAeJ,MAbAqC,EAAU,MAAM;AACd,IAAIrB,KAAQiD,KAAWA,EAAUjD,CAAI;AAAA,EACvC,GAAG,CAACA,GAAMiD,CAAS,CAAC,GAEpB5B,EAAU,MAAM;AACd,IAAIZ,KAASyC,KAASA,EAAQzC,CAAK;AAAA,EACrC,GAAG,CAACA,GAAOyC,CAAO,CAAC,GAEnB7B,EAAU,MAAM;AACd,IAAI,CAAClB,KAAUgD,KAASA,EAAA;AAAA,EAE1B,GAAG,CAAChD,CAAM,CAAC,GAEP,CAACA,EAAQ,QAAO;AAEpB,QAAM0D,IAAgBN,GAAU,SAASX,IACnCkB,IAAeP,GAAU,QAAQV,IACjCkB,IAAUD,IAAe,IAAI,QAAQA,CAAY,QAAQ;AAS/D,MAAInD,IAAgCgD,GAChCK;AACJ,SAAI,OAAOZ,KAAc,WACvBzC,IAAe,KAAK,IAAIgD,GAAeP,CAAS,IACvC,OAAOA,KAAc,aAC9BY,IAAkBZ,IAIlB,gBAAAa;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,cAAW;AAAA,MACX,SAAS,CAAC/F,MAAM;AACd,QAAIA,EAAE,WAAWA,EAAE,iBAAeyD,EAAA;AAAA,MACpC;AAAA,MACA,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,YAAYkC;AAAA,QACZ,gBAAgBE;AAAA,QAChB,sBAAsBA;AAAA,QACtB,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,WAAW;AAAA,QACX,GAAGP;AAAA,MAAA;AAAA,MAGL,UAAA;AAAA,QAAA,gBAAAb,EAAC,WAAO,UAAA,iEAAA,CAAiE;AAAA,QACxE,CAACpC,KACA,gBAAAoC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,eAAW;AAAA,YACX,OAAO;AAAA,cACL,UAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU;AAAA,cACV,YACE;AAAA,YAAA;AAAA,YAEL,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAIH,gBAAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAKe;AAAA,YACL,QAAQE;AAAA,YACR,OAAM;AAAA,YACN,OAAM;AAAA,YACN,OAAO;AAAA,cACL,QAAQ;AAAA,cACR,YAAY;AAAA,cACZ,aAAa;AAAA,cACb,OAAO;AAAA,cACP,UAAAP;AAAA,cACA,QAAQ1C;AAAA,cACR,WAAWqD;AAAA,cACX,cAAcV;AAAA,cACd,WAAW;AAAA,cACX,YAAY;AAAA,cACZ,SAAS/C,IAAU,IAAI;AAAA,cACvB,GAAGkD;AAAA,YAAA;AAAA,UACL;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN;ACvJO,SAASS,KAAyB;AACvC,QAAM,EAAE,MAAAlE,GAAM,QAAAG,GAAQ,MAAAsB,GAAM,OAAAE,GAAO,SAAAC,GAAS,gBAAAC,GAAgB,OAAApB,EAAA,IAC1DzB,EAAA;AACF,SAAO;AAAA,IACL,MAAAgB;AAAA,IACA,iBAAiB,CAAC,CAACA;AAAA,IACnB,QAAAG;AAAA,IACA,MAAAsB;AAAA,IACA,OAAAE;AAAA,IACA,SAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,OAAApB;AAAA,EAAA;AAEJ;"}
|
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