@frak-labs/core-sdk 0.1.0-beta.afa252b0 → 0.1.0-beta.b0bd1f8a

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.
Files changed (139) hide show
  1. package/cdn/bundle.iife.js +14 -0
  2. package/dist/actions-CEEObPYc.js +1 -0
  3. package/dist/actions-DbQhWYx8.cjs +1 -0
  4. package/dist/actions.cjs +1 -1
  5. package/dist/actions.d.cts +3 -1481
  6. package/dist/actions.d.ts +3 -1481
  7. package/dist/actions.js +1 -1
  8. package/dist/bundle.cjs +1 -13
  9. package/dist/bundle.d.cts +6 -2087
  10. package/dist/bundle.d.ts +6 -2087
  11. package/dist/bundle.js +1 -13
  12. package/dist/index-7OZ39x1U.d.ts +195 -0
  13. package/dist/index-C6FxkWPC.d.cts +511 -0
  14. package/dist/index-UFX7xCg3.d.ts +351 -0
  15. package/dist/index-d8xS4ryI.d.ts +511 -0
  16. package/dist/index-p4FqSp8z.d.cts +351 -0
  17. package/dist/index-zDq-VlKx.d.cts +195 -0
  18. package/dist/index.cjs +1 -13
  19. package/dist/index.d.cts +4 -1387
  20. package/dist/index.d.ts +4 -1387
  21. package/dist/index.js +1 -13
  22. package/dist/interaction-DMJ3ZfaF.d.cts +45 -0
  23. package/dist/interaction-KX1h9a7V.d.ts +45 -0
  24. package/dist/interactions-DnfM3oe0.js +1 -0
  25. package/dist/interactions-EIXhNLf6.cjs +1 -0
  26. package/dist/interactions.cjs +1 -1
  27. package/dist/interactions.d.cts +2 -182
  28. package/dist/interactions.d.ts +2 -182
  29. package/dist/interactions.js +1 -1
  30. package/dist/openSso-D--Airj6.d.cts +1018 -0
  31. package/dist/openSso-DsKJ4y0j.d.ts +1018 -0
  32. package/dist/productTypes-BUkXJKZ7.cjs +1 -0
  33. package/dist/productTypes-CGb1MmBF.js +1 -0
  34. package/dist/src-B_xO0AR6.cjs +13 -0
  35. package/dist/src-D2d52OZa.js +13 -0
  36. package/dist/trackEvent-CHnYa85W.js +1 -0
  37. package/dist/trackEvent-GuQm_1Nm.cjs +1 -0
  38. package/package.json +23 -18
  39. package/src/actions/displayEmbeddedWallet.test.ts +194 -0
  40. package/src/actions/displayEmbeddedWallet.ts +20 -0
  41. package/src/actions/displayModal.test.ts +387 -0
  42. package/src/actions/displayModal.ts +131 -0
  43. package/src/actions/getProductInformation.test.ts +133 -0
  44. package/src/actions/getProductInformation.ts +14 -0
  45. package/src/actions/index.ts +29 -0
  46. package/src/actions/openSso.test.ts +407 -0
  47. package/src/actions/openSso.ts +116 -0
  48. package/src/actions/prepareSso.test.ts +223 -0
  49. package/src/actions/prepareSso.ts +48 -0
  50. package/src/actions/referral/processReferral.ts +230 -0
  51. package/src/actions/referral/referralInteraction.ts +57 -0
  52. package/src/actions/sendInteraction.test.ts +219 -0
  53. package/src/actions/sendInteraction.ts +32 -0
  54. package/src/actions/trackPurchaseStatus.test.ts +287 -0
  55. package/src/actions/trackPurchaseStatus.ts +53 -0
  56. package/src/actions/watchWalletStatus.test.ts +372 -0
  57. package/src/actions/watchWalletStatus.ts +94 -0
  58. package/src/actions/wrapper/modalBuilder.ts +212 -0
  59. package/src/actions/wrapper/sendTransaction.ts +62 -0
  60. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  61. package/src/bundle.ts +3 -0
  62. package/src/clients/DebugInfo.ts +182 -0
  63. package/src/clients/createIFrameFrakClient.ts +287 -0
  64. package/src/clients/index.ts +3 -0
  65. package/src/clients/setupClient.test.ts +343 -0
  66. package/src/clients/setupClient.ts +73 -0
  67. package/src/clients/transports/iframeLifecycleManager.test.ts +399 -0
  68. package/src/clients/transports/iframeLifecycleManager.ts +90 -0
  69. package/src/constants/interactionTypes.ts +44 -0
  70. package/src/constants/locales.ts +14 -0
  71. package/src/constants/productTypes.ts +33 -0
  72. package/src/index.ts +101 -0
  73. package/src/interactions/index.ts +5 -0
  74. package/src/interactions/pressEncoder.test.ts +215 -0
  75. package/src/interactions/pressEncoder.ts +53 -0
  76. package/src/interactions/purchaseEncoder.test.ts +291 -0
  77. package/src/interactions/purchaseEncoder.ts +99 -0
  78. package/src/interactions/referralEncoder.test.ts +170 -0
  79. package/src/interactions/referralEncoder.ts +47 -0
  80. package/src/interactions/retailEncoder.test.ts +107 -0
  81. package/src/interactions/retailEncoder.ts +37 -0
  82. package/src/interactions/webshopEncoder.test.ts +56 -0
  83. package/src/interactions/webshopEncoder.ts +30 -0
  84. package/src/types/client.ts +14 -0
  85. package/src/types/compression.ts +22 -0
  86. package/src/types/config.ts +111 -0
  87. package/src/types/context.ts +13 -0
  88. package/src/types/index.ts +71 -0
  89. package/src/types/lifecycle/client.ts +46 -0
  90. package/src/types/lifecycle/iframe.ts +35 -0
  91. package/src/types/lifecycle/index.ts +2 -0
  92. package/src/types/rpc/displayModal.ts +84 -0
  93. package/src/types/rpc/embedded/index.ts +68 -0
  94. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  95. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  96. package/src/types/rpc/interaction.ts +43 -0
  97. package/src/types/rpc/modal/final.ts +46 -0
  98. package/src/types/rpc/modal/generic.ts +46 -0
  99. package/src/types/rpc/modal/index.ts +20 -0
  100. package/src/types/rpc/modal/login.ts +32 -0
  101. package/src/types/rpc/modal/openSession.ts +25 -0
  102. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  103. package/src/types/rpc/modal/transaction.ts +33 -0
  104. package/src/types/rpc/productInformation.ts +59 -0
  105. package/src/types/rpc/sso.ts +80 -0
  106. package/src/types/rpc/walletStatus.ts +35 -0
  107. package/src/types/rpc.ts +158 -0
  108. package/src/types/transport.ts +34 -0
  109. package/src/utils/FrakContext.test.ts +407 -0
  110. package/src/utils/FrakContext.ts +158 -0
  111. package/src/utils/compression/b64.test.ts +181 -0
  112. package/src/utils/compression/b64.ts +29 -0
  113. package/src/utils/compression/compress.test.ts +123 -0
  114. package/src/utils/compression/compress.ts +11 -0
  115. package/src/utils/compression/decompress.test.ts +145 -0
  116. package/src/utils/compression/decompress.ts +11 -0
  117. package/src/utils/compression/index.ts +3 -0
  118. package/src/utils/computeProductId.test.ts +80 -0
  119. package/src/utils/computeProductId.ts +11 -0
  120. package/src/utils/constants.test.ts +23 -0
  121. package/src/utils/constants.ts +4 -0
  122. package/src/utils/formatAmount.test.ts +113 -0
  123. package/src/utils/formatAmount.ts +18 -0
  124. package/src/utils/getCurrencyAmountKey.test.ts +44 -0
  125. package/src/utils/getCurrencyAmountKey.ts +15 -0
  126. package/src/utils/getSupportedCurrency.test.ts +51 -0
  127. package/src/utils/getSupportedCurrency.ts +14 -0
  128. package/src/utils/getSupportedLocale.test.ts +64 -0
  129. package/src/utils/getSupportedLocale.ts +16 -0
  130. package/src/utils/iframeHelper.test.ts +450 -0
  131. package/src/utils/iframeHelper.ts +143 -0
  132. package/src/utils/index.ts +21 -0
  133. package/src/utils/sso.test.ts +361 -0
  134. package/src/utils/sso.ts +119 -0
  135. package/src/utils/ssoUrlListener.ts +60 -0
  136. package/src/utils/trackEvent.test.ts +162 -0
  137. package/src/utils/trackEvent.ts +26 -0
  138. package/cdn/bundle.js +0 -19
  139. package/cdn/bundle.js.LICENSE.txt +0 -10
