@frak-labs/core-sdk 0.2.1 → 1.0.0-beta.10dada3f

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 (93) hide show
  1. package/README.md +1 -2
  2. package/cdn/bundle.js +3 -3
  3. package/dist/actions-Bsl5ub7_.js +1 -0
  4. package/dist/actions-C_B0fn1P.cjs +1 -0
  5. package/dist/actions.cjs +1 -1
  6. package/dist/actions.d.cts +3 -3
  7. package/dist/actions.d.ts +3 -3
  8. package/dist/actions.js +1 -1
  9. package/dist/bundle.cjs +1 -1
  10. package/dist/bundle.d.cts +4 -4
  11. package/dist/bundle.d.ts +4 -4
  12. package/dist/bundle.js +1 -1
  13. package/dist/{computeLegacyProductId-CCAZvLa5.d.cts → index-BSGP3dbi.d.ts} +250 -73
  14. package/dist/{siweAuthenticate-CVigMOxz.d.cts → index-Bh0TuKYS.d.ts} +122 -8
  15. package/dist/{siweAuthenticate-CnCZ7mok.d.ts → index-DW8xes2o.d.cts} +122 -8
  16. package/dist/{computeLegacyProductId-b5cUWdAm.d.ts → index-TRJNS6B5.d.cts} +250 -73
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.cts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +1 -1
  21. package/dist/{openSso-B0g7-807.d.cts → openSso-Bvhy_urG.d.cts} +395 -50
  22. package/dist/{openSso-CMzwvaCa.d.ts → openSso-D2kTUv0-.d.ts} +394 -49
  23. package/dist/sdkConfigStore-B6CkorsU.cjs +1 -0
  24. package/dist/sdkConfigStore-Dx0oAVEO.js +1 -0
  25. package/dist/src-CLF8o8WB.cjs +13 -0
  26. package/dist/src-al3X6r-n.js +13 -0
  27. package/package.json +12 -13
  28. package/src/actions/displayEmbeddedWallet.ts +6 -2
  29. package/src/actions/displayModal.ts +6 -2
  30. package/src/actions/displaySharingPage.ts +49 -0
  31. package/src/actions/ensureIdentity.ts +2 -2
  32. package/src/actions/getMerchantInformation.test.ts +13 -1
  33. package/src/actions/getMerchantInformation.ts +20 -5
  34. package/src/actions/getMergeToken.ts +33 -0
  35. package/src/actions/getUserReferralStatus.ts +42 -0
  36. package/src/actions/index.ts +8 -1
  37. package/src/actions/referral/processReferral.test.ts +73 -8
  38. package/src/actions/referral/processReferral.ts +15 -12
  39. package/src/actions/referral/setupReferral.test.ts +79 -0
  40. package/src/actions/referral/setupReferral.ts +32 -0
  41. package/src/actions/trackPurchaseStatus.test.ts +32 -20
  42. package/src/actions/trackPurchaseStatus.ts +3 -5
  43. package/src/actions/wrapper/modalBuilder.test.ts +4 -2
  44. package/src/actions/wrapper/modalBuilder.ts +6 -8
  45. package/src/clients/createIFrameFrakClient.ts +233 -28
  46. package/src/clients/transports/iframeLifecycleManager.test.ts +14 -94
  47. package/src/clients/transports/iframeLifecycleManager.ts +35 -53
  48. package/src/index.ts +25 -5
  49. package/src/stubs/rrweb.ts +9 -0
  50. package/src/types/config.ts +19 -3
  51. package/src/types/context.ts +16 -4
  52. package/src/types/index.ts +15 -1
  53. package/src/types/lifecycle/client.ts +29 -27
  54. package/src/types/lifecycle/iframe.ts +7 -8
  55. package/src/types/resolvedConfig.ts +138 -0
  56. package/src/types/rpc/displaySharingPage.ts +100 -0
  57. package/src/types/rpc/embedded/index.ts +1 -1
  58. package/src/types/rpc/interaction.ts +4 -0
  59. package/src/types/rpc/userReferralStatus.ts +20 -0
  60. package/src/types/rpc.ts +54 -5
  61. package/src/types/tracking.ts +36 -0
  62. package/src/utils/FrakContext.test.ts +179 -1
  63. package/src/utils/FrakContext.ts +83 -8
  64. package/src/utils/analytics/events/component.ts +58 -0
  65. package/src/utils/analytics/events/index.ts +20 -0
  66. package/src/utils/analytics/events/lifecycle.ts +26 -0
  67. package/src/utils/analytics/events/referral.ts +11 -0
  68. package/src/utils/analytics/index.ts +8 -0
  69. package/src/utils/{trackEvent.test.ts → analytics/trackEvent.test.ts} +22 -30
  70. package/src/utils/analytics/trackEvent.ts +34 -0
  71. package/src/utils/backendUrl.test.ts +2 -2
  72. package/src/utils/backendUrl.ts +1 -1
  73. package/src/utils/cache/index.ts +7 -0
  74. package/src/utils/cache/lruMap.test.ts +55 -0
  75. package/src/utils/cache/lruMap.ts +38 -0
  76. package/src/utils/cache/withCache.test.ts +168 -0
  77. package/src/utils/cache/withCache.ts +124 -0
  78. package/src/utils/inAppBrowser.ts +60 -0
  79. package/src/utils/index.ts +11 -5
  80. package/src/utils/mergeAttribution.test.ts +153 -0
  81. package/src/utils/mergeAttribution.ts +75 -0
  82. package/src/utils/sdkConfigStore.test.ts +405 -0
  83. package/src/utils/sdkConfigStore.ts +263 -0
  84. package/src/utils/sso.ts +3 -7
  85. package/dist/setupClient-BduY6Sym.cjs +0 -13
  86. package/dist/setupClient-ftmdQ-I8.js +0 -13
  87. package/dist/siweAuthenticate-BWmI2_TN.cjs +0 -1
  88. package/dist/siweAuthenticate-zczqxm0a.js +0 -1
  89. package/dist/trackEvent-CeLFVzZn.js +0 -1
  90. package/dist/trackEvent-Ew5r5zfI.cjs +0 -1
  91. package/src/utils/merchantId.test.ts +0 -653
  92. package/src/utils/merchantId.ts +0 -143
  93. package/src/utils/trackEvent.ts +0 -41
