@frak-labs/core-sdk 0.2.1 → 1.0.0-beta.61e6fb99

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 (92) hide show
  1. package/README.md +1 -2
  2. package/cdn/bundle.js +3 -3
  3. package/dist/actions-Di4welXI.cjs +1 -0
  4. package/dist/actions-DyMkUe65.js +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-B_Uj-puh.d.ts} +249 -73
  14. package/dist/{computeLegacyProductId-b5cUWdAm.d.ts → index-ByVpu25D.d.cts} +249 -73
  15. package/dist/{siweAuthenticate-CnCZ7mok.d.ts → index-CGyEOo9J.d.cts} +122 -8
  16. package/dist/{siweAuthenticate-CVigMOxz.d.cts → index-Cdf5j2_W.d.ts} +122 -8
  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-B6pD2oA6.d.ts} +380 -46
  22. package/dist/{openSso-CMzwvaCa.d.ts → openSso-qjaccFd0.d.cts} +379 -45
  23. package/dist/sdkConfigStore-DvwFc6Ym.cjs +1 -0
  24. package/dist/sdkConfigStore-M37skmM8.js +1 -0
  25. package/dist/src-BqpqVHCq.cjs +13 -0
  26. package/dist/src-BxRYON49.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 +4 -8
  38. package/src/actions/referral/processReferral.ts +5 -11
  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/index.ts +15 -1
  52. package/src/types/lifecycle/client.ts +29 -27
  53. package/src/types/lifecycle/iframe.ts +7 -8
  54. package/src/types/resolvedConfig.ts +138 -0
  55. package/src/types/rpc/displaySharingPage.ts +100 -0
  56. package/src/types/rpc/embedded/index.ts +1 -1
  57. package/src/types/rpc/interaction.ts +4 -0
  58. package/src/types/rpc/userReferralStatus.ts +20 -0
  59. package/src/types/rpc.ts +54 -5
  60. package/src/types/tracking.ts +36 -0
  61. package/src/utils/FrakContext.test.ts +151 -0
  62. package/src/utils/FrakContext.ts +67 -1
  63. package/src/utils/analytics/events/component.ts +58 -0
  64. package/src/utils/analytics/events/index.ts +20 -0
  65. package/src/utils/analytics/events/lifecycle.ts +26 -0
  66. package/src/utils/analytics/events/referral.ts +10 -0
  67. package/src/utils/analytics/index.ts +8 -0
  68. package/src/utils/{trackEvent.test.ts → analytics/trackEvent.test.ts} +22 -30
  69. package/src/utils/analytics/trackEvent.ts +34 -0
  70. package/src/utils/backendUrl.test.ts +2 -2
  71. package/src/utils/backendUrl.ts +1 -1
  72. package/src/utils/cache/index.ts +7 -0
  73. package/src/utils/cache/lruMap.test.ts +55 -0
  74. package/src/utils/cache/lruMap.ts +38 -0
  75. package/src/utils/cache/withCache.test.ts +168 -0
  76. package/src/utils/cache/withCache.ts +124 -0
  77. package/src/utils/inAppBrowser.ts +60 -0
  78. package/src/utils/index.ts +11 -5
  79. package/src/utils/mergeAttribution.test.ts +153 -0
  80. package/src/utils/mergeAttribution.ts +75 -0
  81. package/src/utils/sdkConfigStore.test.ts +405 -0
  82. package/src/utils/sdkConfigStore.ts +263 -0
  83. package/src/utils/sso.ts +3 -7
  84. package/dist/setupClient-BduY6Sym.cjs +0 -13
  85. package/dist/setupClient-ftmdQ-I8.js +0 -13
  86. package/dist/siweAuthenticate-BWmI2_TN.cjs +0 -1
  87. package/dist/siweAuthenticate-zczqxm0a.js +0 -1
  88. package/dist/trackEvent-CeLFVzZn.js +0 -1
  89. package/dist/trackEvent-Ew5r5zfI.cjs +0 -1
  90. package/src/utils/merchantId.test.ts +0 -653
  91. package/src/utils/merchantId.ts +0 -143
  92. package/src/utils/trackEvent.ts +0 -41
@@ -1,6 +1,5 @@
1
1
  import { Deferred } from "@frak-labs/frame-connector";
2
2
  import type { FrakLifecycleEvent } from "../../types";
