@frak-labs/core-sdk 0.1.1 → 0.2.0-beta.7898df5b

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 (130) hide show
  1. package/README.md +58 -0
  2. package/cdn/bundle.js +14 -0
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.d.cts +3 -3
  5. package/dist/actions.d.ts +3 -3
  6. package/dist/actions.js +1 -1
  7. package/dist/bundle.cjs +1 -1
  8. package/dist/bundle.d.cts +4 -6
  9. package/dist/bundle.d.ts +4 -6
  10. package/dist/bundle.js +1 -1
  11. package/dist/computeLegacyProductId-CCAZvLa5.d.cts +537 -0
  12. package/dist/computeLegacyProductId-b5cUWdAm.d.ts +537 -0
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +3 -4
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.js +1 -1
  17. package/dist/{openSso-D--Airj6.d.cts → openSso-B0g7-807.d.cts} +173 -136
  18. package/dist/{openSso-DsKJ4y0j.d.ts → openSso-CMzwvaCa.d.ts} +173 -136
  19. package/dist/setupClient-BICl5fdX.js +13 -0
  20. package/dist/setupClient-nl8Dhh4V.cjs +13 -0
  21. package/dist/siweAuthenticate-BWmI2_TN.cjs +1 -0
  22. package/dist/{index-d8xS4ryI.d.ts → siweAuthenticate-CVigMOxz.d.cts} +113 -92
  23. package/dist/{index-C6FxkWPC.d.cts → siweAuthenticate-CnCZ7mok.d.ts} +113 -92
  24. package/dist/siweAuthenticate-zczqxm0a.js +1 -0
  25. package/dist/trackEvent-CeLFVzZn.js +1 -0
  26. package/dist/trackEvent-Ew5r5zfI.cjs +1 -0
  27. package/package.json +11 -22
  28. package/src/actions/displayEmbeddedWallet.ts +1 -0
  29. package/src/actions/displayModal.test.ts +12 -11
  30. package/src/actions/displayModal.ts +7 -18
  31. package/src/actions/ensureIdentity.ts +68 -0
  32. package/src/actions/{getProductInformation.test.ts → getMerchantInformation.test.ts} +33 -50
  33. package/src/actions/getMerchantInformation.ts +16 -0
  34. package/src/actions/index.ts +3 -2
  35. package/src/actions/openSso.ts +4 -2
  36. package/src/actions/referral/processReferral.test.ts +117 -242
  37. package/src/actions/referral/processReferral.ts +134 -204
  38. package/src/actions/referral/referralInteraction.test.ts +4 -12
  39. package/src/actions/referral/referralInteraction.ts +3 -13
  40. package/src/actions/sendInteraction.ts +46 -22
  41. package/src/actions/trackPurchaseStatus.test.ts +354 -141
  42. package/src/actions/trackPurchaseStatus.ts +48 -11
  43. package/src/actions/watchWalletStatus.ts +2 -3
  44. package/src/actions/wrapper/modalBuilder.test.ts +0 -14
  45. package/src/actions/wrapper/modalBuilder.ts +3 -12
  46. package/src/bundle.ts +0 -1
  47. package/src/clients/createIFrameFrakClient.ts +10 -5
  48. package/src/clients/transports/iframeLifecycleManager.test.ts +163 -4
  49. package/src/clients/transports/iframeLifecycleManager.ts +172 -33
  50. package/src/constants/interactionTypes.ts +12 -41
  51. package/src/index.ts +27 -16
  52. package/src/types/config.ts +6 -0
  53. package/src/types/context.ts +48 -6
  54. package/src/types/index.ts +15 -11
  55. package/src/types/lifecycle/client.ts +24 -1
  56. package/src/types/lifecycle/iframe.ts +6 -0
  57. package/src/types/rpc/displayModal.ts +2 -4
  58. package/src/types/rpc/embedded/index.ts +2 -2
  59. package/src/types/rpc/interaction.ts +31 -39
  60. package/src/types/rpc/merchantInformation.ts +77 -0
  61. package/src/types/rpc/modal/index.ts +0 -4
  62. package/src/types/rpc/modal/login.ts +5 -1
  63. package/src/types/rpc/walletStatus.ts +1 -7
  64. package/src/types/rpc.ts +22 -30
  65. package/src/types/tracking.ts +31 -0
  66. package/src/utils/FrakContext.test.ts +270 -186
  67. package/src/utils/FrakContext.ts +78 -56
  68. package/src/utils/backendUrl.test.ts +83 -0
  69. package/src/utils/backendUrl.ts +62 -0
  70. package/src/utils/clientId.test.ts +41 -0
  71. package/src/utils/clientId.ts +43 -0
  72. package/src/utils/compression/compress.test.ts +1 -1
  73. package/src/utils/compression/compress.ts +2 -2
  74. package/src/utils/compression/decompress.test.ts +8 -4
  75. package/src/utils/compression/decompress.ts +2 -2
  76. package/src/utils/{computeProductId.ts → computeLegacyProductId.ts} +2 -2
  77. package/src/utils/constants.ts +5 -0
  78. package/src/utils/deepLinkWithFallback.test.ts +243 -0
  79. package/src/utils/deepLinkWithFallback.ts +103 -0
  80. package/src/utils/formatAmount.ts +6 -0
  81. package/src/utils/iframeHelper.test.ts +18 -5
  82. package/src/utils/iframeHelper.ts +10 -3
  83. package/src/utils/index.ts +16 -1
  84. package/src/utils/merchantId.test.ts +653 -0
  85. package/src/utils/merchantId.ts +143 -0
  86. package/src/utils/sso.ts +18 -11
  87. package/src/utils/trackEvent.test.ts +23 -5
  88. package/src/utils/trackEvent.ts +13 -0
  89. package/cdn/bundle.iife.js +0 -14
  90. package/dist/actions-B5j-i1p0.cjs +0 -1
  91. package/dist/actions-q090Z0oR.js +0 -1
  92. package/dist/index-7OZ39x1U.d.ts +0 -195
  93. package/dist/index-CRsQWnTs.d.cts +0 -351
  94. package/dist/index-Ck1hudEi.d.ts +0 -351
  95. package/dist/index-zDq-VlKx.d.cts +0 -195
  96. package/dist/interaction-DMJ3ZfaF.d.cts +0 -45
  97. package/dist/interaction-KX1h9a7V.d.ts +0 -45
  98. package/dist/interactions-DnfM3oe0.js +0 -1
  99. package/dist/interactions-EIXhNLf6.cjs +0 -1
  100. package/dist/interactions.cjs +0 -1
  101. package/dist/interactions.d.cts +0 -2
  102. package/dist/interactions.d.ts +0 -2
  103. package/dist/interactions.js +0 -1
  104. package/dist/productTypes-BUkXJKZ7.cjs +0 -1
  105. package/dist/productTypes-CGb1MmBF.js +0 -1
  106. package/dist/src-1LQ4eLq5.js +0 -13
  107. package/dist/src-hW71KjPN.cjs +0 -13
  108. package/dist/trackEvent-CHnYa85W.js +0 -1
  109. package/dist/trackEvent-GuQm_1Nm.cjs +0 -1
  110. package/src/actions/getProductInformation.ts +0 -14
  111. package/src/actions/openSso.test.ts +0 -407
  112. package/src/actions/sendInteraction.test.ts +0 -219
  113. package/src/constants/interactionTypes.test.ts +0 -128
  114. package/src/constants/productTypes.test.ts +0 -130
  115. package/src/constants/productTypes.ts +0 -33
  116. package/src/interactions/index.ts +0 -5
  117. package/src/interactions/pressEncoder.test.ts +0 -215
  118. package/src/interactions/pressEncoder.ts +0 -53
  119. package/src/interactions/purchaseEncoder.test.ts +0 -291
  120. package/src/interactions/purchaseEncoder.ts +0 -99
  121. package/src/interactions/referralEncoder.test.ts +0 -170
  122. package/src/interactions/referralEncoder.ts +0 -47
  123. package/src/interactions/retailEncoder.test.ts +0 -107
  124. package/src/interactions/retailEncoder.ts +0 -37
  125. package/src/interactions/webshopEncoder.test.ts +0 -56
  126. package/src/interactions/webshopEncoder.ts +0 -30
  127. package/src/types/rpc/modal/openSession.ts +0 -25
  128. package/src/types/rpc/productInformation.ts +0 -59
  129. package/src/utils/computeProductId.test.ts +0 -80
  130. package/src/utils/sso.test.ts +0 -361