@@ -0,0 +1,450 @@
1
+ /**
2
+ * Tests for iframe helper utilities
3
+ * Tests iframe creation, visibility management, and finder functions
4
+ */
5
+
6
+ import {
7
+ afterEach,
8
+ beforeEach,
9
+ describe,
10
+ expect,
11
+ it,
12
+ vi,
13
+ } from "../../tests/vitest-fixtures";
14
+ import type { FrakWalletSdkConfig } from "../types";
15
+ import {
16
+ baseIframeProps,
17
+ changeIframeVisibility,
18
+ createIframe,
19
+ findIframeInOpener,
20
+ } from "./iframeHelper";
21
+
22
+ describe("iframeHelper", () => {
23
+ describe("baseIframeProps", () => {
24
+ it("should have correct id and name", () => {
25
+ expect(baseIframeProps.id).toBe("frak-wallet");
26
+ expect(baseIframeProps.name).toBe("frak-wallet");
27
+ });
28
+
29
+ it("should have correct title", () => {
30
+ expect(baseIframeProps.title).toBe("Frak Wallet");
31
+ });
32
+
33
+ it("should have correct allow attribute", () => {
34
+ expect(baseIframeProps.allow).toContain(
35
+ "publickey-credentials-get"
36
+ );
37
+ expect(baseIframeProps.allow).toContain("clipboard-write");
38
+ expect(baseIframeProps.allow).toContain("web-share");
39
+ });
40
+
41
+ it("should have correct initial style", () => {
42
+ expect(baseIframeProps.style.width).toBe("0");
43
+ expect(baseIframeProps.style.height).toBe("0");
44
+ expect(baseIframeProps.style.border).toBe("0");
45
+ expect(baseIframeProps.style.position).toBe("absolute");
46
+ expect(baseIframeProps.style.zIndex).toBe(2000001);
47
+ });
48
+ });
49
+
50
+ describe("createIframe", () => {
51
+ let mockIframe: HTMLIFrameElement;
52
+ let appendChildSpy: ReturnType<typeof vi.fn>;
53
+ let querySelectorSpy: ReturnType<typeof vi.fn>;
54
+ let createElementSpy: ReturnType<typeof vi.fn>;
55
+
56
+ beforeEach(() => {
57
+ // Create mock iframe
58
+ mockIframe = {
59
+ id: "",
60
+ name: "",
61
+ allow: "",
62
+ src: "",
63
+ style: {} as CSSStyleDeclaration,
64
+ addEventListener: vi.fn((event, handler) => {
65
+ if (event === "load") {
66
+ // Simulate immediate load
67
+ setTimeout(() => handler(), 0);
68
+ }
69
+ }),
70
+ remove: vi.fn(),
71
+ } as unknown as HTMLIFrameElement;
72
+
73
+ // Mock document methods
74
+ createElementSpy = vi
75
+ .spyOn(document, "createElement")
76
+ .mockReturnValue(mockIframe);
77
+ querySelectorSpy = vi
78
+ .spyOn(document, "querySelector")
79
+ .mockReturnValue(null);
80
+ appendChildSpy = vi
81
+ .spyOn(document.body, "appendChild")
82
+ .mockReturnValue(mockIframe);
83
+ });
84
+
85
+ afterEach(() => {
86
+ createElementSpy.mockRestore();
87
+ querySelectorSpy.mockRestore();
88
+ appendChildSpy.mockRestore();
89
+ });
90
+
91
+ it("should create iframe with correct properties", async () => {
92
+ await createIframe({});
93
+
94
+ expect(document.createElement).toHaveBeenCalledWith("iframe");
95
+ expect(mockIframe.id).toBe("frak-wallet");
96
+ expect(mockIframe.name).toBe("frak-wallet");
97
+ expect(mockIframe.allow).toContain("publickey-credentials-get");
98
+ });
99
+
100
+ it("should append iframe to document body", async () => {
101
+ await createIframe({});
102
+
103
+ expect(document.body.appendChild).toHaveBeenCalledWith(mockIframe);
104
+ });
105
+
106
+ it("should set iframe src to default wallet URL", async () => {
107
+ await createIframe({});
108
+
109
+ expect(mockIframe.src).toBe("https://wallet.frak.id/listener");
110
+ });
111
+
112
+ it("should use config walletUrl when provided", async () => {
113
+ const config: FrakWalletSdkConfig = {
114
+ walletUrl: "https://custom-wallet.com",
115
+ metadata: { name: "Test" },
116
+ };
117
+
118
+ await createIframe({ config });
119
+
120
+ expect(mockIframe.src).toBe("https://custom-wallet.com/listener");
121
+ });
122
+
123
+ it("should use deprecated walletBaseUrl when provided", async () => {
124
+ await createIframe({ walletBaseUrl: "https://legacy-wallet.com" });
125
+
126
+ expect(mockIframe.src).toBe("https://legacy-wallet.com/listener");
127
+ });
128
+
129
+ it("should prefer config.walletUrl over walletBaseUrl", async () => {
130
+ const config: FrakWalletSdkConfig = {
131
+ walletUrl: "https://new-wallet.com",
132
+ metadata: { name: "Test" },
133
+ };
134
+
135
+ await createIframe({
136
+ walletBaseUrl: "https://old-wallet.com",
137
+ config,
138
+ });
139
+
140
+ expect(mockIframe.src).toBe("https://new-wallet.com/listener");
141
+ });
142
+
143
+ it("should remove existing iframe before creating new one", async () => {
144
+ const existingIframe = {
145
+ remove: vi.fn(),
146
+ } as unknown as HTMLIFrameElement;
147
+
148
+ querySelectorSpy.mockReturnValue(existingIframe);
149
+
150
+ await createIframe({});
151
+
152
+ expect(document.querySelector).toHaveBeenCalledWith("#frak-wallet");
153
+ expect(existingIframe.remove).toHaveBeenCalled();
154
+ });
155
+
156
+ it("should resolve promise on iframe load", async () => {
157
+ const result = await createIframe({});
158
+
159
+ expect(result).toBe(mockIframe);
160
+ });
161
+
162
+ it("should set iframe as initially hidden", async () => {
163
+ await createIframe({});
164
+
165
+ // Check that hidden styles were applied
166
+ expect(mockIframe.style.width).toBe("0");
167
+ expect(mockIframe.style.height).toBe("0");
168
+ });
169
+
170
+ it("should set zIndex from baseIframeProps", async () => {
171
+ await createIframe({});
172
+
173
+ expect(mockIframe.style.zIndex).toBe("2000001");
174
+ });
175
+ });
176
+
177
+ describe("changeIframeVisibility", () => {
178
+ let mockIframe: HTMLIFrameElement;
179
+
180
+ beforeEach(() => {
181
+ mockIframe = {
182
+ style: {} as CSSStyleDeclaration,
183
+ } as HTMLIFrameElement;
184
+ });
185
+
186
+ describe("when hiding iframe (isVisible: false)", () => {
187
+ it("should set width and height to 0", () => {
188
+ changeIframeVisibility({
189
+ iframe: mockIframe,
190
+ isVisible: false,
191
+ });
192
+
193
+ expect(mockIframe.style.width).toBe("0");
194
+ expect(mockIframe.style.height).toBe("0");
195
+ });
196
+
197
+ it("should set border to 0", () => {
198
+ changeIframeVisibility({
199
+ iframe: mockIframe,
200
+ isVisible: false,
201
+ });
202
+
203
+ expect(mockIframe.style.border).toBe("0");
204
+ });
205
+
206
+ it("should set position to fixed", () => {
207
+ changeIframeVisibility({
208
+ iframe: mockIframe,
209
+ isVisible: false,
210
+ });
211
+
212
+ expect(mockIframe.style.position).toBe("fixed");
213
+ });
214
+
215
+ it("should move iframe off-screen", () => {
216
+ changeIframeVisibility({
217
+ iframe: mockIframe,
218
+ isVisible: false,
219
+ });
220
+
221
+ expect(mockIframe.style.top).toBe("-1000px");
222
+ expect(mockIframe.style.left).toBe("-1000px");
223
+ });
224
+ });
225
+
226
+ describe("when showing iframe (isVisible: true)", () => {
227
+ it("should set full screen dimensions", () => {
228
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
229
+
230
+ expect(mockIframe.style.width).toBe("100%");
231
+ expect(mockIframe.style.height).toBe("100%");
232
+ });
233
+
234
+ it("should position at top-left", () => {
235
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
236
+
237
+ expect(mockIframe.style.top).toBe("0");
238
+ expect(mockIframe.style.left).toBe("0");
239
+ });
240
+
241
+ it("should set position to fixed", () => {
242
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
243
+
244
+ expect(mockIframe.style.position).toBe("fixed");
245
+ });
246
+
247
+ it("should enable pointer events", () => {
248
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
249
+
250
+ expect(mockIframe.style.pointerEvents).toBe("auto");
251
+ });
252
+ });
253
+
254
+ describe("toggling visibility", () => {
255
+ it("should hide then show correctly", () => {
256
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
257
+ expect(mockIframe.style.width).toBe("100%");
258
+
259
+ changeIframeVisibility({
260
+ iframe: mockIframe,
261
+ isVisible: false,
262
+ });
263
+ expect(mockIframe.style.width).toBe("0");
264
+ });
265
+
266
+ it("should show then hide correctly", () => {
267
+ changeIframeVisibility({
268
+ iframe: mockIframe,
269
+ isVisible: false,
270
+ });
271
+ expect(mockIframe.style.top).toBe("-1000px");
272
+
273
+ changeIframeVisibility({ iframe: mockIframe, isVisible: true });
274
+ expect(mockIframe.style.top).toBe("0");
275
+ });
276
+ });
277
+ });
278
+
279
+ describe("findIframeInOpener", () => {
280
+ let originalOpener: Window;
281
+ let consoleErrorSpy: any;
282
+
283
+ beforeEach(() => {
284
+ originalOpener = window.opener;
285
+ consoleErrorSpy = vi
286
+ .spyOn(console, "error")
287
+ .mockImplementation(() => {});
288
+ });
289
+
290
+ afterEach(() => {
291
+ window.opener = originalOpener;
292
+ consoleErrorSpy.mockRestore();
293
+ });
294
+
295
+ it("should return null when window.opener is not available", () => {
296
+ window.opener = null;
297
+
298
+ const result = findIframeInOpener();
299
+
300
+ expect(result).toBeNull();
301
+ });
302
+
303
+ it("should find iframe in window.opener with default pathname", () => {
304
+ const mockOpener = {
305
+ location: {
306
+ origin: window.location.origin,
307
+ pathname: "/listener",
308
+ },
309
+ frames: [],
310
+ } as unknown as Window;
311
+
312
+ window.opener = mockOpener;
313
+
314
+ const result = findIframeInOpener();
315
+
316
+ expect(result).toBe(mockOpener);
317
+ });
318
+
319
+ it("should find iframe with custom pathname", () => {
320
+ const mockOpener = {
321
+ location: {
322
+ origin: window.location.origin,
323
+ pathname: "/custom-iframe",
324
+ },
325
+ frames: [],
326
+ } as unknown as Window;
327
+
328
+ window.opener = mockOpener;
329
+
330
+ const result = findIframeInOpener("/custom-iframe");
331
+
332
+ expect(result).toBe(mockOpener);
333
+ });
334
+
335
+ it("should search through frames in window.opener", () => {
336
+ const matchingFrame = {
337
+ location: {
338
+ origin: window.location.origin,
339
+ pathname: "/listener",
340
+ },
341
+ } as unknown as Window;
342
+
343
+ const nonMatchingFrame = {
344
+ location: {
345
+ origin: window.location.origin,
346
+ pathname: "/other",
347
+ },
348
+ } as unknown as Window;
349
+
350
+ const mockOpener = {
351
+ location: {
352
+ origin: window.location.origin,
353
+ pathname: "/parent",
354
+ },
355
+ frames: [nonMatchingFrame, matchingFrame],
356
+ length: 2,
357
+ } as unknown as Window;
358
+
359
+ window.opener = mockOpener;
360
+
361
+ const result = findIframeInOpener();
362
+
363
+ expect(result).toBe(matchingFrame);
364
+ });
365
+
366
+ it("should return null when no matching frame is found", () => {
367
+ const mockOpener = {
368
+ location: {
369
+ origin: window.location.origin,
370
+ pathname: "/wrong",
371
+ },
372
+ frames: [],
373
+ } as unknown as Window;
374
+
375
+ window.opener = mockOpener;
376
+
377
+ const result = findIframeInOpener("/listener");
378
+
379
+ expect(result).toBeNull();
380
+ });
381
+
382
+ it("should handle cross-origin frames gracefully", () => {
383
+ const crossOriginFrame = {
384
+ get location() {
385
+ throw new Error(
386
+ "SecurityError: Blocked a frame with origin"
387
+ );
388
+ },
389
+ } as unknown as Window;
390
+
391
+ const mockOpener = {
392
+ location: {
393
+ origin: window.location.origin,
394
+ pathname: "/parent",
395
+ },
396
+ frames: [crossOriginFrame],
397
+ length: 1,
398
+ } as unknown as Window;
399
+
400
+ window.opener = mockOpener;
401
+
402
+ const result = findIframeInOpener();
403
+
404
+ expect(result).toBeNull();
405
+ });
406
+
407
+ it("should handle errors during frame search", () => {
408
+ const mockOpener = {
409
+ location: {
410
+ origin: window.location.origin,
411
+ pathname: "/parent",
412
+ },
413
+ get frames() {
414
+ throw new Error("Access denied");
415
+ },
416
+ } as unknown as Window;
417
+
418
+ window.opener = mockOpener;
419
+
420
+ const result = findIframeInOpener();
421
+
422
+ expect(result).toBeNull();
423
+ expect(consoleErrorSpy).toHaveBeenCalled();
424
+ });
425
+
426
+ it("should match origin correctly", () => {
427
+ const wrongOriginFrame = {
428
+ location: {
429
+ origin: "https://different-origin.com",
430
+ pathname: "/listener",
431
+ },
432
+ } as unknown as Window;
433
+
434
+ const mockOpener = {
435
+ location: {
436
+ origin: "https://different-origin.com",
437
+ pathname: "/parent",
438
+ },
439
+ frames: [wrongOriginFrame],
440
+ length: 1,
441
+ } as unknown as Window;
442
+
443
+ window.opener = mockOpener;
444
+
445
+ const result = findIframeInOpener();
446
+
447
+ expect(result).toBeNull();
448
+ });
449
+ });
450
+ });
@@ -0,0 +1,143 @@
1
+ import type { FrakWalletSdkConfig } from "../types";
2
+
3
+ /**
4
+ * Base props for the iframe
5
+ * @ignore
6
+ */
7
+ export const baseIframeProps = {
8
+ id: "frak-wallet",
9
+ name: "frak-wallet",
10
+ title: "Frak Wallet",
11
+ allow: "publickey-credentials-get *; clipboard-write; web-share *",
12
+ style: {
13
+ width: "0",
14
+ height: "0",
15
+ border: "0",
16
+ position: "absolute",
17
+ zIndex: 2000001,
18
+ top: "-1000px",
19
+ left: "-1000px",
20
+ colorScheme: "auto",
21
+ },
22
+ };
23
+
24
+ /**
25
+ * Create the Frak iframe
26
+ * @param args
27
+ * @param args.walletBaseUrl - Use `config.walletUrl` instead. Will be removed in future versions.
28
+ * @param args.config - The configuration object containing iframe options, including the replacement for `walletBaseUrl`.
29
+ */
30
+ export function createIframe({
31
+ walletBaseUrl,
32
+ config,
33
+ }: {
34
+ walletBaseUrl?: string;
35
+ config?: FrakWalletSdkConfig;
36
+ }): Promise<HTMLIFrameElement | undefined> {
37
+ // Check if the iframe is already created
38
+ const alreadyCreatedIFrame = document.querySelector("#frak-wallet");
39
+
40
+ // If the iframe is already created, remove it
41
+ if (alreadyCreatedIFrame) {
42
+ alreadyCreatedIFrame.remove();
43
+ }
44
+
45
+ const iframe = document.createElement("iframe");
46
+
47
+ // Set the base iframe props
48
+ iframe.id = baseIframeProps.id;
49
+ iframe.name = baseIframeProps.name;
50
+ iframe.allow = baseIframeProps.allow;
51
+ iframe.style.zIndex = baseIframeProps.style.zIndex.toString();
52
+
53
+ changeIframeVisibility({ iframe, isVisible: false });
54
+ document.body.appendChild(iframe);
55
+
56
+ return new Promise((resolve) => {
57
+ iframe?.addEventListener("load", () => resolve(iframe));
58
+ iframe.src = `${config?.walletUrl ?? walletBaseUrl ?? "https://wallet.frak.id"}/listener`;
59
+ });
60
+ }
61
+ /**
62
+ * Change the visibility of the given iframe
63
+ * @ignore
64
+ */
65
+ export function changeIframeVisibility({
66
+ iframe,
67
+ isVisible,
68
+ }: {
69
+ iframe: HTMLIFrameElement;
70
+ isVisible: boolean;
71
+ }) {
72
+ if (!isVisible) {
73
+ iframe.style.width = "0";
74
+ iframe.style.height = "0";
75
+ iframe.style.border = "0";
76
+ iframe.style.position = "fixed";
77
+ iframe.style.top = "-1000px";
78
+ iframe.style.left = "-1000px";
79
+ return;
80
+ }
81
+
82
+ iframe.style.position = "fixed";
83
+ iframe.style.top = "0";
84
+ iframe.style.left = "0";
85
+ iframe.style.width = "100%";
86
+ iframe.style.height = "100%";
87
+ iframe.style.pointerEvents = "auto";
88
+ }
89
+
90
+ /**
91
+ * Find an iframe within window.opener by pathname
92
+ *
93
+ * When a popup is opened via window.open from an iframe, window.opener points to
94
+ * the parent window, not the iframe itself. This utility searches through all frames
95
+ * in window.opener to find an iframe matching the specified pathname.
96
+ *
97
+ * @param pathname - The pathname to search for (default: "/listener")
98
+ * @returns The matching iframe window, or null if not found
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * // Find the default /listener iframe
103
+ * const listenerIframe = findIframeInOpener();
104
+ *
105
+ * // Find a custom iframe
106
+ * const customIframe = findIframeInOpener("/my-custom-iframe");
107
+ * ```
108
+ */
109
+ export function findIframeInOpener(pathname = "/listener"): Window | null {
110
+ if (!window.opener) return null;
111
+
112
+ const frameCheck = (frame: Window) => {
113
+ try {
114
+ return (
115
+ frame.location.origin === window.location.origin &&
116
+ frame.location.pathname === pathname
117
+ );
118
+ } catch {
119
+ // Cross-origin frame, skip
120
+ return false;
121
+ }
122
+ };
123
+
124
+ // Check if the openner window is the right one
125
+ if (frameCheck(window.opener)) return window.opener;
126
+
127
+ // Search through frames in window.opener
128
+ try {
129
+ const frames = window.opener.frames;
130
+ for (let i = 0; i < frames.length; i++) {
131
+ if (frameCheck(frames[i])) {
132
+ return frames[i];
133
+ }
134
+ }
135
+ return null;
136
+ } catch (error) {
137
+ console.error(
138
+ `[findIframeInOpener] Error finding iframe with pathname ${pathname}:`,
139
+ error
140
+ );
141
+ return null;
142
+ }
143
+ }
@@ -0,0 +1,21 @@
1
+ export { Deferred } from "@frak-labs/frame-connector";
2
+ export { base64urlDecode, base64urlEncode } from "./compression/b64";
3
+ export { compressJsonToB64 } from "./compression/compress";
4
+ export { decompressJsonFromB64 } from "./compression/decompress";
5
+ export { FrakContextManager } from "./FrakContext";
6
+ export { formatAmount } from "./formatAmount";
7
+ export { getCurrencyAmountKey } from "./getCurrencyAmountKey";
8
+ export { getSupportedCurrency } from "./getSupportedCurrency";
9
+ export { getSupportedLocale } from "./getSupportedLocale";
10
+ export {
11
+ baseIframeProps,
12
+ createIframe,
13
+ findIframeInOpener,
14
+ } from "./iframeHelper";
15
+ export {
16
+ type AppSpecificSsoMetadata,
17
+ type CompressedSsoData,
18
+ type FullSsoParams,
19
+ generateSsoUrl,
20
+ } from "./sso";
21
+ export { trackEvent } from "./trackEvent";