@@ -3,6 +3,63 @@ import { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
3
3
  import { OpenPanel } from "@openpanel/web";
4
4
  import { SiweMessage } from "viem/siwe";
5
5
 
6
+ //#region src/types/tracking.d.ts
7
+ type UtmParams = {
8
+ source?: string;
9
+ medium?: string;
10
+ campaign?: string;
11
+ term?: string;
12
+ content?: string;
13
+ };
14
+ /**
15
+ * Attribution parameters appended to outbound sharing URLs.
16
+ *
17
+ * Defaults are derived from the V2 Frak context when available:
18
+ * - `utmSource`: `"frak"`
19
+ * - `utmMedium`: `"referral"`
20
+ * - `utmCampaign`: merchantId (`context.m`)
21
+ * - `via`: `"frak"`
22
+ * - `ref`: clientId (`context.c`)
23
+ *
24
+ * Fields explicitly set here override the defaults. Existing params on the
25
+ * base URL are preserved (gap-fill policy) to respect merchant-provided UTMs.
26
+ */
27
+ type AttributionParams = {
28
+ utmSource?: string;
29
+ utmMedium?: string;
30
+ utmCampaign?: string;
31
+ utmContent?: string;
32
+ utmTerm?: string;
33
+ via?: string;
34
+ ref?: string;
35
+ };
36
+ /**
37
+ * Merchant-level attribution defaults.
38
+ *
39
+ * Same shape as {@link AttributionParams} minus `utmContent`, because
40
+ * `utm_content` describes the specific content/creative being shared and is
41
+ * inherently per-call or per-product (never a merchant-wide default).
42
+ *
43
+ * Used as the shape for both:
44
+ * - `FrakWalletSdkConfig.attribution` (SDK-side compile-time defaults)
45
+ * - Backend merchant-config attribution (dashboard-driven defaults)
46
+ */
47
+ type AttributionDefaults = Omit<AttributionParams, "utmContent">;
48
+ type TrackArrivalParams = {
49
+ /** @deprecated V1 legacy — use referrerClientId for v2 contexts */referrerWallet?: Address;
50
+ referrerClientId?: string;
51
+ referrerMerchantId?: string; /** Epoch seconds timestamp from the referral link creation */
52
+ referralTimestamp?: number;
53
+ landingUrl?: string;
54
+ utmParams?: UtmParams;
55
+ };
56
+ type TrackArrivalResult = {
57
+ success: boolean;
58
+ identityGroupId?: string;
59
+ touchpointId?: string;
60
+ error?: string;
61
+ };
62
+ //#endregion
6
63
  //#region src/types/config.d.ts
7
64
  /**
8
65
  * All the currencies available
@@ -31,7 +88,7 @@ type FrakWalletSdkConfig = {
31
88
  /**
32
89
  * Your application name (will be displayed in a few modals and in SSO)
33
90
  */
34
- name: string;
91
+ name?: string;
35
92
  /**
36
93
  * Your merchant ID from the Frak dashboard (UUID format)
37
94
  * Used for referral tracking and analytics
@@ -75,6 +132,20 @@ type FrakWalletSdkConfig = {
75
132
  * @defaultValue window.location.host
76
133
  */
77
134
  domain?: string;
135
+ /**
136
+ * Wait for backend config before rendering components.
137
+ * When true (default), components show a spinner until backend config is resolved.
138
+ * When false, components render immediately with SDK static config / HTML attributes.
139
+ * @defaultValue true
140
+ */
141
+ waitForBackendConfig?: boolean;
142
+ /**
143
+ * Default attribution params (UTM / via / ref) appended to outbound
144
+ * sharing URLs. Per-call `displaySharingPage` overrides win, then backend
145
+ * config, then this SDK-level default. `utm_content` is intentionally
146
+ * excluded — it is per-content/per-product, never a merchant-wide default.
147
+ */
148
+ attribution?: AttributionDefaults;
78
149
  };
79
150
  /**
80
151
  * Custom i18n configuration for the modal
@@ -111,19 +182,125 @@ type FrakWalletSdkConfig = {
111
182
  */
112
183
  type I18nConfig = Record<Language, LocalizedI18nConfig> | LocalizedI18nConfig;
113
184
  /**
114
- * A localized i18n config
185
+ * A localized i18n config (inline objects only — URL-based i18n removed)
115
186
  * @category Config
116
187
  */
