@frak-labs/core-sdk 0.1.0-beta.8d103039 → 0.1.0-beta.d9302e66

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 (67) hide show
  1. package/package.json +20 -16
  2. package/src/actions/displayEmbeddedWallet.test.ts +194 -0
  3. package/src/actions/displayModal.test.ts +387 -0
  4. package/src/actions/getProductInformation.test.ts +133 -0
  5. package/src/actions/index.ts +19 -19
  6. package/src/actions/openSso.test.ts +407 -0
  7. package/src/actions/prepareSso.test.ts +223 -0
  8. package/src/actions/referral/processReferral.ts +1 -1
  9. package/src/actions/referral/referralInteraction.ts +1 -1
  10. package/src/actions/sendInteraction.test.ts +219 -0
  11. package/src/actions/trackPurchaseStatus.test.ts +287 -0
  12. package/src/actions/watchWalletStatus.test.ts +372 -0
  13. package/src/bundle.ts +1 -1
  14. package/src/clients/createIFrameFrakClient.ts +2 -2
  15. package/src/clients/index.ts +1 -1
  16. package/src/clients/setupClient.ts +3 -1
  17. package/src/clients/transports/iframeLifecycleManager.ts +3 -1
  18. package/src/index.ts +72 -74
  19. package/src/interactions/index.ts +2 -2
  20. package/src/interactions/pressEncoder.test.ts +215 -0
  21. package/src/interactions/pressEncoder.ts +1 -1
  22. package/src/interactions/purchaseEncoder.test.ts +291 -0
  23. package/src/interactions/purchaseEncoder.ts +8 -3
  24. package/src/interactions/referralEncoder.test.ts +170 -0
  25. package/src/interactions/retailEncoder.test.ts +107 -0
  26. package/src/interactions/retailEncoder.ts +1 -1
  27. package/src/interactions/webshopEncoder.test.ts +56 -0
  28. package/src/types/index.ts +51 -50
  29. package/src/types/lifecycle/index.ts +1 -1
  30. package/src/types/rpc/embedded/loggedIn.ts +1 -1
  31. package/src/types/rpc/embedded/loggedOut.ts +1 -1
  32. package/src/types/rpc/modal/index.ts +11 -11
  33. package/src/utils/FrakContext.test.ts +338 -0
  34. package/src/utils/FrakContext.ts +8 -2
  35. package/src/utils/compression/b64.test.ts +181 -0
  36. package/src/utils/compression/compress.test.ts +123 -0
  37. package/src/utils/compression/decompress.test.ts +145 -0
  38. package/src/utils/compression/index.ts +1 -1
  39. package/src/utils/computeProductId.test.ts +80 -0
  40. package/src/utils/constants.test.ts +23 -0
  41. package/src/utils/formatAmount.test.ts +113 -0
  42. package/src/utils/getCurrencyAmountKey.test.ts +44 -0
  43. package/src/utils/getSupportedCurrency.test.ts +51 -0
  44. package/src/utils/getSupportedLocale.test.ts +64 -0
  45. package/src/utils/iframeHelper.test.ts +450 -0
  46. package/src/utils/iframeHelper.ts +4 -3
  47. package/src/utils/index.ts +12 -12
  48. package/src/utils/sso.test.ts +361 -0
  49. package/src/utils/trackEvent.test.ts +162 -0
  50. package/cdn/bundle.js +0 -19
  51. package/cdn/bundle.js.LICENSE.txt +0 -10
  52. package/dist/actions.cjs +0 -1
  53. package/dist/actions.d.cts +0 -1481
  54. package/dist/actions.d.ts +0 -1481
  55. package/dist/actions.js +0 -1
  56. package/dist/bundle.cjs +0 -13
  57. package/dist/bundle.d.cts +0 -2087
  58. package/dist/bundle.d.ts +0 -2087
  59. package/dist/bundle.js +0 -13
  60. package/dist/index.cjs +0 -13
  61. package/dist/index.d.cts +0 -1387
  62. package/dist/index.d.ts +0 -1387
  63. package/dist/index.js +0 -13
  64. package/dist/interactions.cjs +0 -1
  65. package/dist/interactions.d.cts +0 -182
  66. package/dist/interactions.d.ts +0 -182
  67. package/dist/interactions.js +0 -1
