@frak-labs/core-sdk 0.2.1-beta.b38eef2e → 0.2.1-beta.d2556d47

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 (59) hide show
  1. package/README.md +1 -2
  2. package/cdn/bundle.js +55 -3
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.d.cts +2 -2
  5. package/dist/actions.d.ts +2 -2
  6. package/dist/actions.js +1 -1
  7. package/dist/bundle.cjs +1 -1
  8. package/dist/bundle.d.cts +4 -4
  9. package/dist/bundle.d.ts +4 -4
  10. package/dist/bundle.js +1 -1
  11. package/dist/{computeLegacyProductId-b5cUWdAm.d.ts → computeLegacyProductId-BP-ciVsp.d.cts} +30 -44
  12. package/dist/{computeLegacyProductId-CCAZvLa5.d.cts → computeLegacyProductId-DiJd7RNo.d.ts} +30 -44
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +1 -1
  17. package/dist/{openSso-B0g7-807.d.cts → openSso-B8v3Vtnh.d.ts} +118 -46
  18. package/dist/{openSso-CMzwvaCa.d.ts → openSso-n_B4LSuW.d.cts} +118 -46
  19. package/dist/setupClient-Dr_UYfTD.cjs +13 -0
  20. package/dist/setupClient-TuhDjVJx.js +13 -0
  21. package/dist/siweAuthenticate-0UPcUqI1.js +1 -0
  22. package/dist/{siweAuthenticate-CVigMOxz.d.cts → siweAuthenticate-CDCsp8EJ.d.ts} +8 -5
  23. package/dist/siweAuthenticate-CfQibjZR.cjs +1 -0
  24. package/dist/{siweAuthenticate-CnCZ7mok.d.ts → siweAuthenticate-yITE-iKh.d.cts} +8 -5
  25. package/dist/trackEvent-5j5kkOCj.js +1 -0
  26. package/dist/trackEvent-B2uom25e.cjs +1 -0
  27. package/package.json +8 -8
  28. package/src/actions/displayEmbeddedWallet.ts +6 -2
  29. package/src/actions/displayModal.ts +6 -2
  30. package/src/actions/ensureIdentity.ts +2 -2
  31. package/src/actions/trackPurchaseStatus.test.ts +32 -20
  32. package/src/actions/trackPurchaseStatus.ts +3 -5
  33. package/src/actions/wrapper/modalBuilder.test.ts +4 -2
  34. package/src/actions/wrapper/modalBuilder.ts +6 -8
  35. package/src/clients/createIFrameFrakClient.ts +146 -25
  36. package/src/clients/transports/iframeLifecycleManager.test.ts +0 -80
  37. package/src/clients/transports/iframeLifecycleManager.ts +0 -44
  38. package/src/index.ts +5 -3
  39. package/src/types/config.ts +10 -3
  40. package/src/types/index.ts +6 -1
  41. package/src/types/lifecycle/client.ts +22 -27
  42. package/src/types/lifecycle/iframe.ts +0 -8
  43. package/src/types/resolvedConfig.ts +104 -0
  44. package/src/types/rpc/interaction.ts +4 -0
  45. package/src/types/rpc.ts +7 -5
  46. package/src/utils/backendUrl.test.ts +2 -2
  47. package/src/utils/backendUrl.ts +1 -1
  48. package/src/utils/index.ts +1 -5
  49. package/src/utils/sdkConfigStore.test.ts +405 -0
  50. package/src/utils/sdkConfigStore.ts +277 -0
  51. package/src/utils/sso.ts +3 -7
  52. package/dist/setupClient-CqTHGvVa.cjs +0 -13
  53. package/dist/setupClient-DTyvAPgh.js +0 -13
  54. package/dist/siweAuthenticate-BWmI2_TN.cjs +0 -1
  55. package/dist/siweAuthenticate-zczqxm0a.js +0 -1
  56. package/dist/trackEvent-CeLFVzZn.js +0 -1
  57. package/dist/trackEvent-Ew5r5zfI.cjs +0 -1
  58. package/src/utils/merchantId.test.ts +0 -653
  59. package/src/utils/merchantId.ts +0 -143
