@emblemvault/auth-sdk 2.3.2 → 2.3.4

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 CHANGED
@@ -103,6 +103,17 @@ Retrieves or generates the vault API key.
103
103
  #### `logout(): void`
104
104
  Clears the current session.
105
105
 
106
+ #### `hydrateSession(session: AuthSession): void`
107
+ Restores a session from external storage. Useful for Node.js environments where `localStorage` is not available. This activates the auto-refresh timer.
108
+
109
+ ```typescript
110
+ // Restore session from file/database
111
+ const saved = await loadSessionFromFile();
112
+ if (saved) {
113
+ auth.hydrateSession(saved);
114
+ }
115
+ ```
116
+
106
117
  #### `on(event, handler): void`
107
118
  Subscribe to authentication events.
108
119
 
@@ -143,6 +154,51 @@ const auth = new EmblemAuthSDK({
143
154
  - Calling `logout()` clears the persisted session
144
155
  - Works gracefully in private browsing mode (falls back to memory-only)
145
156
 
157
+ ### Node.js Usage
158
+
159
+ In Node.js environments, `localStorage` is not available, so you must implement your own session persistence. The SDK's auto-refresh timer still works—use `hydrateSession()` to restore sessions and subscribe to events to keep your storage in sync.
160
+
161
+ ```typescript
162
+ import { EmblemAuthSDK } from '@emblemvault/auth-sdk';
163
+ import fs from 'fs/promises';
164
+
165
+ const SESSION_FILE = './session.json';
166
+
167
+ const auth = new EmblemAuthSDK({
168
+ appId: 'your-app-id',
169
+ persistSession: false // No effect in Node.js, but explicit
170
+ });
171
+
172
+ // Restore session from file on startup
173
+ async function restoreSession() {
174
+ try {
175
+ const data = await fs.readFile(SESSION_FILE, 'utf-8');
176
+ const session = JSON.parse(data);
177
+ auth.hydrateSession(session); // Activates auto-refresh timer
178
+ } catch {
179
+ // No saved session
180
+ }
181
+ }
182
+
183
+ // Keep file in sync with SDK's auto-refresh
184
+ auth.on('sessionRefreshed', async (session) => {
185
+ await fs.writeFile(SESSION_FILE, JSON.stringify(session));
186
+ });
187
+
188
+ auth.on('sessionExpired', async () => {
189
+ await fs.unlink(SESSION_FILE).catch(() => {});
190
+ });
191
+
192
+ // Initialize
193
+ await restoreSession();
194
+ ```
195
+
196
+ **Key points:**
197
+ - `hydrateSession()` restores the session and starts the auto-refresh timer
198
+ - The SDK will automatically refresh tokens ~60 seconds before expiry
199
+ - Subscribe to `sessionRefreshed` to persist the new session after refresh
200
+ - Subscribe to `sessionExpired` to clear your storage when refresh fails
201
+
146
202
  ### Types
147
203
 
148
204
  #### `VaultInfo`
@@ -24,6 +24,9 @@ export declare class SessionManager {
24
24
  private readonly onRefresh?;
25
25
  private readonly events;
26
26
  private readonly refreshSkewMs;
27
+ private consecutiveFailures;
28
+ private readonly maxBackoffMs;
29
+ private readonly baseBackoffMs;
27
30
  constructor(config?: SessionManagerConfig);
28
31
  /**
29
32
  * Set the current session and schedule refresh
@@ -51,6 +54,14 @@ export declare class SessionManager {
51
54
  destroy(): void;
52
55
  private emit;
53
56
  private cancel;
57
+ /**
58
+ * Calculate backoff delay with exponential increase, capped at maxBackoffMs
59
+ */
60
+ private getBackoffDelay;
61
+ /**
62
+ * Schedule a retry after a failed refresh attempt
63
+ */
64
+ private scheduleRetry;
54
65
  private schedule;
55
66
  }
56
67
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../src/SessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,UAAU,oBAAoB;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAElE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,gBAAgB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACjD,kBAAkB,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF,KAAK,eAAe,GAAG,MAAM,eAAe,CAAC;AAC7C,KAAK,mBAAmB,CAAC,CAAC,SAAS,eAAe,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;AAEzE,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAwD;IACnF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoE;IAC3F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,GAAE,oBAAyB;IAK7C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAM7C;;OAEG;IACH,UAAU,IAAI,WAAW,GAAG,IAAI;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAO9E;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAU/E;;OAEG;IACH,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,QAAQ;CA6CjB"}
1
+ {"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../src/SessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,UAAU,oBAAoB;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAElE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,gBAAgB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACjD,kBAAkB,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF,KAAK,eAAe,GAAG,MAAM,eAAe,CAAC;AAC7C,KAAK,mBAAmB,CAAC,CAAC,SAAS,eAAe,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;AAEzE,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAwD;IACnF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoE;IAC3F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAElC,MAAM,GAAE,oBAAyB;IAK7C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAM7C;;OAEG;IACH,UAAU,IAAI,WAAW,GAAG,IAAI;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAO9E;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAU/E;;OAEG;IACH,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,MAAM;IAOd;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,aAAa;IA8DrB,OAAO,CAAC,QAAQ;CAyDjB"}
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).EmblemAuth={})}(this,function(t){"use strict";class e{constructor(t={}){this.session=null,this.timer=null,this.events={},this.onRefresh=t.onRefresh,this.refreshSkewMs=t.refreshSkewMs??6e4}setSession(t){this.session=t,this.schedule(),this.emit("session",t)}getSession(){return this.session}clear(){this.session=null,this.cancel(),this.emit("session",null)}on(t,e){this.events[t]||(this.events[t]=[]),this.events[t].push(e)}off(t,e){const s=this.events[t];if(s){const t=s.indexOf(e);-1!==t&&s.splice(t,1)}}destroy(){this.cancel(),this.session=null,Object.keys(this.events).forEach(t=>{delete this.events[t]})}emit(t,e){const s=this.events[t];if(s)for(const t of s)try{t(e)}catch(t){}}cancel(){this.timer&&(clearTimeout(this.timer),this.timer=null)}schedule(){this.cancel();const t=this.session;if(!t?.expiresAt)return;const e=Date.now(),s=t.expiresAt-e;if(s<=0)return void this.emit("sessionExpired",t);const i=Math.max(0,s-this.refreshSkewMs);this.emit("sessionWillRefresh",{inMs:i,ttl:s}),this.timer=setTimeout(async()=>{if(this.onRefresh)try{const e=await this.onRefresh(t);e?.expiresAt&&e.authToken?(this.setSession(e),this.emit("sessionRefreshed",e)):Date.now()>=t.expiresAt&&this.emit("sessionExpired",t)}catch(e){Date.now()>=t.expiresAt&&this.emit("sessionExpired",t)}else Date.now()>=t.expiresAt&&this.emit("sessionExpired",t)},i)}}class s{constructor(t){if(this.session=null,this.pendingNonce=null,this.overlayEl=null,this._iframeEl=null,this.overlayCleanup=null,this.events={},this._cachedVaultInfo=null,this._visitorId=null,!t?.appId)throw new Error("appId is required");if(this.config={...t,authUrl:t.authUrl??"https://auth.emblemvault.ai",apiUrl:t.apiUrl??"https://api.emblemvault.ai",persistSession:t.persistSession??!0},this.storageKey=`emblem_session_${t.appId}`,this.visitorIdKey=`emblem_visitorId_${t.appId}`,this.messageHandler=this.onMessage.bind(this),window.addEventListener("message",this.messageHandler),this.sessionMgr=new e({onRefresh:async t=>{try{return await this.refreshSession()||t}catch{return t}}}),this.sessionMgr.on("sessionExpired",t=>this.emit("sessionExpired",t)),this.sessionMgr.on("sessionRefreshed",t=>this.emit("sessionRefreshed",t)),this.sessionMgr.on("sessionWillRefresh",t=>this.emit("sessionWillRefresh",t)),this.config.persistSession){const t=this.loadPersistedSession();t&&this.hydrateSession(t),this._visitorId=this.loadPersistedVisitorId()}}async authenticateWallet(t){const{network:e,message:s,signature:i,publicKey:n,address:r}=t;if(!this.config.apiUrl)throw new Error("apiUrl is required for authenticateWallet");if(!e||!s||!i)throw new Error("network, message, signature are required");const a=`${this.config.apiUrl.replace(/\/$/,"")}/api/auth/wallet/verify-external`,o=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,network:e,message:s,signature:i,publicKey:n,address:r})});if(!o.ok){const t=await this.safeJson(o),e=new Error(t?.error||`wallet_verify_${o.status}`);throw e.status=o.status,e.payload=t,e}const c=await o.json(),l=c?.session;return l&&(this.session=l,this.persistSession(l),this.sessionMgr.setSession(l),this.config.onSuccess?.(l),this.emit("session",l)),l}async openAuthModal(){const t=window.location.origin;let e=null,s=null;try{e=await this.getAuthInit({origin:t})}catch(t){s=t}const i=e?.nonce||this.randomId();this.pendingNonce=i;let n=this.resolveModalUrl({nonce:i,origin:t,state:e?.state});if("origin_not_allowed"===s?.payload?.error)try{const e=new URL("/connect",this.config.authUrl);e.searchParams.set("error","origin_not_allowed"),e.searchParams.set("appId",this.config.appId),e.searchParams.set("origin",t),n=e.toString()}catch{}const r=this.config.modalMode||"auto";let a=!1;if("iframe"===r||"auto"===r)try{a=this.openIframeModal(n)}catch(t){}if(!a){const t=this.popupFeatures();if(!window.open(n,"emblem-auth",t)){const t=new Error("Popup blocked. Please allow popups for this site.");this.emitError(t)}}}getSession(){return this.session}getVisitorId(){return this._visitorId}async refreshSession(){try{if(!this.session)return null;const t=this.tryGetOriginFromConfig();if(!t)return this.session;const e=`${t}/api/auth/refresh`,s=Boolean(this.session.refreshToken),i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s?{refreshToken:this.session.refreshToken}:{}),credentials:"include"});if(!i.ok)return this.session;const n=await i.json(),r=n?.session;return r&&(this.session=r,this.persistSession(r),this.sessionMgr.setSession(r),this.emit("session",r)),this.session}catch{return this.session}}async getVaultInfo(){if(this._cachedVaultInfo)return this._cachedVaultInfo;const t=this.getSession();if(!t?.authToken)throw new Error("No session");if(!this.config.apiUrl)throw new Error("apiUrl is required");const e=this.config.apiUrl.replace(/\/$/,""),s=await fetch(`${e}/vault/info`,{method:"POST",headers:{Authorization:`Bearer ${t.authToken}`,"Content-Type":"application/json"},body:"{}"});if(!s.ok)throw new Error("Failed to fetch vault info");const i=await s.json();return this._cachedVaultInfo=i,i}async getVaultApiKey(){if(!this.config.apiUrl)throw new Error("apiUrl is required");const t=this.getSession();if(!t?.authToken)throw new Error("No active session");const e=this.config.apiUrl.replace(/\/$/,""),s=t.authToken,i=t.user?.vaultId;let n=null;try{const t=await fetch(`${e}/vault/info-complete`,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(t.ok){const e=await t.json();n=e?.raw?.pkp?.api_key_hash||e?.raw?.pkp?.apiKeyHash||null}}catch{}if(!n)try{const s=t.appId,r=t.user?.identifier||"",a=s?`${s}:${r}`:r;if(a){const t=await fetch(`${e}/api/vaults/${encodeURIComponent(a)}`);if(t.ok){const e=await t.json(),s=Array.isArray(e)?e.find(t=>String(t.tokenId||t.token_id)===String(i))||e[0]:null;n=s?.api_key_hash||s?.apiKeyHash||null}}}catch{}if(!n){const t=await fetch(`${e}/api/vaults/${encodeURIComponent(i)}/api-key`,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(!t.ok)throw new Error(`gen_key_${t.status}`);const n=await t.json(),r=n?.apiKey||n?.key||null;if(!r)throw new Error("missing_apiKey");return r}const r=await fetch(`${e}/decrypt`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({tokenId:i,dataToEncryptHash:n})});if(!r.ok)throw new Error(`decrypt_${r.status}`);const a=await r.json(),o=a?.decryptedString||null;if(!o)throw new Error("missing_decrypted");return o}hydrateSession(t){t?.authToken&&(this.session=t,this.persistSession(t),this._cachedVaultInfo=null,this.sessionMgr.setSession(t),this.emit("session",t))}logout(){this.session=null,this.persistSession(null),this._visitorId=null,this.persistVisitorId(null),this._cachedVaultInfo=null,this.sessionMgr.clear(),this.emit("session",null)}on(t,e){this.events[t]||(this.events[t]=[]),this.events[t].push(e)}off(t,e){const s=this.events[t];if(s){const t=s.indexOf(e);-1!==t&&s.splice(t,1)}}async getSignerContext(){const t=this.getSession();if(!t?.authToken)throw new Error("No active session. Call openAuthModal() or authenticateWallet() first.");const e=await this.getVaultInfo();return{config:{baseUrl:this.config.apiUrl,getJwt:()=>this.getSession()?.authToken},vaultInfo:{vaultId:e.vaultId,evmAddress:e.evmAddress||"0x",address:e.solanaAddress||e.address||"",tokenId:e.tokenId,created_by:e.created_by}}}async toViemAccount(){const{toViemAccount:t}=await Promise.resolve().then(function(){return l}),{config:e,vaultInfo:s}=await this.getSignerContext();return t(e,s)}async toEthersWallet(t){const{toEthersWallet:e}=await Promise.resolve().then(function(){return u}),{config:s,vaultInfo:i}=await this.getSignerContext();return e(s,t,i)}async toWeb3Adapter(){const{toWeb3Adapter:t}=await Promise.resolve().then(function(){return y}),{config:e,vaultInfo:s}=await this.getSignerContext();return t(e,s)}async toSolanaWeb3Signer(){const{toSolanaWeb3Signer:t}=await Promise.resolve().then(function(){return m}),{config:e,vaultInfo:s}=await this.getSignerContext();return t(e,s)}async toSolanaKitSigner(){const{toSolanaKitSigner:t}=await Promise.resolve().then(function(){return m}),{config:e,vaultInfo:s}=await this.getSignerContext();return t(e,s)}destroy(){window.removeEventListener("message",this.messageHandler),this.sessionMgr.destroy(),this.closeOverlay(),Object.keys(this.events).forEach(t=>{delete this.events[t]})}resolveModalUrl(t){const e=`${this.config.authUrl.replace(/\/$/,"")}/connect`,s=new URL(e,window.location.href);return s.searchParams.set("appId",this.config.appId),s.searchParams.set("origin",t.origin),s.searchParams.set("nonce",t.nonce),t.state&&s.searchParams.set("state",t.state),s.toString()}onMessage(t){try{if(!new Set([window.location.origin,this.tryGetOriginFromConfig()].filter(Boolean)).has(t.origin))return;const e=t.data;if("emblem-auth-cancelled"===e?.type){if(!this.pendingNonce||e.nonce!==this.pendingNonce)return;return this.pendingNonce=null,this.closeOverlay(),this.config.onCancel&&this.config.onCancel(),void this.emit("cancelled",void 0)}if("emblem-auth-success"!==e?.type)return;if(!this.pendingNonce||e.nonce!==this.pendingNonce)return;this.pendingNonce=null;const s=e.session;this.session=s,this.persistSession(s),this.sessionMgr.setSession(s),e.visitorId&&(this._visitorId=e.visitorId,this.persistVisitorId(e.visitorId)),s&&this.config.onSuccess&&this.config.onSuccess(s),this.emit("session",s),this.closeOverlay()}catch(t){this.emitError(t)}}emit(t,e){const s=this.events[t];if(s)for(const t of s)try{t(e)}catch(t){}}emitError(t){this.config.onError?.(t),this.emit("authError",t)}popupFeatures(){const t=Math.max(0,(window.outerHeight-600)/2);return`popup=yes,width=420,height=600,left=${Math.max(0,(window.outerWidth-420)/2)},top=${t}`}randomId(){const t=new Uint8Array(16);if(window.crypto?.getRandomValues)window.crypto.getRandomValues(t);else for(let e=0;e<t.length;e++)t[e]=Math.floor(256*Math.random());return Array.from(t,t=>t.toString(16).padStart(2,"0")).join("")}tryGetOriginFromConfig(){try{if(!this.config.authUrl)return null;return new URL(this.config.authUrl,window.location.href).origin}catch{return null}}async getAuthInit(t){const e=await fetch(`${this.config.authUrl.replace(/\/$/,"")}/api/auth/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,origin:t.origin})}),s=await e.json().catch(()=>null);if(!e.ok){const t=new Error(`init ${e.status}`);throw t.status=e.status,t.payload=s||{},t}return s}openIframeModal(t){this.overlayEl&&this.closeOverlay();const e=document.createElement("div");e.setAttribute("data-emblem-overlay",""),e.style.position="fixed",e.style.inset="0",e.style.background="rgba(0,0,0,0.5)",e.style.zIndex="999999",e.style.display="flex",e.style.alignItems="center",e.style.justifyContent="center";const s=document.createElement("div");s.style.width="min(420px, 95vw)",s.style.height="min(600px, 90vh)",s.style.background="#12161b",s.style.border="1px solid #222b35",s.style.borderRadius="12px",s.style.boxShadow="0 12px 48px rgba(0,0,0,0.7)",s.style.overflow="hidden",s.style.position="relative";const i=document.createElement("button");i.textContent="×",i.setAttribute("aria-label","Close"),i.style.position="absolute",i.style.top="6px",i.style.right="8px",i.style.zIndex="2",i.style.background="transparent",i.style.color="#e6eef8",i.style.border="none",i.style.fontSize="20px",i.style.cursor="pointer",i.onclick=()=>this.cancelAuth();const n=document.createElement("iframe");n.src=t,n.style.width="100%",n.style.height="100%",n.style.border="0",n.referrerPolicy="no-referrer",n.allow="clipboard-read; clipboard-write;",s.appendChild(i),s.appendChild(n),e.appendChild(s),document.body.appendChild(e);const r=t=>{"Escape"===t.key&&this.cancelAuth()};return document.addEventListener("keydown",r,{capture:!0}),this.overlayEl=e,this._iframeEl=n,this.overlayCleanup=()=>{document.removeEventListener("keydown",r,{capture:!0})},!0}closeOverlay(){try{this.overlayCleanup?.()}catch{}this.overlayCleanup=null,this.overlayEl?.parentNode&&this.overlayEl.parentNode.removeChild(this.overlayEl),this.overlayEl=null,this._iframeEl=null}cancelAuth(){this.pendingNonce=null,this.closeOverlay(),this.config.onCancel&&this.config.onCancel(),this.emit("cancelled",void 0)}async safeJson(t){try{return await t.json()}catch{return null}}loadPersistedSession(){try{if("undefined"==typeof localStorage)return null;const t=localStorage.getItem(this.storageKey);if(!t)return null;const e=JSON.parse(t);return e?.authToken&&e?.expiresAt?Date.now()>=e.expiresAt?(localStorage.removeItem(this.storageKey),null):e:null}catch{return null}}persistSession(t){try{if("undefined"==typeof localStorage)return;t&&this.config.persistSession?localStorage.setItem(this.storageKey,JSON.stringify(t)):localStorage.removeItem(this.storageKey)}catch{}}loadPersistedVisitorId(){try{return"undefined"==typeof localStorage?null:localStorage.getItem(this.visitorIdKey)}catch{return null}}persistVisitorId(t){try{if("undefined"==typeof localStorage)return;t&&this.config.persistSession?localStorage.setItem(this.visitorIdKey,t):localStorage.removeItem(this.visitorIdKey)}catch{}}}async function i(t,e,s){const i=s.baseUrl??"https://api.emblemvault.ai",n=await async function(t){if("function"==typeof t.getAuthHeaders){const e=await t.getAuthHeaders();if(e&&"object"==typeof e)return e}const e=t.jwt??("function"==typeof t.getJwt?await t.getJwt():void 0)??t.sdk?.getSession()?.authToken??void 0;if(e)return{Authorization:`Bearer ${e}`};if(t.apiKey)return{"x-api-key":t.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}(s),r=await fetch(`${i}${t}`,{method:"POST",headers:{"content-type":"application/json",...n},body:JSON.stringify(e,(t,e)=>"bigint"==typeof e?e.toString():e)});if(!r.ok){const t=await r.text().catch(()=>"");throw new Error(function(t,e){let s=`Emblem signer error ${t}`;return t>=500?s+=": Internal server error":401===t||403===t?s+=": Authentication failed":404===t?s+=": Resource not found":405===t?s+=": Method not allowed":e&&(s+=`: ${e.substring(0,200)}`),s}(r.status,t))}return r.json()}function n(t,e){const s=Number(t);if(!Number.isSafeInteger(s))throw new Error(`${e} value ${t} exceeds safe integer range (max: ${Number.MAX_SAFE_INTEGER})`);return s}function r(t){return"bigint"==typeof t?"0x"+t.toString(16):t}function a(t){const e={...t};return void 0!==e.value&&(e.value=r(e.value)),void 0!==e.gas&&(e.gasLimit=r(e.gas),delete e.gas),void 0!==e.gasLimit&&(e.gasLimit=r(e.gasLimit)),void 0!==e.gasPrice&&(e.gasPrice=r(e.gasPrice)),void 0!==e.maxFeePerGas&&(e.maxFeePerGas=r(e.maxFeePerGas)),void 0!==e.maxPriorityFeePerGas&&(e.maxPriorityFeePerGas=r(e.maxPriorityFeePerGas)),void 0!==e.nonce&&(e.nonce=n(e.nonce,"nonce")),void 0!==e.chainId&&(e.chainId=n(e.chainId,"chainId")),void 0===e.maxFeePerGas&&void 0===e.maxPriorityFeePerGas||(void 0===e.gasPrice&&void 0!==e.maxFeePerGas&&(e.gasPrice=e.maxFeePerGas),delete e.maxFeePerGas,delete e.maxPriorityFeePerGas),delete e.type,delete e.accessList,delete e.account,delete e.chain,delete e.from,e}function o(t){let e="0x";for(let s=0;s<t.length;s++)e+=t[s].toString(16).padStart(2,"0");return e}async function c(t){const e=await i("/vault/info",{},t);if(!e||!e.vaultId||!e.evmAddress)throw new Error("Invalid vault info response: missing required fields");if(!String(e.evmAddress).startsWith("0x"))throw new Error("Invalid evmAddress format in response");return{vaultId:e.vaultId,tokenId:e.vaultId,address:e.address||"",evmAddress:e.evmAddress,created_by:e.created_by}}var l=Object.freeze({__proto__:null,toViemAccount:async function(t,e){const s=e??await c(t),{evmAddress:n,vaultId:r}=s,{toAccount:l}=await import("viem/accounts");return l({address:n,async signMessage({message:e}){let s,n=!1;if("string"==typeof e)s=e,n=!1;else if(e&&void 0!==e.raw){const t=e.raw;s="string"==typeof t?t:o(t),n=!0}else if(e instanceof Uint8Array)s=o(e),n=!1;else{if("string"!=typeof(a=e)||!/^0x[0-9a-fA-F]*$/.test(a))throw new Error(`Unsupported message type: ${typeof e}. Expected string, Uint8Array, or hex string.`);s=e,n=!1}var a;return(await i("/sign-eth-message",{vaultId:r,message:s,raw:n},t)).signature},async signTypedData(e){const{domain:s,types:n,message:a}=e;return(await i("/sign-typed-message",{vaultId:r,domain:s,types:n,message:a},t)).signature},async signTransaction(e,s){const n=a(e);return(await i("/sign-eth-tx",{vaultId:r,transaction:n},t)).signedTransaction}})}});var h,d,u=Object.freeze({__proto__:null,toEthersWallet:async function(t,e,s){let n;try{const t="ethers";n=await import(t)}catch{throw new Error("ethers is required for toEthersWallet(). Install it with: npm install ethers")}const{AbstractSigner:r,resolveAddress:l}=n,h=s??await c(t);class d extends r{constructor(t,e,s){super(e??null),this._address=null,this._vaultId=null,this._chainId=1,this._config=t,s?.address&&(this._address=s.address),s?.vaultId&&(this._vaultId=s.vaultId),s?.chainId&&(this._chainId=s.chainId)}async initialize(){return this._initPromise||(this._initPromise=c(this._config).then(t=>{this._address=t.evmAddress,this._vaultId=t.vaultId}).catch(t=>{throw this._initPromise=void 0,t})),this._initPromise}async getAddress(){return this._address||await this.initialize(),this._address}getVaultId(){if(!this._vaultId)throw new Error("Wallet not initialized. Call initialize() first.");return this._vaultId}setChainId(t){this._chainId=t}getChainId(){return this._chainId}connect(t){if(!t)throw new Error("Provider cannot be null");return new d(this._config,t,{address:this._address??void 0,vaultId:this._vaultId??void 0,chainId:this._chainId})}async signMessage(t){this._vaultId||await this.initialize();const e="string"==typeof t?t:o(t);return(await i("/sign-eth-message",{vaultId:this._vaultId,message:e},this._config)).signature}async signTypedData(t,e,s){this._vaultId||await this.initialize();const n={...e};n&&n.EIP712Domain&&delete n.EIP712Domain;return(await i("/sign-typed-message",{vaultId:this._vaultId,domain:t,types:n,message:s},this._config)).signature}async _signTypedData(t,e,s){return this.signTypedData(t,e,s)}async signTransaction(t){this._vaultId||await this.initialize();const e=t.from,s=await this.getAddress();if(e&&e.toLowerCase()!==s.toLowerCase())throw new Error("transaction from does not match signer address");const n=this.provider?{...await this.populateTransaction(t)}:{...t};if(n.from&&delete n.from,!("to"in n)||!n.to)throw new Error("Transaction must have a 'to' address");if(void 0===n.nonce||null===n.nonce)throw new Error("Transaction must have a nonce");const r=a(n);return(await i("/sign-eth-tx",{vaultId:this._vaultId,transaction:r,options:{chainId:this._chainId}},this._config)).signedTransaction}async sendTransaction(t){if(!this.provider)throw new Error("Provider required to send transaction");const e=await this.signTransaction(t);return await this.provider.broadcastTransaction(e)}async populateTransaction(t){const e={...t};if(!this.provider)throw new Error("Provider required to populate transaction");const s=e.from?await l(e.from,this.provider):await this.getAddress();let i;if(e.chainId)i=BigInt(e.chainId),this._chainId=Number(e.chainId);else{const t=await this.provider.getNetwork();i=t.chainId,this._chainId=Number(t.chainId)}const n=null!=e.nonce?Number(e.nonce):await this.provider.getTransactionCount(s,"pending"),r=e.to?await l(e.to,this.provider):null,a=e.value?BigInt(e.value.toString()):0n;let o;if(e.gasLimit)o=BigInt(e.gasLimit.toString());else try{o=await this.provider.estimateGas({...e,from:s})}catch{o=21000n}let c=null;if(e.gasPrice||2===e.type)e.gasPrice&&(c=BigInt(e.gasPrice.toString()));else{c=(await this.provider.getFeeData()).gasPrice??null}const h={from:s,to:r,value:a,nonce:n,gasLimit:o,data:e.data,chainId:i,type:e.type||void 0};return null!==c&&(h.gasPrice=c),e.maxFeePerGas&&(h.maxFeePerGas=BigInt(e.maxFeePerGas.toString())),e.maxPriorityFeePerGas&&(h.maxPriorityFeePerGas=BigInt(e.maxPriorityFeePerGas.toString())),h}async signAndBroadcast(t,e=!1){if(!this.provider)throw new Error("Provider required to send transaction");const s=await this.signTransaction(t),i=(await this.provider.broadcastTransaction(s)).hash;return e&&await this.provider.waitForTransaction(i),i}}return new d(t,e,{address:h.evmAddress,vaultId:h.vaultId})}});function f(t,e,s,i){if("a"===s&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(t):i?i.value:e.get(t)}function g(t,e,s,i,n){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?n.call(t,s):n?n.value=s:e.set(t,s),s}"function"==typeof SuppressedError&&SuppressedError;class p{constructor(t,e,s){h.set(this,void 0),d.set(this,void 0),this.address=t,g(this,h,e,"f"),g(this,d,s,"f")}async signMessage(t){const e="string"==typeof t?t:o(t);return(await i("/sign-eth-message",{vaultId:f(this,h,"f"),message:e},f(this,d,"f"))).signature}async signTypedData(t,e,s){return(await i("/sign-typed-message",{vaultId:f(this,h,"f"),domain:t,types:e,message:s},f(this,d,"f"))).signature}async signTransaction(t){const e=a(t);return{rawTransaction:(await i("/sign-eth-tx",{vaultId:f(this,h,"f"),transaction:e},f(this,d,"f"))).signedTransaction}}}h=new WeakMap,d=new WeakMap;var y=Object.freeze({__proto__:null,EmblemWeb3Adapter:p,toWeb3Adapter:async function(t,e){const s=e??await c(t);return new p(s.evmAddress,s.vaultId,t)}});class w{constructor(t,e){this.publicKey=e.address,this.config=t,this.vaultId=e.vaultId}async signMessage(t){const e="string"==typeof t?(new TextEncoder).encode(t):t,s=btoa(String.fromCharCode(...e)),n=await i("/sign-solana-message",{vaultId:this.vaultId,message:s},this.config);try{const t="undefined"!=typeof window?window:void 0;if(t?.bs58)return t.bs58.decode(n.signature);return Uint8Array.from(atob(n.signature),t=>t.charCodeAt(0))}catch(t){if(n.signature.startsWith("0x")){const t=n.signature.slice(2),e=new Uint8Array(t.length/2);for(let s=0;s<t.length;s+=2)e[s/2]=parseInt(t.substr(s,2),16);return e}throw new Error(`Unable to decode signature format: ${t}`)}}async signTransaction(t){const e=this.serializeTransaction(t),s=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:e,broadcast:!1,versionedTransaction:!0},this.config),n=s.serializedSignedTransaction||s.signedTransaction;if(!n)throw new Error("No signed transaction data received from server");return this.deserializeTransaction(n)}serializeTransaction(t){if(t&&"object"==typeof t){if(t.serialize){const e=t.serialize();return btoa(String.fromCharCode(...e))}if(t.instructions||t.recentBlockhash)throw new Error("Cannot serialize unsigned transaction objects. Please use VersionedTransaction.")}return t}deserializeTransaction(t){if("object"==typeof t&&t&&t.serializedSignedTransaction&&(t=t.serializedSignedTransaction),"string"==typeof t)try{const e=atob(t);return new Uint8Array(e.split("").map(t=>t.charCodeAt(0)))}catch(t){throw new Error(`Unable to deserialize transaction response: ${t}`)}return t}getVaultId(){return this.vaultId}async signAllTransactions(t){const e=[];for(const s of t)e.push(await this.signTransaction(s));return e}canSign(t){return t===this.publicKey}async signAndBroadcast(t,e=!0){const s=this.serializeTransaction(t),n=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:s,broadcast:e,versionedTransaction:!0},this.config);if(e){if(!n.transactionSignature)throw new Error("No transaction signature received from broadcast");return n.transactionSignature}if(!n.serializedSignedTransaction)throw new Error("No signed transaction data received from server");return n.serializedSignedTransaction}}var m=Object.freeze({__proto__:null,EmblemSolanaSigner:w,toSolanaWeb3Signer:async function(t,e){const s=e??await c(t);return new w(t,s)},toSolanaKitSigner:async function(t,e){const s=e??await c(t);return new w(t,s)}});t.EmblemAuthSDK=s,t.SessionManager=e,t.default=s,Object.defineProperty(t,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EmblemAuth={})}(this,function(e){"use strict";class t{constructor(e={}){this.session=null,this.timer=null,this.events={},this.consecutiveFailures=0,this.maxBackoffMs=3e4,this.baseBackoffMs=2e3,this.onRefresh=e.onRefresh,this.refreshSkewMs=e.refreshSkewMs??6e4}setSession(e){this.session=e,this.schedule(),this.emit("session",e)}getSession(){return this.session}clear(){this.session=null,this.cancel(),this.emit("session",null)}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){const s=this.events[e];if(s){const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}destroy(){this.cancel(),this.session=null,Object.keys(this.events).forEach(e=>{delete this.events[e]})}emit(e,t){const s=this.events[e];if(s)for(const e of s)try{e(t)}catch(e){}}cancel(){this.timer&&(clearTimeout(this.timer),this.timer=null)}getBackoffDelay(){return Math.min(this.baseBackoffMs*Math.pow(2,Math.max(0,this.consecutiveFailures-1)),this.maxBackoffMs)}scheduleRetry(e){this.cancel();const t=Date.now(),s=e.expiresAt-t;if(s<=0)return void this.emit("sessionExpired",e);const i=Math.min(this.getBackoffDelay(),s-1e3);this.timer=i<=0?setTimeout(()=>{this.emit("sessionExpired",e)},s):setTimeout(async()=>{if(this.onRefresh)try{const t=await this.onRefresh(e);t?.expiresAt&&t.authToken&&t.expiresAt!==e.expiresAt?(this.consecutiveFailures=0,this.setSession(t),this.emit("sessionRefreshed",t)):(this.consecutiveFailures++,Date.now()>=e.expiresAt?this.emit("sessionExpired",e):this.scheduleRetry(e))}catch(t){this.consecutiveFailures++,Date.now()>=e.expiresAt?this.emit("sessionExpired",e):this.scheduleRetry(e)}else Date.now()>=e.expiresAt&&this.emit("sessionExpired",e)},i)}schedule(){this.cancel();const e=this.session;if(!e?.expiresAt)return;const t=Date.now(),s=e.expiresAt-t;if(s<=0)return void this.emit("sessionExpired",e);this.consecutiveFailures=0;const i=Math.max(0,s-this.refreshSkewMs);this.emit("sessionWillRefresh",{inMs:i,ttl:s}),this.timer=setTimeout(async()=>{if(this.onRefresh)try{const t=await this.onRefresh(e);t?.expiresAt&&t.authToken&&t.expiresAt!==e.expiresAt?(this.consecutiveFailures=0,this.setSession(t),this.emit("sessionRefreshed",t)):(this.consecutiveFailures++,Date.now()>=e.expiresAt?this.emit("sessionExpired",e):this.scheduleRetry(e))}catch(t){this.consecutiveFailures++,Date.now()>=e.expiresAt?this.emit("sessionExpired",e):this.scheduleRetry(e)}else Date.now()>=e.expiresAt&&this.emit("sessionExpired",e)},i)}}class s{constructor(e){if(this.session=null,this.pendingNonce=null,this.overlayEl=null,this._iframeEl=null,this.overlayCleanup=null,this.events={},this._cachedVaultInfo=null,this._visitorId=null,!e?.appId)throw new Error("appId is required");if(this.config={...e,authUrl:e.authUrl??"https://auth.emblemvault.ai",apiUrl:e.apiUrl??"https://api.emblemvault.ai",persistSession:e.persistSession??!0},this.storageKey=`emblem_session_${e.appId}`,this.visitorIdKey=`emblem_visitorId_${e.appId}`,this.messageHandler=this.onMessage.bind(this),window.addEventListener("message",this.messageHandler),this.sessionMgr=new t({onRefresh:async e=>{try{return await this.refreshSession()||e}catch{return e}}}),this.sessionMgr.on("sessionExpired",e=>this.emit("sessionExpired",e)),this.sessionMgr.on("sessionRefreshed",e=>this.emit("sessionRefreshed",e)),this.sessionMgr.on("sessionWillRefresh",e=>this.emit("sessionWillRefresh",e)),this.config.persistSession){const e=this.loadPersistedSession();e&&this.hydrateSession(e),this._visitorId=this.loadPersistedVisitorId()}}async authenticateWallet(e){const{network:t,message:s,signature:i,publicKey:n,address:r}=e;if(!this.config.apiUrl)throw new Error("apiUrl is required for authenticateWallet");if(!t||!s||!i)throw new Error("network, message, signature are required");const a=`${this.config.apiUrl.replace(/\/$/,"")}/api/auth/wallet/verify-external`,o=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,network:t,message:s,signature:i,publicKey:n,address:r})});if(!o.ok){const e=await this.safeJson(o),t=new Error(e?.error||`wallet_verify_${o.status}`);throw t.status=o.status,t.payload=e,t}const c=await o.json(),h=c?.session;return h&&(this.session=h,this.persistSession(h),this.sessionMgr.setSession(h),this.config.onSuccess?.(h),this.emit("session",h)),h}async openAuthModal(){const e=window.location.origin;let t=null,s=null;try{t=await this.getAuthInit({origin:e})}catch(e){s=e}const i=t?.nonce||this.randomId();this.pendingNonce=i;let n=this.resolveModalUrl({nonce:i,origin:e,state:t?.state});if("origin_not_allowed"===s?.payload?.error)try{const t=new URL("/connect",this.config.authUrl);t.searchParams.set("error","origin_not_allowed"),t.searchParams.set("appId",this.config.appId),t.searchParams.set("origin",e),n=t.toString()}catch{}const r=this.config.modalMode||"auto";let a=!1;if("iframe"===r||"auto"===r)try{a=this.openIframeModal(n)}catch(e){}if(!a){const e=this.popupFeatures();if(!window.open(n,"emblem-auth",e)){const e=new Error("Popup blocked. Please allow popups for this site.");this.emitError(e)}}}getSession(){return this.session}getVisitorId(){return this._visitorId}async refreshSession(){try{if(!this.session)return null;const e=this.tryGetOriginFromConfig();if(!e)return this.session;const t=`${e}/api/auth/refresh`,s=Boolean(this.session.refreshToken),i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s?{refreshToken:this.session.refreshToken}:{}),credentials:"include"});if(!i.ok)return this.session;const n=await i.json(),r=n?.session;return r&&(this.session=r,this.persistSession(r),this.sessionMgr.setSession(r),this.emit("session",r)),this.session}catch{return this.session}}async getVaultInfo(){if(this._cachedVaultInfo)return this._cachedVaultInfo;const e=this.getSession();if(!e?.authToken)throw new Error("No session");if(!this.config.apiUrl)throw new Error("apiUrl is required");const t=this.config.apiUrl.replace(/\/$/,""),s=await fetch(`${t}/vault/info`,{method:"POST",headers:{Authorization:`Bearer ${e.authToken}`,"Content-Type":"application/json"},body:"{}"});if(!s.ok)throw new Error("Failed to fetch vault info");const i=await s.json();return this._cachedVaultInfo=i,i}async getVaultApiKey(){if(!this.config.apiUrl)throw new Error("apiUrl is required");const e=this.getSession();if(!e?.authToken)throw new Error("No active session");const t=this.config.apiUrl.replace(/\/$/,""),s=e.authToken,i=e.user?.vaultId;let n=null;try{const e=await fetch(`${t}/vault/info-complete`,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(e.ok){const t=await e.json();n=t?.raw?.pkp?.api_key_hash||t?.raw?.pkp?.apiKeyHash||null}}catch{}if(!n)try{const s=e.appId,r=e.user?.identifier||"",a=s?`${s}:${r}`:r;if(a){const e=await fetch(`${t}/api/vaults/${encodeURIComponent(a)}`);if(e.ok){const t=await e.json(),s=Array.isArray(t)?t.find(e=>String(e.tokenId||e.token_id)===String(i))||t[0]:null;n=s?.api_key_hash||s?.apiKeyHash||null}}}catch{}if(!n){const e=await fetch(`${t}/api/vaults/${encodeURIComponent(i)}/api-key`,{method:"POST",headers:{Authorization:`Bearer ${s}`}});if(!e.ok)throw new Error(`gen_key_${e.status}`);const n=await e.json(),r=n?.apiKey||n?.key||null;if(!r)throw new Error("missing_apiKey");return r}const r=await fetch(`${t}/decrypt`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({tokenId:i,dataToEncryptHash:n})});if(!r.ok)throw new Error(`decrypt_${r.status}`);const a=await r.json(),o=a?.decryptedString||null;if(!o)throw new Error("missing_decrypted");return o}hydrateSession(e){e?.authToken&&(this.session=e,this.persistSession(e),this._cachedVaultInfo=null,this.sessionMgr.setSession(e),this.emit("session",e))}logout(){this.session=null,this.persistSession(null),this._visitorId=null,this.persistVisitorId(null),this._cachedVaultInfo=null,this.sessionMgr.clear(),this.emit("session",null)}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){const s=this.events[e];if(s){const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}async getSignerContext(){const e=this.getSession();if(!e?.authToken)throw new Error("No active session. Call openAuthModal() or authenticateWallet() first.");const t=await this.getVaultInfo();return{config:{baseUrl:this.config.apiUrl,getJwt:()=>this.getSession()?.authToken},vaultInfo:{vaultId:t.vaultId,evmAddress:t.evmAddress||"0x",address:t.solanaAddress||t.address||"",tokenId:t.tokenId,created_by:t.created_by}}}async toViemAccount(){const{toViemAccount:e}=await Promise.resolve().then(function(){return h}),{config:t,vaultInfo:s}=await this.getSignerContext();return e(t,s)}async toEthersWallet(e){const{toEthersWallet:t}=await Promise.resolve().then(function(){return u}),{config:s,vaultInfo:i}=await this.getSignerContext();return t(s,e,i)}async toWeb3Adapter(){const{toWeb3Adapter:e}=await Promise.resolve().then(function(){return y}),{config:t,vaultInfo:s}=await this.getSignerContext();return e(t,s)}async toSolanaWeb3Signer(){const{toSolanaWeb3Signer:e}=await Promise.resolve().then(function(){return w}),{config:t,vaultInfo:s}=await this.getSignerContext();return e(t,s)}async toSolanaKitSigner(){const{toSolanaKitSigner:e}=await Promise.resolve().then(function(){return w}),{config:t,vaultInfo:s}=await this.getSignerContext();return e(t,s)}destroy(){window.removeEventListener("message",this.messageHandler),this.sessionMgr.destroy(),this.closeOverlay(),Object.keys(this.events).forEach(e=>{delete this.events[e]})}resolveModalUrl(e){const t=`${this.config.authUrl.replace(/\/$/,"")}/connect`,s=new URL(t,window.location.href);return s.searchParams.set("appId",this.config.appId),s.searchParams.set("origin",e.origin),s.searchParams.set("nonce",e.nonce),e.state&&s.searchParams.set("state",e.state),s.toString()}onMessage(e){try{if(!new Set([window.location.origin,this.tryGetOriginFromConfig()].filter(Boolean)).has(e.origin))return;const t=e.data;if("emblem-auth-cancelled"===t?.type){if(!this.pendingNonce||t.nonce!==this.pendingNonce)return;return this.pendingNonce=null,this.closeOverlay(),this.config.onCancel&&this.config.onCancel(),void this.emit("cancelled",void 0)}if("emblem-auth-success"!==t?.type)return;if(!this.pendingNonce||t.nonce!==this.pendingNonce)return;this.pendingNonce=null;const s=t.session;this.session=s,this.persistSession(s),this.sessionMgr.setSession(s),t.visitorId&&(this._visitorId=t.visitorId,this.persistVisitorId(t.visitorId)),s&&this.config.onSuccess&&this.config.onSuccess(s),this.emit("session",s),this.closeOverlay()}catch(e){this.emitError(e)}}emit(e,t){const s=this.events[e];if(s)for(const e of s)try{e(t)}catch(e){}}emitError(e){this.config.onError?.(e),this.emit("authError",e)}popupFeatures(){const e=Math.max(0,(window.outerHeight-600)/2);return`popup=yes,width=420,height=600,left=${Math.max(0,(window.outerWidth-420)/2)},top=${e}`}randomId(){const e=new Uint8Array(16);if(window.crypto?.getRandomValues)window.crypto.getRandomValues(e);else for(let t=0;t<e.length;t++)e[t]=Math.floor(256*Math.random());return Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}tryGetOriginFromConfig(){try{if(!this.config.authUrl)return null;return new URL(this.config.authUrl,window.location.href).origin}catch{return null}}async getAuthInit(e){const t=await fetch(`${this.config.authUrl.replace(/\/$/,"")}/api/auth/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,origin:e.origin})}),s=await t.json().catch(()=>null);if(!t.ok){const e=new Error(`init ${t.status}`);throw e.status=t.status,e.payload=s||{},e}return s}openIframeModal(e){this.overlayEl&&this.closeOverlay();const t=document.createElement("div");t.setAttribute("data-emblem-overlay",""),t.style.position="fixed",t.style.inset="0",t.style.background="rgba(0,0,0,0.5)",t.style.zIndex="999999",t.style.display="flex",t.style.alignItems="center",t.style.justifyContent="center";const s=document.createElement("div");s.style.width="min(420px, 95vw)",s.style.height="min(600px, 90vh)",s.style.background="#12161b",s.style.border="1px solid #222b35",s.style.borderRadius="12px",s.style.boxShadow="0 12px 48px rgba(0,0,0,0.7)",s.style.overflow="hidden",s.style.position="relative";const i=document.createElement("button");i.textContent="×",i.setAttribute("aria-label","Close"),i.style.position="absolute",i.style.top="6px",i.style.right="8px",i.style.zIndex="2",i.style.background="transparent",i.style.color="#e6eef8",i.style.border="none",i.style.fontSize="20px",i.style.cursor="pointer",i.onclick=()=>this.cancelAuth();const n=document.createElement("iframe");n.src=e,n.style.width="100%",n.style.height="100%",n.style.border="0",n.referrerPolicy="no-referrer",n.allow="clipboard-read; clipboard-write;",s.appendChild(i),s.appendChild(n),t.appendChild(s),document.body.appendChild(t);const r=e=>{"Escape"===e.key&&this.cancelAuth()};return document.addEventListener("keydown",r,{capture:!0}),this.overlayEl=t,this._iframeEl=n,this.overlayCleanup=()=>{document.removeEventListener("keydown",r,{capture:!0})},!0}closeOverlay(){try{this.overlayCleanup?.()}catch{}this.overlayCleanup=null,this.overlayEl?.parentNode&&this.overlayEl.parentNode.removeChild(this.overlayEl),this.overlayEl=null,this._iframeEl=null}cancelAuth(){this.pendingNonce=null,this.closeOverlay(),this.config.onCancel&&this.config.onCancel(),this.emit("cancelled",void 0)}async safeJson(e){try{return await e.json()}catch{return null}}loadPersistedSession(){try{if("undefined"==typeof localStorage)return null;const e=localStorage.getItem(this.storageKey);if(!e)return null;const t=JSON.parse(e);return t?.authToken&&t?.expiresAt?Date.now()>=t.expiresAt?(localStorage.removeItem(this.storageKey),null):t:null}catch{return null}}persistSession(e){try{if("undefined"==typeof localStorage)return;e&&this.config.persistSession?localStorage.setItem(this.storageKey,JSON.stringify(e)):localStorage.removeItem(this.storageKey)}catch{}}loadPersistedVisitorId(){try{return"undefined"==typeof localStorage?null:localStorage.getItem(this.visitorIdKey)}catch{return null}}persistVisitorId(e){try{if("undefined"==typeof localStorage)return;e&&this.config.persistSession?localStorage.setItem(this.visitorIdKey,e):localStorage.removeItem(this.visitorIdKey)}catch{}}}async function i(e,t,s){const i=s.baseUrl??"https://api.emblemvault.ai",n=await async function(e){if("function"==typeof e.getAuthHeaders){const t=await e.getAuthHeaders();if(t&&"object"==typeof t)return t}const t=e.jwt??("function"==typeof e.getJwt?await e.getJwt():void 0)??e.sdk?.getSession()?.authToken??void 0;if(t)return{Authorization:`Bearer ${t}`};if(e.apiKey)return{"x-api-key":e.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}(s),r=await fetch(`${i}${e}`,{method:"POST",headers:{"content-type":"application/json",...n},body:JSON.stringify(t,(e,t)=>"bigint"==typeof t?t.toString():t)});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(function(e,t){let s=`Emblem signer error ${e}`;return e>=500?s+=": Internal server error":401===e||403===e?s+=": Authentication failed":404===e?s+=": Resource not found":405===e?s+=": Method not allowed":t&&(s+=`: ${t.substring(0,200)}`),s}(r.status,e))}return r.json()}function n(e,t){const s=Number(e);if(!Number.isSafeInteger(s))throw new Error(`${t} value ${e} exceeds safe integer range (max: ${Number.MAX_SAFE_INTEGER})`);return s}function r(e){return"bigint"==typeof e?"0x"+e.toString(16):e}function a(e){const t={...e};return void 0!==t.value&&(t.value=r(t.value)),void 0!==t.gas&&(t.gasLimit=r(t.gas),delete t.gas),void 0!==t.gasLimit&&(t.gasLimit=r(t.gasLimit)),void 0!==t.gasPrice&&(t.gasPrice=r(t.gasPrice)),void 0!==t.maxFeePerGas&&(t.maxFeePerGas=r(t.maxFeePerGas)),void 0!==t.maxPriorityFeePerGas&&(t.maxPriorityFeePerGas=r(t.maxPriorityFeePerGas)),void 0!==t.nonce&&(t.nonce=n(t.nonce,"nonce")),void 0!==t.chainId&&(t.chainId=n(t.chainId,"chainId")),void 0===t.maxFeePerGas&&void 0===t.maxPriorityFeePerGas||(void 0===t.gasPrice&&void 0!==t.maxFeePerGas&&(t.gasPrice=t.maxFeePerGas),delete t.maxFeePerGas,delete t.maxPriorityFeePerGas),delete t.type,delete t.accessList,delete t.account,delete t.chain,delete t.from,t}function o(e){let t="0x";for(let s=0;s<e.length;s++)t+=e[s].toString(16).padStart(2,"0");return t}async function c(e){const t=await i("/vault/info",{},e);if(!t||!t.vaultId||!t.evmAddress)throw new Error("Invalid vault info response: missing required fields");if(!String(t.evmAddress).startsWith("0x"))throw new Error("Invalid evmAddress format in response");return{vaultId:t.vaultId,tokenId:t.vaultId,address:t.address||"",evmAddress:t.evmAddress,created_by:t.created_by}}var h=Object.freeze({__proto__:null,toViemAccount:async function(e,t){const s=t??await c(e),{evmAddress:n,vaultId:r}=s,{toAccount:h}=await import("viem/accounts");return h({address:n,async signMessage({message:t}){let s,n=!1;if("string"==typeof t)s=t,n=!1;else if(t&&void 0!==t.raw){const e=t.raw;s="string"==typeof e?e:o(e),n=!0}else if(t instanceof Uint8Array)s=o(t),n=!1;else{if("string"!=typeof(a=t)||!/^0x[0-9a-fA-F]*$/.test(a))throw new Error(`Unsupported message type: ${typeof t}. Expected string, Uint8Array, or hex string.`);s=t,n=!1}var a;return(await i("/sign-eth-message",{vaultId:r,message:s,raw:n},e)).signature},async signTypedData(t){const{domain:s,types:n,message:a}=t;return(await i("/sign-typed-message",{vaultId:r,domain:s,types:n,message:a},e)).signature},async signTransaction(t,s){const n=a(t);return(await i("/sign-eth-tx",{vaultId:r,transaction:n},e)).signedTransaction}})}});var l,d,u=Object.freeze({__proto__:null,toEthersWallet:async function(e,t,s){let n;try{const e="ethers";n=await import(e)}catch{throw new Error("ethers is required for toEthersWallet(). Install it with: npm install ethers")}const{AbstractSigner:r,resolveAddress:h}=n,l=s??await c(e);class d extends r{constructor(e,t,s){super(t??null),this._address=null,this._vaultId=null,this._chainId=1,this._config=e,s?.address&&(this._address=s.address),s?.vaultId&&(this._vaultId=s.vaultId),s?.chainId&&(this._chainId=s.chainId)}async initialize(){return this._initPromise||(this._initPromise=c(this._config).then(e=>{this._address=e.evmAddress,this._vaultId=e.vaultId}).catch(e=>{throw this._initPromise=void 0,e})),this._initPromise}async getAddress(){return this._address||await this.initialize(),this._address}getVaultId(){if(!this._vaultId)throw new Error("Wallet not initialized. Call initialize() first.");return this._vaultId}setChainId(e){this._chainId=e}getChainId(){return this._chainId}connect(e){if(!e)throw new Error("Provider cannot be null");return new d(this._config,e,{address:this._address??void 0,vaultId:this._vaultId??void 0,chainId:this._chainId})}async signMessage(e){this._vaultId||await this.initialize();const t="string"==typeof e?e:o(e);return(await i("/sign-eth-message",{vaultId:this._vaultId,message:t},this._config)).signature}async signTypedData(e,t,s){this._vaultId||await this.initialize();const n={...t};n&&n.EIP712Domain&&delete n.EIP712Domain;return(await i("/sign-typed-message",{vaultId:this._vaultId,domain:e,types:n,message:s},this._config)).signature}async _signTypedData(e,t,s){return this.signTypedData(e,t,s)}async signTransaction(e){this._vaultId||await this.initialize();const t=e.from,s=await this.getAddress();if(t&&t.toLowerCase()!==s.toLowerCase())throw new Error("transaction from does not match signer address");const n=this.provider?{...await this.populateTransaction(e)}:{...e};if(n.from&&delete n.from,!("to"in n)||!n.to)throw new Error("Transaction must have a 'to' address");if(void 0===n.nonce||null===n.nonce)throw new Error("Transaction must have a nonce");const r=a(n);return(await i("/sign-eth-tx",{vaultId:this._vaultId,transaction:r,options:{chainId:this._chainId}},this._config)).signedTransaction}async sendTransaction(e){if(!this.provider)throw new Error("Provider required to send transaction");const t=await this.signTransaction(e);return await this.provider.broadcastTransaction(t)}async populateTransaction(e){const t={...e};if(!this.provider)throw new Error("Provider required to populate transaction");const s=t.from?await h(t.from,this.provider):await this.getAddress();let i;if(t.chainId)i=BigInt(t.chainId),this._chainId=Number(t.chainId);else{const e=await this.provider.getNetwork();i=e.chainId,this._chainId=Number(e.chainId)}const n=null!=t.nonce?Number(t.nonce):await this.provider.getTransactionCount(s,"pending"),r=t.to?await h(t.to,this.provider):null,a=t.value?BigInt(t.value.toString()):0n;let o;if(t.gasLimit)o=BigInt(t.gasLimit.toString());else try{o=await this.provider.estimateGas({...t,from:s})}catch{o=21000n}let c=null;if(t.gasPrice||2===t.type)t.gasPrice&&(c=BigInt(t.gasPrice.toString()));else{c=(await this.provider.getFeeData()).gasPrice??null}const l={from:s,to:r,value:a,nonce:n,gasLimit:o,data:t.data,chainId:i,type:t.type||void 0};return null!==c&&(l.gasPrice=c),t.maxFeePerGas&&(l.maxFeePerGas=BigInt(t.maxFeePerGas.toString())),t.maxPriorityFeePerGas&&(l.maxPriorityFeePerGas=BigInt(t.maxPriorityFeePerGas.toString())),l}async signAndBroadcast(e,t=!1){if(!this.provider)throw new Error("Provider required to send transaction");const s=await this.signTransaction(e),i=(await this.provider.broadcastTransaction(s)).hash;return t&&await this.provider.waitForTransaction(i),i}}return new d(e,t,{address:l.evmAddress,vaultId:l.vaultId})}});function f(e,t,s,i){if("a"===s&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function g(e,t,s,i,n){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?n.call(e,s):n?n.value=s:t.set(e,s),s}"function"==typeof SuppressedError&&SuppressedError;class p{constructor(e,t,s){l.set(this,void 0),d.set(this,void 0),this.address=e,g(this,l,t,"f"),g(this,d,s,"f")}async signMessage(e){const t="string"==typeof e?e:o(e);return(await i("/sign-eth-message",{vaultId:f(this,l,"f"),message:t},f(this,d,"f"))).signature}async signTypedData(e,t,s){return(await i("/sign-typed-message",{vaultId:f(this,l,"f"),domain:e,types:t,message:s},f(this,d,"f"))).signature}async signTransaction(e){const t=a(e);return{rawTransaction:(await i("/sign-eth-tx",{vaultId:f(this,l,"f"),transaction:t},f(this,d,"f"))).signedTransaction}}}l=new WeakMap,d=new WeakMap;var y=Object.freeze({__proto__:null,EmblemWeb3Adapter:p,toWeb3Adapter:async function(e,t){const s=t??await c(e);return new p(s.evmAddress,s.vaultId,e)}});class m{constructor(e,t){this.publicKey=t.address,this.config=e,this.vaultId=t.vaultId}async signMessage(e){const t="string"==typeof e?(new TextEncoder).encode(e):e,s=btoa(String.fromCharCode(...t)),n=await i("/sign-solana-message",{vaultId:this.vaultId,message:s},this.config);try{const e="undefined"!=typeof window?window:void 0;if(e?.bs58)return e.bs58.decode(n.signature);return Uint8Array.from(atob(n.signature),e=>e.charCodeAt(0))}catch(e){if(n.signature.startsWith("0x")){const e=n.signature.slice(2),t=new Uint8Array(e.length/2);for(let s=0;s<e.length;s+=2)t[s/2]=parseInt(e.substr(s,2),16);return t}throw new Error(`Unable to decode signature format: ${e}`)}}async signTransaction(e){const t=this.serializeTransaction(e),s=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:t,broadcast:!1,versionedTransaction:!0},this.config),n=s.serializedSignedTransaction||s.signedTransaction;if(!n)throw new Error("No signed transaction data received from server");return this.deserializeTransaction(n)}serializeTransaction(e){if(e&&"object"==typeof e){if(e.serialize){const t=e.serialize();return btoa(String.fromCharCode(...t))}if(e.instructions||e.recentBlockhash)throw new Error("Cannot serialize unsigned transaction objects. Please use VersionedTransaction.")}return e}deserializeTransaction(e){if("object"==typeof e&&e&&e.serializedSignedTransaction&&(e=e.serializedSignedTransaction),"string"==typeof e)try{const t=atob(e);return new Uint8Array(t.split("").map(e=>e.charCodeAt(0)))}catch(e){throw new Error(`Unable to deserialize transaction response: ${e}`)}return e}getVaultId(){return this.vaultId}async signAllTransactions(e){const t=[];for(const s of e)t.push(await this.signTransaction(s));return t}canSign(e){return e===this.publicKey}async signAndBroadcast(e,t=!0){const s=this.serializeTransaction(e),n=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:s,broadcast:t,versionedTransaction:!0},this.config);if(t){if(!n.transactionSignature)throw new Error("No transaction signature received from broadcast");return n.transactionSignature}if(!n.serializedSignedTransaction)throw new Error("No signed transaction data received from server");return n.serializedSignedTransaction}}var w=Object.freeze({__proto__:null,EmblemSolanaSigner:m,toSolanaWeb3Signer:async function(e,t){const s=t??await c(e);return new m(e,s)},toSolanaKitSigner:async function(e,t){const s=t??await c(e);return new m(e,s)}});e.EmblemAuthSDK=s,e.SessionManager=t,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=emblem-auth.min.js.map