@@ -1,70 +1,71 @@
1
1
  // Rpc related
2
- export type { WalletStatusReturnType } from "./rpc/walletStatus";
3
- export type {
4
- DisplayEmbeddedWalletParamsType,
5
- DisplayEmbeddedWalletResultType,
6
- LoggedOutEmbeddedView,
7
- LoggedInEmbeddedView,
8
- EmbeddedViewActionReferred,
9
- EmbeddedViewActionSharing,
10
- } from "./rpc/embedded";
11
- export type {
12
- SsoMetadata,
13
- OpenSsoParamsType,
14
- OpenSsoReturnType,
15
- PrepareSsoParamsType,
16
- PrepareSsoReturnType,
17
- } from "./rpc/sso";
18
- export type {
19
- TokenAmountType,
20
- GetProductInformationReturnType,
21
- } from "./rpc/productInformation";
22
- export type {
23
- PreparedInteraction,
24
- SendInteractionParamsType,
25
- SendInteractionReturnType,
26
- } from "./rpc/interaction";
27
- export type { IFrameRpcSchema } from "./rpc";
2
+
28
3
  // Client related
29
4
  export type { FrakClient } from "./client";
30
- export type { IFrameTransport, FrakLifecycleEvent } from "./transport";
31
5
  export type {
32
- IFrameLifecycleEvent,
33
- ClientLifecycleEvent,
34
- } from "./lifecycle";
6
+ CompressedData,
7
+ HashProtectedData,
8
+ KeyProvider,
9
+ } from "./compression";
35
10
  export type {
36
- FrakWalletSdkConfig,
37
11
  Currency,
38
- Language,
12
+ FrakWalletSdkConfig,
39
13
  I18nConfig,
14
+ Language,
40
15
  LocalizedI18nConfig,
41
16
  } from "./config";
17
+ // Utils
18
+ export type { FrakContext } from "./context";
42
19
  export type {
43
- CompressedData,
44
- HashProtectedData,
45
- KeyProvider,
46
- } from "./compression";
20
+ ClientLifecycleEvent,
21
+ IFrameLifecycleEvent,
22
+ } from "./lifecycle";
23
+ export type { IFrameRpcSchema } from "./rpc";
47
24
  // Modal related
48
25
  export type {
49
- ModalStepTypes,
50
- ModalRpcStepsInput,
51
- ModalRpcStepsResultType,
52
26
  DisplayModalParamsType,
53
27
  ModalRpcMetadata,
28
+ ModalRpcStepsInput,
29
+ ModalRpcStepsResultType,
30
+ ModalStepTypes,
54
31
  } from "./rpc/displayModal";
55
32
  export type {
56
- ModalStepMetadata,
33
+ DisplayEmbeddedWalletParamsType,
34
+ DisplayEmbeddedWalletResultType,
35
+ EmbeddedViewActionReferred,
36
+ EmbeddedViewActionSharing,
37
+ LoggedInEmbeddedView,
38
+ LoggedOutEmbeddedView,
39
+ } from "./rpc/embedded";
40
+ export type {
41
+ PreparedInteraction,
42
+ SendInteractionParamsType,
43
+ SendInteractionReturnType,
44
+ } from "./rpc/interaction";
45
+ export type {
46
+ FinalActionType,
47
+ FinalModalStepType,
57
48
  LoginModalStepType,
58
- SiweAuthenticateModalStepType,
59
- SiweAuthenticationParams,
60
- SiweAuthenticateReturnType,
61
- SendTransactionTxType,
49
+ ModalStepMetadata,
50
+ OpenInteractionSessionModalStepType,
51
+ OpenInteractionSessionReturnType,
62
52
  SendTransactionModalStepType,
63
53
  SendTransactionReturnType,
64
- OpenInteractionSessionReturnType,
65
- OpenInteractionSessionModalStepType,
66
- FinalModalStepType,
67
- FinalActionType,
54
+ SendTransactionTxType,
55
+ SiweAuthenticateModalStepType,
56
+ SiweAuthenticateReturnType,
57
+ SiweAuthenticationParams,
68
58
  } from "./rpc/modal";
