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

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 (77) hide show
  1. package/cdn/bundle.js +3 -3
  2. package/dist/bundle.cjs +1 -1
  3. package/dist/bundle.js +1 -1
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.js +1 -1
  6. package/package.json +4 -2
  7. package/src/actions/displayEmbeddedWallet.ts +20 -0
  8. package/src/actions/displayModal.ts +131 -0
  9. package/src/actions/getProductInformation.ts +14 -0
  10. package/src/actions/index.ts +29 -0
  11. package/src/actions/openSso.ts +116 -0
  12. package/src/actions/prepareSso.ts +48 -0
  13. package/src/actions/referral/processReferral.ts +230 -0
  14. package/src/actions/referral/referralInteraction.ts +57 -0
  15. package/src/actions/sendInteraction.ts +32 -0
  16. package/src/actions/trackPurchaseStatus.ts +53 -0
  17. package/src/actions/watchWalletStatus.ts +94 -0
  18. package/src/actions/wrapper/modalBuilder.ts +212 -0
  19. package/src/actions/wrapper/sendTransaction.ts +62 -0
  20. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  21. package/src/bundle.ts +3 -0
  22. package/src/clients/DebugInfo.ts +182 -0
  23. package/src/clients/createIFrameFrakClient.ts +287 -0
  24. package/src/clients/index.ts +3 -0
  25. package/src/clients/setupClient.ts +71 -0
  26. package/src/clients/transports/iframeLifecycleManager.ts +88 -0
  27. package/src/constants/interactionTypes.ts +44 -0
  28. package/src/constants/locales.ts +14 -0
  29. package/src/constants/productTypes.ts +33 -0
  30. package/src/index.ts +103 -0
  31. package/src/interactions/index.ts +5 -0
  32. package/src/interactions/pressEncoder.ts +53 -0
  33. package/src/interactions/purchaseEncoder.ts +94 -0
  34. package/src/interactions/referralEncoder.ts +47 -0
  35. package/src/interactions/retailEncoder.ts +37 -0
  36. package/src/interactions/webshopEncoder.ts +30 -0
  37. package/src/types/client.ts +14 -0
  38. package/src/types/compression.ts +22 -0
  39. package/src/types/config.ts +111 -0
  40. package/src/types/context.ts +13 -0
  41. package/src/types/index.ts +70 -0
  42. package/src/types/lifecycle/client.ts +46 -0
  43. package/src/types/lifecycle/iframe.ts +35 -0
  44. package/src/types/lifecycle/index.ts +2 -0
  45. package/src/types/rpc/displayModal.ts +84 -0
  46. package/src/types/rpc/embedded/index.ts +68 -0
  47. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  48. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  49. package/src/types/rpc/interaction.ts +43 -0
  50. package/src/types/rpc/modal/final.ts +46 -0
  51. package/src/types/rpc/modal/generic.ts +46 -0
  52. package/src/types/rpc/modal/index.ts +20 -0
  53. package/src/types/rpc/modal/login.ts +32 -0
  54. package/src/types/rpc/modal/openSession.ts +25 -0
  55. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  56. package/src/types/rpc/modal/transaction.ts +33 -0
  57. package/src/types/rpc/productInformation.ts +59 -0
  58. package/src/types/rpc/sso.ts +80 -0
  59. package/src/types/rpc/walletStatus.ts +35 -0
  60. package/src/types/rpc.ts +158 -0
  61. package/src/types/transport.ts +34 -0
  62. package/src/utils/FrakContext.ts +152 -0
  63. package/src/utils/compression/b64.ts +29 -0
  64. package/src/utils/compression/compress.ts +11 -0
  65. package/src/utils/compression/decompress.ts +11 -0
  66. package/src/utils/compression/index.ts +3 -0
  67. package/src/utils/computeProductId.ts +11 -0
  68. package/src/utils/constants.ts +4 -0
  69. package/src/utils/formatAmount.ts +18 -0
  70. package/src/utils/getCurrencyAmountKey.ts +15 -0
  71. package/src/utils/getSupportedCurrency.ts +14 -0
  72. package/src/utils/getSupportedLocale.ts +16 -0
  73. package/src/utils/iframeHelper.ts +142 -0
  74. package/src/utils/index.ts +21 -0
  75. package/src/utils/sso.ts +119 -0
  76. package/src/utils/ssoUrlListener.ts +60 -0
  77. package/src/utils/trackEvent.ts +26 -0