117
- type LocalizedI18nConfig = `${string}.css` | {
188
+ type LocalizedI18nConfig = {
118
189
  [key: string]: string;
119
190
  };
120
191
  //#endregion
192
+ //#region src/types/resolvedConfig.d.ts
193
+ /**
194
+ * Response from the merchant resolve endpoint
195
+ * @category Config
196
+ */
197
+ type MerchantConfigResponse = {
198
+ merchantId: string;
199
+ name: string;
200
+ domain: string;
201
+ allowedDomains: string[];
202
+ sdkConfig?: ResolvedSdkConfig;
203
+ };
204
+ /**
205
+ * Resolved placement config from backend
206
+ * Translations already flattened: default + lang-specific merged into one record
207
+ * @category Config
208
+ */
209
+ type ResolvedPlacement = {
210
+ /** Per-component configuration within this placement */components?: {
211
+ buttonShare?: {
212
+ text?: string;
213
+ noRewardText?: string;
214
+ clickAction?: "embedded-wallet" | "share-modal" | "sharing-page";
215
+ useReward?: boolean;
216
+ css?: string;
217
+ };
218
+ buttonWallet?: {
219
+ position?: "right" | "left";
220
+ css?: string;
221
+ };
222
+ openInApp?: {
223
+ text?: string;
224
+ css?: string;
225
+ };
226
+ postPurchase?: {
227
+ badgeText?: string;
228
+ refereeText?: string;
229
+ refereeNoRewardText?: string;
230
+ referrerText?: string;
231
+ referrerNoRewardText?: string;
232
+ ctaText?: string;
233
+ ctaNoRewardText?: string;
234
+ css?: string;
235
+ };
236
+ banner?: {
237
+ referralTitle?: string;
238
+ referralDescription?: string;
239
+ referralCta?: string;
240
+ inappTitle?: string;
241
+ inappDescription?: string;
242
+ inappCta?: string;
243
+ css?: string;
244
+ };
245
+ };
246
+ targetInteraction?: string; /** Already flattened: default + lang-specific merged into one record */
247
+ translations?: Record<string, string>; /** Global placement CSS (applied to modals/listener) */
248
+ css?: string;
249
+ };
250
+ /**
251
+ * Resolved SDK config from backend `/resolve` endpoint
252
+ * Language resolution and translation merging already applied
253
+ * @category Config
254
+ */
255
+ type ResolvedSdkConfig = {
256
+ name?: string;
257
+ logoUrl?: string;
258
+ homepageLink?: string;
259
+ currency?: Currency;
260
+ lang?: Language; /** When true, all SDK components should be hidden */
261
+ hidden?: boolean;
262
+ css?: string;
263
+ translations?: Record<string, string>;
264
+ placements?: Record<string, ResolvedPlacement>; /** Global component defaults (used when no placement override exists) */
265
+ components?: ResolvedPlacement["components"];
266
+ /**
267
+ * Default attribution params applied when building outbound sharing URLs.
268
+ * Per-call overrides win over these backend defaults; `utm_content` is
269
+ * intentionally excluded (per-content/per-product, never a merchant default).
270
+ */
271
+ attribution?: AttributionDefaults;
272
+ };
273
+ /**
274
+ * Internal SDK config store state
275
+ * Merged config: backend > SDK static > defaults
276
+ * Components subscribe to this reactively
277
+ * @category Config
278
+ */
279
+ type SdkResolvedConfig = {
280
+ /** Whether the backend config has been resolved */isResolved: boolean; /** Merchant ID from resolution */
281
+ merchantId: string; /** Domain returned by the resolve endpoint */
282
+ domain?: string; /** Domains allowed for this merchant (used by iframe trust check) */
283
+ allowedDomains?: string[]; /** Whether the resolve returned a backend sdkConfig object */
284
+ hasRawSdkConfig?: boolean; /** Merged metadata fields */
285
+ name?: string;
286
+ logoUrl?: string;
287
+ homepageLink?: string;
288
+ lang?: Language;
289
+ currency?: Currency; /** When true, all SDK components should be hidden */
290
+ hidden?: boolean; /** Global CSS from backend config (passed to iframe) */
291
+ css?: string; /** Global translations (for reference / component fallback) */
292
+ translations?: Record<string, string>; /** Named placements (keyed by placement ID) */
293
+ placements?: Record<string, ResolvedPlacement>; /** Global component defaults (fallback for placement-level overrides) */
294
+ components?: ResolvedPlacement["components"]; /** Merged attribution defaults: backend > SDK static config */
295
+ attribution?: AttributionDefaults;
296
+ };
297
+ //#endregion
121
298
  //#region src/types/lifecycle/client.d.ts
122
299
  /**
123
300
  * Event related to the iframe lifecycle
124
301
  * @ignore
125
302
  */
126
- type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | HandshakeResponse | SsoRedirectCompleteEvent | DeepLinkFailedEvent;
303
+ type ClientLifecycleEvent = CustomCssEvent | CustomI18nEvent | RestoreBackupEvent | HearbeatEvent | SsoRedirectCompleteEvent | DeepLinkFailedEvent | ResolvedConfigEvent;
127
304
  type CustomCssEvent = {
128
305
  clientLifecycle: "modal-css";
129
306
  data: {
@@ -146,30 +323,6 @@ type HearbeatEvent = {
146
323
  clientLifecycle: "heartbeat";
147
324
  data?: never;
148
325
  };
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
326
  type SsoRedirectCompleteEvent = {
174
327
  clientLifecycle: "sso-redirect-complete";
175
328
  data: {
@@ -182,6 +335,28 @@ type DeepLinkFailedEvent = {
182
335
  originalUrl: string;
183
336
  };
184
337
  };
338
+ type ResolvedConfigEvent = {
339
+ clientLifecycle: "resolved-config";
340
+ data: {
341
+ merchantId: string; /** The domain the backend resolved this config for */
342
+ domain: string; /** All domains registered for this merchant (for domain proof) */
343
+ allowedDomains: string[]; /** Full URL of the parent page (for interaction tracking) */
344
+ sourceUrl: string;
345
+ /**
346
+ * Pending merge token extracted from URL (?fmt= parameter).
347
+ * When present, listener should execute identity merge in background.
348
+ */
349
+ pendingMergeToken?: string;
350
+ /**
351
+ * Persistent per-origin anonymous id generated on the partner site
352
+ * (SDK-side localStorage). Propagated here so the listener can
353
+ * set it as an OpenPanel global property and stitch SDK events
354
+ * with listener events in the same funnel.
355
+ */
356
+ sdkAnonymousId?: string;
357
+ sdkConfig?: ResolvedSdkConfig;
358
+ };
359
+ };
185
360
  //#endregion
186
361
  //#region src/types/lifecycle/iframe.d.ts
187
362
  /**
@@ -191,19 +366,13 @@ type DeepLinkFailedEvent = {
191
366
  type IFrameLifecycleEvent = {
192
367
  iframeLifecycle: "connected" | "show" | "hide" | "remove-backup";
193
368
  data?: never;
194
- } | DoBackupEvent | HandshakeRequestEvent | RedirectRequestEvent;
369
+ } | DoBackupEvent | RedirectRequestEvent;
195
370
  type DoBackupEvent = {
196
371
  iframeLifecycle: "do-backup";
197
372
  data: {
198
373
  backup?: string;
199
374
  };
200
375
  };
201
- type HandshakeRequestEvent = {
202
- iframeLifecycle: "handshake";
203
- data: {
204
- token: string;
205
- };
206
- };
207
376
  type RedirectRequestEvent = {
208
377
  iframeLifecycle: "redirect";
209
378
  data: {
@@ -218,6 +387,13 @@ type RedirectRequestEvent = {
218
387
  * Used when redirecting out of social browsers to preserve identity across contexts
219
388
  */
220
389
  mergeToken?: string;
390
+ /**
391
+ * When true, open the URL in a new tab via window.open(_blank)
392
+ * instead of navigating the current page.
393
+ * Requires the postMessage to include user activation delegation
394
+ * (includeUserActivation: true) so Safari allows the popup.
395
+ */
396
+ openInNewTab?: boolean;
221
397
  };
222
398
  };
223
399
  //#endregion
@@ -534,6 +710,102 @@ type DisplayModalParamsType<T extends ModalStepTypes[]> = {
534
710
  metadata?: ModalRpcMetadata;
535
711
  };
536
712
  //#endregion
713
+ //#region src/types/rpc/displaySharingPage.d.ts
714
+ /**
715
+ * Product information to display on the sharing page
716
+ * @group Sharing Page
717
+ */
718
+ type SharingPageProduct = {
719
+ /**
720
+ * The product title / name
721
+ */
722
+ title: string;
723
+ /**
724
+ * Optional product image URL
725
+ */
726
+ imageUrl?: string;
727
+ /**
728
+ * Optional product-specific sharing link
729
+ * When provided and the product is selected, this link is used instead of the default sharing link
730
+ */
731
+ link?: string;
732
+ /**
733
+ * Optional `utm_content` value to apply when this product is selected.
734
+ * Falls back to the page-level `attribution.utmContent` when omitted.
735
+ */
736
+ utmContent?: string;
737
+ };
738
+ /**
739
+ * Parameters to display the sharing page
740
+ * @group Sharing Page
741
+ * @group RPC Schema
742
+ */
743
+ type DisplaySharingPageParamsType = {
744
+ /**
745
+ * Products to showcase on the sharing page
746
+ * If provided, they will be displayed in a product card section
747
+ */
748
+ products?: SharingPageProduct[];
749
+ /**
750
+ * Optional link override for sharing
751
+ * If not provided, the sharing link will be generated from the current page URL + merchant context
752
+ */
753
+ link?: string;
754
+ /**
755
+ * Optional attribution overrides for the outbound sharing URL.
756
+ *
757
+ * When provided (even as an empty object), Frak adds standard affiliation
758
+ * params (`utm_source=frak`, `utm_medium=referral`, `utm_campaign=<merchantId>`,
759
+ * `ref=<clientId>`, `via=frak`) alongside `fCtx`. Existing UTMs on the base
760
+ * URL are preserved (gap-fill). Set this to `null` to disable attribution
761
+ * params entirely (only `fCtx` is added).
762
+ *
763
+ * @default {} — defaults applied
764
+ */
765
+ attribution?: AttributionParams | null;
766
+ /**
767
+ * Optional metadata overrides for the sharing page
768
+ */
769
+ metadata?: {
770
+ /**
771
+ * Logo override for the sharing page header
772
+ */
773
+ logo?: string;
774
+ /**
775
+ * Link to the homepage of the calling website
776
+ */
777
+ homepageLink?: string;
778
+ /**
779
+ * The target interaction behind this sharing page
780
+ */
781
+ targetInteraction?: InteractionTypeKey;
782
+ /**
783
+ * i18n overrides for the sharing page
784
+ */
785
+ i18n?: I18nConfig;
786
+ };
787
+ };
788
+ /**
789
+ * Result of the sharing page display
790
+ * @group Sharing Page
791
+ * @group RPC Schema
792
+ */
793
+ type DisplaySharingPageResultType = {
794
+ /**
795
+ * The action the user took
796
+ * - "shared": User used the native share dialog
797
+ * - "copied": User copied the link to clipboard
798
+ * - "dismissed": User dismissed the sharing page without acting
799
+ */
800
+ action: "shared" | "copied" | "dismissed";
801
+ /**
802
+ * The install URL for the Frak app
803
+ * Can be used as a fallback to redirect the user to the install page
804
+ * from the merchant's top-level page (e.g. via `window.location.href`)
805
+ */
806
+ installUrl?: string;
807
+ };
808
+ //#endregion
537
809
  //#region src/types/rpc/embedded/loggedIn.d.ts
538
810
  /**
539
811
  * The different type of action we can have on the embedded view (once the user is logged in)
@@ -691,7 +963,9 @@ type SendInteractionParamsType = {
691
963
  utmTerm?: string;
692
964
  utmContent?: string;
693
965
  } | {
694
- type: "sharing";
966
+ type: "sharing"; /** Epoch seconds timestamp matching the V2 context `t` field embedded in the referral link URL, used for backend correlation */
967
+ sharingTimestamp?: number; /** Merchant order ID linking this sharing event to a purchase (stays server-side, never in URL) */
968
+ purchaseId?: string;
695
969
  } | {
696
970
  type: "custom";
697
971
  customType: string;
@@ -769,6 +1043,28 @@ type GetMerchantInformationReturnType = {
769
1043
  }[];
770
1044
  };
771
1045
  //#endregion
1046
+ //#region src/types/rpc/userReferralStatus.d.ts
1047
+ /**
1048
+ * User referral status returned by `frak_getUserReferralStatus`.
1049
+ *
1050
+ * Generic referral context for the current user on a merchant.
1051
+ * Used by components like `<frak-post-purchase>` and `<frak-referred-banner>`
1052
+ * to adapt their display based on the user's referral relationship.
1053
+ *
1054
+ * Returns `null` when the user's identity cannot be resolved
1055
+ * (e.g. no clientId and no wallet session).
1056
+ *
1057
+ * @group RPC Schema
1058
+ */
1059
+ type UserReferralStatusType = {
1060
+ /**
1061
+ * Whether the user was referred to this merchant by someone else.
1062
+ *
1063
+ * `true` means a referral link exists where this user is the referee.
1064
+ */
1065
+ isReferred: boolean;
1066
+ };
1067
+ //#endregion
772
1068
  //#region src/types/rpc/walletStatus.d.ts
773
1069
  /**
774
1070
  * RPC Response for the method `frak_listenToWalletStatus`
@@ -815,7 +1111,7 @@ type WalletNotConnected = {
815
1111
  * - Response Type: stream (emits updates when wallet status changes)
816
1112
  *
817
1113
  * #### frak_displayModal
818
- * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
1114
+ * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
819
1115
  * - Returns: {@link ModalRpcStepsResultType}
820
1116
  * - Response Type: promise (one-shot)
821
1117
  *
@@ -830,9 +1126,14 @@ type WalletNotConnected = {
830
1126
  * - Response Type: promise (one-shot)
831
1127
  *
832
1128
  * #### frak_displayEmbeddedWallet
833
- * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
1129
+ * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
834
1130
  * - Returns: {@link DisplayEmbeddedWalletResultType}
835
1131
  * - Response Type: promise (one-shot)
1132
+ *
1133
+ * #### frak_displaySharingPage
1134
+ * - Params: [request: {@link DisplaySharingPageParamsType}, configMetadata: {@link FrakWalletSdkConfig}["metadata"], placement?: string]
1135
+ * - Returns: {@link DisplaySharingPageResultType}
1136
+ * - Response Type: promise (one-shot)
836
1137
  */
837
1138
  type IFrameRpcSchema = [
838
1139
  /**
@@ -850,7 +1151,7 @@ type IFrameRpcSchema = [
850
1151
  */
851
1152
  {
852
1153
  Method: "frak_displayModal";
853
- Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"]];
1154
+ Parameters: [requests: ModalRpcStepsInput, metadata: ModalRpcMetadata | undefined, configMetadata: FrakWalletSdkConfig["metadata"], placement?: string];
854
1155
  ReturnType: ModalRpcStepsResultType;
855
1156
  },
856
1157
  /**
@@ -860,7 +1161,7 @@ type IFrameRpcSchema = [
860
1161
  */
861
1162
  {
862
1163
  Method: "frak_prepareSso";
863
- Parameters: [params: PrepareSsoParamsType, name: string, customCss?: string];
1164
+ Parameters: [params: PrepareSsoParamsType, name?: string, customCss?: string];
864
1165
  ReturnType: PrepareSsoReturnType;
865
1166
  },
866
1167
  /**
@@ -871,7 +1172,7 @@ type IFrameRpcSchema = [
871
1172
  */
872
1173
  {
873
1174
  Method: "frak_openSso";
874
- Parameters: [params: OpenSsoParamsType, name: string, customCss?: string];
1175
+ Parameters: [params: OpenSsoParamsType, name?: string, customCss?: string];
875
1176
  ReturnType: OpenSsoReturnType;
876
1177
  },
877
1178
  /**
@@ -892,14 +1193,14 @@ type IFrameRpcSchema = [
892
1193
  */
893
1194
  {
894
1195
  Method: "frak_displayEmbeddedWallet";
895
- Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"]];
1196
+ Parameters: [request: DisplayEmbeddedWalletParamsType, metadata: FrakWalletSdkConfig["metadata"], placement?: string];
896
1197
  ReturnType: DisplayEmbeddedWalletResultType;
897
1198
  },
898
1199
  /**
899
1200
  * Method to send interactions (arrival, sharing, custom events)
900
1201
  * Fire-and-forget method - no return value expected
901
1202
  * merchantId is resolved from context
902
- * clientId is passed via metadata as safeguard against handshake race condition
1203
+ * clientId is passed via metadata as safeguard against race conditions
903
1204
  */
904
1205
  {
905
1206
  Method: "frak_sendInteraction";
@@ -907,6 +1208,39 @@ type IFrameRpcSchema = [
907
1208
  clientId?: string;
908
1209
  }];
909
1210
  ReturnType: undefined;
1211
+ },
1212
+ /**
1213
+ * Method to get the current user's referral status on this merchant.
1214
+ * Returns whether the user was referred (has a referral link as referee).
1215
+ * Returns null when the user's identity cannot be resolved.
1216
+ * This is a one-shot request.
1217
+ */
1218
+ {
1219
+ Method: "frak_getUserReferralStatus";
1220
+ Parameters?: undefined;
1221
+ ReturnType: UserReferralStatusType | null;
1222
+ },
1223
+ /**
1224
+ * Method to display a sharing page with product info and sharing buttons
1225
+ * Resolves on first user action (share/copy) but the page stays visible
1226
+ * This is a one-shot request
1227
+ */
1228
+ {
1229
+ Method: "frak_displaySharingPage";
1230
+ Parameters: [request: DisplaySharingPageParamsType, configMetadata: FrakWalletSdkConfig["metadata"], placement?: string];
1231
+ ReturnType: DisplaySharingPageResultType;
1232
+ },
1233
+ /**
1234
+ * Method to get a merge token for the current anonymous identity.
1235
+ * Used by in-app browser redirect flows to preserve identity
1236
+ * when switching from a WebView to the system browser.
1237
+ * Returns the merge token string, or null if unavailable.
1238
+ * This is a one-shot request.
1239
+ */
1240
+ {
1241
+ Method: "frak_getMergeToken";
1242
+ Parameters?: undefined;
1243
+ ReturnType: string | null;
910
1244
  }];