@@ -1,22 +1,183 @@
1
1
  import { Deferred } from "@frak-labs/frame-connector";
2
2
  import type { FrakLifecycleEvent } from "../../types";
3
+ import { getClientId } from "../../utils/clientId";
3
4
  import { BACKUP_KEY } from "../../utils/constants";
5
+ import {
6
+ isFrakDeepLink,
7
+ triggerDeepLinkWithFallback,
8
+ } from "../../utils/deepLinkWithFallback";
4
9
  import { changeIframeVisibility } from "../../utils/iframeHelper";
5
10
 
11
+ /**
12
+ * Detect iOS in-app browsers (Instagram, Facebook) where server-side
13
+ * 302 redirects to custom URL schemes (x-safari-https://) are silently
14
+ * swallowed by WKWebView. Direct window.location.href assignment works.
15
+ */
16
+ const isIOSInAppBrowser = (() => {
17
+ if (typeof navigator === "undefined") return false;
18
+ const ua = navigator.userAgent;
19
+ // Standard iOS or iPadOS 13+ (reports as Macintosh with touch)
20
+ const isIOS =
21
+ /iPhone|iPad|iPod/i.test(ua) ||
22
+ (/Macintosh/i.test(ua) && navigator.maxTouchPoints > 1);
23
+ if (!isIOS) return false;
24
+ const lower = ua.toLowerCase();
25
+ return (
26
+ lower.includes("instagram") ||
27
+ lower.includes("fban") ||
28
+ lower.includes("fbav") ||
29
+ lower.includes("facebook")
30
+ );
31
+ })();
32
+
6
33
  /** @ignore */