@@ -0,0 +1,158 @@
1
+ import type { Hex } from "viem";
2
+ import type { FrakWalletSdkConfig } from "./config";
3
+ import type {
4
+ ModalRpcMetadata,
5
+ ModalRpcStepsInput,
6
+ ModalRpcStepsResultType,
7
+ } from "./rpc/displayModal";
8
+ import type {
9
+ DisplayEmbeddedWalletParamsType,
10
+ DisplayEmbeddedWalletResultType,
11
+ } from "./rpc/embedded";
12
+ import type {
13
+ PreparedInteraction,
14
+ SendInteractionReturnType,
15
+ } from "./rpc/interaction";
16
+ import type { GetProductInformationReturnType } from "./rpc/productInformation";
17
+ import type {
18
+ OpenSsoParamsType,
19
+ OpenSsoReturnType,
20
+ PrepareSsoParamsType,
21
+ PrepareSsoReturnType,
22
+ } from "./rpc/sso";
23
+ import type { WalletStatusReturnType } from "./rpc/walletStatus";
24
+
25
+ /**
26
+ * RPC interface that's used for the iframe communication
27
+ *
28
+ * Define all the methods available within the iFrame RPC client with response type annotations
29
+ *
30
+ * @group RPC Schema
31
+ *
32
+ * @remarks
33
+ * Each method in the schema now includes a ResponseType field that indicates:
34
+ * - "promise": One-shot request that resolves once
35
+ * - "stream": Streaming request that can emit multiple values
36
+ *
37
+ * ### Methods:
38
+ *
39
+ * #### frak_listenToWalletStatus
40
+ * - Params: None
41
+ * - Returns: {@link WalletStatusReturnType}
42
+ * - Response Type: stream (emits updates when wallet status changes)
43
+ *
44
+ * #### frak_displayModal
45
+ * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
46
+ * - Returns: {@link ModalRpcStepsResultType}
47
+ * - Response Type: promise (one-shot)
48
+ *
49
+ * #### frak_sendInteraction
50
+ * - Params: [productId: Hex, interaction: {@link PreparedInteraction}, signature?: Hex]
51
+ * - Returns: {@link SendInteractionReturnType}
52
+ * - Response Type: promise (one-shot)
53
+ *
54
+ * #### frak_sso
55
+ * - Params: [params: {@link OpenSsoParamsType}, name: string, customCss?: string]
56
+ * - Returns: {@link OpenSsoReturnType}
57
+ * - Response Type: promise (one-shot)
58
+ *
59
+ * #### frak_getProductInformation
60
+ * - Params: None
61
+ * - Returns: {@link GetProductInformationReturnType}
62
+ * - Response Type: promise (one-shot)
63
+ *
64
+ * #### frak_displayEmbeddedWallet
65
+ * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
66
+ * - Returns: {@link DisplayEmbeddedWalletResultType}
67
+ * - Response Type: promise (one-shot)
68
+ */
69
+ export type IFrameRpcSchema = [
70
+ /**
71
+ * Method used to listen to the wallet status
72
+ * This is a streaming method that emits updates when wallet status changes
73
+ */
74
+ {
75
+ Method: "frak_listenToWalletStatus";
76
+ Parameters?: undefined;
77
+ ReturnType: WalletStatusReturnType;
78
+ },
79
+ /**
80
+ * Method to display a modal with the provided steps
81
+ * This is a one-shot request
82
+ */
83
+ {
84
+ Method: "frak_displayModal";
85
+ Parameters: [
86
+ requests: ModalRpcStepsInput,
87
+ metadata: ModalRpcMetadata | undefined,
88
+ configMetadata: FrakWalletSdkConfig["metadata"],
89
+ ];
90
+ ReturnType: ModalRpcStepsResultType;
91
+ },
92
+ /**
93
+ * Method to transmit a user interaction
94
+ * This is a one-shot request
95
+ */
96
+ {
97
+ Method: "frak_sendInteraction";
98
+ Parameters: [
99
+ productId: Hex,
100
+ interaction: PreparedInteraction,
101
+ signature?: Hex,
102
+ ];
103
+ ReturnType: SendInteractionReturnType;
104
+ },
105
+ /**
106
+ * Method to prepare SSO (generate URL for popup)
107
+ * Returns the SSO URL that should be opened in a popup
108
+ * Only used for popup flows (not redirect flows)
109
+ */
110
+ {
111
+ Method: "frak_prepareSso";
112
+ Parameters: [
113
+ params: PrepareSsoParamsType,
114
+ name: string,
115
+ customCss?: string,
116
+ ];
117
+ ReturnType: PrepareSsoReturnType;
118
+ },
119
+ /**
120
+ * Method to open/trigger SSO
121
+ * Either triggers redirect (if openInSameWindow/redirectUrl)
122
+ * Or waits for popup completion (if popup mode)
123
+ * This method handles BOTH redirect and popup flows
124
+ */
125
+ {
126
+ Method: "frak_openSso";
127
+ Parameters: [
128
+ params: OpenSsoParamsType,
129
+ name: string,
130
+ customCss?: string,
131
+ ];
132
+ ReturnType: OpenSsoReturnType;
133
+ },
134
+ /**
135
+ * Method to get current product information's
136
+ * - Is product minted?
137
+ * - Does it have running campaign?
138
+ * - Estimated reward on actions
139
+ * This is a one-shot request
140
+ */
141
+ {
142
+ Method: "frak_getProductInformation";
143
+ Parameters?: undefined;
144
+ ReturnType: GetProductInformationReturnType;
145
+ },
146
+ /**
147
+ * Method to show the embedded wallet, with potential customization
148
+ * This is a one-shot request
149
+ */
150
+ {
151
+ Method: "frak_displayEmbeddedWallet";
152
+ Parameters: [
153
+ request: DisplayEmbeddedWalletParamsType,
154
+ metadata: FrakWalletSdkConfig["metadata"],
155
+ ];
156
+ ReturnType: DisplayEmbeddedWalletResultType;
157
+ },
158
+ ];
@@ -0,0 +1,34 @@
1
+ import type { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
2
+ import type { ClientLifecycleEvent, IFrameLifecycleEvent } from "./lifecycle";
3
+ import type { IFrameRpcSchema } from "./rpc";
4
+
5
+ /**
6
+ * IFrame transport interface
7
+ */
8
+ export type IFrameTransport = {
9
+ /**
10
+ * Wait for the connection to be established
11
+ */
12
+ waitForConnection: Promise<boolean>;
13
+ /**
14
+ * Wait for the setup to be done
15
+ */
16
+ waitForSetup: Promise<void>;
17
+ /**
18
+ * Function used to perform a single request via the iframe transport
19
+ */
20
+ request: RpcClient<IFrameRpcSchema, LifecycleMessage>["request"];
21
+ /**
22
+ * Function used to listen to a request response via the iframe transport
23
+ */
24
+ listenerRequest: RpcClient<IFrameRpcSchema, LifecycleMessage>["listen"];
25
+ /**
26
+ * Function used to destroy the iframe transport
27
+ */
28
+ destroy: () => Promise<void>;
29
+ };
30
+
31
+ /**
32
+ * Represent an iframe event
33
+ */
34
+ export type FrakLifecycleEvent = IFrameLifecycleEvent | ClientLifecycleEvent;
@@ -0,0 +1,152 @@
1
+ import { type Address, bytesToHex, hexToBytes } from "viem";
2
+ import type { FrakContext } from "../types";
3
+ import { base64urlDecode, base64urlEncode } from "./compression/b64";
4
+
5
+ /**
6
+ * The context key
7
+ */
8
+ const contextKey = "fCtx";
9
+
10
+ /**
11
+ * Compress the current Frak context
12
+ * @param context - The context to be compressed
13
+ * @returns A compressed string containing the Frak context
14
+ */
15
+ function compress(context?: Partial<FrakContext>): string | undefined {
16
+ if (!context?.r) return;
17
+ try {
18
+ const bytes = hexToBytes(context.r);
19
+ return base64urlEncode(bytes);
20
+ } catch (e) {
21
+ console.error("Error compressing Frak context", { e, context });
22
+ }
23
+ return undefined;
24
+ }
25
+
26
+ /**
27
+ * Decompress the given Frak context
28
+ * @param context - The raw context to be decompressed into a `FrakContext`
29
+ * @returns The decompressed Frak context, or undefined if it fails
30
+ */
31
+ function decompress(context?: string): FrakContext | undefined {
32
+ if (!context || context.length === 0) return;
33
+ try {
34
+ const bytes = base64urlDecode(context);
35
+ return { r: bytesToHex(bytes, { size: 20 }) as Address };
36
+ } catch (e) {
37
+ console.error("Error decompressing Frak context", { e, context });
38
+ }
39
+ return undefined;
40
+ }
41
+
42
+ /**
43
+ * Parse the current URL into a Frak Context
44
+ * @param args
45
+ * @param args.url - The url to parse
46
+ * @returns The parsed Frak context
47
+ */
48
+ function parse({ url }: { url: string }) {
49
+ if (!url) return null;
50
+
51
+ // Check if the url contain the frak context key
52
+ const urlObj = new URL(url);
53
+ const frakContext = urlObj.searchParams.get(contextKey);
54
+ if (!frakContext) return null;
55
+
56
+ // Decompress and return it
57
+ return decompress(frakContext);
58
+ }
59
+
60
+ /**
61
+ * Populate the current url with the given Frak context
62
+ * @param args
63
+ * @param args.url - The url to update
64
+ * @param args.context - The context to update
65
+ * @returns The new url with the Frak context
66
+ */
67
+ function update({
68
+ url,
69
+ context,
70
+ }: { url?: string; context: Partial<FrakContext> }) {
71
+ if (!url) return null;
72
+
73
+ // Parse the current context
74
+ const currentContext = parse({ url });
75
+
76
+ // Merge the current context with the new context
77
+ const mergedContext = currentContext
78
+ ? { ...currentContext, ...context }
79
+ : context;
80
+
81
+ // If we don't have a referrer, early exit
82
+ if (!mergedContext.r) return null;
83
+
84
+ // Compress it
85
+ const compressedContext = compress(mergedContext);
86
+ if (!compressedContext) return null;
87
+
88
+ // Build the new url and return it
89
+ const urlObj = new URL(url);
90
+ urlObj.searchParams.set(contextKey, compressedContext);
91
+ return urlObj.toString();
92
+ }
93
+
94
+ /**
95
+ * Remove Frak context from current url
96
+ * @param url - The url to update
97
+ * @returns The new url without the Frak context
98
+ */
99
+ function remove(url: string) {
100
+ const urlObj = new URL(url);
101
+ urlObj.searchParams.delete(contextKey);
102
+ return urlObj.toString();
103
+ }
104
+
105
+ /**
106
+ * Replace the current url with the given Frak context
107
+ * @param args
108
+ * @param args.url - The url to update
109
+ * @param args.context - The context to update
110
+ */
111
+ function replaceUrl({
112
+ url: baseUrl,
113
+ context,
114
+ }: { url?: string; context: Partial<FrakContext> | null }) {
115
+ // If no window here early exit
116
+ if (!window.location?.href || typeof window === "undefined") {
117
+ console.error("No window found, can't update context");
118
+ return;
119
+ }
120
+
121
+ // If no url, try to use the current one
122
+ const url = baseUrl ?? window.location.href;
123
+
124
+ // Get our new url with the frak context
125
+ let newUrl: string | null;
126
+ if (context !== null) {
127
+ newUrl = update({
128
+ url,
129
+ context,
130
+ });
131
+ } else {
132
+ newUrl = remove(url);
133
+ }
134
+
135
+ // If no new url, early exit
136
+ if (!newUrl) return;
137
+
138
+ // Update the url
139
+ window.history.replaceState(null, "", newUrl.toString());
140
+ }
141
+
142
+ /**
143
+ * Export our frak context
144
+ */
145
+ export const FrakContextManager = {
146
+ compress,
147
+ decompress,
148
+ parse,
149
+ update,
150
+ remove,
151
+ replaceUrl,
152
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Encode a buffer to a base64url encoded string
3
+ * @param buffer The buffer to encode
4
+ * @returns The encoded string
5
+ */
6
+ export function base64urlEncode(buffer: Uint8Array): string {
7
+ return btoa(Array.from(buffer, (b) => String.fromCharCode(b)).join(""))
8
+ .replace(/\+/g, "-")
9
+ .replace(/\//g, "_")
10
+ .replace(/=+$/, "");
11
+ }
12
+
13
+ /**
14
+ * Decode a base64url encoded string
15
+ * @param value The value to decode
16
+ * @returns The decoded value
17
+ */
18
+ export function base64urlDecode(value: string): Uint8Array {
19
+ const m = value.length % 4;
20
+ return Uint8Array.from(
21
+ atob(
22
+ value
23
+ .replace(/-/g, "+")
24
+ .replace(/_/g, "/")
25
+ .padEnd(value.length + (m === 0 ? 0 : 4 - m), "=")
26
+ ),
27
+ (c) => c.charCodeAt(0)
28
+ );
29
+ }
@@ -0,0 +1,11 @@
1
+ import { compressJson } from "@frak-labs/frame-connector";
2
+ import { base64urlEncode } from "./b64";
3
+
4
+ /**
5
+ * Compress json data
6
+ * @param data
7
+ * @ignore
8
+ */
9
+ export function compressJsonToB64(data: unknown): string {
10
+ return base64urlEncode(compressJson(data));
11
+ }
@@ -0,0 +1,11 @@
1
+ import { decompressJson } from "@frak-labs/frame-connector";
2
+ import { base64urlDecode } from "./b64";
3
+
4
+ /**
5
+ * Decompress json data
6
+ * @param data
7
+ * @ignore
8
+ */
9
+ export function decompressJsonFromB64<T>(data: string): T | null {
10
+ return decompressJson(base64urlDecode(data));
11
+ }
@@ -0,0 +1,3 @@
1
+ export { compressJsonToB64 } from "./compress";
2
+ export { decompressJsonFromB64 } from "./decompress";
3
+ export { base64urlDecode, base64urlEncode } from "./b64";
@@ -0,0 +1,11 @@
1
+ import { keccak256, toHex } from "viem";
2
+
3
+ /**
4
+ * Compute the product id from a domain
5
+ * @ignore
6
+ */
7
+ export function computeProductId({ domain }: { domain?: string } = {}) {
8
+ const effectiveDomain = domain ?? window.location.host;
9
+ const normalizedDomain = effectiveDomain.replace("www.", "");
10
+ return keccak256(toHex(normalizedDomain));
11
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * The backup key for client side backup if needed
3
+ */
4
+ export const BACKUP_KEY = "nexus-wallet-backup";
@@ -0,0 +1,18 @@
1
+ import type { Currency } from "../types";
2
+ import { getSupportedCurrency } from "./getSupportedCurrency";
3
+ import { getSupportedLocale } from "./getSupportedLocale";
4
+
5
+ export function formatAmount(amount: number, currency?: Currency) {
6
+ // Get the supported locale (e.g. "fr-FR")
7
+ const supportedLocale = getSupportedLocale(currency);
8
+
9
+ // Get the supported currency (e.g. "eur")
10
+ const supportedCurrency = getSupportedCurrency(currency);
11
+
12
+ return amount.toLocaleString(supportedLocale, {
13
+ style: "currency",
14
+ currency: supportedCurrency,
15
+ minimumFractionDigits: 0,
16
+ maximumFractionDigits: 2,
17
+ });
18
+ }
@@ -0,0 +1,15 @@
1
+ import type { Currency, TokenAmountType } from "../types";
2
+
3
+ /**
4
+ * Get the currency amount key for a given currency
5
+ * @param currency - The currency to use
6
+ * @returns The currency amount key
7
+ */
8
+ export function getCurrencyAmountKey(
9
+ currency?: Currency
10
+ ): keyof TokenAmountType {
11
+ if (!currency) {
12
+ return "eurAmount";
13
+ }
14
+ return `${currency}Amount` as keyof TokenAmountType;
15
+ }
@@ -0,0 +1,14 @@
1
+ import { locales } from "../constants/locales";
2
+ import type { Currency } from "../types";
3
+
4
+ /**
5
+ * Get the supported currency for a given currency
6
+ * @param currency - The currency to use
7
+ * @returns The supported currency
8
+ */
9
+ export function getSupportedCurrency(currency?: Currency): Currency {
10
+ if (!currency) {
11
+ return "eur";
12
+ }
13
+ return currency in locales ? currency : "eur";
14
+ }
@@ -0,0 +1,16 @@
1
+ import { type LocalesKey, locales } from "../constants/locales";
2
+ import type { Currency } from "../types";
3
+
4
+ /**
5
+ * Get the supported locale for a given currency
6
+ * @param currency - The currency to use
7
+ * @returns The supported locale
8
+ */
9
+ export function getSupportedLocale(
10
+ currency?: Currency
11
+ ): (typeof locales)[LocalesKey] {
12
+ if (!currency) {
13
+ return locales.eur;
14
+ }
15
+ return locales[currency] ?? locales.eur;
16
+ }
@@ -0,0 +1,142 @@
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
+ }: { walletBaseUrl?: string; config?: FrakWalletSdkConfig }): Promise<
34
+ HTMLIFrameElement | undefined
35
+ > {
36
+ // Check if the iframe is already created
37
+ const alreadyCreatedIFrame = document.querySelector("#frak-wallet");
38
+
39
+ // If the iframe is already created, remove it
40
+ if (alreadyCreatedIFrame) {
41
+ alreadyCreatedIFrame.remove();
42
+ }
43
+
44
+ const iframe = document.createElement("iframe");
45
+
46
+ // Set the base iframe props
47
+ iframe.id = baseIframeProps.id;
48
+ iframe.name = baseIframeProps.name;
49
+ iframe.allow = baseIframeProps.allow;
50
+ iframe.style.zIndex = baseIframeProps.style.zIndex.toString();
51
+
52
+ changeIframeVisibility({ iframe, isVisible: false });
53
+ document.body.appendChild(iframe);
54
+
55
+ return new Promise((resolve) => {
56
+ iframe?.addEventListener("load", () => resolve(iframe));
57
+ iframe.src = `${config?.walletUrl ?? walletBaseUrl ?? "https://wallet.frak.id"}/listener`;
58
+ });
59
+ }
60
+ /**
61
+ * Change the visibility of the given iframe
62
+ * @ignore
63
+ */
64
+ export function changeIframeVisibility({
65
+ iframe,
66
+ isVisible,
67
+ }: {
68
+ iframe: HTMLIFrameElement;
69
+ isVisible: boolean;
70
+ }) {
71
+ if (!isVisible) {
72
+ iframe.style.width = "0";
73
+ iframe.style.height = "0";
74
+ iframe.style.border = "0";
75
+ iframe.style.position = "fixed";
76
+ iframe.style.top = "-1000px";
77
+ iframe.style.left = "-1000px";
78
+ return;
79
+ }
80
+
81
+ iframe.style.position = "fixed";
82
+ iframe.style.top = "0";
83
+ iframe.style.left = "0";
84
+ iframe.style.width = "100%";
85
+ iframe.style.height = "100%";
86
+ iframe.style.pointerEvents = "auto";
87
+ }
88
+
89
+ /**
90
+ * Find an iframe within window.opener by pathname
91
+ *
92
+ * When a popup is opened via window.open from an iframe, window.opener points to
93
+ * the parent window, not the iframe itself. This utility searches through all frames
94
+ * in window.opener to find an iframe matching the specified pathname.
95
+ *
96
+ * @param pathname - The pathname to search for (default: "/listener")
97
+ * @returns The matching iframe window, or null if not found
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * // Find the default /listener iframe
102
+ * const listenerIframe = findIframeInOpener();
103
+ *
104
+ * // Find a custom iframe
105
+ * const customIframe = findIframeInOpener("/my-custom-iframe");
106
+ * ```
107
+ */
108
+ export function findIframeInOpener(pathname = "/listener"): Window | null {
109
+ if (!window.opener) return null;
110
+
111
+ const frameCheck = (frame: Window) => {
112
+ try {
113
+ return (
114
+ frame.location.origin === window.location.origin &&
115
+ frame.location.pathname === pathname
116
+ );
117
+ } catch {
118
+ // Cross-origin frame, skip
119
+ return false;
120
+ }
121
+ };
122
+
123
+ // Check if the openner window is the right one
124
+ if (frameCheck(window.opener)) return window.opener;
125
+
126
+ // Search through frames in window.opener
127
+ try {
128
+ const frames = window.opener.frames;
129
+ for (let i = 0; i < frames.length; i++) {
130
+ if (frameCheck(frames[i])) {
131
+ return frames[i];
132
+ }
133
+ }
134
+ return null;
135
+ } catch (error) {
136
+ console.error(
137
+ `[findIframeInOpener] Error finding iframe with pathname ${pathname}:`,
138
+ error
139
+ );
140
+ return null;
141
+ }
142
+ }
@@ -0,0 +1,21 @@
1
+ export {
2
+ createIframe,
3
+ baseIframeProps,
4
+ findIframeInOpener,
5
+ } from "./iframeHelper";
6
+ export { compressJsonToB64 } from "./compression/compress";
7
+ export { decompressJsonFromB64 } from "./compression/decompress";
8
+ export { base64urlDecode, base64urlEncode } from "./compression/b64";
9
+ export { FrakContextManager } from "./FrakContext";
10
+ export { getSupportedCurrency } from "./getSupportedCurrency";
11
+ export { getSupportedLocale } from "./getSupportedLocale";
12
+ export { getCurrencyAmountKey } from "./getCurrencyAmountKey";
13
+ export { formatAmount } from "./formatAmount";
14
+ export { trackEvent } from "./trackEvent";
15
+ export { Deferred } from "@frak-labs/frame-connector";
16
+ export {
17
+ generateSsoUrl,
18
+ type CompressedSsoData,
19
+ type FullSsoParams,
20
+ type AppSpecificSsoMetadata,
21
+ } from "./sso";