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

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 (60) hide show
  1. package/cdn/bundle.js +3 -55
  2. package/dist/actions-D4aBXbdp.cjs +1 -0
  3. package/dist/actions-Dq_uN-wn.js +1 -0
  4. package/dist/actions.cjs +1 -1
  5. package/dist/actions.d.cts +3 -3
  6. package/dist/actions.d.ts +3 -3
  7. package/dist/actions.js +1 -1
  8. package/dist/bundle.cjs +1 -1
  9. package/dist/bundle.d.cts +4 -4
  10. package/dist/bundle.d.ts +4 -4
  11. package/dist/bundle.js +1 -1
  12. package/dist/{computeLegacyProductId-BP-ciVsp.d.cts → index-BV5D9DsW.d.ts} +71 -3
  13. package/dist/{siweAuthenticate-yITE-iKh.d.cts → index-BphwTmKA.d.cts} +115 -4
  14. package/dist/{computeLegacyProductId-DiJd7RNo.d.ts → index-Dwmo109y.d.cts} +71 -3
  15. package/dist/{siweAuthenticate-CDCsp8EJ.d.ts → index-_f8EuN_1.d.ts} +115 -4
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.d.cts +3 -3
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +1 -1
  20. package/dist/{openSso-B8v3Vtnh.d.ts → openSso-BwEK2M98.d.cts} +174 -7
  21. package/dist/{openSso-n_B4LSuW.d.cts → openSso-C1Wzl5-i.d.ts} +174 -7
  22. package/dist/src-B3Dusips.cjs +13 -0
  23. package/dist/src-CnnhYPyK.js +13 -0
  24. package/dist/trackEvent-BqJqRZ-u.cjs +1 -0
  25. package/dist/trackEvent-Bqq4jd6R.js +1 -0
  26. package/package.json +9 -10
  27. package/src/actions/displaySharingPage.ts +49 -0
  28. package/src/actions/getMerchantInformation.test.ts +13 -1
  29. package/src/actions/getMerchantInformation.ts +20 -5
  30. package/src/actions/getMergeToken.ts +33 -0
  31. package/src/actions/getUserReferralStatus.ts +42 -0
  32. package/src/actions/index.ts +8 -1
  33. package/src/actions/referral/setupReferral.test.ts +79 -0
  34. package/src/actions/referral/setupReferral.ts +32 -0
  35. package/src/clients/createIFrameFrakClient.ts +5 -2
  36. package/src/clients/transports/iframeLifecycleManager.test.ts +14 -14
  37. package/src/clients/transports/iframeLifecycleManager.ts +35 -9
  38. package/src/index.ts +12 -1
  39. package/src/stubs/rrweb.ts +9 -0
  40. package/src/types/index.ts +7 -0
  41. package/src/types/lifecycle/iframe.ts +7 -0
  42. package/src/types/resolvedConfig.ts +26 -2
  43. package/src/types/rpc/displaySharingPage.ts +82 -0
  44. package/src/types/rpc/embedded/index.ts +1 -1
  45. package/src/types/rpc/userReferralStatus.ts +20 -0
  46. package/src/types/rpc.ts +47 -0
  47. package/src/utils/cache/index.ts +7 -0
  48. package/src/utils/cache/lruMap.test.ts +55 -0
  49. package/src/utils/cache/lruMap.ts +38 -0
  50. package/src/utils/cache/withCache.test.ts +168 -0
  51. package/src/utils/cache/withCache.ts +124 -0
  52. package/src/utils/inAppBrowser.ts +60 -0
  53. package/src/utils/index.ts +6 -0
  54. package/src/utils/sdkConfigStore.ts +21 -35
  55. package/dist/setupClient-Dr_UYfTD.cjs +0 -13
  56. package/dist/setupClient-TuhDjVJx.js +0 -13
  57. package/dist/siweAuthenticate-0UPcUqI1.js +0 -1
  58. package/dist/siweAuthenticate-CfQibjZR.cjs +0 -1
  59. package/dist/trackEvent-5j5kkOCj.js +0 -1
  60. package/dist/trackEvent-B2uom25e.cjs +0 -1