7
34
  export type IframeLifecycleManager = {
8
35
  isConnected: Promise<boolean>;
9
36
  handleEvent: (messageEvent: FrakLifecycleEvent) => Promise<void>;
10
37
  };
11
38
 
39
+ /**
40
+ * Handle backup storage
41
+ */
42
+ function handleBackup(backup: string | undefined): void {
43
+ if (backup) {
44
+ localStorage.setItem(BACKUP_KEY, backup);
45
+ } else {
46
+ localStorage.removeItem(BACKUP_KEY);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Handle handshake with iframe — sends client metadata so the listener can resolve the correct merchant
52
+ * @param iframe - The iframe element to post the handshake response to
53
+ * @param token - The handshake token received from the iframe
54
+ * @param targetOrigin - The target origin for postMessage security
55
+ * @param configDomain - Optional override domain for merchant resolution in tunneled/proxied environments
56
+ */
57
+ function handleHandshake(
58
+ iframe: HTMLIFrameElement,
59
+ token: string,
60
+ targetOrigin: string,
61
+ configDomain?: string
62
+ ): void {
63
+ const url = new URL(window.location.href);
64
+ const pendingMergeToken = url.searchParams.get("fmt") ?? undefined;
65
+
66
+ iframe.contentWindow?.postMessage(
67
+ {
68
+ clientLifecycle: "handshake-response",
69
+ data: {
70
+ token,
71
+ currentUrl: window.location.href,
72
+ pendingMergeToken,
73
+ configDomain,
74
+ clientId: getClientId(),
75
+ },
76
+ },
77
+ targetOrigin
78
+ );
79
+
80
+ if (pendingMergeToken) {
81
+ url.searchParams.delete("fmt");
82
+ window.history.replaceState({}, "", url.toString());
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Compute final redirect URL with parameter substitution
88
+ */
89
+ function computeRedirectUrl(
90
+ baseRedirectUrl: string,
91
+ mergeToken?: string
92
+ ): string {
93
+ try {
94
+ const redirectUrl = new URL(baseRedirectUrl);
95
+ if (!redirectUrl.searchParams.has("u")) {
96
+ return baseRedirectUrl;
97
+ }
98
+
99
+ redirectUrl.searchParams.delete("u");
100
+ redirectUrl.searchParams.append("u", window.location.href);
101
+
102
+ if (mergeToken) {
103
+ redirectUrl.searchParams.append("fmt", mergeToken);
104
+ }
105
+
106
+ return redirectUrl.toString();
107
+ } catch {
108
+ return baseRedirectUrl;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Redirect current page to Safari via x-safari-https:// scheme.
114
+ * Used on iOS in-app browsers where backend 302 → custom scheme fails.
115
+ */
116
+ function redirectToSafari(mergeToken?: string) {
117
+ const url = new URL(window.location.href);
118
+ if (mergeToken) {
119
+ url.searchParams.set("fmt", mergeToken);
120
+ }
121
+ const scheme =
122
+ url.protocol === "http:" ? "x-safari-http" : "x-safari-https";
123
+ window.location.href = `${scheme}://${url.host}${url.pathname}${url.search}${url.hash}`;
124
+ }
125
+
126
+ /**
127
+ * Check if this is a social/in-app-browser escape redirect (contains /common/social)
128
+ */
129
+ function isSocialRedirect(url: string): boolean {
130
+ return url.includes("/common/social");
131
+ }
132
+
133
+ /**
134
+ * Handle redirect with deep link fallback
135
+ */
136
+ function handleRedirect(
137
+ iframe: HTMLIFrameElement,
138
+ baseRedirectUrl: string,
139
+ targetOrigin: string,
140
+ mergeToken?: string
141
+ ): void {
142
+ if (isFrakDeepLink(baseRedirectUrl)) {
143
+ const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);
144
+ triggerDeepLinkWithFallback(finalUrl, {
145
+ onFallback: () => {
146
+ iframe.contentWindow?.postMessage(
147
+ {
148
+ clientLifecycle: "deep-link-failed",
149
+ data: { originalUrl: finalUrl },
150
+ },
151
+ targetOrigin
152
+ );
153
+ },
154
+ });
155
+ } else if (isIOSInAppBrowser && isSocialRedirect(baseRedirectUrl)) {
156
+ // iOS WKWebView silently swallows 302 redirects to custom URL
157
+ // schemes — bypass the server redirect entirely
158
+ redirectToSafari(mergeToken);
159
+ } else {
160
+ const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);
161
+ window.location.href = finalUrl;
162
+ }
163
+ }
164
+
12
165
  /**
13
166
  * Create a new iframe lifecycle handler
167
+ * @param args
168
+ * @param args.iframe - The iframe element used for wallet communication
169
+ * @param args.targetOrigin - The wallet URL origin for postMessage security
170
+ * @param args.configDomain - Optional domain override forwarded during handshake for tunneled/proxied environments
14
171
  * @ignore
15
172
  */
16
173
  export function createIFrameLifecycleManager({
17
174
  iframe,
175
+ targetOrigin,
176
+ configDomain,
18
177
  }: {
19
178
  iframe: HTMLIFrameElement;
179
+ targetOrigin: string;
180
+ configDomain?: string;
20
181
  }): IframeLifecycleManager {
21
182
  // Create the isConnected listener
22
183
  const isConnectedDeferred = new Deferred<boolean>();
@@ -34,11 +195,7 @@ export function createIFrameLifecycleManager({
34
195
  break;
35
196
  // Perform a frak backup
36
197
  case "do-backup":
37
- if (data.backup) {
38
- localStorage.setItem(BACKUP_KEY, data.backup);
39
- } else {
40
- localStorage.removeItem(BACKUP_KEY);
41
- }
198
+ handleBackup(data.backup);
42
199
  break;
43
200
  // Remove frak backup
44
201
  case "remove-backup":
@@ -47,39 +204,21 @@ export function createIFrameLifecycleManager({
47
204
  // Change iframe visibility
48
205
  case "show":
49
206
  case "hide":
50
- changeIframeVisibility({
51
- iframe,
52
- isVisible: event === "show",
53
- });
207
+ changeIframeVisibility({ iframe, isVisible: event === "show" });
54
208
  break;
55
209
  // Handshake handling
56
- case "handshake": {
57
- iframe.contentWindow?.postMessage(
58
- {
59
- clientLifecycle: "handshake-response",
60
- data: {
61
- token: data.token,
62
- currentUrl: window.location.href,
63
- },
64
- },
65
- "*"
66
- );
210
+ case "handshake":
211
+ handleHandshake(iframe, data.token, targetOrigin, configDomain);
67
212
  break;
68
- }
69
213
  // Redirect handling
70
- case "redirect": {
71
- const redirectUrl = new URL(data.baseRedirectUrl);
72
-
73
- // If we got a u append the current location dynamicly
74
- if (redirectUrl.searchParams.has("u")) {
75
- redirectUrl.searchParams.delete("u");
76
- redirectUrl.searchParams.append("u", window.location.href);
77
- window.location.href = redirectUrl.toString();
78
- } else {
79
- window.location.href = data.baseRedirectUrl;
80
- }
214
+ case "redirect":
215
+ handleRedirect(
216
+ iframe,
217
+ data.baseRedirectUrl,
218
+ targetOrigin,
219
+ data.mergeToken
220
+ );
81
221
  break;
82
- }
83
222
  }
84
223
  };
85
224
 
@@ -1,44 +1,15 @@
1
1
  /**
2
- * The final keys for each interaction types (e.g. `openArticle`) -> interaction type
2
+ * The supported interaction type keys
3
+ *
4
+ * - `referral` - User arrived via a referral link
5
+ * - `create_referral_link` - User created/shared a referral link
6
+ * - `purchase` - User completed a purchase
7
+ * - `custom.${string}` - Custom interaction type defined per campaign
8
+ *
3
9
  * @inline
4
10
  */
5
- export type InteractionTypesKey = {
6
- [K in keyof typeof interactionTypes]: keyof (typeof interactionTypes)[K];
7
- }[keyof typeof interactionTypes];
8
-
9
- /**
10
- * The keys for each interaction types (e.g. `press.openArticle`) -> category_type.interaction_type
11
- * @inline
12
- */
13
- export type FullInteractionTypesKey = {
14
- [Category in keyof typeof interactionTypes]: `${Category & string}.${keyof (typeof interactionTypes)[Category] & string}`;
15
- }[keyof typeof interactionTypes];
16
-
17
- /**
18
- * Each interactions types according to the product types
19
- */
20
- export const interactionTypes = {
21
- press: {
22
- openArticle: "0xc0a24ffb",
23
- readArticle: "0xd5bd0fbe",
24
- },
25
- dapp: {
26
- proofVerifiableStorageUpdate: "0x2ab2aeef",
27
- callableVerifiableStorageUpdate: "0xa07da986",
28
- },
29
- webshop: {
30
- open: "0xb311798f",
31
- },
32
- referral: {
33
- referred: "0x010cc3b9",
34
- createLink: "0xb2c0f17c",
35
- },
36
- purchase: {
37
- started: "0xd87e90c3",
38
- completed: "0x8403aeb4",
39
- unsafeCompleted: "0x4d5b14e0",
40
- },
41
- retail: {
42
- customerMeeting: "0x74489004",
43
- },
44
- } as const;
11
+ export type InteractionTypeKey =
12
+ | "referral"
13
+ | "create_referral_link"
14
+ | "purchase"
15
+ | `custom.${string}`;
package/src/index.ts CHANGED
@@ -6,18 +6,10 @@ export {
6
6
  DebugInfoGatherer,
7
7
  setupClient,
8
8
  } from "./clients";
9
- export {
10
- type FullInteractionTypesKey,
11
- type InteractionTypesKey,
12
- interactionTypes,
13
- } from "./constants/interactionTypes";
9
+
10
+ export type { InteractionTypeKey } from "./constants/interactionTypes";
14
11
  export { type LocalesKey, locales } from "./constants/locales";
15
- // Constants
16
- export {
17
- type ProductTypesKey,
18
- productTypes,
19
- productTypesMask,
20
- } from "./constants/productTypes";
12
+
21
13
  // Types
22
14
  export type {
23
15
  ClientLifecycleEvent,
@@ -29,16 +21,19 @@ export type {
29
21
  DisplayModalParamsType,
30
22
  EmbeddedViewActionReferred,
31
23
  EmbeddedViewActionSharing,
24
+ EstimatedReward,
32
25
  FinalActionType,
33
26
  FinalModalStepType,
34
27
  // Client
35
28
  FrakClient,
36
29
  // Utils
37
30
  FrakContext,
31
+ FrakContextV1,
32
+ FrakContextV2,
38
33
  FrakLifecycleEvent,
39
34
  // Config
40
35
  FrakWalletSdkConfig,
41
- GetProductInformationReturnType,
36
+ GetMerchantInformationReturnType,
42
37
  HashProtectedData,
43
38
  I18nConfig,
44
39
  IFrameLifecycleEvent,
@@ -59,15 +54,13 @@ export type {
59
54
  ModalStepMetadata,
60
55
  // RPC Modal generics
61
56
  ModalStepTypes,
62
- OpenInteractionSessionModalStepType,
63
- OpenInteractionSessionReturnType,
64
57
  OpenSsoParamsType,
65
58
  OpenSsoReturnType,
66
- PreparedInteraction,
67
59
  PrepareSsoParamsType,
68
60
  PrepareSsoReturnType,
61
+ RewardTier,
62
+ // RPC Interaction
69
63
  SendInteractionParamsType,
70
- SendInteractionReturnType,
71
64
  SendTransactionModalStepType,
72
65
  SendTransactionReturnType,
73
66
  SendTransactionTxType,
@@ -76,9 +69,14 @@ export type {
76
69
  SiweAuthenticationParams,
77
70
  SsoMetadata,
78
71
  TokenAmountType,
72
+ // Tracking
73
+ TrackArrivalParams,
74
+ TrackArrivalResult,
75
+ UtmParams,
79
76
  // Rpc
80
77
  WalletStatusReturnType,
81
78
  } from "./types";
79
+ export { isV1Context, isV2Context } from "./types";
82
80
  // Utils
83
81
  export {
84
82
  type AppSpecificSsoMetadata,
@@ -86,16 +84,29 @@ export {
86
84
  base64urlEncode,
87
85
  baseIframeProps,
88
86
  type CompressedSsoData,
87
+ clearMerchantIdCache,
89
88
  compressJsonToB64,
90
89
  createIframe,
90
+ DEEP_LINK_SCHEME,
91
+ type DeepLinkFallbackOptions,
91
92
  decompressJsonFromB64,
92
93
  FrakContextManager,
94
+ type FrakEvent,
93
95
  type FullSsoParams,
96
+ fetchMerchantId,
94
97
  findIframeInOpener,
95
98
  formatAmount,
96
99
  generateSsoUrl,
100
+ getBackendUrl,
101
+ getClientId,
97
102
  getCurrencyAmountKey,
98
103
  getSupportedCurrency,
99
104
  getSupportedLocale,
105
+ isChromiumAndroid,
106
+ isFrakDeepLink,
107
+ resolveMerchantId,
108
+ toAndroidIntentUrl,
100
109
  trackEvent,
110
+ triggerDeepLinkWithFallback,
101
111
  } from "./utils";
112
+ export { computeLegacyProductId } from "./utils/computeLegacyProductId";
@@ -28,6 +28,12 @@ export type FrakWalletSdkConfig = {
28
28
  * Your application name (will be displayed in a few modals and in SSO)
29
29
  */
30
30
  name: string;
31
+ /**
32
+ * Your merchant ID from the Frak dashboard (UUID format)
33
+ * Used for referral tracking and analytics
34
+ * If not provided, will be auto-fetched from the backend using your domain
35
+ */
36
+ merchantId?: string;
31
37
  /**
32
38
  * Language to display in the modal
33
39
  * If undefined, will default to the browser language
@@ -1,13 +1,55 @@
1
1
  import type { Address } from "viem";
2
2
 
3
3
  /**
4
- * The current Frak Context
5
- *
6
- * For now, only contain a referrer address.
7
- *
4
+ * V1 (legacy) Frak Context — contains only the referrer wallet address.
5
+ * Used for backward compatibility with old sharing links.
8
6
  * @ignore
9
7
  */
10
- export type FrakContext = {
11
- // Referrer address
8
+ export type FrakContextV1 = {
9
+ /** Referrer wallet address */
12
10
  r: Address;
13
11
  };
12
+
13
+ /**
14
+ * V2 Frak Context — anonymous-first referral context.
15
+ * Contains the sharer's clientId, merchantId, and link creation timestamp.
16
+ * @ignore
17
+ */
18
+ export type FrakContextV2 = {
19
+ /** Version discriminator */
20
+ v: 2;
21
+ /** Sharer's anonymous clientId (UUID from localStorage) */
22
+ c: string;
23
+ /** Merchant ID (UUID) */
24
+ m: string;
25
+ /** Link creation timestamp (epoch seconds) */
26
+ t: number;
27
+ };
28
+
29
+ /**
30
+ * The current Frak Context — union of all versions.
31
+ *
32
+ * - No `v` field → V1 (legacy wallet address)
33
+ * - `v: 2` → V2 (anonymous clientId-based)
34
+ *
35
+ * @ignore
36
+ */
37
+ export type FrakContext = FrakContextV1 | FrakContextV2;
38
+
39
+ /**
40
+ * Type guard: check if a context is V1 (legacy wallet address).
41
+ * @param ctx - The Frak context to check
42
+ * @returns True if the context is a V1 context
43
+ */
44
+ export function isV1Context(ctx: FrakContext): ctx is FrakContextV1 {
45
+ return "r" in ctx && !("v" in ctx);
46
+ }
47
+
48
+ /**
49
+ * Type guard: check if a context is V2 (anonymous clientId-based).
50
+ * @param ctx - The Frak context to check
51
+ * @returns True if the context is a V2 context
52
+ */
53
+ export function isV2Context(ctx: FrakContext): ctx is FrakContextV2 {
54
+ return "v" in ctx && ctx.v === 2;
55
+ }
@@ -15,7 +15,9 @@ export type {
15
15
  LocalizedI18nConfig,
16
16
  } from "./config";
17
17
  // Utils
18
- export type { FrakContext } from "./context";
18
+ export type { FrakContext, FrakContextV1, FrakContextV2 } from "./context";
19
+ export { isV1Context, isV2Context } from "./context";
20
+
19
21
  export type {
20
22
  ClientLifecycleEvent,
21
23
  IFrameLifecycleEvent,
@@ -37,18 +39,18 @@ export type {
37
39
  LoggedInEmbeddedView,
38
40
  LoggedOutEmbeddedView,
39
41
  } from "./rpc/embedded";
42
+ export type { SendInteractionParamsType } from "./rpc/interaction";
40
43
  export type {
41
- PreparedInteraction,
42
- SendInteractionParamsType,
43
- SendInteractionReturnType,
44
- } from "./rpc/interaction";
44
+ EstimatedReward,
45
+ GetMerchantInformationReturnType,
46
+ RewardTier,
47
+ TokenAmountType,
48
+ } from "./rpc/merchantInformation";
45
49
  export type {
46
50
  FinalActionType,
47
51
  FinalModalStepType,
48
52
  LoginModalStepType,
49
53
  ModalStepMetadata,
50
- OpenInteractionSessionModalStepType,
51
- OpenInteractionSessionReturnType,
52
54
  SendTransactionModalStepType,
53
55
  SendTransactionReturnType,
54
56
  SendTransactionTxType,
@@ -56,10 +58,6 @@ export type {
56
58
  SiweAuthenticateReturnType,
57
59
  SiweAuthenticationParams,
58
60
  } from "./rpc/modal";
59
- export type {
60
- GetProductInformationReturnType,
61
- TokenAmountType,
62
- } from "./rpc/productInformation";
63
61
  export type {
64
62
  OpenSsoParamsType,
65
63
  OpenSsoReturnType,
@@ -68,4 +66,10 @@ export type {
68
66
  SsoMetadata,
69
67
  } from "./rpc/sso";
70
68
  export type { WalletStatusReturnType } from "./rpc/walletStatus";
69
+ // Tracking
70
+ export type {
71
+ TrackArrivalParams,
72
+ TrackArrivalResult,
73
+ UtmParams,
74
+ } from "./tracking";
71
75
  export type { FrakLifecycleEvent, IFrameTransport } from "./transport";
@@ -10,7 +10,8 @@ export type ClientLifecycleEvent =
10
10
  | RestoreBackupEvent
11
11
  | HearbeatEvent
12
12
  | HandshakeResponse
13
- | SsoRedirectCompleteEvent;
13
+ | SsoRedirectCompleteEvent
14
+ | DeepLinkFailedEvent;
14
15
 
15
16
  type CustomCssEvent = {
16
17
  clientLifecycle: "modal-css";
@@ -37,6 +38,23 @@ type HandshakeResponse = {
37
38
  data: {
38
39
  token: string;
39
40
  currentUrl: string;
41
+ /**
42
+ * Pending merge token extracted from URL (?fmt= parameter)
43
+ * When present, listener should execute identity merge in background
44
+ * URL is cleaned after handshake response is sent
45
+ */
46
+ pendingMergeToken?: string;
47
+ /**
48
+ * Client ID for identity tracking (belt & suspenders fallback)
49
+ * Primary delivery is via iframe URL query param; handshake is backup for SSR
50
+ */
51
+ clientId?: string;
52
+ /**
53
+ * Explicit domain from SDK config (FrakWalletSdkConfig.domain)
54
+ * When present, listener should prefer this over URL-derived domain
55
+ * for merchant resolution (handles proxied/tunneled environments)
56
+ */
57
+ configDomain?: string;
40
58
  };
41
59
  };
42
60
 
@@ -44,3 +62,8 @@ type SsoRedirectCompleteEvent = {
44
62
  clientLifecycle: "sso-redirect-complete";
45
63
  data: { compressed: string };
46
64
  };
65
+
66
+ type DeepLinkFailedEvent = {
67
+ clientLifecycle: "deep-link-failed";
68
+ data: { originalUrl: string };
69
+ };
@@ -31,5 +31,11 @@ type RedirectRequestEvent = {
31
31
  * If it contain a query param `u`, the client need will suffix the current url to the base url
32
32
  */
33
33
  baseRedirectUrl: string;
34
+ /**
35
+ * Optional merge token for anonymous identity merging
36
+ * When provided, appended as ?fmt= query parameter to the final redirect URL
37
+ * Used when redirecting out of social browsers to preserve identity across contexts
38
+ */
39
+ mergeToken?: string;
34
40
  };
35
41
  };
@@ -1,9 +1,8 @@
1
- import type { FullInteractionTypesKey } from "../../constants/interactionTypes";
1
+ import type { InteractionTypeKey } from "../../constants/interactionTypes";
2
2
  import type { I18nConfig } from "../config";
3
3
  import type {
4
4
  FinalModalStepType,
5
5
  LoginModalStepType,
6
- OpenInteractionSessionModalStepType,
7
6
  SendTransactionModalStepType,
8
7
  SiweAuthenticateModalStepType,
9
8
  } from "./modal";
@@ -16,7 +15,6 @@ export type ModalStepTypes =
16
15
  | LoginModalStepType
17
16
  | SiweAuthenticateModalStepType
18
17
  | SendTransactionModalStepType
19
- | OpenInteractionSessionModalStepType
20
18
  | FinalModalStepType;
21
19
 
22
20
  /**
@@ -54,7 +52,7 @@ export type ModalRpcMetadata = {
54
52
  title?: string;
55
53
  icon?: string;
56
54
  };
57
- targetInteraction?: FullInteractionTypesKey;
55
+ targetInteraction?: InteractionTypeKey;
58
56
  /**
59
57
  * Some i18n override for the displayed modal (i.e. update the displayed text only for this modal)
60
58
  */
@@ -1,5 +1,5 @@
1
1
  import type { Address } from "viem";
2
- import type { FullInteractionTypesKey } from "../../../constants/interactionTypes";
2
+ import type { InteractionTypeKey } from "../../../constants/interactionTypes";
3
3
  import type { I18nConfig } from "../../config";
4
4
  import type {
5
5
  EmbeddedViewActionReferred,
@@ -46,7 +46,7 @@ export type DisplayEmbeddedWalletParamsType = {
46
46
  /**
47
47
  * The target interaction behind this modal
48
48
  */
49
- targetInteraction?: FullInteractionTypesKey;
49
+ targetInteraction?: InteractionTypeKey;
50
50
  /**
51
51
  * The position of the component
52
52
  */