911
1245
  //#endregion
912
1246
  //#region src/types/transport.d.ts
@@ -962,15 +1296,26 @@ type FrakContextV1 = {
962
1296
  /** Referrer wallet address */r: Address;
963
1297
  };
964
1298
  /**
965
- * V2 Frak Context — anonymous-first referral context.
966
- * Contains the sharer's clientId, merchantId, and link creation timestamp.
1299
+ * V2 Frak Context — anonymous-first referral context with optional wallet.
1300
+ *
1301
+ * Carries merchant context (`m`) and creation timestamp (`t`) unconditionally.
1302
+ * Identifies the sharer via either the anonymous clientId (`c`) or, when the
1303
+ * sharer is authenticated, the stronger wallet identifier (`w`). A valid V2
1304
+ * context MUST contain at least one of `c` or `w`; both may be present when
1305
+ * a logged-in user shares a link (best attribution signal).
1306
+ *
1307
+ * `w` takes precedence as the source of truth because the wallet is bound to
1308
+ * the user's WebAuthn credential, survives localStorage clears, and is global
1309
+ * across merchants — unlike `c`, which is a per-browser UUID.
1310
+ *
967
1311
  * @ignore
968
1312
  */
969
1313
  type FrakContextV2 = {
970
- /** Version discriminator */v: 2; /** Sharer's anonymous clientId (UUID from localStorage) */
971
- c: string; /** Merchant ID (UUID) */
1314
+ /** Version discriminator */v: 2; /** Merchant ID (UUID) */
972
1315
  m: string; /** Link creation timestamp (epoch seconds) */
973
- t: number;
1316
+ t: number; /** Sharer's anonymous clientId (UUID from localStorage). Optional when `w` is provided. */
1317
+ c?: string; /** Sharer's wallet address. Preferred source of truth when the sharer is authenticated. Optional when `c` is provided. */
1318
+ w?: Address;
974
1319
  };