@@ -0,0 +1,79 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { REFERRAL_SUCCESS_EVENT, setupReferral } from "./setupReferral";
3
+
4
+ vi.mock("./referralInteraction", () => ({
5
+ referralInteraction: vi.fn(),
6
+ }));
7
+
8
+ import { referralInteraction } from "./referralInteraction";
9
+
10
+ describe("setupReferral", () => {
11
+ beforeEach(() => {
12
+ vi.clearAllMocks();
13
+ });
14
+
15
+ afterEach(() => {
16
+ vi.restoreAllMocks();
17
+ });
18
+
19
+ it("should dispatch referral success event on successful referral", async () => {
20
+ vi.mocked(referralInteraction).mockResolvedValue("success");
21
+ const listener = vi.fn();
22
+ window.addEventListener(REFERRAL_SUCCESS_EVENT, listener);
23
+
24
+ await setupReferral({ config: {} } as any);
25
+
26
+ expect(listener).toHaveBeenCalledTimes(1);
27
+ expect(listener).toHaveBeenCalledWith(expect.any(Event));
28
+
29
+ window.removeEventListener(REFERRAL_SUCCESS_EVENT, listener);
30
+ });
31
+
32
+ it("should not dispatch event when referral state is not success", async () => {
33
+ vi.mocked(referralInteraction).mockResolvedValue("no-referrer");
34
+ const listener = vi.fn();
35
+ window.addEventListener(REFERRAL_SUCCESS_EVENT, listener);
36
+
37
+ await setupReferral({ config: {} } as any);
38
+
39
+ expect(listener).not.toHaveBeenCalled();
40
+
41
+ window.removeEventListener(REFERRAL_SUCCESS_EVENT, listener);
42
+ });
43
+
44
+ it("should not dispatch event when referral returns undefined", async () => {
45
+ vi.mocked(referralInteraction).mockResolvedValue(undefined);
46
+ const listener = vi.fn();
47
+ window.addEventListener(REFERRAL_SUCCESS_EVENT, listener);
48
+
49
+ await setupReferral({ config: {} } as any);
50
+
51
+ expect(listener).not.toHaveBeenCalled();
52
+
53
+ window.removeEventListener(REFERRAL_SUCCESS_EVENT, listener);
54
+ });
55
+
56
+ it("should silently catch errors and log warning", async () => {
57
+ vi.mocked(referralInteraction).mockRejectedValue(
58
+ new Error("network failure")
59
+ );
60
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
61
+ const listener = vi.fn();
62
+ window.addEventListener(REFERRAL_SUCCESS_EVENT, listener);
63
+
64
+ await setupReferral({ config: {} } as any);
65
+
66
+ expect(listener).not.toHaveBeenCalled();
67
+ expect(warnSpy).toHaveBeenCalledWith(
68
+ "[Frak] Referral setup failed",
69
+ expect.any(Error)
70
+ );
71
+
72
+ window.removeEventListener(REFERRAL_SUCCESS_EVENT, listener);
73
+ warnSpy.mockRestore();
74
+ });
75
+
76
+ it("should export the correct event name constant", () => {
77
+ expect(REFERRAL_SUCCESS_EVENT).toBe("frak:referral-success");
78
+ });
79
+ });
@@ -0,0 +1,32 @@
1
+ import type { FrakClient } from "../../types";
2
+ import { referralInteraction } from "./referralInteraction";
3
+
4
+ /**
5
+ * Custom event name dispatched on successful referral processing.
6
+ *
7
+ * Fired once per page load when a valid referral context is found in the URL
8
+ * and successfully tracked. Consumers (e.g. `<frak-banner>`) listen for this
9
+ * to display a referral success message.
10
+ */
11
+ export const REFERRAL_SUCCESS_EVENT = "frak:referral-success";
12
+
13
+ /**
14
+ * Process referral context and emit a DOM event on success.
15
+ *
16
+ * - Calls {@link referralInteraction} to detect and track any referral in the URL
17
+ * - On `"success"`, dispatches a bare {@link REFERRAL_SUCCESS_EVENT} on `window`
18
+ * - Silently swallows errors (fire-and-forget during SDK init)
19
+ *
20
+ * @param client - The initialized Frak client
21
+ */
22
+ export async function setupReferral(client: FrakClient): Promise<void> {
23
+ try {
24
+ const state = await referralInteraction(client);
25
+
26
+ if (state === "success") {
27
+ window.dispatchEvent(new Event(REFERRAL_SUCCESS_EVENT));
28
+ }
29
+ } catch (error) {
30
+ console.warn("[Frak] Referral setup failed", error);
31
+ }
32
+ }
@@ -12,6 +12,7 @@ import type { FrakWalletSdkConfig } from "../types/config";
12
12
  import type { SdkResolvedConfig } from "../types/resolvedConfig";