3
- import { getClientId } from "../../utils/clientId";
4
3
  import { BACKUP_KEY } from "../../utils/constants";
5
4
  import {
6
5
  isFrakDeepLink,
@@ -33,7 +32,7 @@ const isIOSInAppBrowser = (() => {
33
32
  /** @ignore */
34
33
  export type IframeLifecycleManager = {
35
34
  isConnected: Promise<boolean>;
36
- handleEvent: (messageEvent: FrakLifecycleEvent) => Promise<void>;
35
+ handleEvent: (messageEvent: FrakLifecycleEvent) => void;
37
36
  };
38
37
 
39
38
  /**
@@ -47,42 +46,6 @@ function handleBackup(backup: string | undefined): void {
47
46
  }
48
47
  }
49
48
 
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
49
  /**
87
50
  * Compute final redirect URL with parameter substitution
88
51
  */
@@ -96,12 +59,12 @@ function computeRedirectUrl(
96
59
  return baseRedirectUrl;
97
60
  }
98
61
 
99
- redirectUrl.searchParams.delete("u");
100
- redirectUrl.searchParams.append("u", window.location.href);
62
+ // Append merge token to the page URL so it survives
63
+ // the backend /common/social redirect chain
64
+ const finalPageUrl = appendMergeToken(window.location.href, mergeToken);
101
65
 
102
- if (mergeToken) {
103
- redirectUrl.searchParams.append("fmt", mergeToken);
104
- }
66
+ redirectUrl.searchParams.delete("u");
67
+ redirectUrl.searchParams.append("u", finalPageUrl);
105
68
 
106
69
  return redirectUrl.toString();
107
70
  } catch {
@@ -130,6 +93,21 @@ function isSocialRedirect(url: string): boolean {
130
93
  return url.includes("/common/social");
131
94
  }
132
95
 
96
+ /**
97
+ * Append merge token to a URL as the `fmt` query parameter.
98
+ */
99
+ function appendMergeToken(urlString: string, mergeToken?: string): string {
100
+ if (!mergeToken) return urlString;
101
+ try {
102
+ const url = new URL(urlString);
103
+ url.searchParams.set("fmt", mergeToken);
104
+ return url.toString();
105
+ } catch {
106
+ const sep = urlString.includes("?") ? "&" : "?";
107
+ return `${urlString}${sep}fmt=${encodeURIComponent(mergeToken)}`;
108
+ }
109
+ }
110
+
133
111
  /**
134
112
  * Handle redirect with deep link fallback
135
113
  */
@@ -137,8 +115,18 @@ function handleRedirect(
137
115
  iframe: HTMLIFrameElement,
138
116
  baseRedirectUrl: string,
139
117
  targetOrigin: string,
140
- mergeToken?: string
118
+ mergeToken?: string,
119
+ openInNewTab?: boolean
141
120
  ): void {
121
+ // If requested, open in a new tab instead of navigating the current page.
122
+ // This preserves the merchant page while triggering universal links.
123
+ // Requires the iframe postMessage to include user activation delegation.
124
+ if (openInNewTab) {
125
+ const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);
126
+ window.open(finalUrl, "_blank");
127
+ return;
128
+ }
129
+
142
130
  if (isFrakDeepLink(baseRedirectUrl)) {
143
131
  const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);
144
132
  triggerDeepLinkWithFallback(finalUrl, {
@@ -167,23 +155,20 @@ function handleRedirect(
167
155
  * @param args
168
156
  * @param args.iframe - The iframe element used for wallet communication
169
157
  * @param args.targetOrigin - The wallet URL origin for postMessage security
170
- * @param args.configDomain - Optional domain override forwarded during handshake for tunneled/proxied environments
171
158
  * @ignore
172
159
  */
173
160
  export function createIFrameLifecycleManager({
174
161
  iframe,
175
162
  targetOrigin,
176
- configDomain,
177
163
  }: {
178
164
  iframe: HTMLIFrameElement;
179
165
  targetOrigin: string;
180
- configDomain?: string;
181
166
  }): IframeLifecycleManager {
182
167
  // Create the isConnected listener
183
168
  const isConnectedDeferred = new Deferred<boolean>();
184
169
 
185
170
  // Build the handler itself
186
- const handler = async (messageEvent: FrakLifecycleEvent) => {
171
+ const handler = (messageEvent: FrakLifecycleEvent) => {
187
172
  if (!("iframeLifecycle" in messageEvent)) return;
188
173
 
189
174
  const { iframeLifecycle: event, data } = messageEvent;
@@ -206,17 +191,14 @@ export function createIFrameLifecycleManager({
206
191
  case "hide":
207
192
  changeIframeVisibility({ iframe, isVisible: event === "show" });
208
193
  break;
209
- // Handshake handling
210
- case "handshake":
211
- handleHandshake(iframe, data.token, targetOrigin, configDomain);
212
- break;
213
194
  // Redirect handling
214
195
  case "redirect":
215
196
  handleRedirect(
216
197
  iframe,
217
198
  data.baseRedirectUrl,
218
199
  targetOrigin,
219
- data.mergeToken
200
+ data.mergeToken,
201
+ data.openInNewTab
220
202
  );
221
203
  break;
222
204
  }
package/src/index.ts CHANGED
@@ -12,6 +12,8 @@ export { type LocalesKey, locales } from "./constants/locales";
12
12
 
13
13
  // Types
14
14
  export type {
15
+ AttributionDefaults,
16
+ AttributionParams,
15
17
  ClientLifecycleEvent,
16
18
  CompressedData,
17
19
  Currency,
@@ -19,6 +21,9 @@ export type {
19
21
  DisplayEmbeddedWalletParamsType,
20
22
  DisplayEmbeddedWalletResultType,
21
23
  DisplayModalParamsType,
24
+ // RPC Sharing page
25
+ DisplaySharingPageParamsType,
26
+ DisplaySharingPageResultType,
22
27
  EmbeddedViewActionReferred,
23
28
  EmbeddedViewActionSharing,
24
29
  EstimatedReward,
@@ -47,6 +52,7 @@ export type {
47
52
  LoggedInEmbeddedView,
48
53
  LoggedOutEmbeddedView,
49
54
  LoginModalStepType,
55
+ MerchantConfigResponse,
50
56
  ModalRpcMetadata,
51
57
  ModalRpcStepsInput,
52
58
  ModalRpcStepsResultType,
@@ -58,12 +64,16 @@ export type {
58
64
  OpenSsoReturnType,
59
65
  PrepareSsoParamsType,
60
66
  PrepareSsoReturnType,
67
+ ResolvedPlacement,
68
+ ResolvedSdkConfig,
61
69
  RewardTier,
70
+ SdkResolvedConfig,
62
71
  // RPC Interaction
63
72
  SendInteractionParamsType,
64
73
  SendTransactionModalStepType,
65
74
  SendTransactionReturnType,
66
75
  SendTransactionTxType,
76
+ SharingPageProduct,
67
77
  SiweAuthenticateModalStepType,
68
78
  SiweAuthenticateReturnType,
69
79
  SiweAuthenticationParams,
@@ -72,8 +82,9 @@ export type {
72
82
  // Tracking
73
83
  TrackArrivalParams,
74
84
  TrackArrivalResult,
75
- UtmParams,
76
85
  // Rpc
86
+ UserReferralStatusType,
87
+ UtmParams,
77
88
  WalletStatusReturnType,
78
89
  } from "./types";
79
90
  export { isV1Context, isV2Context } from "./types";
@@ -84,29 +95,38 @@ export {
84
95
  base64urlEncode,
85
96
  baseIframeProps,
86
97
  type CompressedSsoData,
87
- clearMerchantIdCache,
98
+ clearAllCache,
88
99
  compressJsonToB64,
89
100
  createIframe,
90
101
  DEEP_LINK_SCHEME,
91
102
  type DeepLinkFallbackOptions,
92
103
  decompressJsonFromB64,
93
104
  FrakContextManager,
94
- type FrakEvent,
95
105
  type FullSsoParams,
96
- fetchMerchantId,
97
106
  findIframeInOpener,
98
107
  formatAmount,
99
108
  generateSsoUrl,
100
109
  getBackendUrl,
110
+ getCache,
101
111
  getClientId,
102
112
  getCurrencyAmountKey,
103
113
  getSupportedCurrency,
104
114
  getSupportedLocale,
105
115
  isChromiumAndroid,
106
116
  isFrakDeepLink,
107
- resolveMerchantId,
117
+ isInAppBrowser,
118
+ isIOS,
119
+ type MergeAttributionInput,
120
+ mergeAttribution,
121
+ redirectToExternalBrowser,
122
+ sdkConfigStore,
108
123
  toAndroidIntentUrl,
109
124
  trackEvent,
110
125
  triggerDeepLinkWithFallback,
126
+ withCache,
111
127
  } from "./utils";
128
+ export type {
129
+ SdkEventMap,
130
+ SdkHandshakeFailureReason,
131
+ } from "./utils/analytics";
112
132
  export { computeLegacyProductId } from "./utils/computeLegacyProductId";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stub for rrweb. The @openpanel/web package statically imports `record` from
3
+ * rrweb even when session replay is disabled. This stub replaces the module so
4
+ * that rrweb is not included in the bundle.
5
+ * @see https://github.com/Openpanel-dev/openpanel/issues/336
6
+ */
7
+ export function record() {
8
+ return () => {};
9
+ }
@@ -1,3 +1,5 @@
1
+ import type { AttributionDefaults } from "./tracking";
2
+
1
3
  /**
2
4
  * All the currencies available
3
5
  * @category Config
@@ -27,7 +29,7 @@ export type FrakWalletSdkConfig = {
27
29
  /**
28
30
  * Your application name (will be displayed in a few modals and in SSO)
29
31
  */
30
- name: string;
32
+ name?: string;
31
33
  /**
32
34
  * Your merchant ID from the Frak dashboard (UUID format)
33
35
  * Used for referral tracking and analytics
@@ -71,6 +73,20 @@ export type FrakWalletSdkConfig = {
71
73
  * @defaultValue window.location.host
72
74
  */
73
75
  domain?: string;
76
+ /**
77
+ * Wait for backend config before rendering components.
78
+ * When true (default), components show a spinner until backend config is resolved.
79
+ * When false, components render immediately with SDK static config / HTML attributes.
80
+ * @defaultValue true
81
+ */
82
+ waitForBackendConfig?: boolean;
83
+ /**
84
+ * Default attribution params (UTM / via / ref) appended to outbound
85
+ * sharing URLs. Per-call `displaySharingPage` overrides win, then backend
86
+ * config, then this SDK-level default. `utm_content` is intentionally
87
+ * excluded — it is per-content/per-product, never a merchant-wide default.
88
+ */
89
+ attribution?: AttributionDefaults;
74
90
  };
75
91
 
76
92
  /**
@@ -111,7 +127,7 @@ export type I18nConfig =
111
127
  | LocalizedI18nConfig;
112
128
 
113
129
  /**
114
- * A localized i18n config
130
+ * A localized i18n config (inline objects only — URL-based i18n removed)
115
131
  * @category Config
116
132
  */
117
- export type LocalizedI18nConfig = `${string}.css` | { [key: string]: string };
133
+ export type LocalizedI18nConfig = { [key: string]: string };
@@ -17,11 +17,16 @@ export type {
17
17
  // Utils
18
18
  export type { FrakContext, FrakContextV1, FrakContextV2 } from "./context";
19
19
  export { isV1Context, isV2Context } from "./context";
20
-
21
20
  export type {
22
21
  ClientLifecycleEvent,
23
22
  IFrameLifecycleEvent,
24
23
  } from "./lifecycle";
24
+ export type {
25
+ MerchantConfigResponse,
26
+ ResolvedPlacement,
27
+ ResolvedSdkConfig,
28
+ SdkResolvedConfig,
29
+ } from "./resolvedConfig";
25
30
  export type { IFrameRpcSchema } from "./rpc";
26
31
  // Modal related
27
32
  export type {
@@ -31,6 +36,12 @@ export type {
31
36
  ModalRpcStepsResultType,
32
37
  ModalStepTypes,
33
38
  } from "./rpc/displayModal";
39
+ // Sharing page related
40
+ export type {
41
+ DisplaySharingPageParamsType,
42
+ DisplaySharingPageResultType,
43
+ SharingPageProduct,
44
+ } from "./rpc/displaySharingPage";
34
45
  export type {
35
46
  DisplayEmbeddedWalletParamsType,
36
47
  DisplayEmbeddedWalletResultType,
@@ -65,9 +76,12 @@ export type {
65
76
  PrepareSsoReturnType,
66
77
  SsoMetadata,
67
78
  } from "./rpc/sso";
79
+ export type { UserReferralStatusType } from "./rpc/userReferralStatus";
68
80
  export type { WalletStatusReturnType } from "./rpc/walletStatus";
69
81
  // Tracking
70
82
  export type {
83
+ AttributionDefaults,
84
+ AttributionParams,
71
85
  TrackArrivalParams,
72
86
  TrackArrivalResult,
73
87
  UtmParams,
@@ -1,4 +1,5 @@
1
1
  import type { I18nConfig } from "../config";
2
+ import type { ResolvedSdkConfig } from "../resolvedConfig";
2
3
 
3
4
  /**
4
5
  * Event related to the iframe lifecycle
@@ -9,9 +10,9 @@ export type ClientLifecycleEvent =
9
10
  | CustomI18nEvent
10
11
  | RestoreBackupEvent
11
12
  | HearbeatEvent
12
- | HandshakeResponse
13
13
  | SsoRedirectCompleteEvent
14
- | DeepLinkFailedEvent;
14
+ | DeepLinkFailedEvent
15
+ | ResolvedConfigEvent;
15
16
 
16
17
  type CustomCssEvent = {
17
18
  clientLifecycle: "modal-css";
@@ -33,31 +34,6 @@ type HearbeatEvent = {
33
34
  data?: never;
34
35
  };
35
36
 
36
- type HandshakeResponse = {
37
- clientLifecycle: "handshake-response";
38
- data: {
39
- token: string;
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;
58
- };
59
- };
60
-
61
37
  type SsoRedirectCompleteEvent = {
62
38
  clientLifecycle: "sso-redirect-complete";
63
39
  data: { compressed: string };
@@ -67,3 +43,29 @@ type DeepLinkFailedEvent = {
67
43
  clientLifecycle: "deep-link-failed";
68
44
  data: { originalUrl: string };
69
45
  };
46
+
47
+ type ResolvedConfigEvent = {
48
+ clientLifecycle: "resolved-config";
49
+ data: {
50
+ merchantId: string;
51
+ /** The domain the backend resolved this config for */
52
+ domain: string;
53
+ /** All domains registered for this merchant (for domain proof) */
54
+ allowedDomains: string[];
55
+ /** Full URL of the parent page (for interaction tracking) */
56
+ sourceUrl: string;
57
+ /**
58
+ * Pending merge token extracted from URL (?fmt= parameter).
59
+ * When present, listener should execute identity merge in background.
60
+ */
61
+ pendingMergeToken?: string;
62
+ /**
63
+ * Persistent per-origin anonymous id generated on the partner site
64
+ * (SDK-side localStorage). Propagated here so the listener can
65
+ * set it as an OpenPanel global property and stitch SDK events
66
+ * with listener events in the same funnel.
67
+ */
68
+ sdkAnonymousId?: string;
69
+ sdkConfig?: ResolvedSdkConfig;
70
+ };
71
+ };
@@ -8,7 +8,6 @@ export type IFrameLifecycleEvent =
8
8
  data?: never;
9
9
  }
10
10
  | DoBackupEvent
11
- | HandshakeRequestEvent
12
11
  | RedirectRequestEvent;
13
12
 
14
13
  type DoBackupEvent = {
@@ -16,13 +15,6 @@ type DoBackupEvent = {
16
15
  data: { backup?: string };
17
16
  };
18
17
 
19
- type HandshakeRequestEvent = {
20
- iframeLifecycle: "handshake";
21
- data: {
22
- token: string;
23
- };
24
- };
25
-
26
18
  type RedirectRequestEvent = {
27
19
  iframeLifecycle: "redirect";
28
20
  data: {
@@ -37,5 +29,12 @@ type RedirectRequestEvent = {
37
29
  * Used when redirecting out of social browsers to preserve identity across contexts
38
30
  */
39
31
  mergeToken?: string;
32
+ /**
33
+ * When true, open the URL in a new tab via window.open(_blank)
34
+ * instead of navigating the current page.
35
+ * Requires the postMessage to include user activation delegation
36
+ * (includeUserActivation: true) so Safari allows the popup.
37
+ */
38
+ openInNewTab?: boolean;
40
39
  };
41
40
  };
@@ -0,0 +1,138 @@
1
+ import type { Currency, Language } from "./config";
2
+ import type { AttributionDefaults } from "./tracking";
3
+
4
+ /**
5
+ * Response from the merchant resolve endpoint
6
+ * @category Config
7
+ */
8
+ export type MerchantConfigResponse = {
9
+ merchantId: string;
10
+ name: string;
11
+ domain: string;
12
+ allowedDomains: string[];
13
+ sdkConfig?: ResolvedSdkConfig;
14
+ };
15
+
16
+ /**
17
+ * Resolved placement config from backend
18
+ * Translations already flattened: default + lang-specific merged into one record
19
+ * @category Config
20
+ */
21
+ export type ResolvedPlacement = {
22
+ /** Per-component configuration within this placement */
23
+ components?: {
24
+ buttonShare?: {
25
+ text?: string;
26
+ noRewardText?: string;
27
+ clickAction?: "embedded-wallet" | "share-modal" | "sharing-page";
28
+ useReward?: boolean;
29
+ css?: string;
30
+ };
31
+ buttonWallet?: {
32
+ position?: "right" | "left";
33
+ css?: string;
34
+ };
35
+ openInApp?: {
36
+ text?: string;
37
+ css?: string;
38
+ };
39
+ postPurchase?: {
40
+ badgeText?: string;
41
+ refereeText?: string;
42
+ refereeNoRewardText?: string;
43
+ referrerText?: string;
44
+ referrerNoRewardText?: string;
45
+ ctaText?: string;
46
+ ctaNoRewardText?: string;
47
+ css?: string;
48
+ };
49
+ banner?: {
50
+ referralTitle?: string;
51
+ referralDescription?: string;
52
+ referralCta?: string;
53
+ inappTitle?: string;
54
+ inappDescription?: string;
55
+ inappCta?: string;
56
+ css?: string;
57
+ };
58
+ };
59
+ targetInteraction?: string;
60
+ /** Already flattened: default + lang-specific merged into one record */
61
+ translations?: Record<string, string>;
62
+ /** Global placement CSS (applied to modals/listener) */
63
+ css?: string;
64
+ };
65
+
66
+ /**
67
+ * Resolved SDK config from backend `/resolve` endpoint
68
+ * Language resolution and translation merging already applied
69
+ * @category Config
70
+ */
71
+ export type ResolvedSdkConfig = {
72
+ name?: string;
73
+ logoUrl?: string;
74
+ homepageLink?: string;
75
+ currency?: Currency;
76
+ lang?: Language;
77
+ /** When true, all SDK components should be hidden */
78
+ hidden?: boolean;
79
+ css?: string;
80
+ translations?: Record<string, string>;
81
+ placements?: Record<string, ResolvedPlacement>;
82
+ /** Global component defaults (used when no placement override exists) */
83
+ components?: ResolvedPlacement["components"];
84
+ /**
85
+ * Default attribution params applied when building outbound sharing URLs.
86
+ * Per-call overrides win over these backend defaults; `utm_content` is
87
+ * intentionally excluded (per-content/per-product, never a merchant default).
88
+ */
89
+ attribution?: AttributionDefaults;
90
+ };
91
+
92
+ /**
93
+ * Internal SDK config store state
94
+ * Merged config: backend > SDK static > defaults
95
+ * Components subscribe to this reactively
96
+ * @category Config
97
+ */
98
+ export type SdkResolvedConfig = {
99
+ /** Whether the backend config has been resolved */
100
+ isResolved: boolean;
101
+
102
+ /** Merchant ID from resolution */
103
+ merchantId: string;
104
+
105
+ /** Domain returned by the resolve endpoint */
106
+ domain?: string;
107
+
108
+ /** Domains allowed for this merchant (used by iframe trust check) */
109
+ allowedDomains?: string[];
110
+
111
+ /** Whether the resolve returned a backend sdkConfig object */
112
+ hasRawSdkConfig?: boolean;
113
+
114
+ /** Merged metadata fields */
115
+ name?: string;
116
+ logoUrl?: string;
117
+ homepageLink?: string;
118
+ lang?: Language;
119
+ currency?: Currency;
120
+
121
+ /** When true, all SDK components should be hidden */
122
+ hidden?: boolean;
123
+
124
+ /** Global CSS from backend config (passed to iframe) */
125
+ css?: string;
126
+
127
+ /** Global translations (for reference / component fallback) */
128
+ translations?: Record<string, string>;
129
+
130
+ /** Named placements (keyed by placement ID) */
131
+ placements?: Record<string, ResolvedPlacement>;
132
+
133
+ /** Global component defaults (fallback for placement-level overrides) */
134
+ components?: ResolvedPlacement["components"];
135
+
136
+ /** Merged attribution defaults: backend > SDK static config */
137
+ attribution?: AttributionDefaults;
138
+ };