975
1320
  /**
976
1321
  * The current Frak Context — union of all versions.
@@ -1052,4 +1397,4 @@ declare const ssoPopupName = "frak-sso";
1052
1397
  */
1053
1398
  declare function openSso(client: FrakClient, args: OpenSsoParamsType): Promise<OpenSsoReturnType>;
1054
1399
  //#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 };
1400
+ export { ResolvedPlacement as $, ModalRpcMetadata as A, LoginModalStepType as B, EmbeddedViewActionReferred as C, DisplaySharingPageResultType as D, DisplaySharingPageParamsType as E, SendTransactionReturnType as F, SsoMetadata as G, OpenSsoReturnType as H, SendTransactionTxType as I, ModalStepMetadata as J, FinalActionType as K, SiweAuthenticateModalStepType as L, ModalRpcStepsResultType as M, ModalStepTypes as N, SharingPageProduct as O, SendTransactionModalStepType as P, MerchantConfigResponse as Q, SiweAuthenticateReturnType as R, LoggedOutEmbeddedView as S, LoggedInEmbeddedView as T, PrepareSsoParamsType as U, OpenSsoParamsType as V, PrepareSsoReturnType as W, IFrameLifecycleEvent as X, InteractionTypeKey as Y, ClientLifecycleEvent as Z, RewardTier as _, FrakContextV1 as a, Language as at, DisplayEmbeddedWalletParamsType as b, isV2Context as c, AttributionParams as ct, IFrameTransport as d, UtmParams as dt, ResolvedSdkConfig as et, IFrameRpcSchema as f, GetMerchantInformationReturnType as g, EstimatedReward as h, FrakContext as i, I18nConfig as it, ModalRpcStepsInput as j, DisplayModalParamsType as k, FrakClient as l, TrackArrivalParams as lt, UserReferralStatusType as m, ssoPopupFeatures as n, Currency as nt, FrakContextV2 as o, LocalizedI18nConfig as ot, WalletStatusReturnType as p, FinalModalStepType as q, ssoPopupName as r, FrakWalletSdkConfig as rt, isV1Context as s, AttributionDefaults as st, openSso as t, SdkResolvedConfig as tt, FrakLifecycleEvent as u, TrackArrivalResult as ut, TokenAmountType as v, EmbeddedViewActionSharing as w, DisplayEmbeddedWalletResultType as x, SendInteractionParamsType as y, SiweAuthenticationParams as z };
@@ -0,0 +1 @@
1
+ let e=require(`viem`),t=require(`@frak-labs/frame-connector`);const n=`frak-client-id`;function r(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function i(){if(typeof window>`u`||!window.localStorage)return console.warn(`[Frak SDK] No Window / localStorage available to save the clientId`),r();let e=localStorage.getItem(n);return e||(e=r(),localStorage.setItem(n,e)),e}function a({domain:t}={}){return(0,e.keccak256)((0,e.toHex)((t??window.location.host).replace(`www.`,``)))}function o(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join(``)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function s(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(e.length+(t===0?0:4-t),`=`)),e=>e.charCodeAt(0))}function c(e){return o((0,t.jsonEncode)(e))}function l(e,t,n,r,i,a){let o=c(u({redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,merchantId:n,metadata:{name:r,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink},clientId:i})),s=new URL(e);return s.pathname=`/sso`,s.searchParams.set(`p`,o),s.toString()}function u(e){return{r:e.redirectUrl,cId:e.clientId,d:e.directExit,l:e.lang,m:e.merchantId,md:{n:e.metadata?.name,css:e.metadata?.css,l:e.metadata?.logoUrl,h:e.metadata?.homepageLink}}}const d=`menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800`,f=`frak-sso`;async function p(e,t){let{metadata:n,customizations:r,walletUrl:o}=e.config;if(t.openInSameWindow??!!t.redirectUrl)return await e.request({method:`frak_openSso`,params:[t,n.name,r?.css]});let s=t.ssoPopupUrl??l(o??`https://wallet.frak.id`,t,a(),n.name,i(),r?.css),c=window.open(s,f,d);if(!c)throw Error(`Popup was blocked. Please allow popups for this site.`);return c.focus(),await e.request({method:`frak_openSso`,params:[t,n.name,r?.css]})??{}}function ee(e,t,n){if(!e){console.debug(`[Frak] No client provided, skipping event tracking`);return}try{e.openPanel?.track(t,n)}catch(e){console.debug(`[Frak] Failed to track event:`,t,e)}}const m=`https://backend.frak.id`;function te(e){return e.includes(`localhost:3000`)||e.includes(`localhost:3010`)}function h(e){return te(e)?`https://localhost:3030`:e.includes(`wallet-dev.frak.id`)||e.includes(`wallet.gcp-dev.frak.id`)?`https://backend.gcp-dev.frak.id`:m}function g(e){if(e)return h(e);if(typeof window<`u`){let e=window.FrakSetup?.client?.config?.walletUrl;if(e)return h(e)}return m}var _=class extends Map{maxSize;constructor(e){super(),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}};const v=new _(1024),y=new _(1024),b=3e4,x=new _(1024);async function S(e,{cacheKey:t,cacheTime:n=b}){if(n>0){let e=y.get(t);if(e&&Date.now()-e.created<n)return e.data}let r=x.get(t);if(r&&Date.now()-r<1e3)throw Error(`Cache: ${t} recently failed, backing off`);let i=v.get(t);i||(i=e(),v.set(t,i));try{let e=await i;return y.set(t,{data:e,created:Date.now()}),x.delete(t),e}catch(e){throw x.set(t,Date.now()),e}finally{v.delete(t)}}function C(e){return{clear:()=>{v.delete(e),y.delete(e)},has:(t=b)=>{let n=y.get(e);return n?Date.now()-n.created<t:!1}}}function w(){v.clear(),y.clear(),x.clear()}function T(e){return(0,t.jsonDecode)(s(e))}function E(e){return`r`in e&&!(`v`in e)}function D(e){return`v`in e&&e.v===2}const O=`fCtx`;function k(t){if(t)try{return D(t)?!t.m||!t.t||!t.c&&!t.w?void 0:c({v:2,m:t.m,t:t.t,...t.c?{c:t.c}:{},...t.w?{w:t.w}:{}}):o((0,e.hexToBytes)(t.r))}catch(e){console.error(`Error compressing Frak context`,{e,context:t})}}function A(t){if(!(!t||t.length===0))try{let n=T(t);if(n&&typeof n==`object`&&n.v===2)return!n.m||!n.t||!n.c&&!n.w?void 0:{v:2,m:n.m,t:n.t,...n.c?{c:n.c}:{},...n.w?{w:n.w}:{}};let r=(0,e.bytesToHex)(s(t),{size:20});if((0,e.isAddress)(r))return{r}}catch(e){console.error(`Error decompressing Frak context`,{e,context:t})}}function ne({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(O);return t?A(t):null}const j=`frak`;function M(e,t){let n=D(e);return{utm_source:t.utmSource??j,utm_medium:t.utmMedium??`referral`,utm_campaign:t.utmCampaign??(n?e.m:void 0),utm_content:t.utmContent,utm_term:t.utmTerm,via:t.via??j,ref:t.ref??(n?e.c:void 0)}}function N(e,t,n){let r=M(t,n??{});for(let[t,n]of Object.entries(r))n===void 0||n===``||e.searchParams.has(t)||e.searchParams.set(t,n)}function P({url:e,context:t,attribution:n}){if(!e)return null;let r=k(t);if(!r)return null;let i=new URL(e);return i.searchParams.set(O,r),N(i,t,n),i.toString()}function F(e){let t=new URL(e);return t.searchParams.delete(O),t.toString()}function I({url:e,context:t}){if(!window.location?.href||typeof window>`u`){console.error(`No window found, can't update context`);return}let n=e??window.location.href,r;r=t===null?F(n):P({url:n,context:t}),r&&window.history.replaceState(null,``,r.toString())}const L={compress:k,decompress:A,parse:ne,update:P,remove:F,replaceUrl:I},R=`__frakSdkConfig`,z=`frak-config-cache`,B=`frak-merchant-id`,V={key:z},H=typeof window<`u`;function U(){return{isResolved:!1,merchantId:``}}let W=null;function G(){if(!H)return null;try{let e=localStorage.getItem(V.key);if(!e)return null;let t=JSON.parse(e);return t.config?.isResolved?(W=t,t):null}catch{return null}}function K(){return(W??G())?.config}function q(){let e=W??G();return e?Date.now()-e.timestamp<3e4:!1}function J(e){if(!(!H||!e.isResolved))try{let t={config:e,timestamp:Date.now()};localStorage.setItem(V.key,JSON.stringify(t)),W=t}catch{}}function Y(){if(H){W=null;try{localStorage.removeItem(V.key)}catch{}}}function X(){H&&(window[R]||(window[R]=K()??U()))}X();function Z(){return H?window[R]??U():U()}function Q(e){H&&window.dispatchEvent(new CustomEvent(`frak:config`,{detail:e}))}function re(e){return e??(H?window.location.hostname:``)}async function ie(e,t,n){try{let r=g(t),i=n?`&lang=${encodeURIComponent(n)}`:``,a=await fetch(`${r}/user/merchant/resolve?domain=${encodeURIComponent(e)}${i}`);if(!a.ok){console.warn(`[Frak SDK] Merchant lookup failed for domain ${e}: ${a.status}`);return}let o=await a.json();if(H)try{sessionStorage.setItem(B,o.merchantId)}catch{}return o}catch(e){console.warn(`[Frak SDK] Failed to fetch merchant config:`,e);return}}const $={getConfig:Z,get isResolved(){return Z().isResolved},get isCacheFresh(){return q()},setCacheScope(e,t){V.key=`${z}:${`${e}:${t??``}`}`,W=null},setConfig(e){if(H&&(window[R]=e),J(e),Q(e),H&&e.merchantId)try{sessionStorage.setItem(B,e.merchantId)}catch{}},reset(){let e=K()??U();H&&(window[R]=e),Q(e)},clearCache(){if(Y(),w(),H)try{sessionStorage.removeItem(B)}catch{}},resolve(e,t,n){let r=re(e);return r?S(async()=>{let e=await ie(r,t,n);if(!e)throw Error(`Config resolution returned empty`);return e},{cacheKey:`sdkConfig:${r}:${n??``}`,cacheTime:1/0}).catch(()=>void 0):Promise.resolve(void 0)},getMerchantId(){let e=Z();if(e.isResolved&&e.merchantId)return e.merchantId;if(H)try{return sessionStorage.getItem(B)??void 0}catch{}},async resolveMerchantId(e,t){return $.getMerchantId()||(await $.resolve(e,t))?.merchantId}};Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return ee}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return i}});
@@ -0,0 +1 @@
1
+ import{bytesToHex as e,hexToBytes as t,isAddress as n,keccak256 as r,toHex as i}from"viem";import{jsonDecode as a,jsonEncode as o}from"@frak-labs/frame-connector";const s=`frak-client-id`;function c(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function l(){if(typeof window>`u`||!window.localStorage)return console.warn(`[Frak SDK] No Window / localStorage available to save the clientId`),c();let e=localStorage.getItem(s);return e||(e=c(),localStorage.setItem(s,e)),e}function u({domain:e}={}){return r(i((e??window.location.host).replace(`www.`,``)))}function d(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join(``)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function f(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(e.length+(t===0?0:4-t),`=`)),e=>e.charCodeAt(0))}function p(e){return d(o(e))}function m(e,t,n,r,i,a){let o=p(ee({redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,merchantId:n,metadata:{name:r,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink},clientId:i})),s=new URL(e);return s.pathname=`/sso`,s.searchParams.set(`p`,o),s.toString()}function ee(e){return{r:e.redirectUrl,cId:e.clientId,d:e.directExit,l:e.lang,m:e.merchantId,md:{n:e.metadata?.name,css:e.metadata?.css,l:e.metadata?.logoUrl,h:e.metadata?.homepageLink}}}const h=`menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800`,g=`frak-sso`;async function _(e,t){let{metadata:n,customizations:r,walletUrl:i}=e.config;if(t.openInSameWindow??!!t.redirectUrl)return await e.request({method:`frak_openSso`,params:[t,n.name,r?.css]});let a=t.ssoPopupUrl??m(i??`https://wallet.frak.id`,t,u(),n.name,l(),r?.css),o=window.open(a,g,h);if(!o)throw Error(`Popup was blocked. Please allow popups for this site.`);return o.focus(),await e.request({method:`frak_openSso`,params:[t,n.name,r?.css]})??{}}function v(e,t,n){if(!e){console.debug(`[Frak] No client provided, skipping event tracking`);return}try{e.openPanel?.track(t,n)}catch(e){console.debug(`[Frak] Failed to track event:`,t,e)}}const y=`https://backend.frak.id`;function b(e){return e.includes(`localhost:3000`)||e.includes(`localhost:3010`)}function x(e){return b(e)?`https://localhost:3030`:e.includes(`wallet-dev.frak.id`)||e.includes(`wallet.gcp-dev.frak.id`)?`https://backend.gcp-dev.frak.id`:y}function S(e){if(e)return x(e);if(typeof window<`u`){let e=window.FrakSetup?.client?.config?.walletUrl;if(e)return x(e)}return y}var C=class extends Map{maxSize;constructor(e){super(),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}};const w=new C(1024),T=new C(1024),E=3e4,D=new C(1024);async function O(e,{cacheKey:t,cacheTime:n=E}){if(n>0){let e=T.get(t);if(e&&Date.now()-e.created<n)return e.data}let r=D.get(t);if(r&&Date.now()-r<1e3)throw Error(`Cache: ${t} recently failed, backing off`);let i=w.get(t);i||(i=e(),w.set(t,i));try{let e=await i;return T.set(t,{data:e,created:Date.now()}),D.delete(t),e}catch(e){throw D.set(t,Date.now()),e}finally{w.delete(t)}}function k(e){return{clear:()=>{w.delete(e),T.delete(e)},has:(t=E)=>{let n=T.get(e);return n?Date.now()-n.created<t:!1}}}function A(){w.clear(),T.clear(),D.clear()}function j(e){return a(f(e))}function M(e){return`r`in e&&!(`v`in e)}function N(e){return`v`in e&&e.v===2}const P=`fCtx`;function F(e){if(e)try{return N(e)?!e.m||!e.t||!e.c&&!e.w?void 0:p({v:2,m:e.m,t:e.t,...e.c?{c:e.c}:{},...e.w?{w:e.w}:{}}):d(t(e.r))}catch(t){console.error(`Error compressing Frak context`,{e:t,context:e})}}function I(t){if(!(!t||t.length===0))try{let r=j(t);if(r&&typeof r==`object`&&r.v===2)return!r.m||!r.t||!r.c&&!r.w?void 0:{v:2,m:r.m,t:r.t,...r.c?{c:r.c}:{},...r.w?{w:r.w}:{}};let i=e(f(t),{size:20});if(n(i))return{r:i}}catch(e){console.error(`Error decompressing Frak context`,{e,context:t})}}function te({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(P);return t?I(t):null}const L=`frak`;function R(e,t){let n=N(e);return{utm_source:t.utmSource??L,utm_medium:t.utmMedium??`referral`,utm_campaign:t.utmCampaign??(n?e.m:void 0),utm_content:t.utmContent,utm_term:t.utmTerm,via:t.via??L,ref:t.ref??(n?e.c:void 0)}}function z(e,t,n){let r=R(t,n??{});for(let[t,n]of Object.entries(r))n===void 0||n===``||e.searchParams.has(t)||e.searchParams.set(t,n)}function B({url:e,context:t,attribution:n}){if(!e)return null;let r=F(t);if(!r)return null;let i=new URL(e);return i.searchParams.set(P,r),z(i,t,n),i.toString()}function V(e){let t=new URL(e);return t.searchParams.delete(P),t.toString()}function ne({url:e,context:t}){if(!window.location?.href||typeof window>`u`){console.error(`No window found, can't update context`);return}let n=e??window.location.href,r;r=t===null?V(n):B({url:n,context:t}),r&&window.history.replaceState(null,``,r.toString())}const re={compress:F,decompress:I,parse:te,update:B,remove:V,replaceUrl:ne},H=`__frakSdkConfig`,U=`frak-config-cache`,W=`frak-merchant-id`,G={key:U},K=typeof window<`u`;function q(){return{isResolved:!1,merchantId:``}}let J=null;function Y(){if(!K)return null;try{let e=localStorage.getItem(G.key);if(!e)return null;let t=JSON.parse(e);return t.config?.isResolved?(J=t,t):null}catch{return null}}function X(){return(J??Y())?.config}function ie(){let e=J??Y();return e?Date.now()-e.timestamp<3e4:!1}function ae(e){if(!(!K||!e.isResolved))try{let t={config:e,timestamp:Date.now()};localStorage.setItem(G.key,JSON.stringify(t)),J=t}catch{}}function oe(){if(K){J=null;try{localStorage.removeItem(G.key)}catch{}}}function se(){K&&(window[H]||(window[H]=X()??q()))}se();function Z(){return K?window[H]??q():q()}function Q(e){K&&window.dispatchEvent(new CustomEvent(`frak:config`,{detail:e}))}function ce(e){return e??(K?window.location.hostname:``)}async function le(e,t,n){try{let r=S(t),i=n?`&lang=${encodeURIComponent(n)}`:``,a=await fetch(`${r}/user/merchant/resolve?domain=${encodeURIComponent(e)}${i}`);if(!a.ok){console.warn(`[Frak SDK] Merchant lookup failed for domain ${e}: ${a.status}`);return}let o=await a.json();if(K)try{sessionStorage.setItem(W,o.merchantId)}catch{}return o}catch(e){console.warn(`[Frak SDK] Failed to fetch merchant config:`,e);return}}const $={getConfig:Z,get isResolved(){return Z().isResolved},get isCacheFresh(){return ie()},setCacheScope(e,t){G.key=`${U}:${`${e}:${t??``}`}`,J=null},setConfig(e){if(K&&(window[H]=e),ae(e),Q(e),K&&e.merchantId)try{sessionStorage.setItem(W,e.merchantId)}catch{}},reset(){let e=X()??q();K&&(window[H]=e),Q(e)},clearCache(){if(oe(),A(),K)try{sessionStorage.removeItem(W)}catch{}},resolve(e,t,n){let r=ce(e);return r?O(async()=>{let e=await le(r,t,n);if(!e)throw Error(`Config resolution returned empty`);return e},{cacheKey:`sdkConfig:${r}:${n??``}`,cacheTime:1/0}).catch(()=>void 0):Promise.resolve(void 0)},getMerchantId(){let e=Z();if(e.isResolved&&e.merchantId)return e.merchantId;if(K)try{return sessionStorage.getItem(W)??void 0}catch{}},async resolveMerchantId(e,t){return $.getMerchantId()||(await $.resolve(e,t))?.merchantId}};export{d as _,j as a,O as c,_ as d,h as f,f as g,p as h,N as i,S as l,m,re as n,A as o,g as p,M as r,k as s,$ as t,v as u,u as v,l as y};