13
13
  import type { IFrameRpcSchema } from "../types/rpc";
14
14
  import { getClientId } from "../utils";
15
+ import { clearAllCache } from "../utils/cache";
15
16
  import { BACKUP_KEY } from "../utils/constants";
16
17
  import { sdkConfigStore } from "../utils/sdkConfigStore";
17
18
  import { setupSsoUrlListener } from "../utils/ssoUrlListener";
@@ -124,9 +125,9 @@ export function createIFrameFrakClient({
124
125
  ],
125
126
  // Add lifecycle handlers to process iframe lifecycle events
126
127
  lifecycleHandlers: {
127
- iframeLifecycle: async (event, _context) => {
128
+ iframeLifecycle: (event, _context) => {
128
129
  // Delegate to lifecycle manager (cast for type compatibility)
129
- await lifecycleManager.handleEvent(event);
130
+ lifecycleManager.handleEvent(event);
130
131
  },
131
132
  },
132
133
  });
@@ -138,6 +139,7 @@ export function createIFrameFrakClient({
138
139
  stopHeartbeat();
139
140
  rpcClient.cleanup();
140
141
  iframe.remove();
142
+ clearAllCache();
141
143
  sdkConfigStore.clearCache();
142
144
  sdkConfigStore.reset();
143
145
  };
@@ -316,6 +318,7 @@ async function postConnectionSetup({
316
318
  css: raw.css,
317
319
  translations: raw.translations,
318
320
  placements: raw.placements,
321
+ components: raw.components,
319
322
  }
320
323
  : {
321
324
  isResolved: true,
@@ -102,7 +102,7 @@ describe("createIFrameLifecycleManager", () => {
102
102
  iframeLifecycle: "connected" as const,
103
103
  };
104
104
 
105
- await manager.handleEvent(event);
105
+ manager.handleEvent(event);
106
106
 
107
107
  await expect(manager.isConnected).resolves.toBe(true);
108
108
  });
@@ -126,7 +126,7 @@ describe("createIFrameLifecycleManager", () => {
126
126
  data: { backup },
127
127
  };
128
128
 
129
- await manager.handleEvent(event);
129
+ manager.handleEvent(event);
130
130
 
131
131
  expect(localStorage.getItem("frak-backup-key")).toBe(backup);
132
132
  });
@@ -150,7 +150,7 @@ describe("createIFrameLifecycleManager", () => {
150
150
  data: {},
151
151
  };
152
152
 
153
- await manager.handleEvent(event);
153
+ manager.handleEvent(event);
154
154
 
155
155
  expect(localStorage.getItem("frak-backup-key")).toBeNull();
156
156
  });
@@ -173,7 +173,7 @@ describe("createIFrameLifecycleManager", () => {
173
173
  iframeLifecycle: "remove-backup" as const,
174
174
  };
175
175
 
176
- await manager.handleEvent(event);
176
+ manager.handleEvent(event);
177
177
 
178
178
  expect(localStorage.getItem("frak-backup-key")).toBeNull();
179
179
  });