69
- // Utils
70
- export type { FrakContext } from "./context";
59
+ export type {
60
+ GetProductInformationReturnType,
61
+ TokenAmountType,
62
+ } from "./rpc/productInformation";
63
+ export type {
64
+ OpenSsoParamsType,
65
+ OpenSsoReturnType,
66
+ PrepareSsoParamsType,
67
+ PrepareSsoReturnType,
68
+ SsoMetadata,
69
+ } from "./rpc/sso";
70
+ export type { WalletStatusReturnType } from "./rpc/walletStatus";
71
+ export type { FrakLifecycleEvent, IFrameTransport } from "./transport";
@@ -1,2 +1,2 @@
1
- export type { IFrameLifecycleEvent } from "./iframe";
2
1
  export type { ClientLifecycleEvent } from "./client";
2
+ export type { IFrameLifecycleEvent } from "./iframe";
@@ -17,7 +17,7 @@ export type EmbeddedViewActionSharing = {
17
17
  popupTitle?: string;
18
18
  /**
19
19
  * The text that will be shared alongside the link.
20
- * Can contain the variable {LINK} to specify where the link is placed, otherwise it will be added at the end
20
+ * Can contain the variable `{LINK}` to specify where the link is placed, otherwise it will be added at the end
21
21
  * @deprecated Use the top level `config.metadata.i18n` instead
22
22
  */
23
23
  text?: string;
@@ -10,7 +10,7 @@ export type LoggedOutEmbeddedView = {
10
10
  /**
11
11
  * The main CTA for the logged out view
12
12
  * - can include some variable, available ones are:
13
- * - {REWARD} -> The maximum reward a user can receive when interacting on your website
13
+ * - `{REWARD}` -> The maximum reward a user can receive when interacting on your website
14
14
  * - can be formatted in markdown
15
15
  *
16
16
  * If not set, it will default to a internationalized message
@@ -1,20 +1,20 @@
1
+ export type {
2
+ FinalActionType,
3
+ FinalModalStepType,
4
+ } from "./final";
5
+ export type { ModalStepMetadata } from "./generic";
1
6
  export type { LoginModalStepType } from "./login";
7
+ export type {
8
+ OpenInteractionSessionModalStepType,
9
+ OpenInteractionSessionReturnType,
10
+ } from "./openSession";
2
11
  export type {
3
12
  SiweAuthenticateModalStepType,
4
- SiweAuthenticationParams,
5
13
  SiweAuthenticateReturnType,
14
+ SiweAuthenticationParams,
6
15
  } from "./siweAuthenticate";
7
16
  export type {
8
17
  SendTransactionModalStepType,
9
- SendTransactionTxType,
10
18
  SendTransactionReturnType,
19
+ SendTransactionTxType,
11
20
  } from "./transaction";
12
- export type {
13
- OpenInteractionSessionReturnType,
14
- OpenInteractionSessionModalStepType,
15
- } from "./openSession";
16
- export type { ModalStepMetadata } from "./generic";
17
- export type {
18
- FinalModalStepType,
19
- FinalActionType,
20
- } from "./final";
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Tests for FrakContextManager utility
3
+ * Tests Frak context compression, URL parsing, and management
4
+ */
5
+
6
+ import type { Address } from "viem";
7
+ import {
8
+ afterEach,
9
+ beforeEach,
10
+ describe,
11
+ expect,
12
+ it,
13
+ vi,
14
+ } from "../../tests/vitest-fixtures";
15
+ import type { FrakContext } from "../types";
16
+ import { FrakContextManager } from "./FrakContext";
17
+
18
+ describe("FrakContextManager", () => {
19
+ let consoleErrorSpy: any;
20
+
21
+ beforeEach(() => {
22
+ consoleErrorSpy = vi
23
+ .spyOn(console, "error")
24
+ .mockImplementation(() => {});
25
+ });
26
+
27
+ afterEach(() => {
28
+ consoleErrorSpy.mockRestore();
29
+ });
30
+
31
+ describe("compress", () => {
32
+ it("should compress context with referrer address", () => {
33
+ const context: Partial<FrakContext> = {
34
+ r: "0x1234567890123456789012345678901234567890" as Address,
35
+ };
36
+
37
+ const result = FrakContextManager.compress(context);
38
+
39
+ expect(result).toBeDefined();
40
+ expect(typeof result).toBe("string");
41
+ expect(result?.length).toBeGreaterThan(0);
42
+ // Base64url should not contain +, /, or =
43
+ expect(result).not.toMatch(/[+/=]/);
44
+ });
45
+
46
+ it("should return undefined when context has no referrer", () => {
47
+ const context: Partial<FrakContext> = {};
48
+
49
+ const result = FrakContextManager.compress(context);
50
+
51
+ expect(result).toBeUndefined();
52
+ });
53
+
54
+ it("should return undefined when context is undefined", () => {
55
+ const result = FrakContextManager.compress(undefined);
56
+
57
+ expect(result).toBeUndefined();
58
+ });
59
+
60
+ it("should handle compression errors gracefully", () => {
61
+ const invalidContext = {
62
+ r: "invalid-address" as Address,
63
+ };
64
+
65
+ const result = FrakContextManager.compress(invalidContext);
66
+
67
+ expect(consoleErrorSpy).toHaveBeenCalled();
68
+ expect(result).toBeUndefined();
69
+ });
70
+ });
71
+
72
+ describe("decompress", () => {
73
+ it("should decompress valid base64url context", () => {
74
+ // First compress a context
75
+ const originalContext: FrakContext = {
76
+ r: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
77
+ };
78
+ const compressed = FrakContextManager.compress(originalContext);
79
+
80
+ // Then decompress it
81
+ const result = FrakContextManager.decompress(compressed);
82
+
83
+ expect(result).toBeDefined();
84
+ expect(result?.r).toBe(
85
+ "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
86
+ );
87
+ });
88
+
89
+ it("should return undefined for empty string", () => {
90
+ const result = FrakContextManager.decompress("");
91
+
92
+ expect(result).toBeUndefined();
93
+ });
94
+
95
+ it("should return undefined for undefined input", () => {
96
+ const result = FrakContextManager.decompress(undefined);
97
+
98
+ expect(result).toBeUndefined();
99
+ });
100
+
101
+ it("should handle decompression errors gracefully", () => {
102
+ const result = FrakContextManager.decompress(
103
+ "invalid-base64url!@#"
104
+ );
105
+
106
+ expect(consoleErrorSpy).toHaveBeenCalled();
107
+ expect(result).toBeUndefined();
108
+ });
109
+
110
+ it("should round-trip compress and decompress", () => {
111
+ const original: FrakContext = {
112
+ r: "0x1234567890123456789012345678901234567890" as Address,
113
+ };
114
+
115
+ const compressed = FrakContextManager.compress(original);
116
+ const decompressed = FrakContextManager.decompress(compressed);
117
+
118
+ expect(decompressed).toEqual(original);
119
+ });
120
+ });
121
+
122
+ describe("parse", () => {
123
+ it("should parse URL with fCtx parameter", () => {
124
+ const context: FrakContext = {
125
+ r: "0x1234567890123456789012345678901234567890" as Address,
126
+ };
127
+ const compressed = FrakContextManager.compress(context);
128
+ const url = `https://example.com?fCtx=${compressed}`;
129
+
130
+ const result = FrakContextManager.parse({ url });
131
+
132
+ expect(result).toBeDefined();
133
+ expect(result?.r).toBe(
134
+ "0x1234567890123456789012345678901234567890"
135
+ );
136
+ });
137
+
138
+ it("should return null for URL without fCtx parameter", () => {
139
+ const url = "https://example.com?other=param";
140
+
141
+ const result = FrakContextManager.parse({ url });
142
+
143
+ expect(result).toBeNull();
144
+ });
145
+
146
+ it("should return null for empty URL", () => {
147
+ const result = FrakContextManager.parse({ url: "" });
148
+
149
+ expect(result).toBeNull();
150
+ });
151
+
152
+ it("should parse URL with multiple parameters", () => {
153
+ const context: FrakContext = {
154
+ r: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
155
+ };
156
+ const compressed = FrakContextManager.compress(context);
157
+ const url = `https://example.com?foo=bar&fCtx=${compressed}&baz=qux`;
158
+
159
+ const result = FrakContextManager.parse({ url });
160
+
161
+ expect(result).toBeDefined();
162
+ expect(result?.r).toBe(
163
+ "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
164
+ );
165
+ });
166
+
167
+ it("should return undefined for malformed fCtx parameter", () => {
168
+ // Use a string that will fail base64url decoding
169
+ const url = "https://example.com?fCtx=!!!invalid!!!";
170
+
171
+ const result = FrakContextManager.parse({ url });
172
+
173
+ // Should handle the error and return undefined
174
+ expect(result).toBeUndefined();
175
+ });
176
+ });
177
+
178
+ describe("update", () => {
179
+ it("should add fCtx to URL without existing context", () => {
180
+ const url = "https://example.com";
181
+ const context: FrakContext = {
182
+ r: "0x1234567890123456789012345678901234567890" as Address,
183
+ };
184
+
185
+ const result = FrakContextManager.update({ url, context });
186
+
187
+ expect(result).toBeDefined();
188
+ expect(result).toContain("fCtx=");
189
+ expect(result).toContain("https://example.com");
190
+ });
191
+
192
+ it("should merge with existing context in URL", () => {
193
+ const existingContext: FrakContext = {
194
+ r: "0x1234567890123456789012345678901234567890" as Address,
195
+ };
196
+ const compressed = FrakContextManager.compress(existingContext);
197
+ const url = `https://example.com?fCtx=${compressed}`;
198
+
199
+ const newContext: FrakContext = {
200
+ r: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
201
+ };
202
+
203
+ const result = FrakContextManager.update({
204
+ url,
205
+ context: newContext,
206
+ });
207
+
208
+ expect(result).toBeDefined();
209
+ expect(result).toContain("fCtx=");
210
+
211
+ // Parse the result and check it has the new referrer
212
+ const parsedResult = FrakContextManager.parse({ url: result! });
213
+ expect(parsedResult?.r).toBe(
214
+ "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
215
+ );
216
+ });
217
+
218
+ it("should return null when URL is undefined", () => {
219
+ const context: FrakContext = {
220
+ r: "0x1234567890123456789012345678901234567890" as Address,
221
+ };
222
+
223
+ const result = FrakContextManager.update({
224
+ url: undefined,
225
+ context,
226
+ });
227
+
228
+ expect(result).toBeNull();
229
+ });
230
+
231
+ it("should return null when context has no referrer", () => {
232
+ const url = "https://example.com";
233
+ const context: Partial<FrakContext> = {};
234
+
235
+ const result = FrakContextManager.update({ url, context });
236
+
237
+ expect(result).toBeNull();
238
+ });
239
+
240
+ it("should preserve other URL parameters", () => {
241
+ const url = "https://example.com?foo=bar&baz=qux";
242
+ const context: FrakContext = {
243
+ r: "0x1234567890123456789012345678901234567890" as Address,
244
+ };
245
+
246
+ const result = FrakContextManager.update({ url, context });
247
+
248
+ expect(result).toContain("foo=bar");
249
+ expect(result).toContain("baz=qux");
250
+ expect(result).toContain("fCtx=");
251
+ });
252
+
253
+ it("should preserve URL hash", () => {
254
+ const url = "https://example.com#section";
255
+ const context: FrakContext = {
256
+ r: "0x1234567890123456789012345678901234567890" as Address,
257
+ };
258
+
259
+ const result = FrakContextManager.update({ url, context });
260
+
261
+ expect(result).toContain("#section");
262
+ expect(result).toContain("fCtx=");
263
+ });
264
+ });
265
+
266
+ describe("remove", () => {
267
+ it("should remove fCtx parameter from URL", () => {
268
+ const context: FrakContext = {
269
+ r: "0x1234567890123456789012345678901234567890" as Address,
270
+ };
271
+ const compressed = FrakContextManager.compress(context);
272
+ const url = `https://example.com?fCtx=${compressed}`;
273
+
274
+ const result = FrakContextManager.remove(url);
275
+
276
+ expect(result).toBe("https://example.com/");
277
+ expect(result).not.toContain("fCtx");
278
+ });
279
+
280
+ it("should preserve other parameters when removing fCtx", () => {
281
+ const context: FrakContext = {
282
+ r: "0x1234567890123456789012345678901234567890" as Address,
283
+ };
284
+ const compressed = FrakContextManager.compress(context);
285
+ const url = `https://example.com?foo=bar&fCtx=${compressed}&baz=qux`;
286
+
287
+ const result = FrakContextManager.remove(url);
288
+
289
+ expect(result).toContain("foo=bar");
290
+ expect(result).toContain("baz=qux");
291
+ expect(result).not.toContain("fCtx");
292
+ });
293
+
294
+ it("should handle URL without fCtx parameter", () => {
295
+ const url = "https://example.com?foo=bar";
296
+
297
+ const result = FrakContextManager.remove(url);
298
+
299
+ expect(result).toContain("foo=bar");
300
+ expect(result).not.toContain("fCtx");
301
+ });
302
+
303
+ it("should preserve URL hash", () => {
304
+ const url = "https://example.com?fCtx=test#section";
305
+
306
+ const result = FrakContextManager.remove(url);
307
+
308
+ expect(result).toContain("#section");
309
+ expect(result).not.toContain("fCtx");
310
+ });
311
+ });
312
+
313
+ describe("replaceUrl", () => {
314
+ // Note: replaceUrl tests are skipped because window.location cannot be mocked
315
+ // in JSDOM environment. The function is primarily used for browser DOM manipulation
316
+ // and is tested through integration/E2E tests.
317
+
318
+ it.skip("should update window.location with context", () => {
319
+ // Skip: Cannot mock window.location in JSDOM
320
+ });
321
+
322
+ it.skip("should use provided URL instead of window.location.href", () => {
323
+ // Skip: Cannot mock window.location in JSDOM
324
+ });
325
+
326
+ it.skip("should remove fCtx when context is null", () => {
327
+ // Skip: Cannot mock window.location in JSDOM
328
+ });
329
+
330
+ it.skip("should not call replaceState when context has no referrer", () => {
331
+ // Skip: Cannot mock window.location in JSDOM
332
+ });
333
+
334
+ it.skip("should handle missing window gracefully", () => {
335
+ // Skip: Cannot mock window.location in JSDOM
336
+ });
337
+ });
338
+ });
@@ -67,7 +67,10 @@ function parse({ url }: { url: string }) {
67
67
  function update({
68
68
  url,
69
69
  context,
70
- }: { url?: string; context: Partial<FrakContext> }) {
70
+ }: {
71
+ url?: string;
72
+ context: Partial<FrakContext>;
73
+ }) {
71
74
  if (!url) return null;
72
75
 
73
76
  // Parse the current context
@@ -111,7 +114,10 @@ function remove(url: string) {
111
114
  function replaceUrl({
112
115
  url: baseUrl,
113
116
  context,
114
- }: { url?: string; context: Partial<FrakContext> | null }) {
117
+ }: {
118
+ url?: string;
119
+ context: Partial<FrakContext> | null;
120
+ }) {
115
121
  // If no window here early exit
116
122
  if (!window.location?.href || typeof window === "undefined") {
117
123
  console.error("No window found, can't update context");