@embarkai/ui-kit 0.4.20 → 0.4.22

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.
@@ -0,0 +1,1514 @@
1
+ import {
2
+ __esm,
3
+ __export,
4
+ __toCommonJS
5
+ } from "./chunk-DBFKMKYT.js";
6
+
7
+ // src/config/assets.tsx
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var GoogleIcon, TelegramIcon, TwitterIcon, DiscordIcon;
10
+ var init_assets = __esm({
11
+ "src/config/assets.tsx"() {
12
+ GoogleIcon = ({ className }) => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className, children: [
13
+ /* @__PURE__ */ jsx(
14
+ "path",
15
+ {
16
+ fill: "#4285F4",
17
+ d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
18
+ }
19
+ ),
20
+ /* @__PURE__ */ jsx(
21
+ "path",
22
+ {
23
+ fill: "#34A853",
24
+ d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
25
+ }
26
+ ),
27
+ /* @__PURE__ */ jsx(
28
+ "path",
29
+ {
30
+ fill: "#FBBC05",
31
+ d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
32
+ }
33
+ ),
34
+ /* @__PURE__ */ jsx(
35
+ "path",
36
+ {
37
+ fill: "#EA4335",
38
+ d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
39
+ }
40
+ )
41
+ ] });
42
+ TelegramIcon = ({ className }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M9.78 18.65l.28-4.23 7.68-6.92c.34-.31-.07-.46-.52-.19L7.74 13.3 3.64 12c-.88-.25-.89-.86.2-1.3l15.97-6.16c.73-.33 1.43.18 1.15 1.3l-2.72 12.81c-.19.91-.74 1.13-1.5.71L12.6 16.3l-1.99 1.93c-.23.23-.42.42-.83.42z" }) });
43
+ TwitterIcon = ({ className }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) });
44
+ DiscordIcon = ({ className }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className, children: /* @__PURE__ */ jsx(
45
+ "path",
46
+ {
47
+ fill: "currentColor",
48
+ d: "M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418Z"
49
+ }
50
+ ) });
51
+ }
52
+ });
53
+
54
+ // src/config/initial.tsx
55
+ var initial_exports = {};
56
+ __export(initial_exports, {
57
+ DEFAULT_PROVIDER_CONFIG: () => DEFAULT_PROVIDER_CONFIG,
58
+ getBlockscoutApiUrl: () => getBlockscoutApiUrl,
59
+ getIframeUrl: () => getIframeUrl,
60
+ getServiceUrls: () => getServiceUrls
61
+ });
62
+ import { BSC_TESTNET } from "@embarkai/core/read";
63
+ function getServiceUrls() {
64
+ const config = DEFAULT_PROVIDER_CONFIG;
65
+ const globalOverride = typeof window !== "undefined" && window.__EMBARK_SERVICES__ || {};
66
+ const searchParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : void 0;
67
+ const paramTss = searchParams?.get("tssUrl") || void 0;
68
+ const paramVault = searchParams?.get("shareVaultUrl") || void 0;
69
+ const paramDashboard = searchParams?.get("dashboardApiUrl") || void 0;
70
+ const buildTss = "https://api.lumiapassport.com/tss";
71
+ const buildVault = "https://api.lumiapassport.com/vault";
72
+ const buildRequireWasm = false;
73
+ const buildWasmDebug = false;
74
+ const defaultTss = "/tss";
75
+ const defaultVault = "/vault";
76
+ const result = {
77
+ tssUrl: paramTss || globalOverride.tssUrl || buildTss || config.services.tssUrl || defaultTss,
78
+ shareVaultUrl: paramVault || globalOverride.shareVaultUrl || buildVault || config.services.shareVaultUrl || defaultVault,
79
+ dashboardApiUrl: paramDashboard || globalOverride.dashboardApiUrl || config.services.dashboardApiUrl || "https://dashboard.lumiapassport.com/api",
80
+ tssRequireWasm: globalOverride.tssRequireWasm ?? buildRequireWasm ?? config.services.tssRequireWasm ?? false,
81
+ tssWasmDebug: globalOverride.tssWasmDebug ?? buildWasmDebug ?? config.services.tssWasmDebug ?? false
82
+ };
83
+ return result;
84
+ }
85
+ function getIframeUrl() {
86
+ const config = DEFAULT_PROVIDER_CONFIG;
87
+ const buildIframeUrl = "https://auth.lumiapassport.com";
88
+ const iframeUrl = buildIframeUrl || config.services.iframeUrl || "https://auth.lumiapassport.com";
89
+ return iframeUrl;
90
+ }
91
+ function getBlockscoutApiUrl(chainConfig) {
92
+ return chainConfig?.blockscoutApiUrl || null;
93
+ }
94
+ var DEFAULT_PROVIDER_CONFIG;
95
+ var init_initial = __esm({
96
+ "src/config/initial.tsx"() {
97
+ init_assets();
98
+ DEFAULT_PROVIDER_CONFIG = {
99
+ passkey: {
100
+ enabled: true,
101
+ showCreateButton: true,
102
+ primaryButtonText: "Sign in with Passkey"
103
+ },
104
+ email: {
105
+ enabled: true,
106
+ placeholder: "Enter your email",
107
+ buttonText: "Continue",
108
+ verificationTitle: "Check your email"
109
+ },
110
+ social: {
111
+ enabled: true,
112
+ providers: [
113
+ { id: "google", name: "Google", enabled: true, icon: GoogleIcon, comingSoon: true },
114
+ {
115
+ id: "telegram",
116
+ name: "Telegram",
117
+ enabled: true,
118
+ icon: TelegramIcon,
119
+ comingSoon: false,
120
+ meta: {
121
+ botUsername: "LumiaPassportDevBot",
122
+ salt: "demo"
123
+ }
124
+ }
125
+ ]
126
+ },
127
+ wallet: {
128
+ enabled: false,
129
+ supportedChains: [1, 137, 56],
130
+ requireSignature: true,
131
+ walletConnectProjectId: void 0,
132
+ mode: "linked",
133
+ allowedWallets: void 0
134
+ },
135
+ // preferedColorMode: 'light', // undefined means 'auto'
136
+ ui: {
137
+ title: "Sign in",
138
+ subtitle: void 0,
139
+ authOrder: ["email", "social", "wallet"]
140
+ },
141
+ network: {
142
+ name: BSC_TESTNET.name,
143
+ symbol: BSC_TESTNET.nativeCurrency.symbol,
144
+ chainId: BSC_TESTNET.id,
145
+ rpcUrl: BSC_TESTNET.rpcUrls[0],
146
+ explorerUrl: BSC_TESTNET.blockExplorerUrl,
147
+ testnet: true,
148
+ forceChain: false
149
+ },
150
+ services: {
151
+ tssUrl: "",
152
+ // Will fallback to build-time env vars
153
+ shareVaultUrl: "",
154
+ // Will fallback to build-time env vars
155
+ iframeUrl: "https://auth.lumiapassport.com",
156
+ // Secure iframe for MPC operations
157
+ tssRequireWasm: false,
158
+ tssWasmDebug: false
159
+ },
160
+ features: {
161
+ mpcSecurity: true,
162
+ strictMode: false,
163
+ requestDeduplication: true,
164
+ kycNeeded: false,
165
+ displayNameNeeded: false,
166
+ showActiveBalanceAsFiat: false
167
+ },
168
+ warnings: {
169
+ backupWarning: true,
170
+ emailNotConnectedWarning: true
171
+ },
172
+ kyc: {
173
+ provider: void 0,
174
+ options: void 0
175
+ }
176
+ };
177
+ }
178
+ });
179
+
180
+ // src/lib/errors.ts
181
+ var AccountAbstractionError = class _AccountAbstractionError extends Error {
182
+ constructor(message, code) {
183
+ super(message);
184
+ this.code = code;
185
+ this.name = "LumiaPassportError";
186
+ if (Error.captureStackTrace) {
187
+ Error.captureStackTrace(this, _AccountAbstractionError);
188
+ }
189
+ }
190
+ };
191
+ var _UserRejectedError = class _UserRejectedError extends AccountAbstractionError {
192
+ constructor(message = "User rejected transaction") {
193
+ super(message, _UserRejectedError.CODE);
194
+ this.name = "UserRejectedError";
195
+ }
196
+ };
197
+ _UserRejectedError.CODE = "USER_REJECTED";
198
+ var UserRejectedError = _UserRejectedError;
199
+ var ErrorCodes = {
200
+ MPC_SIGNING_ERROR: "MPC_SIGNING_ERROR",
201
+ USER_REJECTED: UserRejectedError.CODE
202
+ };
203
+ function createAccountAbstractionError(params) {
204
+ const { message, code } = params;
205
+ if (!code) {
206
+ return new Error(message);
207
+ }
208
+ switch (code) {
209
+ case UserRejectedError.CODE:
210
+ return new UserRejectedError(message);
211
+ default:
212
+ return new AccountAbstractionError(message, code);
213
+ }
214
+ }
215
+
216
+ // src/internal/lib/iframe-manager.ts
217
+ var IframeManager = class {
218
+ constructor(config) {
219
+ this.iframe = null;
220
+ this.channelToken = null;
221
+ this.isReady = false;
222
+ this.initPromise = null;
223
+ // Message handling
224
+ this.pendingRequests = /* @__PURE__ */ new Map();
225
+ this.usedNonces = /* @__PURE__ */ new Set();
226
+ this.messageListener = null;
227
+ // Track hidden dialog elements for restoration
228
+ this.hiddenElements = [];
229
+ this.pointerGuardCleanup = null;
230
+ this.iframeHideTimeout = null;
231
+ this.iframeActive = false;
232
+ this.providerSessions = /* @__PURE__ */ new Map();
233
+ this.providerConnections = /* @__PURE__ */ new Map();
234
+ // Timeouts
235
+ this.REQUEST_TIMEOUT = 3e5;
236
+ // 5 minutes (for user interactions like consent)
237
+ this.NONCE_EXPIRY = 3e5;
238
+ // 5 minutes
239
+ this.HEARTBEAT_INTERVAL = 5 * 60 * 1e3;
240
+ this.isReconnecting = false;
241
+ // SSE event listeners
242
+ this.sseListeners = /* @__PURE__ */ new Set();
243
+ this.iframeUrl = config.iframeUrl;
244
+ this.projectId = config.projectId;
245
+ this.debug = config.debug || false;
246
+ this.onWalletReadyCallback = config.onWalletReady;
247
+ this.readyPromise = new Promise((resolve) => {
248
+ this.readyResolve = resolve;
249
+ });
250
+ this.log("[IframeManager] Initialized with:", {
251
+ iframeUrl: this.iframeUrl,
252
+ projectId: this.projectId
253
+ });
254
+ }
255
+ setDebug(debug) {
256
+ this.debug = debug;
257
+ }
258
+ /**
259
+ * Initialize iframe and wait for it to be ready
260
+ */
261
+ async initialize() {
262
+ if (this.initPromise) {
263
+ return this.initPromise;
264
+ }
265
+ this.initPromise = this.doInitialize();
266
+ return this.initPromise;
267
+ }
268
+ async doInitialize() {
269
+ this.iframe = document.createElement("iframe");
270
+ let iframeUrl = this.iframeUrl;
271
+ this.iframe.src = iframeUrl;
272
+ this.iframe.id = "lumia-passport-iframe";
273
+ this.iframe.style.display = "none";
274
+ this.iframe.style.position = "fixed";
275
+ this.iframe.style.top = "0";
276
+ this.iframe.style.left = "0";
277
+ this.iframe.style.width = "100%";
278
+ this.iframe.style.height = "100%";
279
+ this.iframe.style.border = "none";
280
+ this.iframe.style.zIndex = "999999";
281
+ this.iframe.style.background = "rgba(0, 0, 0, 0.5)";
282
+ this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox");
283
+ this.iframe.setAttribute("allow", "publickey-credentials-get *; publickey-credentials-create *");
284
+ this.messageListener = this.handleMessage.bind(this);
285
+ window.addEventListener("message", this.messageListener);
286
+ document.body.appendChild(this.iframe);
287
+ await this.readyPromise;
288
+ await this.authenticateSDK();
289
+ await this.primeProviderSessions();
290
+ this.startHeartbeat();
291
+ this.setupVisibilityHandler();
292
+ this.log("[IframeManager] \u2705 Iframe ready and authenticated");
293
+ }
294
+ /**
295
+ * Set the onWalletReady callback
296
+ */
297
+ setOnWalletReady(callback) {
298
+ this.onWalletReadyCallback = callback;
299
+ }
300
+ /**
301
+ * Authenticate SDK with iframe to establish secure channel
302
+ */
303
+ async authenticateSDK() {
304
+ const response = await this.sendMessage("SDK_AUTH", {
305
+ projectId: this.projectId
306
+ });
307
+ if (response.type === "IFRAME_MSG_SDK_AUTH_SUCCESS") {
308
+ this.channelToken = response.sessionToken;
309
+ } else {
310
+ throw new Error("SDK channel authentication failed");
311
+ }
312
+ }
313
+ /**
314
+ * Start periodic heartbeat to check SDK channel validity
315
+ */
316
+ startHeartbeat() {
317
+ if (this.heartbeatInterval) {
318
+ clearInterval(this.heartbeatInterval);
319
+ }
320
+ this.heartbeatInterval = setInterval(async () => {
321
+ if (!this.channelToken) return;
322
+ try {
323
+ const response = await this.sendMessage("SDK_CHANNEL_HEARTBEAT", {});
324
+ if (!response.valid) {
325
+ this.log("[IframeManager] SDK channel invalid, reconnecting...");
326
+ await this.reconnect();
327
+ }
328
+ } catch (error) {
329
+ this.log("[IframeManager] Heartbeat failed:", error);
330
+ await this.reconnect();
331
+ }
332
+ }, this.HEARTBEAT_INTERVAL);
333
+ this.log("[IframeManager] Heartbeat started (interval: 5 min)");
334
+ }
335
+ /**
336
+ * Stop heartbeat
337
+ */
338
+ stopHeartbeat() {
339
+ if (this.heartbeatInterval) {
340
+ clearInterval(this.heartbeatInterval);
341
+ this.heartbeatInterval = void 0;
342
+ this.log("[IframeManager] Heartbeat stopped");
343
+ }
344
+ }
345
+ /**
346
+ * Reconnect SDK channel after it becomes invalid
347
+ */
348
+ async reconnect() {
349
+ if (this.isReconnecting) {
350
+ this.log("[IframeManager] Already reconnecting, skipping...");
351
+ return;
352
+ }
353
+ this.isReconnecting = true;
354
+ this.log("[IframeManager] Reconnecting SDK channel...");
355
+ try {
356
+ this.channelToken = null;
357
+ await this.authenticateSDK();
358
+ this.log("[IframeManager] \u2705 SDK channel reconnected");
359
+ } catch (error) {
360
+ this.log("[IframeManager] \u274C Reconnect failed:", error);
361
+ throw error;
362
+ } finally {
363
+ this.isReconnecting = false;
364
+ }
365
+ }
366
+ /**
367
+ * Setup visibility change handler to check channel when tab becomes visible
368
+ */
369
+ setupVisibilityHandler() {
370
+ if (typeof document === "undefined") return;
371
+ document.addEventListener("visibilitychange", async () => {
372
+ if (document.visibilityState === "visible" && this.channelToken) {
373
+ this.log("[IframeManager] Tab visible, checking SDK channel...");
374
+ try {
375
+ const response = await this.sendMessage("SDK_CHANNEL_HEARTBEAT", {});
376
+ if (!response.valid) {
377
+ this.log("[IframeManager] SDK channel expired while tab was hidden");
378
+ await this.reconnect();
379
+ }
380
+ } catch (error) {
381
+ this.log("[IframeManager] Channel check failed, reconnecting...");
382
+ await this.reconnect();
383
+ }
384
+ }
385
+ });
386
+ this.log("[IframeManager] Visibility handler setup");
387
+ }
388
+ /**
389
+ * Normalize legacy message types from cached iframe versions.
390
+ * Old iframe sends LUMIA_PASSPORT_*, new iframe sends IFRAME_MSG_*.
391
+ * This ensures both work until all browser caches expire.
392
+ */
393
+ normalizeMessageType(type) {
394
+ if (type.startsWith("LUMIA_PASSPORT_")) {
395
+ return type.replace("LUMIA_PASSPORT_", "IFRAME_MSG_");
396
+ }
397
+ return type;
398
+ }
399
+ /**
400
+ * Handle incoming postMessage events
401
+ */
402
+ handleMessage(event) {
403
+ const message = event.data;
404
+ if (!message || typeof message !== "object" || !message.type) {
405
+ return;
406
+ }
407
+ message.type = this.normalizeMessageType(message.type);
408
+ const validIframeTypes = [
409
+ "IFRAME_MSG_IFRAME_READY",
410
+ "IFRAME_MSG_WALLET_READY",
411
+ "IFRAME_MSG_SHOW_IFRAME",
412
+ "IFRAME_MSG_HIDE_IFRAME",
413
+ "IFRAME_MSG_SDK_AUTH_SUCCESS",
414
+ "IFRAME_MSG_AUTH_SUCCESS",
415
+ "IFRAME_MSG_DKG_SUCCESS",
416
+ "IFRAME_MSG_SIGNATURE",
417
+ "IFRAME_MSG_ADDRESS",
418
+ "IFRAME_MSG_KEYSHARE_STATUS",
419
+ "IFRAME_MSG_TRUSTED_APPS_LIST",
420
+ "IFRAME_MSG_TRUSTED_APP_REMOVED",
421
+ "IFRAME_MSG_REQUEST_NEW_TOKEN",
422
+ "IFRAME_MSG_TOKEN_REFRESHED",
423
+ "IFRAME_MSG_HEARTBEAT_RESPONSE",
424
+ "IFRAME_MSG_RESPONSE",
425
+ "IFRAME_MSG_ERROR",
426
+ "IFRAME_SSE_EVENT"
427
+ ];
428
+ if (!validIframeTypes.includes(message.type) && !message.messageId) {
429
+ return;
430
+ }
431
+ const iframeOrigin = new URL(this.iframeUrl).origin;
432
+ if (event.origin !== iframeOrigin) {
433
+ this.log("[IframeManager] \u26A0\uFE0F Ignored message from invalid origin:", {
434
+ received: event.origin,
435
+ expected: iframeOrigin
436
+ });
437
+ return;
438
+ }
439
+ if (message.type === "IFRAME_SSE_EVENT") {
440
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_SSE_EVENT", message.data);
441
+ for (const listener of this.sseListeners) {
442
+ try {
443
+ listener(message.data);
444
+ } catch (err) {
445
+ console.error("[IframeManager] SSE listener error:", err);
446
+ }
447
+ }
448
+ return;
449
+ }
450
+ if (message.type === "IFRAME_MSG_IFRAME_READY") {
451
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_MSG_IFRAME_READY");
452
+ this.isReady = true;
453
+ this.readyResolve();
454
+ return;
455
+ }
456
+ if (message.type === "IFRAME_MSG_WALLET_READY") {
457
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_MSG_WALLET_READY", message.data);
458
+ if (this.onWalletReadyCallback) {
459
+ this.onWalletReadyCallback(message.data);
460
+ }
461
+ return;
462
+ }
463
+ if (message.type === "IFRAME_MSG_SHOW_IFRAME") {
464
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_MSG_SHOW_IFRAME");
465
+ this.showIframe();
466
+ return;
467
+ }
468
+ if (message.type === "IFRAME_MSG_HIDE_IFRAME") {
469
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_MSG_HIDE_IFRAME");
470
+ this.hideIframe();
471
+ return;
472
+ }
473
+ if (message.type === "IFRAME_MSG_REQUEST_NEW_TOKEN") {
474
+ this.log("[IframeManager] \u{1F4E8} Received IFRAME_MSG_REQUEST_NEW_TOKEN");
475
+ this.handleTokenRefreshRequest(message).catch((error) => {
476
+ this.log("[IframeManager] \u274C Token refresh error:", error);
477
+ });
478
+ return;
479
+ }
480
+ if (message.messageId) {
481
+ const pending = this.pendingRequests.get(message.messageId);
482
+ if (pending) {
483
+ this.pendingRequests.delete(message.messageId);
484
+ if (message.type === "IFRAME_MSG_ERROR") {
485
+ const passportError = createAccountAbstractionError({
486
+ message: message.error || "Unknown error",
487
+ code: message.code
488
+ });
489
+ pending.reject(passportError);
490
+ } else {
491
+ pending.resolve(message.data || message);
492
+ }
493
+ }
494
+ }
495
+ }
496
+ /**
497
+ * Handle token refresh request from iframe
498
+ */
499
+ async handleTokenRefreshRequest(message) {
500
+ try {
501
+ const { jwtTokenManager } = await import("./auth-S7VX73GX.js");
502
+ const refreshSuccess = await jwtTokenManager.refreshAccessToken();
503
+ if (refreshSuccess) {
504
+ const newAccessToken = jwtTokenManager.getAccessToken();
505
+ if (this.iframe && this.iframe.contentWindow) {
506
+ const iframeOrigin = new URL(this.iframeUrl).origin;
507
+ this.iframe.contentWindow.postMessage(
508
+ {
509
+ type: "IFRAME_MSG_TOKEN_REFRESHED",
510
+ messageId: message.messageId,
511
+ accessToken: newAccessToken,
512
+ timestamp: Date.now()
513
+ },
514
+ iframeOrigin
515
+ );
516
+ }
517
+ } else {
518
+ if (this.iframe && this.iframe.contentWindow) {
519
+ const iframeOrigin = new URL(this.iframeUrl).origin;
520
+ this.iframe.contentWindow.postMessage(
521
+ {
522
+ type: "IFRAME_MSG_TOKEN_REFRESHED",
523
+ messageId: message.messageId,
524
+ error: "Token refresh failed",
525
+ timestamp: Date.now()
526
+ },
527
+ iframeOrigin
528
+ );
529
+ }
530
+ }
531
+ } catch (error) {
532
+ if (this.iframe && this.iframe.contentWindow) {
533
+ const iframeOrigin = new URL(this.iframeUrl).origin;
534
+ this.iframe.contentWindow.postMessage(
535
+ {
536
+ type: "IFRAME_MSG_TOKEN_REFRESHED",
537
+ messageId: message.messageId,
538
+ error: error instanceof Error ? error.message : "Unknown error",
539
+ timestamp: Date.now()
540
+ },
541
+ iframeOrigin
542
+ );
543
+ }
544
+ }
545
+ }
546
+ /**
547
+ * Send secure message to iframe
548
+ */
549
+ async sendMessage(type, data) {
550
+ if (this.initPromise && type !== "SDK_AUTH") {
551
+ await this.initPromise;
552
+ }
553
+ if (!this.iframe || !this.iframe.contentWindow) {
554
+ throw new Error("Iframe not initialized");
555
+ }
556
+ if (!this.channelToken && type !== "SDK_AUTH") {
557
+ throw createAccountAbstractionError({ message: "SDK channel not established. Initialize iframe first." });
558
+ }
559
+ const messageId = this.generateMessageId();
560
+ const nonce = this.generateNonce();
561
+ const timestamp = Date.now();
562
+ const message = {
563
+ type,
564
+ messageId,
565
+ timestamp,
566
+ nonce,
567
+ projectId: this.projectId,
568
+ data: {
569
+ ...data,
570
+ sessionToken: this.channelToken
571
+ // named sessionToken for backward compatibility with iframe
572
+ }
573
+ };
574
+ if (this.channelToken) {
575
+ message.hmac = await this.computeHMAC(message);
576
+ }
577
+ const responsePromise = new Promise((resolve, reject) => {
578
+ this.pendingRequests.set(messageId, {
579
+ resolve,
580
+ reject,
581
+ timestamp
582
+ });
583
+ setTimeout(() => {
584
+ if (this.pendingRequests.has(messageId)) {
585
+ this.pendingRequests.delete(messageId);
586
+ reject(new Error(`Request timeout: ${type}`));
587
+ }
588
+ }, this.REQUEST_TIMEOUT);
589
+ });
590
+ const iframeOrigin = new URL(this.iframeUrl).origin;
591
+ this.iframe.contentWindow.postMessage(message, iframeOrigin);
592
+ this.log(`[IframeManager] \u{1F4E4} Sent message: ${type}`, { messageId });
593
+ return responsePromise;
594
+ }
595
+ onSseEvent(listener) {
596
+ this.sseListeners.add(listener);
597
+ return () => {
598
+ this.sseListeners.delete(listener);
599
+ };
600
+ }
601
+ /**
602
+ * Compute HMAC for message authentication using SDK channel token
603
+ */
604
+ async computeHMAC(message) {
605
+ const encoder = new TextEncoder();
606
+ const payload = JSON.stringify({
607
+ type: message.type,
608
+ messageId: message.messageId,
609
+ timestamp: message.timestamp,
610
+ nonce: message.nonce,
611
+ data: JSON.stringify(message.data)
612
+ });
613
+ const data = encoder.encode(payload);
614
+ const key = encoder.encode(this.channelToken);
615
+ const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
616
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, data);
617
+ return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
618
+ }
619
+ /**
620
+ * Generate unique message ID
621
+ */
622
+ generateMessageId() {
623
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
624
+ }
625
+ /**
626
+ * Generate nonce for replay protection
627
+ */
628
+ generateNonce() {
629
+ const randomBytes = new Uint8Array(16);
630
+ crypto.getRandomValues(randomBytes);
631
+ return Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
632
+ }
633
+ // ========================================
634
+ // UI Methods
635
+ // ========================================
636
+ /**
637
+ * Show iframe (for consent/confirmation UI)
638
+ */
639
+ showIframe() {
640
+ if (this.iframe) {
641
+ if (this.iframeHideTimeout) {
642
+ clearTimeout(this.iframeHideTimeout);
643
+ this.iframeHideTimeout = null;
644
+ }
645
+ this.hideAllDialogs();
646
+ this.iframeActive = true;
647
+ this.enablePointerEventGuards();
648
+ this.iframe.style.display = "block";
649
+ this.iframe.style.zIndex = "2147483648";
650
+ this.iframe.style.pointerEvents = "auto";
651
+ this.iframe.style.visibility = "visible";
652
+ this.iframe.style.background = "rgba(0, 0, 0, 0.5)";
653
+ }
654
+ }
655
+ /**
656
+ * Hide iframe
657
+ */
658
+ hideIframe() {
659
+ if (this.iframe) {
660
+ this.log("[IframeManager] hideIframe called - making iframe non-blocking");
661
+ this.iframeActive = false;
662
+ this.iframe.style.zIndex = "1";
663
+ this.iframe.style.background = "transparent";
664
+ this.iframe.style.pointerEvents = "none";
665
+ this.iframe.style.visibility = "hidden";
666
+ this.log("[IframeManager] iframe z-index set to 1, transparent, pointer-events: none");
667
+ this.restoreAllDialogs();
668
+ this.disablePointerEventGuards();
669
+ this.iframeHideTimeout = setTimeout(() => {
670
+ if (this.iframe) {
671
+ this.log("[IframeManager] Fully hiding iframe with display: none");
672
+ this.iframe.style.display = "none";
673
+ this.iframe.style.zIndex = "999999";
674
+ this.iframe.style.background = "rgba(0, 0, 0, 0.5)";
675
+ this.iframe.style.pointerEvents = "auto";
676
+ this.iframe.style.visibility = "visible";
677
+ }
678
+ this.iframeHideTimeout = null;
679
+ }, 200);
680
+ }
681
+ }
682
+ /**
683
+ * Hide all Radix Dialog modals (SendModal, etc.)
684
+ */
685
+ hideAllDialogs() {
686
+ this.hiddenElements = [];
687
+ const allElements = document.body.querySelectorAll("*");
688
+ allElements.forEach((element) => {
689
+ const htmlElement = element;
690
+ if (htmlElement.id === "lumia-passport-iframe" || htmlElement.closest("#lumia-passport-iframe")) {
691
+ return;
692
+ }
693
+ if (htmlElement.hasAttribute("data-lumia-auth-modal") || htmlElement.closest("[data-lumia-auth-modal]")) {
694
+ return;
695
+ }
696
+ const computedStyle = window.getComputedStyle(htmlElement);
697
+ const zIndex = parseInt(computedStyle.zIndex, 10);
698
+ const isRadixElement = htmlElement.hasAttribute("data-radix-focus-guard") || htmlElement.hasAttribute("data-radix-portal") || htmlElement.hasAttribute("data-radix-popper-content-wrapper");
699
+ if (!isNaN(zIndex) && zIndex > 1e6 || isRadixElement) {
700
+ this.hiddenElements.push({
701
+ element: htmlElement,
702
+ originalDisplay: htmlElement.style.display || "",
703
+ originalVisibility: htmlElement.style.visibility || "",
704
+ originalPointerEvents: htmlElement.style.pointerEvents || "",
705
+ originalZIndex: htmlElement.style.zIndex || ""
706
+ });
707
+ htmlElement.style.display = "none";
708
+ htmlElement.style.visibility = "hidden";
709
+ htmlElement.style.pointerEvents = "none";
710
+ htmlElement.style.zIndex = "-1";
711
+ const elementType = isRadixElement ? "Radix element" : `z-index ${zIndex}`;
712
+ this.log(
713
+ `[IframeManager] Hiding ${elementType}: ${htmlElement.className || "(no class)"} (Tag: ${htmlElement.tagName})`
714
+ );
715
+ }
716
+ });
717
+ this.log(`[IframeManager] Hidden ${this.hiddenElements.length} elements total`);
718
+ }
719
+ /**
720
+ * Restore all previously hidden Radix Dialog modals
721
+ */
722
+ restoreAllDialogs() {
723
+ this.log(`[IframeManager] Restoring ${this.hiddenElements.length} elements`);
724
+ this.hiddenElements.forEach(
725
+ ({ element, originalDisplay, originalVisibility, originalPointerEvents, originalZIndex }) => {
726
+ element.style.display = originalDisplay;
727
+ element.style.visibility = originalVisibility;
728
+ element.style.pointerEvents = originalPointerEvents || "auto";
729
+ element.style.zIndex = originalZIndex;
730
+ this.log(
731
+ `[IframeManager] Restored element: ${element.className} (Tag: ${element.tagName}), pointerEvents set to: ${element.style.pointerEvents}`
732
+ );
733
+ }
734
+ );
735
+ this.hiddenElements = [];
736
+ this.log(`[IframeManager] All elements restored`);
737
+ }
738
+ /**
739
+ * Stop Radix dismissable layers from swallowing the first click on the iframe.
740
+ * Radix attaches pointer event guards at the document level; we intercept at window
741
+ * capture phase so user clicks flow straight into the consent iframe.
742
+ */
743
+ enablePointerEventGuards() {
744
+ if (this.pointerGuardCleanup) {
745
+ return;
746
+ }
747
+ const handler = (event) => {
748
+ if (!this.iframeActive || !this.iframe) {
749
+ return;
750
+ }
751
+ const target = event.target;
752
+ const composedPath = typeof event.composedPath === "function" ? event.composedPath() : [];
753
+ const hitsIframe = target === this.iframe || Array.isArray(composedPath) && composedPath.includes(this.iframe);
754
+ if (!hitsIframe) {
755
+ return;
756
+ }
757
+ if (typeof event.stopImmediatePropagation === "function") {
758
+ ;
759
+ event.stopImmediatePropagation();
760
+ }
761
+ event.stopPropagation();
762
+ };
763
+ const options = { capture: true };
764
+ const eventNames = ["pointerdown", "mousedown", "mouseup", "click", "touchstart", "focusin"];
765
+ eventNames.forEach((eventName) => {
766
+ window.addEventListener(eventName, handler, options);
767
+ });
768
+ this.pointerGuardCleanup = () => {
769
+ eventNames.forEach((eventName) => {
770
+ window.removeEventListener(eventName, handler, options);
771
+ });
772
+ this.pointerGuardCleanup = null;
773
+ };
774
+ this.log("[IframeManager] Enabled pointer event guards for iframe consent");
775
+ }
776
+ /**
777
+ * Remove pointer interception once iframe is hidden again.
778
+ */
779
+ disablePointerEventGuards() {
780
+ if (this.pointerGuardCleanup) {
781
+ this.pointerGuardCleanup();
782
+ this.pointerGuardCleanup = null;
783
+ this.log("[IframeManager] Disabled pointer event guards for iframe consent");
784
+ }
785
+ }
786
+ // ========================================
787
+ // Public API Methods
788
+ // ========================================
789
+ /**
790
+ * Authenticate user with application
791
+ */
792
+ async authenticate(userId, avatar, displayName) {
793
+ const response = await this.sendMessage("AUTHENTICATE", {
794
+ userId,
795
+ projectId: this.projectId,
796
+ avatar,
797
+ displayName
798
+ });
799
+ if (response.type === "IFRAME_MSG_AUTH_SUCCESS") {
800
+ return {
801
+ userId: response.userId,
802
+ address: response.address
803
+ };
804
+ }
805
+ throw new Error("Authentication failed");
806
+ }
807
+ /**
808
+ * Start DKG (Distributed Key Generation)
809
+ */
810
+ async startDKG(userId, accessToken) {
811
+ const response = await this.sendMessage("START_DKG", {
812
+ userId,
813
+ projectId: this.projectId,
814
+ accessToken
815
+ // Pass access token for TSS API authentication
816
+ });
817
+ if (response.type === "IFRAME_MSG_DKG_SUCCESS") {
818
+ return response.ownerAddress;
819
+ }
820
+ throw new Error("DKG failed");
821
+ }
822
+ /**
823
+ * Sign transaction
824
+ */
825
+ async signTransaction(userId, transaction, accessToken, avatar, displayName) {
826
+ if (!avatar || !displayName) {
827
+ const { jwtTokenManager } = await import("@embarkai/core/auth");
828
+ avatar = avatar || jwtTokenManager.getAvatar();
829
+ displayName = displayName || jwtTokenManager.getDisplayName();
830
+ }
831
+ const response = await this.sendMessage("SIGN_TRANSACTION", {
832
+ userId,
833
+ projectId: this.projectId,
834
+ transaction,
835
+ accessToken,
836
+ // Pass access token for TSS API authentication
837
+ avatar,
838
+ displayName
839
+ });
840
+ if (response.type === "IFRAME_MSG_SIGNATURE") {
841
+ return response.signature;
842
+ }
843
+ throw new Error("Transaction signing failed");
844
+ }
845
+ /**
846
+ * Sign EIP712 typed data
847
+ */
848
+ async signTypedData(userId, typedData, digest32, accessToken, avatar, displayName) {
849
+ console.log("[IframeManager] signTypedData: Sending SIGN_TYPED_DATA message", {
850
+ userId,
851
+ primaryType: typedData.primaryType,
852
+ digest32
853
+ });
854
+ if (!avatar || !displayName) {
855
+ const { jwtTokenManager } = await import("@embarkai/core/auth");
856
+ avatar = avatar || jwtTokenManager.getAvatar();
857
+ displayName = displayName || jwtTokenManager.getDisplayName();
858
+ }
859
+ const response = await this.sendMessage("SIGN_TYPED_DATA", {
860
+ userId,
861
+ projectId: this.projectId,
862
+ typedData,
863
+ digest32,
864
+ accessToken,
865
+ // Pass access token for TSS API authentication
866
+ avatar,
867
+ displayName
868
+ });
869
+ console.log("[IframeManager] signTypedData: Response received", {
870
+ type: response.type,
871
+ hasSignature: !!response.signature
872
+ });
873
+ if (response.type === "IFRAME_MSG_EIP712_SIGNATURE") {
874
+ return response.signature;
875
+ }
876
+ throw new Error("EIP712 signing failed");
877
+ }
878
+ /**
879
+ * Get user's wallet address
880
+ */
881
+ async getAddress(userId) {
882
+ const response = await this.sendMessage("GET_ADDRESS", {
883
+ userId
884
+ });
885
+ if (response.type === "IFRAME_MSG_ADDRESS") {
886
+ return response.address;
887
+ }
888
+ return void 0;
889
+ }
890
+ /**
891
+ * Check if user has a keyshare
892
+ */
893
+ async checkKeyshare(userId) {
894
+ const response = await this.sendMessage("CHECK_KEYSHARE", {
895
+ userId
896
+ });
897
+ if (response.type === "IFRAME_MSG_KEYSHARE_STATUS") {
898
+ return {
899
+ hasKeyshare: response.hasKeyshare,
900
+ address: response.address
901
+ };
902
+ }
903
+ return { hasKeyshare: false };
904
+ }
905
+ /**
906
+ * Open social auth popup (Telegram, Discord, Twitter, etc.)
907
+ */
908
+ async openSocialAuthPopup(provider, config) {
909
+ this.log(`[IframeManager] Opening social auth popup for provider: ${provider}`);
910
+ return new Promise((resolve, reject) => {
911
+ const normalizedProviderName = provider.toLowerCase();
912
+ const providerKey = normalizedProviderName === "twitter" ? "x" : normalizedProviderName;
913
+ const xMode = config?.mode === "link" ? "link" : "login";
914
+ const sessionBeforeFlow = providerKey === "x" ? this.getKnownSessionId("x") : null;
915
+ const connectionBeforeFlow = providerKey === "x" ? this.getKnownProviderConnection("x") : null;
916
+ let popupUrl;
917
+ if (providerKey === "telegram") {
918
+ const iframeOrigin = new URL(this.iframeUrl).origin;
919
+ const botUsername = config?.botUsername;
920
+ this.log("[IframeManager] Telegram config:", config);
921
+ this.log("[IframeManager] Bot username:", botUsername);
922
+ const url = new URL(`${iframeOrigin}/oauth/telegram.html`);
923
+ if (botUsername) {
924
+ url.searchParams.set("bot", botUsername);
925
+ }
926
+ popupUrl = url.toString();
927
+ this.log("[IframeManager] Popup URL:", popupUrl);
928
+ } else if (providerKey === "x") {
929
+ const iframeOrigin = new URL(this.iframeUrl).origin;
930
+ this.log("[IframeManager] X OAuth config:", config);
931
+ const url = new URL(`${iframeOrigin}/oauth/x.html`);
932
+ const { getServiceUrls: getServiceUrls2 } = (init_initial(), __toCommonJS(initial_exports));
933
+ const { tssUrl } = getServiceUrls2();
934
+ const projectId = typeof window !== "undefined" ? window.__EMBARK_PROJECT_ID__ : void 0;
935
+ this.log("[IframeManager] TSS URL from config:", tssUrl);
936
+ this.log("[IframeManager] Project ID:", projectId);
937
+ url.searchParams.set("tssUrl", tssUrl);
938
+ url.searchParams.set("mode", xMode);
939
+ if (projectId) {
940
+ url.searchParams.set("projectId", projectId);
941
+ }
942
+ if (xMode === "link" && config?.token) {
943
+ url.searchParams.set("token", config.token);
944
+ }
945
+ popupUrl = url.toString();
946
+ this.log("[IframeManager] X OAuth Popup URL:", popupUrl);
947
+ } else {
948
+ reject(new Error(`Provider ${provider} not supported yet`));
949
+ return;
950
+ }
951
+ const popup = window.open(
952
+ popupUrl,
953
+ `${provider}_auth`,
954
+ "width=550,height=650,menubar=no,toolbar=no,location=no,status=no"
955
+ );
956
+ if (!popup) {
957
+ reject(new Error("Failed to open popup. Please allow popups for this site."));
958
+ return;
959
+ }
960
+ let checkClosed;
961
+ let resolved = false;
962
+ let messageHandler = null;
963
+ let pendingCancellation = false;
964
+ const cleanup = (closePopup = true) => {
965
+ if (messageHandler) {
966
+ window.removeEventListener("message", messageHandler);
967
+ }
968
+ if (checkClosed) {
969
+ clearInterval(checkClosed);
970
+ checkClosed = void 0;
971
+ }
972
+ if (closePopup && !popup.closed) {
973
+ popup.close();
974
+ }
975
+ };
976
+ const finalize = (result, closePopup = true) => {
977
+ if (resolved) {
978
+ return;
979
+ }
980
+ resolved = true;
981
+ cleanup(closePopup);
982
+ if (result.success && providerKey === "x") {
983
+ this.updateProviderTrackingFromUser("x", result.user);
984
+ }
985
+ resolve({
986
+ provider,
987
+ ...result
988
+ });
989
+ };
990
+ messageHandler = (event) => {
991
+ const iframeOrigin = new URL(this.iframeUrl).origin;
992
+ if (event.origin !== iframeOrigin) {
993
+ return;
994
+ }
995
+ const eventProvider = event.data.provider?.toLowerCase();
996
+ if (event.data.type === "TELEGRAM_AUTH_SUCCESS" && eventProvider === "telegram" && providerKey === "telegram") {
997
+ this.log("[IframeManager] Telegram auth successful from popup:", event.data);
998
+ finalize({
999
+ success: true,
1000
+ user: event.data.user
1001
+ });
1002
+ } else if (event.data.type === "X_AUTH_SUCCESS" && eventProvider === "x" && providerKey === "x") {
1003
+ this.log("[IframeManager] X auth successful from popup:", event.data);
1004
+ if (event.data.tokens) {
1005
+ this.log("[IframeManager] Tokens received in postMessage, storing via jwtTokenManager");
1006
+ import("./auth-S7VX73GX.js").then(({ jwtTokenManager }) => {
1007
+ return jwtTokenManager.setTokens({
1008
+ accessToken: event.data.tokens.accessToken,
1009
+ refreshToken: event.data.tokens.refreshToken,
1010
+ userId: event.data.user.userId,
1011
+ expiresIn: event.data.user.expiresIn || 3600,
1012
+ hasKeyshare: event.data.user.hasKeyshare || false,
1013
+ displayName: event.data.user.displayName || null,
1014
+ providers: event.data.user.providers || ["x"]
1015
+ });
1016
+ }).then(() => {
1017
+ this.log("[IframeManager] Tokens stored successfully");
1018
+ }).catch((tokenError) => {
1019
+ this.log("[IframeManager] Warning: Failed to store tokens:", tokenError);
1020
+ });
1021
+ }
1022
+ finalize({
1023
+ success: true,
1024
+ user: event.data.user,
1025
+ mode: event.data.mode
1026
+ });
1027
+ } else if (event.data.type === "TELEGRAM_AUTH_CANCELLED" && eventProvider === "telegram" && providerKey === "telegram") {
1028
+ this.log("[IframeManager] Telegram auth cancelled by user");
1029
+ finalize(
1030
+ {
1031
+ success: false,
1032
+ error: "User cancelled authentication"
1033
+ },
1034
+ false
1035
+ );
1036
+ } else if (event.data.type === "X_AUTH_CANCELLED" && eventProvider === "x" && providerKey === "x") {
1037
+ this.log("[IframeManager] X auth cancelled by user");
1038
+ pendingCancellation = true;
1039
+ this.verifyAuthSession({ provider: "x" }).then((verification) => {
1040
+ const sessionId = verification.sessionId || null;
1041
+ const providers = verification.providers || [];
1042
+ const isConnectedNow = providers.includes("x");
1043
+ const sessionChanged = !!sessionId && sessionId !== sessionBeforeFlow;
1044
+ const connectionGained = connectionBeforeFlow === false && isConnectedNow;
1045
+ if (verification.success) {
1046
+ if (sessionChanged || connectionGained) {
1047
+ this.log(
1048
+ "[IframeManager] X auth cancellation fallback found active session change, treating as success"
1049
+ );
1050
+ finalize(
1051
+ {
1052
+ success: true,
1053
+ user: verification.user,
1054
+ mode: xMode
1055
+ },
1056
+ true
1057
+ );
1058
+ } else {
1059
+ this.log("[IframeManager] X auth fallback did not detect new session, treating as cancellation");
1060
+ if (popup.closed) {
1061
+ finalize(
1062
+ {
1063
+ success: false,
1064
+ error: "User cancelled authentication"
1065
+ },
1066
+ false
1067
+ );
1068
+ }
1069
+ }
1070
+ } else {
1071
+ this.log("[IframeManager] X auth fallback verification reported no session, treating as cancellation");
1072
+ if (popup.closed) {
1073
+ finalize(
1074
+ {
1075
+ success: false,
1076
+ error: "User cancelled authentication"
1077
+ },
1078
+ false
1079
+ );
1080
+ }
1081
+ }
1082
+ }).catch((error) => {
1083
+ this.log("[IframeManager] X auth fallback verification error:", error);
1084
+ if (popup.closed) {
1085
+ finalize(
1086
+ {
1087
+ success: false,
1088
+ error: "User cancelled authentication"
1089
+ },
1090
+ false
1091
+ );
1092
+ }
1093
+ });
1094
+ } else if (event.data.type === "X_AUTH_ERROR" && eventProvider === "x" && providerKey === "x") {
1095
+ this.log("[IframeManager] X auth error from popup:", event.data.error);
1096
+ finalize({
1097
+ success: false,
1098
+ error: event.data.error || "Authentication failed"
1099
+ });
1100
+ }
1101
+ };
1102
+ window.addEventListener("message", messageHandler);
1103
+ checkClosed = setInterval(() => {
1104
+ if (popup.closed) {
1105
+ if (checkClosed) {
1106
+ clearInterval(checkClosed);
1107
+ checkClosed = void 0;
1108
+ }
1109
+ if (!resolved) {
1110
+ if (providerKey === "x" && pendingCancellation) {
1111
+ finalize(
1112
+ {
1113
+ success: false,
1114
+ error: "User cancelled authentication"
1115
+ },
1116
+ false
1117
+ );
1118
+ } else {
1119
+ finalize(
1120
+ {
1121
+ success: false,
1122
+ error: "Popup was closed"
1123
+ },
1124
+ false
1125
+ );
1126
+ }
1127
+ }
1128
+ }
1129
+ }, 500);
1130
+ });
1131
+ }
1132
+ /**
1133
+ * Verify current auth session with the backend.
1134
+ * Can optionally track session state for a specific provider.
1135
+ * @param options.silent - If true, suppress logging
1136
+ * @param options.provider - Optional provider key to track session state for (e.g., 'x', 'telegram')
1137
+ */
1138
+ async verifyAuthSession(options = {}) {
1139
+ if (typeof window === "undefined" || typeof fetch === "undefined") {
1140
+ return { success: false };
1141
+ }
1142
+ try {
1143
+ const { getServiceUrls: getServiceUrls2 } = (init_initial(), __toCommonJS(initial_exports));
1144
+ const { tssUrl } = getServiceUrls2();
1145
+ const projectId = typeof window !== "undefined" ? window.__EMBARK_PROJECT_ID__ : void 0;
1146
+ if (!tssUrl) {
1147
+ this.log("[IframeManager] Session verify aborted: missing TSS URL");
1148
+ return { success: false };
1149
+ }
1150
+ const verifyUrl = new URL(`${tssUrl}/api/auth/verify`);
1151
+ if (projectId) {
1152
+ verifyUrl.searchParams.set("projectId", projectId);
1153
+ }
1154
+ const { jwtTokenManager, ensureValidToken } = await import("./auth-S7VX73GX.js");
1155
+ const hasValidToken = await ensureValidToken();
1156
+ const headers = {
1157
+ "Content-Type": "application/json"
1158
+ };
1159
+ if (hasValidToken) {
1160
+ const accessToken = jwtTokenManager.getAccessToken();
1161
+ if (accessToken) {
1162
+ headers["Authorization"] = `Bearer ${accessToken}`;
1163
+ }
1164
+ }
1165
+ let response = await fetch(verifyUrl.toString(), {
1166
+ method: "GET",
1167
+ headers,
1168
+ credentials: "include"
1169
+ });
1170
+ if (response.status === 401) {
1171
+ this.log("[IframeManager] Session verify got 401, attempting token refresh...");
1172
+ const refreshSuccess = await jwtTokenManager.refreshAccessToken();
1173
+ if (refreshSuccess) {
1174
+ const newAccessToken = jwtTokenManager.getAccessToken();
1175
+ if (newAccessToken) {
1176
+ headers["Authorization"] = `Bearer ${newAccessToken}`;
1177
+ this.log("[IframeManager] Token refreshed, retrying verify...");
1178
+ response = await fetch(verifyUrl.toString(), {
1179
+ method: "GET",
1180
+ headers,
1181
+ credentials: "include"
1182
+ });
1183
+ }
1184
+ }
1185
+ }
1186
+ if (!response.ok) {
1187
+ this.log("[IframeManager] Session verify returned status:", response.status);
1188
+ if (options.provider) {
1189
+ this.recordProviderSession(options.provider, null);
1190
+ this.recordProviderConnection(options.provider, false);
1191
+ }
1192
+ return { success: false };
1193
+ }
1194
+ let userData = void 0;
1195
+ try {
1196
+ userData = await response.json();
1197
+ } catch (jsonError) {
1198
+ this.log("[IframeManager] Session verify JSON parse failed:", jsonError);
1199
+ }
1200
+ const sessionId = this.extractSessionId(userData);
1201
+ const providers = this.getProvidersList(userData);
1202
+ if (options.provider) {
1203
+ this.recordProviderSession(options.provider, sessionId);
1204
+ this.recordProviderConnection(options.provider, providers.includes(options.provider));
1205
+ }
1206
+ if (!options.silent) {
1207
+ this.log("[IframeManager] Session verify result:", {
1208
+ sessionId,
1209
+ providers
1210
+ });
1211
+ }
1212
+ return {
1213
+ success: true,
1214
+ user: userData,
1215
+ sessionId,
1216
+ providers
1217
+ };
1218
+ } catch (error) {
1219
+ this.log("[IframeManager] Session verify error:", error);
1220
+ if (options.provider) {
1221
+ this.recordProviderSession(options.provider, null);
1222
+ this.recordProviderConnection(options.provider, false);
1223
+ }
1224
+ return { success: false };
1225
+ }
1226
+ }
1227
+ /**
1228
+ * Prime provider session/cache state to detect future changes.
1229
+ */
1230
+ async primeProviderSessions() {
1231
+ try {
1232
+ await this.verifyAuthSession({ silent: true });
1233
+ } catch (error) {
1234
+ this.log("[IframeManager] Prime provider sessions failed:", error);
1235
+ }
1236
+ }
1237
+ getKnownSessionId(providerKey) {
1238
+ return this.providerSessions.has(providerKey) ? this.providerSessions.get(providerKey) ?? null : null;
1239
+ }
1240
+ getKnownProviderConnection(providerKey) {
1241
+ if (!this.providerConnections.has(providerKey)) {
1242
+ return null;
1243
+ }
1244
+ const value = this.providerConnections.get(providerKey);
1245
+ return typeof value === "boolean" ? value : null;
1246
+ }
1247
+ recordProviderSession(providerKey, sessionId) {
1248
+ this.providerSessions.set(providerKey, sessionId ?? null);
1249
+ }
1250
+ recordProviderConnection(providerKey, connected) {
1251
+ this.providerConnections.set(providerKey, connected);
1252
+ }
1253
+ updateProviderTrackingFromUser(providerKey, user) {
1254
+ if (!user) {
1255
+ return;
1256
+ }
1257
+ if (providerKey === "x") {
1258
+ const sessionId = this.extractSessionId(user);
1259
+ this.recordProviderSession("x", sessionId);
1260
+ const providers = this.getProvidersList(user);
1261
+ this.recordProviderConnection("x", providers.includes("x"));
1262
+ }
1263
+ }
1264
+ extractSessionId(user) {
1265
+ if (!user || typeof user !== "object") {
1266
+ return null;
1267
+ }
1268
+ if (typeof user.sessionId === "string") {
1269
+ return user.sessionId;
1270
+ }
1271
+ if (user.data && typeof user.data.sessionId === "string") {
1272
+ return user.data.sessionId;
1273
+ }
1274
+ return null;
1275
+ }
1276
+ getProvidersList(user) {
1277
+ if (!user || typeof user !== "object") {
1278
+ return [];
1279
+ }
1280
+ const providersRaw = user.providers || user.data?.providers;
1281
+ if (!Array.isArray(providersRaw)) {
1282
+ return [];
1283
+ }
1284
+ return providersRaw.map((provider) => typeof provider === "string" ? provider.toLowerCase() : null).filter((provider) => provider !== null);
1285
+ }
1286
+ /**
1287
+ * Get trusted apps for user
1288
+ */
1289
+ async getTrustedApps(userId) {
1290
+ const response = await this.sendMessage("GET_TRUSTED_APPS", {
1291
+ userId
1292
+ });
1293
+ if (response.type === "IFRAME_MSG_TRUSTED_APPS_LIST") {
1294
+ return response.apps || [];
1295
+ }
1296
+ return [];
1297
+ }
1298
+ /**
1299
+ * Remove app from trusted list
1300
+ */
1301
+ async removeTrustedApp(userId, projectId, origin) {
1302
+ const response = await this.sendMessage("REMOVE_TRUSTED_APP", {
1303
+ userId,
1304
+ projectId,
1305
+ appOrigin: origin
1306
+ });
1307
+ if (response.type === "IFRAME_MSG_TRUSTED_APP_REMOVED") {
1308
+ return response.success;
1309
+ }
1310
+ return false;
1311
+ }
1312
+ /**
1313
+ * Clear all authorizations for current project (for testing consent flow)
1314
+ * This will force the consent modal to appear on next authentication
1315
+ */
1316
+ async clearAuthorizations() {
1317
+ try {
1318
+ const response = await this.sendMessage("CLEAR_AUTHORIZATIONS", {
1319
+ projectId: this.projectId
1320
+ });
1321
+ this.log("[IframeManager] Cleared authorizations for project:", this.projectId);
1322
+ return response.success || true;
1323
+ } catch (error) {
1324
+ this.log("[IframeManager] Failed to clear authorizations:", error);
1325
+ return false;
1326
+ }
1327
+ }
1328
+ /**
1329
+ * Create backup of keyshare
1330
+ */
1331
+ async createBackup(userId, backupRequest, accessToken) {
1332
+ const response = await this.sendMessage("CREATE_BACKUP", {
1333
+ userId,
1334
+ backupRequest,
1335
+ accessToken
1336
+ // Pass access token for TSS API authentication
1337
+ });
1338
+ if (response.type === "IFRAME_MSG_BACKUP_CREATED") {
1339
+ return response.result;
1340
+ }
1341
+ throw new Error("Unexpected response type");
1342
+ }
1343
+ /**
1344
+ * Restore keyshare from server backup
1345
+ */
1346
+ async restoreFromServer(userId, password, accessToken) {
1347
+ const response = await this.sendMessage("RESTORE_BACKUP", {
1348
+ userId,
1349
+ password,
1350
+ accessToken
1351
+ // Pass access token for TSS API authentication
1352
+ });
1353
+ if (response.type === "IFRAME_MSG_BACKUP_RESTORED") {
1354
+ return response.result;
1355
+ }
1356
+ throw new Error("Unexpected response type");
1357
+ }
1358
+ /**
1359
+ * Encrypt backup data without uploading (for cloud/local backups)
1360
+ * Returns encrypted data that parent can upload/download
1361
+ */
1362
+ async encryptBackupData(userId, password) {
1363
+ const response = await this.sendMessage("ENCRYPT_BACKUP_DATA", {
1364
+ userId,
1365
+ password
1366
+ });
1367
+ if (response.type === "IFRAME_MSG_BACKUP_ENCRYPTED") {
1368
+ return response.encryptedData;
1369
+ }
1370
+ throw new Error("Unexpected response type");
1371
+ }
1372
+ /**
1373
+ * Encrypt current keyshare as EnvelopeEncryptedShare (for Guardian backup)
1374
+ */
1375
+ async encryptEnvelopeData(userId, password) {
1376
+ const response = await this.sendMessage("ENCRYPT_ENVELOPE_DATA", { userId, password });
1377
+ if (response.type === "IFRAME_MSG_ENVELOPE_ENCRYPTED") {
1378
+ return response.envelopeJson;
1379
+ }
1380
+ throw new Error("Unexpected response type");
1381
+ }
1382
+ /**
1383
+ * Decrypt EnvelopeEncryptedShare and restore keyshare (for Guardian restore)
1384
+ */
1385
+ async decryptAndRestoreEnvelope(userId, envelopeJson, password) {
1386
+ const response = await this.sendMessage("DECRYPT_AND_RESTORE_ENVELOPE", {
1387
+ userId,
1388
+ envelopeJson,
1389
+ password
1390
+ });
1391
+ if (response.type === "IFRAME_MSG_ENVELOPE_RESTORED") {
1392
+ return response.result;
1393
+ }
1394
+ throw new Error("Unexpected response type");
1395
+ }
1396
+ /**
1397
+ * Restore keyshare from local file backup
1398
+ */
1399
+ async restoreFromLocalFile(userId, fileContent, password) {
1400
+ const response = await this.sendMessage("RESTORE_FROM_FILE", {
1401
+ userId,
1402
+ fileContent,
1403
+ password
1404
+ });
1405
+ if (response.type === "IFRAME_MSG_FILE_RESTORED") {
1406
+ return response.result;
1407
+ }
1408
+ throw new Error("Unexpected response type");
1409
+ }
1410
+ /**
1411
+ * Get backup status for user
1412
+ */
1413
+ async getBackupStatus(userId) {
1414
+ const response = await this.sendMessage("GET_BACKUP_STATUS", {
1415
+ userId
1416
+ });
1417
+ if (response.type === "IFRAME_MSG_BACKUP_STATUS") {
1418
+ return response.status;
1419
+ }
1420
+ throw new Error("Unexpected response type");
1421
+ }
1422
+ /**
1423
+ * Get available cloud providers
1424
+ */
1425
+ async getCloudProviders() {
1426
+ const response = await this.sendMessage("GET_CLOUD_PROVIDERS", {});
1427
+ if (response.type === "IFRAME_MSG_CLOUD_PROVIDERS") {
1428
+ return response.providers;
1429
+ }
1430
+ throw new Error("Unexpected response type");
1431
+ }
1432
+ /**
1433
+ * Cleanup and destroy iframe
1434
+ */
1435
+ destroy() {
1436
+ this.log("[IframeManager] Destroying iframe...");
1437
+ this.stopHeartbeat();
1438
+ this.pendingRequests.forEach((pending) => {
1439
+ pending.reject(new Error("Iframe manager destroyed"));
1440
+ });
1441
+ this.pendingRequests.clear();
1442
+ if (this.messageListener) {
1443
+ window.removeEventListener("message", this.messageListener);
1444
+ this.messageListener = null;
1445
+ }
1446
+ if (this.iframe && this.iframe.parentNode) {
1447
+ this.iframe.parentNode.removeChild(this.iframe);
1448
+ }
1449
+ this.iframe = null;
1450
+ this.isReady = false;
1451
+ this.channelToken = null;
1452
+ this.initPromise = null;
1453
+ this.readyPromise = new Promise((resolve) => {
1454
+ this.readyResolve = resolve;
1455
+ });
1456
+ this.log("[IframeManager] \u2705 Destroyed");
1457
+ }
1458
+ /**
1459
+ * Debug logging
1460
+ */
1461
+ log(message, data) {
1462
+ if (this.debug) {
1463
+ if (data) {
1464
+ console.log(message, data);
1465
+ } else {
1466
+ console.log(message);
1467
+ }
1468
+ }
1469
+ }
1470
+ };
1471
+ var iframeManagerInstance = null;
1472
+ function getIframeManager(config) {
1473
+ if (!iframeManagerInstance && config) {
1474
+ iframeManagerInstance = new IframeManager(config);
1475
+ }
1476
+ if (!iframeManagerInstance) {
1477
+ throw new Error(
1478
+ 'IframeManager not initialized. This usually means projectId is not configured.\n\nTo fix this:\n1. Get your projectId from https://dashboard.lumiapassport.com/\n2. Add it to Provider:\n <Provider projectId="your-project-id">\n\nFor more details, see the documentation.'
1479
+ );
1480
+ }
1481
+ return iframeManagerInstance;
1482
+ }
1483
+ async function waitForIframe() {
1484
+ const manager = getIframeManager();
1485
+ await manager.initialize();
1486
+ }
1487
+ function destroyIframeManager() {
1488
+ if (iframeManagerInstance) {
1489
+ iframeManagerInstance.destroy();
1490
+ iframeManagerInstance = null;
1491
+ }
1492
+ }
1493
+
1494
+ export {
1495
+ GoogleIcon,
1496
+ TelegramIcon,
1497
+ TwitterIcon,
1498
+ DiscordIcon,
1499
+ init_assets,
1500
+ DEFAULT_PROVIDER_CONFIG,
1501
+ getServiceUrls,
1502
+ getIframeUrl,
1503
+ getBlockscoutApiUrl,
1504
+ init_initial,
1505
+ AccountAbstractionError,
1506
+ UserRejectedError,
1507
+ ErrorCodes,
1508
+ createAccountAbstractionError,
1509
+ IframeManager,
1510
+ getIframeManager,
1511
+ waitForIframe,
1512
+ destroyIframeManager
1513
+ };
1514
+ //# sourceMappingURL=chunk-ISXWRCDC.js.map