@@ -198,7 +198,7 @@ describe("createIFrameLifecycleManager", () => {
198
198
  iframeLifecycle: "show" as const,
199
199
  };
200
200
 
201
- await manager.handleEvent(event);
201
+ manager.handleEvent(event);
202
202
 
203
203
  expect(changeIframeVisibility).toHaveBeenCalledWith({
204
204
  iframe: mockIframe,
@@ -224,7 +224,7 @@ describe("createIFrameLifecycleManager", () => {
224
224
  iframeLifecycle: "hide" as const,
225
225
  };
226
226
 
227
- await manager.handleEvent(event);
227
+ manager.handleEvent(event);
228
228
 
229
229
  expect(changeIframeVisibility).toHaveBeenCalledWith({
230
230
  iframe: mockIframe,
@@ -259,7 +259,7 @@ describe("createIFrameLifecycleManager", () => {
259
259
  },
260
260
  };
261
261
 
262
- await manager.handleEvent(event);
262
+ manager.handleEvent(event);
263
263
 
264
264
  expect(window.location.href).toBe(
265
265
  "https://redirect.com/?u=https%3A%2F%2Foriginal.com"
@@ -291,7 +291,7 @@ describe("createIFrameLifecycleManager", () => {
291
291
  },
292
292
  };
293
293
 
294
- await manager.handleEvent(event);
294
+ manager.handleEvent(event);
295
295
 
296
296
  expect(window.location.href).toBe("https://redirect.com/path");
297
297
  });
@@ -325,7 +325,7 @@ describe("createIFrameLifecycleManager", () => {
325
325
  },
326
326
  };
327
327
 
328
- await manager.handleEvent(event);
328
+ manager.handleEvent(event);
329
329
 
330
330
  expect(triggerDeepLinkWithFallback).toHaveBeenCalledWith(
331
331
  "frakwallet://wallet",
@@ -370,7 +370,7 @@ describe("createIFrameLifecycleManager", () => {
370
370
  },
371
371
  };
372
372
 
373
- await manager.handleEvent(event);
373
+ manager.handleEvent(event);
374
374
 
375
375
  // Extract the onFallback callback from the mock call
376
376
  const callArgs = (triggerDeepLinkWithFallback as any).mock.calls[0];
@@ -419,7 +419,7 @@ describe("createIFrameLifecycleManager", () => {
419
419
  },
420
420
  };
421
421
 
422
- await manager.handleEvent(event);
422
+ manager.handleEvent(event);
423
423
 
424
424
  // Should NOT call fallback detection
425
425
  expect(triggerDeepLinkWithFallback).not.toHaveBeenCalled();
@@ -445,7 +445,7 @@ describe("createIFrameLifecycleManager", () => {
445
445
  } as any;
446
446
 
447
447
  // Should not throw
448
- await expect(manager.handleEvent(event)).resolves.toBeUndefined();
448
+ expect(manager.handleEvent(event)).toBeUndefined();
449
449
  });
450
450
 
451
451
  test("should only process events with iframeLifecycle", async () => {
@@ -463,13 +463,13 @@ describe("createIFrameLifecycleManager", () => {
463
463
  });
464
464
 
465
465
  // Event without iframeLifecycle
466
- await manager.handleEvent({ randomEvent: "show" } as any);
466
+ manager.handleEvent({ randomEvent: "show" } as any);
467
467
 
468
468
  // changeIframeVisibility should not be called
469
469
  expect(changeIframeVisibility).not.toHaveBeenCalled();
470
470
 
471
471
  // Event with iframeLifecycle
472
- await manager.handleEvent({ iframeLifecycle: "show" as const });
472
+ manager.handleEvent({ iframeLifecycle: "show" as const });
473
473
 
474
474
  // Now it should be called
475
475
  expect(changeIframeVisibility).toHaveBeenCalled();
@@ -32,7 +32,7 @@ const isIOSInAppBrowser = (() => {
32
32
  /** @ignore */
33
33
  export type IframeLifecycleManager = {
34
34
  isConnected: Promise<boolean>;
35
- handleEvent: (messageEvent: FrakLifecycleEvent) => Promise<void>;
35
+ handleEvent: (messageEvent: FrakLifecycleEvent) => void;
36
36
  };
37
37
 
38
38
  /**
@@ -59,12 +59,12 @@ function computeRedirectUrl(
59
59
  return baseRedirectUrl;
60
60
  }
61
61
 
62
- redirectUrl.searchParams.delete("u");
63
- 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);
64
65
 
65
- if (mergeToken) {
66
- redirectUrl.searchParams.append("fmt", mergeToken);
67
- }
66
+ redirectUrl.searchParams.delete("u");
67
+ redirectUrl.searchParams.append("u", finalPageUrl);
68
68
 
69
69
  return redirectUrl.toString();
70
70
  } catch {
@@ -93,6 +93,21 @@ function isSocialRedirect(url: string): boolean {
93
93
  return url.includes("/common/social");
94
94
  }
95
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
+
96
111
  /**
97
112
  * Handle redirect with deep link fallback
98
113
  */
@@ -100,8 +115,18 @@ function handleRedirect(
100
115
  iframe: HTMLIFrameElement,
101
116
  baseRedirectUrl: string,
102
117
  targetOrigin: string,
103
- mergeToken?: string
118
+ mergeToken?: string,
119
+ openInNewTab?: boolean
104
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
+
105
130
  if (isFrakDeepLink(baseRedirectUrl)) {
106
131
  const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);
107
132
  triggerDeepLinkWithFallback(finalUrl, {
@@ -143,7 +168,7 @@ export function createIFrameLifecycleManager({
143
168
  const isConnectedDeferred = new Deferred<boolean>();
144
169
 
145
170
  // Build the handler itself
146
- const handler = async (messageEvent: FrakLifecycleEvent) => {
171
+ const handler = (messageEvent: FrakLifecycleEvent) => {
147
172
  if (!("iframeLifecycle" in messageEvent)) return;
148
173
 
149
174
  const { iframeLifecycle: event, data } = messageEvent;
@@ -172,7 +197,8 @@ export function createIFrameLifecycleManager({
172
197
  iframe,
173
198
  data.baseRedirectUrl,
174
199
  targetOrigin,
175
- data.mergeToken
200
+ data.mergeToken,
201
+ data.openInNewTab
176
202
  );
177
203
  break;
178
204
  }
package/src/index.ts CHANGED
@@ -19,6 +19,9 @@ export type {
19
19
  DisplayEmbeddedWalletParamsType,
20
20
  DisplayEmbeddedWalletResultType,
21
21
  DisplayModalParamsType,
22
+ // RPC Sharing page
23
+ DisplaySharingPageParamsType,
24
+ DisplaySharingPageResultType,
22
25
  EmbeddedViewActionReferred,
23
26
  EmbeddedViewActionSharing,
24
27
  EstimatedReward,
@@ -68,6 +71,7 @@ export type {
68
71
  SendTransactionModalStepType,
69
72
  SendTransactionReturnType,
70
73
  SendTransactionTxType,
74
+ SharingPageProduct,
71
75
  SiweAuthenticateModalStepType,
72
76
  SiweAuthenticateReturnType,
73
77
  SiweAuthenticationParams,
@@ -76,8 +80,9 @@ export type {
76
80
  // Tracking
77
81
  TrackArrivalParams,
78
82
  TrackArrivalResult,
79
- UtmParams,
80
83
  // Rpc
84
+ UserReferralStatusType,
85
+ UtmParams,
81
86
  WalletStatusReturnType,
82
87
  } from "./types";
83
88
  export { isV1Context, isV2Context } from "./types";
@@ -88,6 +93,7 @@ export {
88
93
  base64urlEncode,
89
94
  baseIframeProps,
90
95
  type CompressedSsoData,
96
+ clearAllCache,
91
97
  compressJsonToB64,
92
98
  createIframe,
93
99
  DEEP_LINK_SCHEME,
@@ -100,15 +106,20 @@ export {
100
106
  formatAmount,
101
107
  generateSsoUrl,
102
108
  getBackendUrl,
109
+ getCache,
103
110
  getClientId,
104
111
  getCurrencyAmountKey,
105
112
  getSupportedCurrency,
106
113
  getSupportedLocale,
107
114
  isChromiumAndroid,
108
115
  isFrakDeepLink,
116
+ isInAppBrowser,
117
+ isIOS,
118
+ redirectToExternalBrowser,
109
119
  sdkConfigStore,
110
120
  toAndroidIntentUrl,
111
121
  trackEvent,
112
122
  triggerDeepLinkWithFallback,
123
+ withCache,
113
124
  } from "./utils";
114
125
  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
+ }
@@ -36,6 +36,12 @@ export type {
36
36
  ModalRpcStepsResultType,
37
37
  ModalStepTypes,
38
38
  } from "./rpc/displayModal";
39
+ // Sharing page related
40
+ export type {
41
+ DisplaySharingPageParamsType,
42
+ DisplaySharingPageResultType,
43
+ SharingPageProduct,
44
+ } from "./rpc/displaySharingPage";
39
45
  export type {
40
46
  DisplayEmbeddedWalletParamsType,
41
47
  DisplayEmbeddedWalletResultType,
@@ -70,6 +76,7 @@ export type {
70
76
  PrepareSsoReturnType,
71
77
  SsoMetadata,
72
78
  } from "./rpc/sso";
79
+ export type { UserReferralStatusType } from "./rpc/userReferralStatus";
73
80
  export type { WalletStatusReturnType } from "./rpc/walletStatus";
74
81
  // Tracking
75
82
  export type {
@@ -29,5 +29,12 @@ type RedirectRequestEvent = {
29
29
  * Used when redirecting out of social browsers to preserve identity across contexts
30
30
  */
31
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;
32
39
  };
33
40
  };
@@ -23,18 +23,37 @@ export type ResolvedPlacement = {
23
23
  buttonShare?: {
24
24
  text?: string;
25
25
  noRewardText?: string;
26
- clickAction?: "embedded-wallet" | "share-modal";
26
+ clickAction?: "embedded-wallet" | "share-modal" | "sharing-page";
27
27
  useReward?: boolean;
28
28
  css?: string;
29
29
  };
30
30
  buttonWallet?: {
31
- position?: "bottom-right" | "bottom-left";
31
+ position?: "right" | "left";
32
32
  css?: string;
33
33
  };
34
34
  openInApp?: {
35
35
  text?: string;
36
36
  css?: string;
37
37
  };
38
+ postPurchase?: {
39
+ badgeText?: string;
40
+ refereeText?: string;
41
+ refereeNoRewardText?: string;
42
+ referrerText?: string;
43
+ referrerNoRewardText?: string;
44
+ ctaText?: string;
45
+ ctaNoRewardText?: string;
46
+ css?: string;
47
+ };
48
+ banner?: {
49
+ referralTitle?: string;
50
+ referralDescription?: string;
51
+ referralCta?: string;
52
+ inappTitle?: string;
53
+ inappDescription?: string;
54
+ inappCta?: string;
55
+ css?: string;
56
+ };
38
57
  };
39
58
  targetInteraction?: string;
40
59
  /** Already flattened: default + lang-specific merged into one record */
@@ -59,6 +78,8 @@ export type ResolvedSdkConfig = {
59
78
  css?: string;
60
79
  translations?: Record<string, string>;
61
80
  placements?: Record<string, ResolvedPlacement>;
81
+ /** Global component defaults (used when no placement override exists) */
82
+ components?: ResolvedPlacement["components"];
62
83
  };
63
84
 
64
85
  /**
@@ -101,4 +122,7 @@ export type SdkResolvedConfig = {
101
122
 
102
123
  /** Named placements (keyed by placement ID) */
103
124
  placements?: Record<string, ResolvedPlacement>;
125
+
126
+ /** Global component defaults (fallback for placement-level overrides) */
127
+ components?: ResolvedPlacement["components"];
104
128
  };
@@ -0,0 +1,82 @@
1
+ import type { InteractionTypeKey } from "../../constants/interactionTypes";
2
+ import type { I18nConfig } from "../config";
3
+
4
+ /**
5
+ * Product information to display on the sharing page
6
+ * @group Sharing Page
7
+ */
8
+ export type SharingPageProduct = {
9
+ /**
10
+ * The product title / name
11
+ */
12
+ title: string;
13
+ /**
14
+ * Optional product image URL
15
+ */
16
+ imageUrl?: string;
17
+ /**
18
+ * Optional product-specific sharing link
19
+ * When provided and the product is selected, this link is used instead of the default sharing link
20
+ */
21
+ link?: string;
22
+ };
23
+
24
+ /**
25
+ * Parameters to display the sharing page
26
+ * @group Sharing Page
27
+ * @group RPC Schema
28
+ */
29
+ export type DisplaySharingPageParamsType = {
30
+ /**
31
+ * Products to showcase on the sharing page
32
+ * If provided, they will be displayed in a product card section
33
+ */
34
+ products?: SharingPageProduct[];
35
+ /**
36
+ * Optional link override for sharing
37
+ * If not provided, the sharing link will be generated from the current page URL + merchant context
38
+ */
39
+ link?: string;
40
+ /**
41
+ * Optional metadata overrides for the sharing page
42
+ */
43
+ metadata?: {
44
+ /**
45
+ * Logo override for the sharing page header
46
+ */
47
+ logo?: string;
48
+ /**
49
+ * Link to the homepage of the calling website
50
+ */
51
+ homepageLink?: string;
52
+ /**
53
+ * The target interaction behind this sharing page
54
+ */
55
+ targetInteraction?: InteractionTypeKey;
56
+ /**
57
+ * i18n overrides for the sharing page
58
+ */
59
+ i18n?: I18nConfig;
60
+ };
61
+ };
62
+
63
+ /**
64
+ * Result of the sharing page display
65
+ * @group Sharing Page
66
+ * @group RPC Schema
67
+ */
68
+ export type DisplaySharingPageResultType = {
69
+ /**
70
+ * The action the user took
71
+ * - "shared": User used the native share dialog
72
+ * - "copied": User copied the link to clipboard
73
+ * - "dismissed": User dismissed the sharing page without acting
74
+ */
75
+ action: "shared" | "copied" | "dismissed";
76
+ /**
77
+ * The install URL for the Frak app
78
+ * Can be used as a fallback to redirect the user to the install page
79
+ * from the merchant's top-level page (e.g. via `window.location.href`)
80
+ */
81
+ installUrl?: string;
82
+ };
@@ -9,10 +9,10 @@ import type {
9
9
  import type { LoggedOutEmbeddedView } from "./loggedOut";
10
10
 
11
11
  export type {
12
+ EmbeddedViewActionReferred,
12
13
  EmbeddedViewActionSharing,
13
14
  LoggedInEmbeddedView,
14
15
  LoggedOutEmbeddedView,
15
- EmbeddedViewActionReferred,
16
16
  };
17
17
 
18
18
  /**
@@ -0,0 +1,20 @@
1
+ /**
2
+ * User referral status returned by `frak_getUserReferralStatus`.
3
+ *
4
+ * Generic referral context for the current user on a merchant.
5
+ * Used by components like `<frak-post-purchase>` and `<frak-referred-banner>`
6
+ * to adapt their display based on the user's referral relationship.
7
+ *
8
+ * Returns `null` when the user's identity cannot be resolved
9
+ * (e.g. no clientId and no wallet session).
10
+ *
11
+ * @group RPC Schema
12
+ */
13
+ export type UserReferralStatusType = {
14
+ /**
15
+ * Whether the user was referred to this merchant by someone else.
16
+ *
17
+ * `true` means a referral link exists where this user is the referee.
18
+ */
19
+ isReferred: boolean;
20
+ };