@@ -1,6 +1,6 @@
1
- import { OpenPanel } from "@openpanel/web";
2
- import { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
3
1
  import { Address, Hex } from "viem";
2
+ import { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
3
+ import { OpenPanel } from "@openpanel/web";
4
4
  import { SiweMessage } from "viem/siwe";
5
5
 
6
6
  //#region src/types/config.d.ts
@@ -31,7 +31,7 @@ type FrakWalletSdkConfig = {
31
31
  /**
32
32
  * Your application name (will be displayed in a few modals and in SSO)
33
33
  */
34
- name: string;
34
+ name?: string;
35
35
  /**
36
36
  * Your merchant ID from the Frak dashboard (UUID format)
37
37
  * Used for referral tracking and analytics
@@ -75,6 +75,13 @@ type FrakWalletSdkConfig = {
75
75
  * @defaultValue window.location.host
76
76
  */
77
77
  domain?: string;
78
+ /**
79
+ * Wait for backend config before rendering components.
80
+ * When true (default), components show a spinner until backend config is resolved.
81
+ * When false, components render immediately with SDK static config / HTML attributes.
82
+ * @defaultValue true
83
+ */
84
+ waitForBackendConfig?: boolean;
78
85
  };
79
86
  /**
80
87
  * Custom i18n configuration for the modal
@@ -111,19 +118,97 @@ type FrakWalletSdkConfig = {
111
118
  */
112
119
  type I18nConfig = Record<Language, LocalizedI18nConfig> | LocalizedI18nConfig;
113
120
  /**
114
- * A localized i18n config
121
+ * A localized i18n config (inline objects only — URL-based i18n removed)
115
122
  * @category Config
116
123
  */
117
- type LocalizedI18nConfig = `${string}.css` | {
124
+ type LocalizedI18nConfig = {
118
125
  [key: string]: string;
119
126
  };
120
127
  //#endregion
128
+ //#region src/types/resolvedConfig.d.ts
129
+ /**
130
+ * Response from the merchant resolve endpoint
131
+ * @category Config
132
+ */
133
+ type MerchantConfigResponse = {
134
+ merchantId: string;
135
+ name: string;
136
+ domain: string;
137
+ allowedDomains: string[];
138
+ sdkConfig?: ResolvedSdkConfig;
139
+ };
140
+ /**
141
+ * Resolved placement config from backend
142
+ * Translations already flattened: default + lang-specific merged into one record
143
+ * @category Config
144
+ */
145
+ type ResolvedPlacement = {
146
+ /** Per-component configuration within this placement */components?: {
147
+ buttonShare?: {
148
+ text?: string;
149
+ noRewardText?: string;
150
+ clickAction?: "embedded-wallet" | "share-modal";
151
+ useReward?: boolean;
152
+ css?: string;
153
+ };
154
+ buttonWallet?: {
155
+ position?: "bottom-right" | "bottom-left";
156
+ css?: string;
157
+ };
158
+ openInApp?: {
159
+ text?: string;
160
+ css?: string;
161
+ };
162
+ };
163
+ targetInteraction?: string; /** Already flattened: default + lang-specific merged into one record */
164
+ translations?: Record<string, string>; /** Global placement CSS (applied to modals/listener) */
165
+ css?: string;
166
+ };
167
+ /**
168
+ * Resolved SDK config from backend `/resolve` endpoint
169
+ * Language resolution and translation merging already applied
170
+ * @category Config
171
+ */
172
+ type ResolvedSdkConfig = {
173
+ name?: string;
174
+ logoUrl?: string;
175
+ homepageLink?: string;
176
+ currency?: Currency;
177
+ lang?: Language; /** When true, all SDK components should be hidden */
178
+ hidden?: boolean;
179
+ css?: string;
180
+ translations?: Record<string, string>;
181
+ placements?: Record<string, ResolvedPlacement>;
182
+ };
183
+ /**
184
+ * Internal SDK config store state
185
+ * Merged config: backend > SDK static > defaults
186
+ * Components subscribe to this reactively
187
+ * @category Config
188
+ */
189
+ type SdkResolvedConfig = {
190
+ /** Whether the backend config has been resolved */isResolved: boolean; /** Merchant ID from resolution */
191
+ merchantId: string; /** Domain returned by the resolve endpoint */
192
+ domain?: string; /** Domains allowed for this merchant (used by iframe trust check) */
193
+ allowedDomains?: string[]; /** Whether the resolve returned a backend sdkConfig object */
194
+ hasRawSdkConfig?: boolean; /** Merged metadata fields */
195
+ name?: string;
196
+ logoUrl?: string;
197
+ homepageLink?: string;
198
+ lang?: Language;
199
+ currency?: Currency; /** When true, all SDK components should be hidden */
200
+ hidden?: boolean; /** Global CSS from backend config (passed to iframe) */
201
+ css?: string; /** Global translations (for reference / component fallback) */
202
+ translations?: Record<string, string>; /** Named placements (keyed by placement ID) */
203
+ placements?: Record<string, ResolvedPlacement>;
204
+ };
205
+ //#endregion
121
206
  //#region src/types/lifecycle/client.d.ts
122
207
  /**
123
208
  * Event related to the iframe lifecycle
124
209
  * @ignore
125
210
  */
126
- type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | HandshakeResponse | SsoRedirectCompleteEvent | DeepLinkFailedEvent;
211
+ type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | SsoRedirectCompleteEvent | DeepLinkFailedEvent | ResolvedConfigEvent;
127
212
  type CustomCssEvent = {
128
213
  clientLifecycle: "modal-css";
129
214
  data: {
@@ -146,30 +231,6 @@ type HearbeatEvent = {
146
231
  clientLifecycle: "heartbeat";
147
232
  data?: never;
148
233
  };
149
- type HandshakeResponse = {
150
- clientLifecycle: "handshake-response";
151
- data: {
152
- token: string;
153
- currentUrl: string;
154
- /**
155
- * Pending merge token extracted from URL (?fmt= parameter)
156
- * When present, listener should execute identity merge in background
157
- * URL is cleaned after handshake response is sent
158
- */
159
- pendingMergeToken?: string;
160
- /**
161
- * Client ID for identity tracking (belt & suspenders fallback)
162
- * Primary delivery is via iframe URL query param; handshake is backup for SSR
163
- */
164
- clientId?: string;
165
- /**
166
- * Explicit domain from SDK config (FrakWalletSdkConfig.domain)
167
- * When present, listener should prefer this over URL-derived domain
168
- * for merchant resolution (handles proxied/tunneled environments)
169
- */
170
- configDomain?: string;
171
- };
172
- };
173
234
  type SsoRedirectCompleteEvent = {
174
235
  clientLifecycle: "sso-redirect-complete";
175
236
  data: {
@@ -182,6 +243,21 @@ type DeepLinkFailedEvent = {
182
243
  originalUrl: string;
183
244
  };
184
245
  };
246
+ type ResolvedConfigEvent = {
247
+ clientLifecycle: "resolved-config";
248
+ data: {
249
+ merchantId: string; /** The domain the backend resolved this config for */
250
+ domain: string; /** All domains registered for this merchant (for domain proof) */
251
+ allowedDomains: string[]; /** Full URL of the parent page (for interaction tracking) */
252
+ sourceUrl: string;
253
+ /**
254
+ * Pending merge token extracted from URL (?fmt= parameter).
255
+ * When present, listener should execute identity merge in background.
256
+ */
257
+ pendingMergeToken?: string;
258
+ sdkConfig?: ResolvedSdkConfig;
259
+ };
260
+ };
185
261
  //#endregion
186
262
  //#region src/types/lifecycle/iframe.d.ts
187
263
  /**
@@ -191,19 +267,13 @@ type DeepLinkFailedEvent = {
191
267
  type IFrameLifecycleEvent = {
192
268
  iframeLifecycle: "connected" | "show" | "hide" | "remove-backup";
193
269
  data?: never;
194
- } | DoBackupEvent | HandshakeRequestEvent | RedirectRequestEvent;
270
+ } | DoBackupEvent | RedirectRequestEvent;
195
271
  type DoBackupEvent = {
196
272
  iframeLifecycle: "do-backup";
197
273
  data: {
198
274
  backup?: string;
199
275
  };
200
276
  };
201
- type HandshakeRequestEvent = {
202
- iframeLifecycle: "handshake";
203
- data: {
204
- token: string;
205
- };
206
- };
207
277
  type RedirectRequestEvent = {
208
278
  iframeLifecycle: "redirect";
209
279
  data: {
@@ -691,7 +761,9 @@ type SendInteractionParamsType = {
691
761
  utmTerm?: string;
692
762
  utmContent?: string;
693
763
  } | {
694
- type: "sharing";
764
+ type: "sharing"; /** Epoch seconds timestamp matching the V2 context `t` field embedded in the referral link URL, used for backend correlation */
765
+ sharingTimestamp?: number; /** Merchant order ID linking this sharing event to a purchase (stays server-side, never in URL) */
766
+ purchaseId?: string;
695
767
  } | {
696
768
  type: "custom";
697
769
  customType: string;
@@ -815,7 +887,7 @@ type WalletNotConnected = {
815
887
  * - Response Type: stream (emits updates when wallet status changes)
816
888
  *
817
889
  * #### frak_displayModal
818
- * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
890
+ * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
819
891
  * - Returns: {@link ModalRpcStepsResultType}
820
892
  * - Response Type: promise (one-shot)
821
893
  *
@@ -830,7 +902,7 @@ type WalletNotConnected = {
830
902
  * - Response Type: promise (one-shot)
831
903
  *
832
904
  * #### frak_displayEmbeddedWallet
833
- * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
905
+ * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
834
906
  * - Returns: {@link DisplayEmbeddedWalletResultType}
835
907
  * - Response Type: promise (one-shot)
836
908
  */
@@ -850,7 +922,7 @@ type IFrameRpcSchema = [
850
922
  */
851
923
  {
852
924
  Method: "frak_displayModal";
853
- Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"]];
925
+ Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"], placement?: string];
854
926
  ReturnType: ModalRpcStepsResultType;
855
927
  },
856
928
  /**
@@ -860,7 +932,7 @@ type IFrameRpcSchema = [
860
932
  */
861
933
  {
862
934
  Method: "frak_prepareSso";
863
- Parameters: [params: PrepareSsoParamsType, name: string, customCss?: string];
935
+ Parameters: [params: PrepareSsoParamsType, name?: string, customCss?: string];
864
936
  ReturnType: PrepareSsoReturnType;
865
937
  },
866
938
  /**
@@ -871,7 +943,7 @@ type IFrameRpcSchema = [
871
943
  */
872
944
  {
873
945
  Method: "frak_openSso";
874
- Parameters: [params: OpenSsoParamsType, name: string, customCss?: string];
946
+ Parameters: [params: OpenSsoParamsType, name?: string, customCss?: string];
875
947
  ReturnType: OpenSsoReturnType;
876
948
  },
877
949
  /**
@@ -892,14 +964,14 @@ type IFrameRpcSchema = [
892
964
  */
893
965
  {
894
966
  Method: "frak_displayEmbeddedWallet";
895
- Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"]];
967
+ Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"], placement?: string];
896
968
  ReturnType: DisplayEmbeddedWalletResultType;
897
969
  },
898
970
  /**
899
971
  * Method to send interactions (arrival, sharing, custom events)
900
972
  * Fire-and-forget method - no return value expected
901
973
  * merchantId is resolved from context
902
- * clientId is passed via metadata as safeguard against handshake race condition
974
+ * clientId is passed via metadata as safeguard against race conditions
903
975
  */
904
976
  {
905
977
  Method: "frak_sendInteraction";
@@ -1052,4 +1124,4 @@ declare const ssoPopupName = "frak-sso";
1052
1124
  */
1053
1125
  declare function openSso(client: FrakClient, args: OpenSsoParamsType): Promise<OpenSsoReturnType>;
1054
1126
  //#endregion
1055
- export { SendTransactionModalStepType as A, PrepareSsoReturnType as B, EmbeddedViewActionSharing as C, ModalRpcStepsInput as D, ModalRpcMetadata as E, SiweAuthenticationParams as F, InteractionTypeKey as G, FinalActionType as H, LoginModalStepType as I, Currency as J, IFrameLifecycleEvent as K, OpenSsoParamsType as L, SendTransactionTxType as M, SiweAuthenticateModalStepType as N, ModalRpcStepsResultType as O, SiweAuthenticateReturnType as P, LocalizedI18nConfig as Q, OpenSsoReturnType as R, EmbeddedViewActionReferred as S, DisplayModalParamsType as T, FinalModalStepType as U, SsoMetadata as V, ModalStepMetadata as W, I18nConfig as X, FrakWalletSdkConfig as Y, Language as Z, TokenAmountType as _, FrakContextV1 as a, DisplayEmbeddedWalletResultType as b, isV2Context as c, IFrameTransport as d, IFrameRpcSchema as f, RewardTier as g, GetMerchantInformationReturnType as h, FrakContext as i, SendTransactionReturnType as j, ModalStepTypes as k, FrakClient as l, EstimatedReward as m, ssoPopupFeatures as n, FrakContextV2 as o, WalletStatusReturnType as p, ClientLifecycleEvent as q, ssoPopupName as r, isV1Context as s, openSso as t, FrakLifecycleEvent as u, SendInteractionParamsType as v, LoggedInEmbeddedView as w, LoggedOutEmbeddedView as x, DisplayEmbeddedWalletParamsType as y, PrepareSsoParamsType as z };
1127
+ export { FrakWalletSdkConfig as $, SendTransactionModalStepType as A, PrepareSsoReturnType as B, EmbeddedViewActionSharing as C, ModalRpcStepsInput as D, ModalRpcMetadata as E, SiweAuthenticationParams as F, InteractionTypeKey as G, FinalActionType as H, LoginModalStepType as I, MerchantConfigResponse as J, IFrameLifecycleEvent as K, OpenSsoParamsType as L, SendTransactionTxType as M, SiweAuthenticateModalStepType as N, ModalRpcStepsResultType as O, SiweAuthenticateReturnType as P, Currency as Q, OpenSsoReturnType as R, EmbeddedViewActionReferred as S, DisplayModalParamsType as T, FinalModalStepType as U, SsoMetadata as V, ModalStepMetadata as W, ResolvedSdkConfig as X, ResolvedPlacement as Y, SdkResolvedConfig as Z, TokenAmountType as _, FrakContextV1 as a, DisplayEmbeddedWalletResultType as b, isV2Context as c, IFrameTransport as d, I18nConfig as et, IFrameRpcSchema as f, RewardTier as g, GetMerchantInformationReturnType as h, FrakContext as i, SendTransactionReturnType as j, ModalStepTypes as k, FrakClient as l, EstimatedReward as m, ssoPopupFeatures as n, LocalizedI18nConfig as nt, FrakContextV2 as o, WalletStatusReturnType as p, ClientLifecycleEvent as q, ssoPopupName as r, isV1Context as s, openSso as t, Language as tt, FrakLifecycleEvent as u, SendInteractionParamsType as v, LoggedInEmbeddedView as w, LoggedOutEmbeddedView as x, DisplayEmbeddedWalletParamsType as y, PrepareSsoParamsType as z };
@@ -1,6 +1,6 @@
1
- import { Address, Hex } from "viem";
2
- import { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
3
1
  import { OpenPanel } from "@openpanel/web";
2
+ import { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
3
+ import { Address, Hex } from "viem";
4
4
  import { SiweMessage } from "viem/siwe";
5
5
 
6
6
  //#region src/types/config.d.ts
@@ -31,7 +31,7 @@ type FrakWalletSdkConfig = {
31
31
  /**
32
32
  * Your application name (will be displayed in a few modals and in SSO)
33
33
  */
34
- name: string;
34
+ name?: string;
35
35
  /**
36
36
  * Your merchant ID from the Frak dashboard (UUID format)
37
37
  * Used for referral tracking and analytics
@@ -75,6 +75,13 @@ type FrakWalletSdkConfig = {
75
75
  * @defaultValue window.location.host
76
76
  */
77
77
  domain?: string;
78
+ /**
79
+ * Wait for backend config before rendering components.
80
+ * When true (default), components show a spinner until backend config is resolved.
81
+ * When false, components render immediately with SDK static config / HTML attributes.
82
+ * @defaultValue true
83
+ */
84
+ waitForBackendConfig?: boolean;
78
85
  };
79
86
  /**
80
87
  * Custom i18n configuration for the modal
@@ -111,19 +118,97 @@ type FrakWalletSdkConfig = {
111
118
  */
112
119
  type I18nConfig = Record<Language, LocalizedI18nConfig> | LocalizedI18nConfig;
113
120
  /**
114
- * A localized i18n config
121
+ * A localized i18n config (inline objects only — URL-based i18n removed)
115
122
  * @category Config
116
123
  */
117
- type LocalizedI18nConfig = `${string}.css` | {
124
+ type LocalizedI18nConfig = {
118
125
  [key: string]: string;
119
126
  };
120
127
  //#endregion
128
+ //#region src/types/resolvedConfig.d.ts
129
+ /**
130
+ * Response from the merchant resolve endpoint
131
+ * @category Config
132
+ */
133
+ type MerchantConfigResponse = {
134
+ merchantId: string;
135
+ name: string;
136
+ domain: string;
137
+ allowedDomains: string[];
138
+ sdkConfig?: ResolvedSdkConfig;
139
+ };
140
+ /**
141
+ * Resolved placement config from backend
142
+ * Translations already flattened: default + lang-specific merged into one record
143
+ * @category Config
144
+ */
145
+ type ResolvedPlacement = {
146
+ /** Per-component configuration within this placement */components?: {
147
+ buttonShare?: {
148
+ text?: string;
149
+ noRewardText?: string;
150
+ clickAction?: "embedded-wallet" | "share-modal";
151
+ useReward?: boolean;
152
+ css?: string;
153
+ };
154
+ buttonWallet?: {
155
+ position?: "bottom-right" | "bottom-left";
156
+ css?: string;
157
+ };
158
+ openInApp?: {
159
+ text?: string;
160
+ css?: string;
161
+ };
162
+ };
163
+ targetInteraction?: string; /** Already flattened: default + lang-specific merged into one record */
164
+ translations?: Record<string, string>; /** Global placement CSS (applied to modals/listener) */
165
+ css?: string;
166
+ };
167
+ /**
168
+ * Resolved SDK config from backend `/resolve` endpoint
169
+ * Language resolution and translation merging already applied
170
+ * @category Config
171
+ */
172
+ type ResolvedSdkConfig = {
173
+ name?: string;
174
+ logoUrl?: string;
175
+ homepageLink?: string;
176
+ currency?: Currency;
177
+ lang?: Language; /** When true, all SDK components should be hidden */
178
+ hidden?: boolean;
179
+ css?: string;
180
+ translations?: Record<string, string>;
181
+ placements?: Record<string, ResolvedPlacement>;
182
+ };
183
+ /**
184
+ * Internal SDK config store state
185
+ * Merged config: backend > SDK static > defaults
186
+ * Components subscribe to this reactively
187
+ * @category Config
188
+ */
189
+ type SdkResolvedConfig = {
190
+ /** Whether the backend config has been resolved */isResolved: boolean; /** Merchant ID from resolution */
191
+ merchantId: string; /** Domain returned by the resolve endpoint */
192
+ domain?: string; /** Domains allowed for this merchant (used by iframe trust check) */
193
+ allowedDomains?: string[]; /** Whether the resolve returned a backend sdkConfig object */
194
+ hasRawSdkConfig?: boolean; /** Merged metadata fields */
195
+ name?: string;
196
+ logoUrl?: string;
197
+ homepageLink?: string;
198
+ lang?: Language;
199
+ currency?: Currency; /** When true, all SDK components should be hidden */
200
+ hidden?: boolean; /** Global CSS from backend config (passed to iframe) */
201
+ css?: string; /** Global translations (for reference / component fallback) */
202
+ translations?: Record<string, string>; /** Named placements (keyed by placement ID) */
203
+ placements?: Record<string, ResolvedPlacement>;
204
+ };
205
+ //#endregion
121
206
  //#region src/types/lifecycle/client.d.ts
122
207
  /**
123
208
  * Event related to the iframe lifecycle
124
209
  * @ignore
125
210
  */
126
- type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | HandshakeResponse | SsoRedirectCompleteEvent | DeepLinkFailedEvent;
211
+ type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | SsoRedirectCompleteEvent | DeepLinkFailedEvent | ResolvedConfigEvent;
127
212
  type CustomCssEvent = {
128
213
  clientLifecycle: "modal-css";
129
214
  data: {
@@ -146,30 +231,6 @@ type HearbeatEvent = {
146
231
  clientLifecycle: "heartbeat";
147
232
  data?: never;
148
233
  };
149
- type HandshakeResponse = {
150
- clientLifecycle: "handshake-response";
151
- data: {
152
- token: string;
153
- currentUrl: string;
154
- /**
155
- * Pending merge token extracted from URL (?fmt= parameter)
156
- * When present, listener should execute identity merge in background
157
- * URL is cleaned after handshake response is sent
158
- */
159
- pendingMergeToken?: string;
160
- /**
161
- * Client ID for identity tracking (belt & suspenders fallback)
162
- * Primary delivery is via iframe URL query param; handshake is backup for SSR
163
- */
164
- clientId?: string;
165
- /**
166
- * Explicit domain from SDK config (FrakWalletSdkConfig.domain)
167
- * When present, listener should prefer this over URL-derived domain
168
- * for merchant resolution (handles proxied/tunneled environments)
169
- */
170
- configDomain?: string;
171
- };
172
- };
173
234
  type SsoRedirectCompleteEvent = {
174
235
  clientLifecycle: "sso-redirect-complete";
175
236
  data: {
@@ -182,6 +243,21 @@ type DeepLinkFailedEvent = {
182
243
  originalUrl: string;
183
244
  };
184
245
  };
246
+ type ResolvedConfigEvent = {
247
+ clientLifecycle: "resolved-config";
248
+ data: {
249
+ merchantId: string; /** The domain the backend resolved this config for */
250
+ domain: string; /** All domains registered for this merchant (for domain proof) */
251
+ allowedDomains: string[]; /** Full URL of the parent page (for interaction tracking) */
252
+ sourceUrl: string;
253
+ /**
254
+ * Pending merge token extracted from URL (?fmt= parameter).
255
+ * When present, listener should execute identity merge in background.
256
+ */
257
+ pendingMergeToken?: string;
258
+ sdkConfig?: ResolvedSdkConfig;
259
+ };
260
+ };
185
261
  //#endregion
186
262
  //#region src/types/lifecycle/iframe.d.ts
187
263
  /**
@@ -191,19 +267,13 @@ type DeepLinkFailedEvent = {
191
267
  type IFrameLifecycleEvent = {
192
268
  iframeLifecycle: "connected" | "show" | "hide" | "remove-backup";
193
269
  data?: never;
194
- } | DoBackupEvent | HandshakeRequestEvent | RedirectRequestEvent;
270
+ } | DoBackupEvent | RedirectRequestEvent;
195
271
  type DoBackupEvent = {
196
272
  iframeLifecycle: "do-backup";
197
273
  data: {
198
274
  backup?: string;
199
275
  };
200
276
  };
201
- type HandshakeRequestEvent = {
202
- iframeLifecycle: "handshake";
203
- data: {
204
- token: string;
205
- };
206
- };
207
277
  type RedirectRequestEvent = {
208
278
  iframeLifecycle: "redirect";
209
279
  data: {
@@ -691,7 +761,9 @@ type SendInteractionParamsType = {
691
761
  utmTerm?: string;
692
762
  utmContent?: string;
693
763
  } | {
694
- type: "sharing";
764
+ type: "sharing"; /** Epoch seconds timestamp matching the V2 context `t` field embedded in the referral link URL, used for backend correlation */
765
+ sharingTimestamp?: number; /** Merchant order ID linking this sharing event to a purchase (stays server-side, never in URL) */
766
+ purchaseId?: string;
695
767
  } | {
696
768
  type: "custom";
697
769
  customType: string;
@@ -815,7 +887,7 @@ type WalletNotConnected = {
815
887
  * - Response Type: stream (emits updates when wallet status changes)
816
888
  *
817
889
  * #### frak_displayModal
818
- * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
890
+ * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
819
891
  * - Returns: {@link ModalRpcStepsResultType}
820
892
  * - Response Type: promise (one-shot)
821
893
  *
@@ -830,7 +902,7 @@ type WalletNotConnected = {
830
902
  * - Response Type: promise (one-shot)
831
903
  *
832
904
  * #### frak_displayEmbeddedWallet
833
- * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
905
+ * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
834
906
  * - Returns: {@link DisplayEmbeddedWalletResultType}
835
907
  * - Response Type: promise (one-shot)
836
908
  */
@@ -850,7 +922,7 @@ type IFrameRpcSchema = [
850
922
  */
851
923
  {
852
924
  Method: "frak_displayModal";
853
- Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"]];
925
+ Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"], placement?: string];
854
926
  ReturnType: ModalRpcStepsResultType;
855
927
  },
856
928
  /**
@@ -860,7 +932,7 @@ type IFrameRpcSchema = [
860
932
  */
861
933
  {
862
934
  Method: "frak_prepareSso";
863
- Parameters: [params: PrepareSsoParamsType, name: string, customCss?: string];
935
+ Parameters: [params: PrepareSsoParamsType, name?: string, customCss?: string];
864
936
  ReturnType: PrepareSsoReturnType;
865
937
  },
866
938
  /**
@@ -871,7 +943,7 @@ type IFrameRpcSchema = [
871
943
  */
872
944
  {
873
945
  Method: "frak_openSso";
874
- Parameters: [params: OpenSsoParamsType, name: string, customCss?: string];
946
+ Parameters: [params: OpenSsoParamsType, name?: string, customCss?: string];
875
947
  ReturnType: OpenSsoReturnType;
876
948
  },
877
949
  /**
@@ -892,14 +964,14 @@ type IFrameRpcSchema = [
892
964
  */
893
965
  {
894
966
  Method: "frak_displayEmbeddedWallet";
895
- Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"]];
967
+ Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"], placement?: string];
896
968
  ReturnType: DisplayEmbeddedWalletResultType;
897
969
  },
898
970
  /**
899
971
  * Method to send interactions (arrival, sharing, custom events)
900
972
  * Fire-and-forget method - no return value expected
901
973
  * merchantId is resolved from context
902
- * clientId is passed via metadata as safeguard against handshake race condition
974
+ * clientId is passed via metadata as safeguard against race conditions
903
975
  */
904
976
  {
905
977
  Method: "frak_sendInteraction";
@@ -1052,4 +1124,4 @@ declare const ssoPopupName = "frak-sso";
1052
1124
  */
1053
1125
  declare function openSso(client: FrakClient, args: OpenSsoParamsType): Promise<OpenSsoReturnType>;
1054
1126
  //#endregion
1055
- export { SendTransactionModalStepType as A, PrepareSsoReturnType as B, EmbeddedViewActionSharing as C, ModalRpcStepsInput as D, ModalRpcMetadata as E, SiweAuthenticationParams as F, InteractionTypeKey as G, FinalActionType as H, LoginModalStepType as I, Currency as J, IFrameLifecycleEvent as K, OpenSsoParamsType as L, SendTransactionTxType as M, SiweAuthenticateModalStepType as N, ModalRpcStepsResultType as O, SiweAuthenticateReturnType as P, LocalizedI18nConfig as Q, OpenSsoReturnType as R, EmbeddedViewActionReferred as S, DisplayModalParamsType as T, FinalModalStepType as U, SsoMetadata as V, ModalStepMetadata as W, I18nConfig as X, FrakWalletSdkConfig as Y, Language as Z, TokenAmountType as _, FrakContextV1 as a, DisplayEmbeddedWalletResultType as b, isV2Context as c, IFrameTransport as d, IFrameRpcSchema as f, RewardTier as g, GetMerchantInformationReturnType as h, FrakContext as i, SendTransactionReturnType as j, ModalStepTypes as k, FrakClient as l, EstimatedReward as m, ssoPopupFeatures as n, FrakContextV2 as o, WalletStatusReturnType as p, ClientLifecycleEvent as q, ssoPopupName as r, isV1Context as s, openSso as t, FrakLifecycleEvent as u, SendInteractionParamsType as v, LoggedInEmbeddedView as w, LoggedOutEmbeddedView as x, DisplayEmbeddedWalletParamsType as y, PrepareSsoParamsType as z };
1127
+ export { FrakWalletSdkConfig as $, SendTransactionModalStepType as A, PrepareSsoReturnType as B, EmbeddedViewActionSharing as C, ModalRpcStepsInput as D, ModalRpcMetadata as E, SiweAuthenticationParams as F, InteractionTypeKey as G, FinalActionType as H, LoginModalStepType as I, MerchantConfigResponse as J, IFrameLifecycleEvent as K, OpenSsoParamsType as L, SendTransactionTxType as M, SiweAuthenticateModalStepType as N, ModalRpcStepsResultType as O, SiweAuthenticateReturnType as P, Currency as Q, OpenSsoReturnType as R, EmbeddedViewActionReferred as S, DisplayModalParamsType as T, FinalModalStepType as U, SsoMetadata as V, ModalStepMetadata as W, ResolvedSdkConfig as X, ResolvedPlacement as Y, SdkResolvedConfig as Z, TokenAmountType as _, FrakContextV1 as a, DisplayEmbeddedWalletResultType as b, isV2Context as c, IFrameTransport as d, I18nConfig as et, IFrameRpcSchema as f, RewardTier as g, GetMerchantInformationReturnType as h, FrakContext as i, SendTransactionReturnType as j, ModalStepTypes as k, FrakClient as l, EstimatedReward as m, ssoPopupFeatures as n, LocalizedI18nConfig as nt, FrakContextV2 as o, WalletStatusReturnType as p, ClientLifecycleEvent as q, ssoPopupName as r, isV1Context as s, openSso as t, Language as tt, FrakLifecycleEvent as u, SendInteractionParamsType as v, LoggedInEmbeddedView as w, LoggedOutEmbeddedView as x, DisplayEmbeddedWalletParamsType as y, PrepareSsoParamsType as z };
@@ -0,0 +1,13 @@
1
+ const e=require(`./trackEvent-B2uom25e.cjs`);let t=require(`@frak-labs/frame-connector`),n=require(`@openpanel/web`);const r=`nexus-wallet-backup`,i=`frakwallet://`;function a(){let e=navigator.userAgent;return/Android/i.test(e)&&/Chrome\/\d+/i.test(e)}function o(e){return`intent://${e.slice(13)}#Intent;scheme=frakwallet;end`}function s(e,t){let n=t?.timeout??2500,r=!1,i=()=>{document.hidden&&(r=!0)};document.addEventListener(`visibilitychange`,i);let s=a()&&c(e)?o(e):e;window.location.href=s,setTimeout(()=>{document.removeEventListener(`visibilitychange`,i),r||t?.onFallback?.()},n)}function c(e){return e.startsWith(i)}const l={eur:`fr-FR`,usd:`en-US`,gbp:`en-GB`};function u(e){return e&&e in l?e:`eur`}function d(e){return e?l[e]??l.eur:l.eur}function f(e,t){let n=d(t),r=u(t);return e.toLocaleString(n,{style:`currency`,currency:r,minimumFractionDigits:0,maximumFractionDigits:2})}function p(e){return e?`${e}Amount`:`eurAmount`}const m={id:`frak-wallet`,name:`frak-wallet`,title:`Frak Wallet`,allow:`publickey-credentials-get *; clipboard-write; web-share *`,style:{width:`0`,height:`0`,border:`0`,position:`absolute`,zIndex:2000001,top:`-1000px`,left:`-1000px`,colorScheme:`auto`}};function h({walletBaseUrl:t,config:n}){let r=document.querySelector(`#frak-wallet`);r&&r.remove();let i=document.createElement(`iframe`);i.id=m.id,i.name=m.name,i.allow=m.allow,i.style.zIndex=m.style.zIndex.toString(),g({iframe:i,isVisible:!1});let a=n?.walletUrl??t??`https://wallet.frak.id`,o=e.g();return i.src=`${a}/listener?clientId=${encodeURIComponent(o)}`,new Promise(e=>{i.addEventListener(`load`,()=>e(i)),document.body.appendChild(i)})}function g({iframe:e,isVisible:t}){if(!t){e.style.width=`0`,e.style.height=`0`,e.style.border=`0`,e.style.position=`fixed`,e.style.top=`-1000px`,e.style.left=`-1000px`;return}e.style.position=`fixed`,e.style.top=`0`,e.style.left=`0`,e.style.width=`100%`,e.style.height=`100%`,e.style.pointerEvents=`auto`}function _(e=`/listener`){if(!window.opener)return null;let t=t=>{try{return t.location.origin===window.location.origin&&t.location.pathname===e}catch{return!1}};if(t(window.opener))return window.opener;try{let e=window.opener.frames;for(let n=0;n<e.length;n++)if(t(e[n]))return e[n];return null}catch(t){return console.error(`[findIframeInOpener] Error finding iframe with pathname ${e}:`,t),null}}function v(e,t){if(typeof window>`u`)return;let n=new URL(window.location.href),r=n.searchParams.get(`sso`);r&&(t.then(()=>{e.sendLifecycle({clientLifecycle:`sso-redirect-complete`,data:{compressed:r}}),console.log(`[SSO URL Listener] Forwarded compressed SSO data to iframe`)}).catch(e=>{console.error(`[SSO URL Listener] Failed to forward SSO data:`,e)}),n.searchParams.delete(`sso`),window.history.replaceState({},``,n.toString()),console.log(`[SSO URL Listener] SSO parameter detected and URL cleaned`))}var y=class e{config;iframe;isSetupDone=!1;lastResponse=null;lastRequest=null;constructor(e,t){this.config=e,this.iframe=t,this.lastRequest=null,this.lastResponse=null}setLastResponse(e,t){this.lastResponse={message:e,response:t,timestamp:Date.now()}}setLastRequest(e){this.lastRequest={event:e,timestamp:Date.now()}}updateSetupStatus(e){this.isSetupDone=e}base64Encode(e){try{return btoa(JSON.stringify(e))}catch(e){return console.warn(`Failed to encode debug data`,e),btoa(`Failed to encode data`)}}getIframeStatus(){return this.iframe?{loading:this.iframe.hasAttribute(`loading`),url:this.iframe.src,readyState:this.iframe.contentDocument?.readyState?this.iframe.contentDocument.readyState===`complete`?1:0:-1,contentWindow:!!this.iframe.contentWindow,isConnected:this.iframe.isConnected}:null}getNavigatorInfo(){return navigator?{userAgent:navigator.userAgent,language:navigator.language,onLine:navigator.onLine,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio}:null}gatherDebugInfo(e){let n=this.getIframeStatus(),r=this.getNavigatorInfo(),i=`Unknown`;return e instanceof t.FrakRpcError?i=`FrakRpcError: ${e.code} '${e.message}'`:e instanceof Error?i=e.message:typeof e==`string`&&(i=e),{timestamp:new Date().toISOString(),encodedUrl:btoa(window.location.href),encodedConfig:this.config?this.base64Encode(this.config):`no-config`,navigatorInfo:r?this.base64Encode(r):`no-navigator`,iframeStatus:n?this.base64Encode(n):`not-iframe`,lastRequest:this.lastRequest?this.base64Encode(this.lastRequest):`No Frak request logged`,lastResponse:this.lastResponse?this.base64Encode(this.lastResponse):`No Frak response logged`,clientStatus:this.isSetupDone?`setup`:`not-setup`,error:i}}static empty(){return new e}formatDebugInfo(e){let t=this.gatherDebugInfo(e);return`
2
+ Debug Information:
3
+ -----------------
4
+ Timestamp: ${t.timestamp}
5
+ URL: ${t.encodedUrl}
6
+ Config: ${t.encodedConfig}
7
+ Navigator Info: ${t.navigatorInfo}
8
+ IFrame Status: ${t.iframeStatus}
9
+ Last Request: ${t.lastRequest}
10
+ Last Response: ${t.lastResponse}
11
+ Client Status: ${t.clientStatus}
12
+ Error: ${t.error}
13
+ `.trim()}};const b=(()=>{if(typeof navigator>`u`)return!1;let e=navigator.userAgent;if(!(/iPhone|iPad|iPod/i.test(e)||/Macintosh/i.test(e)&&navigator.maxTouchPoints>1))return!1;let t=e.toLowerCase();return t.includes(`instagram`)||t.includes(`fban`)||t.includes(`fbav`)||t.includes(`facebook`)})();function x(e){e?localStorage.setItem(r,e):localStorage.removeItem(r)}function S(e,t){try{let n=new URL(e);return n.searchParams.has(`u`)?(n.searchParams.delete(`u`),n.searchParams.append(`u`,window.location.href),t&&n.searchParams.append(`fmt`,t),n.toString()):e}catch{return e}}function C(e){let t=new URL(window.location.href);e&&t.searchParams.set(`fmt`,e);let n=t.protocol===`http:`?`x-safari-http`:`x-safari-https`;window.location.href=`${n}://${t.host}${t.pathname}${t.search}${t.hash}`}function w(e){return e.includes(`/common/social`)}function T(e,t,n,r){if(c(t)){let i=S(t,r);s(i,{onFallback:()=>{e.contentWindow?.postMessage({clientLifecycle:`deep-link-failed`,data:{originalUrl:i}},n)}})}else if(b&&w(t))C(r);else{let e=S(t,r);window.location.href=e}}function E({iframe:e,targetOrigin:n}){let i=new t.Deferred;return{handleEvent:async t=>{if(!(`iframeLifecycle`in t))return;let{iframeLifecycle:a,data:o}=t;switch(a){case`connected`:i.resolve(!0);break;case`do-backup`:x(o.backup);break;case`remove-backup`:localStorage.removeItem(r);break;case`show`:case`hide`:g({iframe:e,isVisible:a===`show`});break;case`redirect`:T(e,o.baseRedirectUrl,n,o.mergeToken);break}},isConnected:i.promise}}function D({config:r,iframe:i}){let a=r?.walletUrl??`https://wallet.frak.id`,o=typeof navigator<`u`?navigator.language?.split(`-`)[0]:void 0,s=r.metadata.lang??(o===`en`||o===`fr`?o:void 0),c=r.domain??(typeof window<`u`?window.location.hostname:``);e.n.setCacheScope(c,s),e.n.reset();let l=e.n.isCacheFresh?void 0:e.n.resolve(r.domain,r.walletUrl,s),u=E({iframe:i,targetOrigin:a}),d=new t.Deferred,f=new y(r,i);if(!i.contentWindow)throw new t.FrakRpcError(t.RpcErrorCodes.configError,`The iframe does not have a content window`);let p=(0,t.createRpcClient)({emittingTransport:i.contentWindow,listeningTransport:window,targetOrigin:a,middleware:[{async onRequest(e,n){if(!await u.isConnected)throw new t.FrakRpcError(t.RpcErrorCodes.clientNotConnected,`The iframe provider isn't connected yet`);return await d.promise,n}},{onRequest(e,t){return f.setLastRequest(e),t},onResponse(e,t){return f.setLastResponse(e,t),t}}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await u.handleEvent(e)}}}),m=O(p,u),h=async()=>{m(),p.cleanup(),i.remove(),e.n.clearCache(),e.n.reset()},g;console.log(`[Frak SDK] Initializing OpenPanel`),g=new n.OpenPanel({apiUrl:`https://op-api.gcp.frak.id`,clientId:`6eacc8d7-49ac-4936-95e9-81ef29449570`,trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:t,payload:n})=>(t!==`track`||!n?.properties||`sdkVersion`in n.properties||(n.properties={...n.properties,sdkVersion:`0.2.1`,userAnonymousClientId:e.g()}),!0)}),g.setGlobalProperties({sdkVersion:`0.2.1`,userAnonymousClientId:e.g()}),g.init();let _=k({config:r,rpcClient:p,lifecycleManager:u,configPromise:l,contextSent:d}).then(()=>f.updateSetupStatus(!0)).catch(e=>{throw d.reject(e),e});return{config:r,debugInfo:f,waitForConnection:u.isConnected,waitForSetup:_,request:p.request,listenerRequest:p.listen,destroy:h,openPanel:g}}function O(e,t){let n,r,i=()=>e.sendLifecycle({clientLifecycle:`heartbeat`});async function a(){i(),n=setInterval(i,1e3),r=setTimeout(()=>{o(),console.log(`Heartbeat timeout: connection failed`)},3e4),await t.isConnected,o()}function o(){n&&clearInterval(n),r&&clearTimeout(r)}return a(),o}async function k({config:t,rpcClient:n,lifecycleManager:i,configPromise:a,contextSent:o}){await i.isConnected,v(n,i.isConnected);let s=new URL(window.location.href),c=s.searchParams.get(`fmt`)??void 0;c&&(s.searchParams.delete(`fmt`),window.history.replaceState({},``,s.toString()));let l=n=>{let r=n?.merchantId??t.metadata.merchantId??``,i=n?.domain??``,a=n?.allowedDomains??[],o=n?.sdkConfig;e.n.setConfig(o?{isResolved:!0,merchantId:r,domain:i,allowedDomains:a,hasRawSdkConfig:!0,name:o.name??t.metadata.name,logoUrl:o.logoUrl??t.metadata.logoUrl,homepageLink:o.homepageLink??t.metadata.homepageLink,lang:o.lang??t.metadata.lang,currency:o.currency??t.metadata.currency,hidden:o.hidden,css:o.css,translations:o.translations,placements:o.placements}:{isResolved:!0,merchantId:r,domain:i,allowedDomains:a,name:t.metadata.name,logoUrl:t.metadata.logoUrl,homepageLink:t.metadata.homepageLink,lang:t.metadata.lang,currency:t.metadata.currency})},u=!1,d=e=>{let t=u?void 0:c;u=!0;let r=e.hasRawSdkConfig?{name:e.name,logoUrl:e.logoUrl,homepageLink:e.homepageLink,lang:e.lang,currency:e.currency,hidden:e.hidden,css:e.css,translations:e.translations,placements:e.placements}:void 0;n.sendLifecycle({clientLifecycle:`resolved-config`,data:{merchantId:e.merchantId,domain:e.domain??``,allowedDomains:e.allowedDomains??[],sourceUrl:window.location.href,...t&&{pendingMergeToken:t},...r&&{sdkConfig:r}}})};e.n.isResolved&&(d(e.n.getConfig()),o.resolve()),a&&(l(await a),d(e.n.getConfig()),o.resolve());async function f(){let e=t.customizations?.css;e&&n.sendLifecycle({clientLifecycle:`modal-css`,data:{cssLink:e}})}async function p(){let e=t.customizations?.i18n;e&&n.sendLifecycle({clientLifecycle:`modal-i18n`,data:{i18n:e}})}async function m(){if(typeof window>`u`)return;let e=window.localStorage.getItem(r);e&&n.sendLifecycle({clientLifecycle:`restore-backup`,data:{backup:e}})}await Promise.allSettled([f(),p(),m()])}async function A({config:e}){let t=j(e),n=await h({config:t});if(!n){console.error(`Failed to create iframe`);return}let r=D({config:t,iframe:n});if(await r.waitForSetup,!await r.waitForConnection){console.error(`Failed to connect to client`);return}return r}function j(e){let t=u(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return u}});
@@ -0,0 +1,13 @@
1
+ import{g as e,n as t}from"./trackEvent-5j5kkOCj.js";import{Deferred as n,FrakRpcError as r,RpcErrorCodes as i,createRpcClient as a}from"@frak-labs/frame-connector";import{OpenPanel as o}from"@openpanel/web";const s=`nexus-wallet-backup`,c=`frakwallet://`;function l(){let e=navigator.userAgent;return/Android/i.test(e)&&/Chrome\/\d+/i.test(e)}function u(e){return`intent://${e.slice(13)}#Intent;scheme=frakwallet;end`}function d(e,t){let n=t?.timeout??2500,r=!1,i=()=>{document.hidden&&(r=!0)};document.addEventListener(`visibilitychange`,i);let a=l()&&f(e)?u(e):e;window.location.href=a,setTimeout(()=>{document.removeEventListener(`visibilitychange`,i),r||t?.onFallback?.()},n)}function f(e){return e.startsWith(c)}const p={eur:`fr-FR`,usd:`en-US`,gbp:`en-GB`};function m(e){return e&&e in p?e:`eur`}function h(e){return e?p[e]??p.eur:p.eur}function g(e,t){let n=h(t),r=m(t);return e.toLocaleString(n,{style:`currency`,currency:r,minimumFractionDigits:0,maximumFractionDigits:2})}function _(e){return e?`${e}Amount`:`eurAmount`}const v={id:`frak-wallet`,name:`frak-wallet`,title:`Frak Wallet`,allow:`publickey-credentials-get *; clipboard-write; web-share *`,style:{width:`0`,height:`0`,border:`0`,position:`absolute`,zIndex:2000001,top:`-1000px`,left:`-1000px`,colorScheme:`auto`}};function y({walletBaseUrl:t,config:n}){let r=document.querySelector(`#frak-wallet`);r&&r.remove();let i=document.createElement(`iframe`);i.id=v.id,i.name=v.name,i.allow=v.allow,i.style.zIndex=v.style.zIndex.toString(),b({iframe:i,isVisible:!1});let a=n?.walletUrl??t??`https://wallet.frak.id`,o=e();return i.src=`${a}/listener?clientId=${encodeURIComponent(o)}`,new Promise(e=>{i.addEventListener(`load`,()=>e(i)),document.body.appendChild(i)})}function b({iframe:e,isVisible:t}){if(!t){e.style.width=`0`,e.style.height=`0`,e.style.border=`0`,e.style.position=`fixed`,e.style.top=`-1000px`,e.style.left=`-1000px`;return}e.style.position=`fixed`,e.style.top=`0`,e.style.left=`0`,e.style.width=`100%`,e.style.height=`100%`,e.style.pointerEvents=`auto`}function x(e=`/listener`){if(!window.opener)return null;let t=t=>{try{return t.location.origin===window.location.origin&&t.location.pathname===e}catch{return!1}};if(t(window.opener))return window.opener;try{let e=window.opener.frames;for(let n=0;n<e.length;n++)if(t(e[n]))return e[n];return null}catch(t){return console.error(`[findIframeInOpener] Error finding iframe with pathname ${e}:`,t),null}}function S(e,t){if(typeof window>`u`)return;let n=new URL(window.location.href),r=n.searchParams.get(`sso`);r&&(t.then(()=>{e.sendLifecycle({clientLifecycle:`sso-redirect-complete`,data:{compressed:r}}),console.log(`[SSO URL Listener] Forwarded compressed SSO data to iframe`)}).catch(e=>{console.error(`[SSO URL Listener] Failed to forward SSO data:`,e)}),n.searchParams.delete(`sso`),window.history.replaceState({},``,n.toString()),console.log(`[SSO URL Listener] SSO parameter detected and URL cleaned`))}var C=class e{config;iframe;isSetupDone=!1;lastResponse=null;lastRequest=null;constructor(e,t){this.config=e,this.iframe=t,this.lastRequest=null,this.lastResponse=null}setLastResponse(e,t){this.lastResponse={message:e,response:t,timestamp:Date.now()}}setLastRequest(e){this.lastRequest={event:e,timestamp:Date.now()}}updateSetupStatus(e){this.isSetupDone=e}base64Encode(e){try{return btoa(JSON.stringify(e))}catch(e){return console.warn(`Failed to encode debug data`,e),btoa(`Failed to encode data`)}}getIframeStatus(){return this.iframe?{loading:this.iframe.hasAttribute(`loading`),url:this.iframe.src,readyState:this.iframe.contentDocument?.readyState?this.iframe.contentDocument.readyState===`complete`?1:0:-1,contentWindow:!!this.iframe.contentWindow,isConnected:this.iframe.isConnected}:null}getNavigatorInfo(){return navigator?{userAgent:navigator.userAgent,language:navigator.language,onLine:navigator.onLine,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio}:null}gatherDebugInfo(e){let t=this.getIframeStatus(),n=this.getNavigatorInfo(),i=`Unknown`;return e instanceof r?i=`FrakRpcError: ${e.code} '${e.message}'`:e instanceof Error?i=e.message:typeof e==`string`&&(i=e),{timestamp:new Date().toISOString(),encodedUrl:btoa(window.location.href),encodedConfig:this.config?this.base64Encode(this.config):`no-config`,navigatorInfo:n?this.base64Encode(n):`no-navigator`,iframeStatus:t?this.base64Encode(t):`not-iframe`,lastRequest:this.lastRequest?this.base64Encode(this.lastRequest):`No Frak request logged`,lastResponse:this.lastResponse?this.base64Encode(this.lastResponse):`No Frak response logged`,clientStatus:this.isSetupDone?`setup`:`not-setup`,error:i}}static empty(){return new e}formatDebugInfo(e){let t=this.gatherDebugInfo(e);return`
2
+ Debug Information:
3
+ -----------------
4
+ Timestamp: ${t.timestamp}
5
+ URL: ${t.encodedUrl}
6
+ Config: ${t.encodedConfig}
7
+ Navigator Info: ${t.navigatorInfo}
8
+ IFrame Status: ${t.iframeStatus}
9
+ Last Request: ${t.lastRequest}
10
+ Last Response: ${t.lastResponse}
11
+ Client Status: ${t.clientStatus}
12
+ Error: ${t.error}
13
+ `.trim()}};const w=(()=>{if(typeof navigator>`u`)return!1;let e=navigator.userAgent;if(!(/iPhone|iPad|iPod/i.test(e)||/Macintosh/i.test(e)&&navigator.maxTouchPoints>1))return!1;let t=e.toLowerCase();return t.includes(`instagram`)||t.includes(`fban`)||t.includes(`fbav`)||t.includes(`facebook`)})();function T(e){e?localStorage.setItem(s,e):localStorage.removeItem(s)}function E(e,t){try{let n=new URL(e);return n.searchParams.has(`u`)?(n.searchParams.delete(`u`),n.searchParams.append(`u`,window.location.href),t&&n.searchParams.append(`fmt`,t),n.toString()):e}catch{return e}}function D(e){let t=new URL(window.location.href);e&&t.searchParams.set(`fmt`,e);let n=t.protocol===`http:`?`x-safari-http`:`x-safari-https`;window.location.href=`${n}://${t.host}${t.pathname}${t.search}${t.hash}`}function O(e){return e.includes(`/common/social`)}function k(e,t,n,r){if(f(t)){let i=E(t,r);d(i,{onFallback:()=>{e.contentWindow?.postMessage({clientLifecycle:`deep-link-failed`,data:{originalUrl:i}},n)}})}else if(w&&O(t))D(r);else{let e=E(t,r);window.location.href=e}}function A({iframe:e,targetOrigin:t}){let r=new n;return{handleEvent:async n=>{if(!(`iframeLifecycle`in n))return;let{iframeLifecycle:i,data:a}=n;switch(i){case`connected`:r.resolve(!0);break;case`do-backup`:T(a.backup);break;case`remove-backup`:localStorage.removeItem(s);break;case`show`:case`hide`:b({iframe:e,isVisible:i===`show`});break;case`redirect`:k(e,a.baseRedirectUrl,t,a.mergeToken);break}},isConnected:r.promise}}function j({config:s,iframe:c}){let l=s?.walletUrl??`https://wallet.frak.id`,u=typeof navigator<`u`?navigator.language?.split(`-`)[0]:void 0,d=s.metadata.lang??(u===`en`||u===`fr`?u:void 0),f=s.domain??(typeof window<`u`?window.location.hostname:``);t.setCacheScope(f,d),t.reset();let p=t.isCacheFresh?void 0:t.resolve(s.domain,s.walletUrl,d),m=A({iframe:c,targetOrigin:l}),h=new n,g=new C(s,c);if(!c.contentWindow)throw new r(i.configError,`The iframe does not have a content window`);let _=a({emittingTransport:c.contentWindow,listeningTransport:window,targetOrigin:l,middleware:[{async onRequest(e,t){if(!await m.isConnected)throw new r(i.clientNotConnected,`The iframe provider isn't connected yet`);return await h.promise,t}},{onRequest(e,t){return g.setLastRequest(e),t},onResponse(e,t){return g.setLastResponse(e,t),t}}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await m.handleEvent(e)}}}),v=M(_,m),y=async()=>{v(),_.cleanup(),c.remove(),t.clearCache(),t.reset()},b;console.log(`[Frak SDK] Initializing OpenPanel`),b=new o({apiUrl:`https://op-api.gcp.frak.id`,clientId:`6eacc8d7-49ac-4936-95e9-81ef29449570`,trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:t,payload:n})=>(t!==`track`||!n?.properties||`sdkVersion`in n.properties||(n.properties={...n.properties,sdkVersion:`0.2.1`,userAnonymousClientId:e()}),!0)}),b.setGlobalProperties({sdkVersion:`0.2.1`,userAnonymousClientId:e()}),b.init();let x=N({config:s,rpcClient:_,lifecycleManager:m,configPromise:p,contextSent:h}).then(()=>g.updateSetupStatus(!0)).catch(e=>{throw h.reject(e),e});return{config:s,debugInfo:g,waitForConnection:m.isConnected,waitForSetup:x,request:_.request,listenerRequest:_.listen,destroy:y,openPanel:b}}function M(e,t){let n,r,i=()=>e.sendLifecycle({clientLifecycle:`heartbeat`});async function a(){i(),n=setInterval(i,1e3),r=setTimeout(()=>{o(),console.log(`Heartbeat timeout: connection failed`)},3e4),await t.isConnected,o()}function o(){n&&clearInterval(n),r&&clearTimeout(r)}return a(),o}async function N({config:e,rpcClient:n,lifecycleManager:r,configPromise:i,contextSent:a}){await r.isConnected,S(n,r.isConnected);let o=new URL(window.location.href),c=o.searchParams.get(`fmt`)??void 0;c&&(o.searchParams.delete(`fmt`),window.history.replaceState({},``,o.toString()));let l=n=>{let r=n?.merchantId??e.metadata.merchantId??``,i=n?.domain??``,a=n?.allowedDomains??[],o=n?.sdkConfig;t.setConfig(o?{isResolved:!0,merchantId:r,domain:i,allowedDomains:a,hasRawSdkConfig:!0,name:o.name??e.metadata.name,logoUrl:o.logoUrl??e.metadata.logoUrl,homepageLink:o.homepageLink??e.metadata.homepageLink,lang:o.lang??e.metadata.lang,currency:o.currency??e.metadata.currency,hidden:o.hidden,css:o.css,translations:o.translations,placements:o.placements}:{isResolved:!0,merchantId:r,domain:i,allowedDomains:a,name:e.metadata.name,logoUrl:e.metadata.logoUrl,homepageLink:e.metadata.homepageLink,lang:e.metadata.lang,currency:e.metadata.currency})},u=!1,d=e=>{let t=u?void 0:c;u=!0;let r=e.hasRawSdkConfig?{name:e.name,logoUrl:e.logoUrl,homepageLink:e.homepageLink,lang:e.lang,currency:e.currency,hidden:e.hidden,css:e.css,translations:e.translations,placements:e.placements}:void 0;n.sendLifecycle({clientLifecycle:`resolved-config`,data:{merchantId:e.merchantId,domain:e.domain??``,allowedDomains:e.allowedDomains??[],sourceUrl:window.location.href,...t&&{pendingMergeToken:t},...r&&{sdkConfig:r}}})};t.isResolved&&(d(t.getConfig()),a.resolve()),i&&(l(await i),d(t.getConfig()),a.resolve());async function f(){let t=e.customizations?.css;t&&n.sendLifecycle({clientLifecycle:`modal-css`,data:{cssLink:t}})}async function p(){let t=e.customizations?.i18n;t&&n.sendLifecycle({clientLifecycle:`modal-i18n`,data:{i18n:t}})}async function m(){if(typeof window>`u`)return;let e=window.localStorage.getItem(s);e&&n.sendLifecycle({clientLifecycle:`restore-backup`,data:{backup:e}})}await Promise.allSettled([f(),p(),m()])}async function P({config:e}){let t=F(e),n=await y({config:t});if(!n){console.error(`Failed to create iframe`);return}let r=j({config:t,iframe:n});if(await r.waitForSetup,!await r.waitForConnection){console.error(`Failed to connect to client`);return}return r}function F(e){let t=m(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}export{y as a,g as c,p as d,l as f,c as g,d as h,v as i,h as l,u as m,j as n,x as o,f as p,C as r,_ as s,P as t,m as u};
@@ -0,0 +1 @@
1
+ import{a as e,g as t,i as n,n as r,r as i,s as a,t as o}from"./trackEvent-5j5kkOCj.js";import{isAddressEqual as s}from"viem";import{Deferred as c}from"@frak-labs/frame-connector";import{generateSiweNonce as l}from"viem/siwe";async function u(e,t,n){return await e.request({method:`frak_displayEmbeddedWallet`,params:n?[t,e.config.metadata,n]:[t,e.config.metadata]})}async function d(e,{steps:t,metadata:n},r){return await e.request({method:`frak_displayModal`,params:r?[t,n,e.config.metadata,r]:[t,n,e.config.metadata]})}async function f(e){if(typeof window>`u`)return;let n=t();if(!n)return;let i=await r.resolveMerchantId();if(!i)return;let o=`frak-identity-ensured-${i}`;if(!window.sessionStorage.getItem(o))try{let t=a();(await fetch(`${t}/user/identity/ensure`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`,"x-wallet-sdk-auth":e,"x-frak-client-id":n},body:JSON.stringify({merchantId:i})})).ok&&window.sessionStorage.setItem(o,`1`)}catch{}}async function p(e){return await e.request({method:`frak_getMerchantInformation`})}async function m(e,t){let{metadata:n,customizations:r}=e.config;return await e.request({method:`frak_prepareSso`,params:[t,n.name,r?.css]})}async function h(e,n){try{await e.request({method:`frak_sendInteraction`,params:[n,{clientId:t()}]})}catch{console.warn(`[Frak SDK] Failed to send interaction:`,n.type)}}function g(t,r,i){let a=typeof window<`u`?window.location.href:void 0;return e(r)?(o(t,`user_referred_started`,{properties:{referrerClientId:r.c,walletStatus:i?.key}}),h(t,{type:`arrival`,referrerClientId:r.c,referrerMerchantId:r.m,referralTimestamp:r.t,landingUrl:a}),!0):n(r)?(o(t,`user_referred_started`,{properties:{referrer:r.r,walletStatus:i?.key}}),h(t,{type:`arrival`,referrerWallet:r.r,landingUrl:a}),!0):!1}function _(e){let n=t();return n?{v:2,c:n,m:e,t:Math.floor(Date.now()/1e3)}:null}function v(r,i){return e(r)?t()===r.c:n(r)&&i?.wallet?s(r.r,i.wallet):!1}function y(t,{walletStatus:n,frakContext:r,options:a}){if(!r)return`no-referrer`;if(v(r,n))return`self-referral`;if(!g(t,r,n))return`no-referrer`;let s=e(r)?r.m:a?.merchantId,c=a?.alwaysAppendUrl&&s?_(s):null;return i.replaceUrl({url:window.location?.href,context:c}),o(t,`user_referred_completed`,{properties:{status:`success`}}),`success`}async function b(e,{options:t}={}){let n=i.parse({url:window.location.href}),r=await S(e);try{return y(e,{walletStatus:r,frakContext:n,options:t})}catch(e){console.warn(`Error processing referral`,{error:e})}}async function x(e){if(typeof window>`u`){console.warn(`[Frak] No window found, can't track purchase`);return}let n=window.sessionStorage.getItem(`frak-wallet-interaction-token`),i=t();if(!n&&!i){console.warn(`[Frak] No identity found, skipping purchase check`);return}let o=e.merchantId??await r.resolveMerchantId();if(!o){console.warn(`[Frak] No merchant id found, skipping purchase check`);return}let s={Accept:`application/json`,"Content-Type":`application/json`};n&&(s[`x-wallet-sdk-auth`]=n),i&&(s[`x-frak-client-id`]=i);let c=a();await fetch(`${c}/user/track/purchase`,{method:`POST`,headers:s,body:JSON.stringify({customerId:e.customerId,orderId:e.orderId,token:e.token,merchantId:o})})}function S(e,t){if(!t)return e.request({method:`frak_listenToWalletStatus`}).then(t=>(C(e,t),t));let n=new c,r=!1;return e.listenerRequest({method:`frak_listenToWalletStatus`},i=>{C(e,i),t(i),r||=(n.resolve(i),!0)}),n.promise}function C(e,t){typeof window>`u`||(e.openPanel?.setGlobalProperties({wallet:t.wallet??null}),t.interactionToken?(window.sessionStorage.setItem(`frak-wallet-interaction-token`,t.interactionToken),f(t.interactionToken)):window.sessionStorage.removeItem(`frak-wallet-interaction-token`))}function w(e,{metadata:t,login:n}){return T(e,{steps:{login:n??{}},metadata:t})}function T(e,t){function n(n){return T(e,{...t,steps:{...t.steps,sendTransaction:n}})}function r(n){return T(e,{...t,steps:{...t.steps,final:{...n,action:{key:`reward`}}}})}function i(n,r){return T(e,{...t,steps:{...t.steps,final:{...r,action:{key:`sharing`,options:n}}}})}async function a(n,r){return n&&(t.metadata=n(t.metadata??{})),await d(e,t,r)}return{params:t,sendTx:n,reward:r,sharing:i,display:a}}async function E(e,{tx:t,metadata:n}){return(await d(e,{metadata:n,steps:{login:{},sendTransaction:{tx:t}}})).sendTransaction}async function D(e,{siwe:t,metadata:n}){let r=e.config?.domain??window.location.host,i=t?.statement??`I confirm that I want to use my Frak wallet on: ${e.config.metadata.name}`;return(await d(e,{metadata:n,steps:{login:{},siweAuthenticate:{siwe:{...t,statement:i,nonce:t?.nonce??l(),uri:t?.uri??`https://${r}`,version:t?.version??`1`,domain:r}}}})).siweAuthenticate}export{x as a,h as c,f as d,d as f,S as i,m as l,E as n,b as o,u as p,w as r,y as s,D as t,p as u};