@frak-labs/core-sdk 0.0.19 → 0.1.0-beta.00226d62

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 (148) hide show
  1. package/cdn/bundle.iife.js +14 -0
  2. package/dist/actions-CEEObPYc.js +1 -0
  3. package/dist/actions-DbQhWYx8.cjs +1 -0
  4. package/dist/actions.cjs +1 -1
  5. package/dist/actions.d.cts +3 -1400
  6. package/dist/actions.d.ts +3 -1400
  7. package/dist/actions.js +1 -1
  8. package/dist/bundle.cjs +1 -13
  9. package/dist/bundle.d.cts +6 -2022
  10. package/dist/bundle.d.ts +6 -2022
  11. package/dist/bundle.js +1 -13
  12. package/dist/index-7OZ39x1U.d.ts +195 -0
  13. package/dist/index-C6FxkWPC.d.cts +511 -0
  14. package/dist/index-UFX7xCg3.d.ts +351 -0
  15. package/dist/index-d8xS4ryI.d.ts +511 -0
  16. package/dist/index-p4FqSp8z.d.cts +351 -0
  17. package/dist/index-zDq-VlKx.d.cts +195 -0
  18. package/dist/index.cjs +1 -13
  19. package/dist/index.d.cts +4 -1373
  20. package/dist/index.d.ts +4 -1373
  21. package/dist/index.js +1 -13
  22. package/dist/interaction-DMJ3ZfaF.d.cts +45 -0
  23. package/dist/interaction-KX1h9a7V.d.ts +45 -0
  24. package/dist/interactions-DnfM3oe0.js +1 -0
  25. package/dist/interactions-EIXhNLf6.cjs +1 -0
  26. package/dist/interactions.cjs +1 -1
  27. package/dist/interactions.d.cts +2 -182
  28. package/dist/interactions.d.ts +2 -182
  29. package/dist/interactions.js +1 -1
  30. package/dist/openSso-D--Airj6.d.cts +1018 -0
  31. package/dist/openSso-DsKJ4y0j.d.ts +1018 -0
  32. package/dist/productTypes-BUkXJKZ7.cjs +1 -0
  33. package/dist/productTypes-CGb1MmBF.js +1 -0
  34. package/dist/src-B_xO0AR6.cjs +13 -0
  35. package/dist/src-D2d52OZa.js +13 -0
  36. package/dist/trackEvent-CHnYa85W.js +1 -0
  37. package/dist/trackEvent-GuQm_1Nm.cjs +1 -0
  38. package/package.json +27 -18
  39. package/src/actions/displayEmbeddedWallet.test.ts +194 -0
  40. package/src/actions/displayEmbeddedWallet.ts +20 -0
  41. package/src/actions/displayModal.test.ts +387 -0
  42. package/src/actions/displayModal.ts +131 -0
  43. package/src/actions/getProductInformation.test.ts +133 -0
  44. package/src/actions/getProductInformation.ts +14 -0
  45. package/src/actions/index.ts +29 -0
  46. package/src/actions/openSso.test.ts +407 -0
  47. package/src/actions/openSso.ts +116 -0
  48. package/src/actions/prepareSso.test.ts +223 -0
  49. package/src/actions/prepareSso.ts +48 -0
  50. package/src/actions/referral/processReferral.test.ts +357 -0
  51. package/src/actions/referral/processReferral.ts +230 -0
  52. package/src/actions/referral/referralInteraction.test.ts +153 -0
  53. package/src/actions/referral/referralInteraction.ts +57 -0
  54. package/src/actions/sendInteraction.test.ts +219 -0
  55. package/src/actions/sendInteraction.ts +32 -0
  56. package/src/actions/trackPurchaseStatus.test.ts +287 -0
  57. package/src/actions/trackPurchaseStatus.ts +53 -0
  58. package/src/actions/watchWalletStatus.test.ts +372 -0
  59. package/src/actions/watchWalletStatus.ts +94 -0
  60. package/src/actions/wrapper/modalBuilder.test.ts +253 -0
  61. package/src/actions/wrapper/modalBuilder.ts +212 -0
  62. package/src/actions/wrapper/sendTransaction.test.ts +164 -0
  63. package/src/actions/wrapper/sendTransaction.ts +62 -0
  64. package/src/actions/wrapper/siweAuthenticate.test.ts +290 -0
  65. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  66. package/src/bundle.ts +3 -0
  67. package/src/clients/DebugInfo.test.ts +418 -0
  68. package/src/clients/DebugInfo.ts +182 -0
  69. package/src/clients/createIFrameFrakClient.ts +287 -0
  70. package/src/clients/index.ts +3 -0
  71. package/src/clients/setupClient.test.ts +343 -0
  72. package/src/clients/setupClient.ts +73 -0
  73. package/src/clients/transports/iframeLifecycleManager.test.ts +399 -0
  74. package/src/clients/transports/iframeLifecycleManager.ts +90 -0
  75. package/src/constants/interactionTypes.test.ts +128 -0
  76. package/src/constants/interactionTypes.ts +44 -0
  77. package/src/constants/locales.ts +14 -0
  78. package/src/constants/productTypes.test.ts +130 -0
  79. package/src/constants/productTypes.ts +33 -0
  80. package/src/index.ts +101 -0
  81. package/src/interactions/index.ts +5 -0
  82. package/src/interactions/pressEncoder.test.ts +215 -0
  83. package/src/interactions/pressEncoder.ts +53 -0
  84. package/src/interactions/purchaseEncoder.test.ts +291 -0
  85. package/src/interactions/purchaseEncoder.ts +99 -0
  86. package/src/interactions/referralEncoder.test.ts +170 -0
  87. package/src/interactions/referralEncoder.ts +47 -0
  88. package/src/interactions/retailEncoder.test.ts +107 -0
  89. package/src/interactions/retailEncoder.ts +37 -0
  90. package/src/interactions/webshopEncoder.test.ts +56 -0
  91. package/src/interactions/webshopEncoder.ts +30 -0
  92. package/src/types/client.ts +14 -0
  93. package/src/types/compression.ts +22 -0
  94. package/src/types/config.ts +111 -0
  95. package/src/types/context.ts +13 -0
  96. package/src/types/index.ts +71 -0
  97. package/src/types/lifecycle/client.ts +46 -0
  98. package/src/types/lifecycle/iframe.ts +35 -0
  99. package/src/types/lifecycle/index.ts +2 -0
  100. package/src/types/rpc/displayModal.ts +84 -0
  101. package/src/types/rpc/embedded/index.ts +68 -0
  102. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  103. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  104. package/src/types/rpc/interaction.ts +43 -0
  105. package/src/types/rpc/modal/final.ts +46 -0
  106. package/src/types/rpc/modal/generic.ts +46 -0
  107. package/src/types/rpc/modal/index.ts +20 -0
  108. package/src/types/rpc/modal/login.ts +32 -0
  109. package/src/types/rpc/modal/openSession.ts +25 -0
  110. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  111. package/src/types/rpc/modal/transaction.ts +33 -0
  112. package/src/types/rpc/productInformation.ts +59 -0
  113. package/src/types/rpc/sso.ts +80 -0
  114. package/src/types/rpc/walletStatus.ts +35 -0
  115. package/src/types/rpc.ts +158 -0
  116. package/src/types/transport.ts +34 -0
  117. package/src/utils/FrakContext.test.ts +407 -0
  118. package/src/utils/FrakContext.ts +158 -0
  119. package/src/utils/compression/b64.test.ts +181 -0
  120. package/src/utils/compression/b64.ts +29 -0
  121. package/src/utils/compression/compress.test.ts +123 -0
  122. package/src/utils/compression/compress.ts +11 -0
  123. package/src/utils/compression/decompress.test.ts +145 -0
  124. package/src/utils/compression/decompress.ts +11 -0
  125. package/src/utils/compression/index.ts +3 -0
  126. package/src/utils/computeProductId.test.ts +80 -0
  127. package/src/utils/computeProductId.ts +11 -0
  128. package/src/utils/constants.test.ts +23 -0
  129. package/src/utils/constants.ts +4 -0
  130. package/src/utils/formatAmount.test.ts +113 -0
  131. package/src/utils/formatAmount.ts +18 -0
  132. package/src/utils/getCurrencyAmountKey.test.ts +44 -0
  133. package/src/utils/getCurrencyAmountKey.ts +15 -0
  134. package/src/utils/getSupportedCurrency.test.ts +51 -0
  135. package/src/utils/getSupportedCurrency.ts +14 -0
  136. package/src/utils/getSupportedLocale.test.ts +64 -0
  137. package/src/utils/getSupportedLocale.ts +16 -0
  138. package/src/utils/iframeHelper.test.ts +450 -0
  139. package/src/utils/iframeHelper.ts +143 -0
  140. package/src/utils/index.ts +21 -0
  141. package/src/utils/sso.test.ts +361 -0
  142. package/src/utils/sso.ts +119 -0
  143. package/src/utils/ssoUrlListener.test.ts +252 -0
  144. package/src/utils/ssoUrlListener.ts +60 -0
  145. package/src/utils/trackEvent.test.ts +162 -0
  146. package/src/utils/trackEvent.ts +26 -0
  147. package/cdn/bundle.js +0 -19
  148. package/cdn/bundle.js.LICENSE.txt +0 -10
@@ -0,0 +1,158 @@
1
+ import { type Address, bytesToHex, hexToBytes } from "viem";
2
+ import type { FrakContext } from "../types";
3
+ import { base64urlDecode, base64urlEncode } from "./compression/b64";
4
+
5
+ /**
6
+ * The context key
7
+ */
8
+ const contextKey = "fCtx";
9
+
10
+ /**
11
+ * Compress the current Frak context
12
+ * @param context - The context to be compressed
13
+ * @returns A compressed string containing the Frak context
14
+ */
15
+ function compress(context?: Partial<FrakContext>): string | undefined {
16
+ if (!context?.r) return;
17
+ try {
18
+ const bytes = hexToBytes(context.r);
19
+ return base64urlEncode(bytes);
20
+ } catch (e) {
21
+ console.error("Error compressing Frak context", { e, context });
22
+ }
23
+ return undefined;
24
+ }
25
+
26
+ /**
27
+ * Decompress the given Frak context
28
+ * @param context - The raw context to be decompressed into a `FrakContext`
29
+ * @returns The decompressed Frak context, or undefined if it fails
30
+ */
31
+ function decompress(context?: string): FrakContext | undefined {
32
+ if (!context || context.length === 0) return;
33
+ try {
34
+ const bytes = base64urlDecode(context);
35
+ return { r: bytesToHex(bytes, { size: 20 }) as Address };
36
+ } catch (e) {
37
+ console.error("Error decompressing Frak context", { e, context });
38
+ }
39
+ return undefined;
40
+ }
41
+
42
+ /**
43
+ * Parse the current URL into a Frak Context
44
+ * @param args
45
+ * @param args.url - The url to parse
46
+ * @returns The parsed Frak context
47
+ */
48
+ function parse({ url }: { url: string }) {
49
+ if (!url) return null;
50
+
51
+ // Check if the url contain the frak context key
52
+ const urlObj = new URL(url);
53
+ const frakContext = urlObj.searchParams.get(contextKey);
54
+ if (!frakContext) return null;
55
+
56
+ // Decompress and return it
57
+ return decompress(frakContext);
58
+ }
59
+
60
+ /**
61
+ * Populate the current url with the given Frak context
62
+ * @param args
63
+ * @param args.url - The url to update
64
+ * @param args.context - The context to update
65
+ * @returns The new url with the Frak context
66
+ */
67
+ function update({
68
+ url,
69
+ context,
70
+ }: {
71
+ url?: string;
72
+ context: Partial<FrakContext>;
73
+ }) {
74
+ if (!url) return null;
75
+
76
+ // Parse the current context
77
+ const currentContext = parse({ url });
78
+
79
+ // Merge the current context with the new context
80
+ const mergedContext = currentContext
81
+ ? { ...currentContext, ...context }
82
+ : context;
83
+
84
+ // If we don't have a referrer, early exit
85
+ if (!mergedContext.r) return null;
86
+
87
+ // Compress it
88
+ const compressedContext = compress(mergedContext);
89
+ if (!compressedContext) return null;
90
+
91
+ // Build the new url and return it
92
+ const urlObj = new URL(url);
93
+ urlObj.searchParams.set(contextKey, compressedContext);
94
+ return urlObj.toString();
95
+ }
96
+
97
+ /**
98
+ * Remove Frak context from current url
99
+ * @param url - The url to update
100
+ * @returns The new url without the Frak context
101
+ */
102
+ function remove(url: string) {
103
+ const urlObj = new URL(url);
104
+ urlObj.searchParams.delete(contextKey);
105
+ return urlObj.toString();
106
+ }
107
+
108
+ /**
109
+ * Replace the current url with the given Frak context
110
+ * @param args
111
+ * @param args.url - The url to update
112
+ * @param args.context - The context to update
113
+ */
114
+ function replaceUrl({
115
+ url: baseUrl,
116
+ context,
117
+ }: {
118
+ url?: string;
119
+ context: Partial<FrakContext> | null;
120
+ }) {
121
+ // If no window here early exit
122
+ if (!window.location?.href || typeof window === "undefined") {
123
+ console.error("No window found, can't update context");
124
+ return;
125
+ }
126
+
127
+ // If no url, try to use the current one
128
+ const url = baseUrl ?? window.location.href;
129
+
130
+ // Get our new url with the frak context
131
+ let newUrl: string | null;
132
+ if (context !== null) {
133
+ newUrl = update({
134
+ url,
135
+ context,
136
+ });
137
+ } else {
138
+ newUrl = remove(url);
139
+ }
140
+
141
+ // If no new url, early exit
142
+ if (!newUrl) return;
143
+
144
+ // Update the url
145
+ window.history.replaceState(null, "", newUrl.toString());
146
+ }
147
+
148
+ /**
149
+ * Export our frak context
150
+ */
151
+ export const FrakContextManager = {
152
+ compress,
153
+ decompress,
154
+ parse,
155
+ update,
156
+ remove,
157
+ replaceUrl,
158
+ };
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Tests for base64url encoding and decoding utilities
3
+ * Tests encoding, decoding, and round-trip operations
4
+ */
5
+
6
+ import { describe, expect, it, test } from "../../../tests/vitest-fixtures";
7
+ import { base64urlDecode, base64urlEncode } from "./b64";
8
+
9
+ describe("base64urlEncode", () => {
10
+ test("should encode empty Uint8Array", ({ mockUint8Arrays }) => {
11
+ const result = base64urlEncode(mockUint8Arrays.empty);
12
+
13
+ expect(result).toBe("");
14
+ });
15
+
16
+ test("should encode simple Uint8Array", ({ mockUint8Arrays }) => {
17
+ const result = base64urlEncode(mockUint8Arrays.simple);
18
+
19
+ // "Hello" should encode to "SGVsbG8"
20
+ expect(result).toBe("SGVsbG8");
21
+ });
22
+
23
+ test("should replace + with - for URL safety", () => {
24
+ // Create data that would produce + in standard base64
25
+ const data = new Uint8Array([0xfb, 0xff]);
26
+ const result = base64urlEncode(data);
27
+
28
+ // Should not contain +
29
+ expect(result).not.toContain("+");
30
+ expect(result).toContain("-");
31
+ });
32
+
33
+ test("should replace / with _ for URL safety", () => {
34
+ // Create data that would produce / in standard base64
35
+ const data = new Uint8Array([0xff, 0xff]);
36
+ const result = base64urlEncode(data);
37
+
38
+ // Should not contain /
39
+ expect(result).not.toContain("/");
40
+ expect(result).toContain("_");
41
+ });
42
+
43
+ test("should remove padding =", () => {
44
+ // Create data that would have padding
45
+ const data = new Uint8Array([72]); // "H" -> "SA==" in standard base64
46
+ const result = base64urlEncode(data);
47
+
48
+ // Should not contain =
49
+ expect(result).not.toContain("=");
50
+ expect(result).toBe("SA");
51
+ });
52
+
53
+ test("should handle various byte lengths", () => {
54
+ const lengths = [1, 2, 3, 4, 5, 10, 20];
55
+
56
+ for (const length of lengths) {
57
+ const data = new Uint8Array(length).fill(65); // Fill with 'A' character code
58
+ const result = base64urlEncode(data);
59
+
60
+ // Should produce a string
61
+ expect(typeof result).toBe("string");
62
+ expect(result.length).toBeGreaterThan(0);
63
+ }
64
+ });
65
+
66
+ test("should handle complex byte array", ({ mockUint8Arrays }) => {
67
+ const result = base64urlEncode(mockUint8Arrays.complex);
68
+
69
+ // Should produce valid base64url string
70
+ expect(result).toMatch(/^[A-Za-z0-9_-]*$/);
71
+ });
72
+ });
73
+
74
+ describe("base64urlDecode", () => {
75
+ test("should decode empty string", ({ mockBase64Strings }) => {
76
+ const result = base64urlDecode(mockBase64Strings.empty);
77
+
78
+ expect(result).toEqual(new Uint8Array([]));
79
+ expect(result.length).toBe(0);
80
+ });
81
+
82
+ test("should decode simple base64url string", ({ mockBase64Strings }) => {
83
+ const result = base64urlDecode(mockBase64Strings.simple);
84
+
85
+ // "SGVsbG8" should decode to "Hello"
86
+ const expected = new Uint8Array([72, 101, 108, 108, 111]);
87
+ expect(result).toEqual(expected);
88
+ });
89
+
90
+ test("should handle strings without padding", () => {
91
+ // Base64url strings don't have padding
92
+ const encoded = "SGVsbG8"; // "Hello" without padding
93
+ const result = base64urlDecode(encoded);
94
+
95
+ const expected = new Uint8Array([72, 101, 108, 108, 111]);
96
+ expect(result).toEqual(expected);
97
+ });
98
+
99
+ test("should reverse URL-safe character replacements", ({
100
+ mockBase64Strings,
101
+ }) => {
102
+ const result = base64urlDecode(mockBase64Strings.withSpecialChars);
103
+
104
+ // Should handle - and _ characters
105
+ expect(result).toBeInstanceOf(Uint8Array);
106
+ });
107
+
108
+ test("should handle various valid base64url string lengths", () => {
109
+ // Use valid base64url strings
110
+ const testStrings = ["QQ", "QUI", "QUJD", "QUJDRA"]; // "A", "AB", "ABC", "ABCD" encoded
111
+
112
+ for (const str of testStrings) {
113
+ const result = base64urlDecode(str);
114
+
115
+ // Should produce Uint8Array
116
+ expect(result).toBeInstanceOf(Uint8Array);
117
+ }
118
+ });
119
+ });
120
+
121
+ describe("base64url round-trip", () => {
122
+ test("should successfully round-trip empty data", ({ mockUint8Arrays }) => {
123
+ const encoded = base64urlEncode(mockUint8Arrays.empty);
124
+ const decoded = base64urlDecode(encoded);
125
+
126
+ expect(decoded).toEqual(mockUint8Arrays.empty);
127
+ });
128
+
129
+ test("should successfully round-trip simple data", ({
130
+ mockUint8Arrays,
131
+ }) => {
132
+ const encoded = base64urlEncode(mockUint8Arrays.simple);
133
+ const decoded = base64urlDecode(encoded);
134
+
135
+ expect(decoded).toEqual(mockUint8Arrays.simple);
136
+ });
137
+
138
+ test("should successfully round-trip complex data", ({
139
+ mockUint8Arrays,
140
+ }) => {
141
+ const encoded = base64urlEncode(mockUint8Arrays.complex);
142
+ const decoded = base64urlDecode(encoded);
143
+
144
+ expect(decoded).toEqual(mockUint8Arrays.complex);
145
+ });
146
+
147
+ it("should handle all byte values (0-255)", () => {
148
+ // Test with all possible byte values
149
+ const allBytes = new Uint8Array(256);
150
+ for (let i = 0; i < 256; i++) {
151
+ allBytes[i] = i;
152
+ }
153
+
154
+ const encoded = base64urlEncode(allBytes);
155
+ const decoded = base64urlDecode(encoded);
156
+
157
+ expect(decoded).toEqual(allBytes);
158
+ });
159
+
160
+ it("should preserve binary data integrity", () => {
161
+ const binaryData = new Uint8Array([0, 1, 127, 128, 255, 254, 100, 200]);
162
+
163
+ const encoded = base64urlEncode(binaryData);
164
+ const decoded = base64urlDecode(encoded);
165
+
166
+ expect(decoded).toEqual(binaryData);
167
+ });
168
+
169
+ it("should handle random data correctly", () => {
170
+ // Generate some pseudo-random data
171
+ const randomData = new Uint8Array(32);
172
+ for (let i = 0; i < 32; i++) {
173
+ randomData[i] = Math.floor(Math.random() * 256);
174
+ }
175
+
176
+ const encoded = base64urlEncode(randomData);
177
+ const decoded = base64urlDecode(encoded);
178
+
179
+ expect(decoded).toEqual(randomData);
180
+ });
181
+ });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Encode a buffer to a base64url encoded string
3
+ * @param buffer The buffer to encode
4
+ * @returns The encoded string
5
+ */
6
+ export function base64urlEncode(buffer: Uint8Array): string {
7
+ return btoa(Array.from(buffer, (b) => String.fromCharCode(b)).join(""))
8
+ .replace(/\+/g, "-")
9
+ .replace(/\//g, "_")
10
+ .replace(/=+$/, "");
11
+ }
12
+
13
+ /**
14
+ * Decode a base64url encoded string
15
+ * @param value The value to decode
16
+ * @returns The decoded value
17
+ */
18
+ export function base64urlDecode(value: string): Uint8Array {
19
+ const m = value.length % 4;
20
+ return Uint8Array.from(
21
+ atob(
22
+ value
23
+ .replace(/-/g, "+")
24
+ .replace(/_/g, "/")
25
+ .padEnd(value.length + (m === 0 ? 0 : 4 - m), "=")
26
+ ),
27
+ (c) => c.charCodeAt(0)
28
+ );
29
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Tests for compressJsonToB64 utility function
3
+ * Tests JSON compression to base64url-encoded string
4
+ */
5
+
6
+ import { vi } from "vitest";
7
+
8
+ // Mock the frame-connector module - must be before imports
9
+ vi.mock("@frak-labs/frame-connector", () => ({
10
+ compressJson: vi.fn((data: unknown) => {
11
+ // Simple mock: convert JSON to Uint8Array
12
+ const jsonString = JSON.stringify(data);
13
+ return new TextEncoder().encode(jsonString);
14
+ }),
15
+ }));
16
+
17
+ import { describe, expect, it } from "../../../tests/vitest-fixtures";
18
+ import { compressJsonToB64 } from "./compress";
19
+
20
+ describe("compressJsonToB64", () => {
21
+ describe("success cases", () => {
22
+ it("should compress and encode simple object", () => {
23
+ const data = { key: "value" };
24
+ const result = compressJsonToB64(data);
25
+
26
+ // Result should be a base64url-encoded string
27
+ expect(result).toBeDefined();
28
+ expect(typeof result).toBe("string");
29
+ expect(result.length).toBeGreaterThan(0);
30
+ // Base64url should not contain +, /, or = characters
31
+ expect(result).not.toMatch(/[+/=]/);
32
+ });
33
+
34
+ it("should compress and encode array data", () => {
35
+ const data = [1, 2, 3, 4, 5];
36
+ const result = compressJsonToB64(data);
37
+
38
+ expect(result).toBeDefined();
39
+ expect(typeof result).toBe("string");
40
+ expect(result.length).toBeGreaterThan(0);
41
+ expect(result).not.toMatch(/[+/=]/);
42
+ });
43
+
44
+ it("should compress and encode nested object", () => {
45
+ const data = {
46
+ user: {
47
+ name: "John",
48
+ address: {
49
+ city: "Paris",
50
+ country: "France",
51
+ },
52
+ },
53
+ };
54
+ const result = compressJsonToB64(data);
55
+
56
+ expect(result).toBeDefined();
57
+ expect(typeof result).toBe("string");
58
+ expect(result.length).toBeGreaterThan(0);
59
+ });
60
+
61
+ it("should compress and encode string data", () => {
62
+ const data = "Hello, World!";
63
+ const result = compressJsonToB64(data);
64
+
65
+ expect(result).toBeDefined();
66
+ expect(typeof result).toBe("string");
67
+ expect(result.length).toBeGreaterThan(0);
68
+ });
69
+
70
+ it("should compress and encode number data", () => {
71
+ const data = 12345;
72
+ const result = compressJsonToB64(data);
73
+
74
+ expect(result).toBeDefined();
75
+ expect(typeof result).toBe("string");
76
+ expect(result.length).toBeGreaterThan(0);
77
+ });
78
+
79
+ it("should compress and encode boolean data", () => {
80
+ const data = true;
81
+ const result = compressJsonToB64(data);
82
+
83
+ expect(result).toBeDefined();
84
+ expect(typeof result).toBe("string");
85
+ expect(result.length).toBeGreaterThan(0);
86
+ });
87
+
88
+ it("should compress and encode null", () => {
89
+ const data = null;
90
+ const result = compressJsonToB64(data);
91
+
92
+ expect(result).toBeDefined();
93
+ expect(typeof result).toBe("string");
94
+ expect(result.length).toBeGreaterThan(0);
95
+ });
96
+ });
97
+
98
+ describe("edge cases", () => {
99
+ it("should handle empty object", () => {
100
+ const data = {};
101
+ const result = compressJsonToB64(data);
102
+
103
+ expect(result).toBeDefined();
104
+ expect(typeof result).toBe("string");
105
+ });
106
+
107
+ it("should handle empty array", () => {
108
+ const data: unknown[] = [];
109
+ const result = compressJsonToB64(data);
110
+
111
+ expect(result).toBeDefined();
112
+ expect(typeof result).toBe("string");
113
+ });
114
+
115
+ it("should handle empty string", () => {
116
+ const data = "";
117
+ const result = compressJsonToB64(data);
118
+
119
+ expect(result).toBeDefined();
120
+ expect(typeof result).toBe("string");
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,11 @@
1
+ import { compressJson } from "@frak-labs/frame-connector";
2
+ import { base64urlEncode } from "./b64";
3
+
4
+ /**
5
+ * Compress json data
6
+ * @param data
7
+ * @ignore
8
+ */
9
+ export function compressJsonToB64(data: unknown): string {
10
+ return base64urlEncode(compressJson(data));
11
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Tests for decompressJsonFromB64 utility function
3
+ * Tests decompression of base64url-encoded JSON strings
4
+ */
5
+
6
+ import { vi } from "vitest";
7
+
8
+ // Mock the frame-connector module - must be before imports
9
+ vi.mock("@frak-labs/frame-connector", () => ({
10
+ compressJson: vi.fn((data: unknown) => {
11
+ // Simple mock: convert JSON to Uint8Array
12
+ const jsonString = JSON.stringify(data);
13
+ return new TextEncoder().encode(jsonString);
14
+ }),
15
+ decompressJson: vi.fn((data: Uint8Array) => {
16
+ // Simple mock: convert Uint8Array back to JSON
17
+ const jsonString = new TextDecoder().decode(data);
18
+ return JSON.parse(jsonString);
19
+ }),
20
+ }));
21
+
22
+ import { describe, expect, it } from "../../../tests/vitest-fixtures";
23
+ import { compressJsonToB64 } from "./compress";
24
+ import { decompressJsonFromB64 } from "./decompress";
25
+
26
+ describe("decompressJsonFromB64", () => {
27
+ describe("success cases", () => {
28
+ it("should decompress simple object", () => {
29
+ const original = { key: "value" };
30
+ const compressed = compressJsonToB64(original);
31
+ const decompressed =
32
+ decompressJsonFromB64<typeof original>(compressed);
33
+
34
+ expect(decompressed).toEqual(original);
35
+ });
36
+
37
+ it("should decompress array data", () => {
38
+ const original = [1, 2, 3, 4, 5];
39
+ const compressed = compressJsonToB64(original);
40
+ const decompressed =
41
+ decompressJsonFromB64<typeof original>(compressed);
42
+
43
+ expect(decompressed).toEqual(original);
44
+ });
45
+
46
+ it("should decompress nested object", () => {
47
+ const original = {
48
+ user: {
49
+ name: "John",
50
+ address: {
51
+ city: "Paris",
52
+ country: "France",
53
+ },
54
+ },
55
+ };
56
+ const compressed = compressJsonToB64(original);
57
+ const decompressed =
58
+ decompressJsonFromB64<typeof original>(compressed);
59
+
60
+ expect(decompressed).toEqual(original);
61
+ });
62
+
63
+ it("should decompress string data", () => {
64
+ const original = "Hello, World!";
65
+ const compressed = compressJsonToB64(original);
66
+ const decompressed = decompressJsonFromB64<string>(compressed);
67
+
68
+ expect(decompressed).toBe(original);
69
+ });
70
+
71
+ it("should decompress number data", () => {
72
+ const original = 12345;
73
+ const compressed = compressJsonToB64(original);
74
+ const decompressed = decompressJsonFromB64<number>(compressed);
75
+
76
+ expect(decompressed).toBe(original);
77
+ });
78
+
79
+ it("should decompress boolean data", () => {
80
+ const original = true;
81
+ const compressed = compressJsonToB64(original);
82
+ const decompressed = decompressJsonFromB64<boolean>(compressed);
83
+
84
+ expect(decompressed).toBe(original);
85
+ });
86
+
87
+ it("should decompress null", () => {
88
+ const original = null;
89
+ const compressed = compressJsonToB64(original);
90
+ const decompressed = decompressJsonFromB64<null>(compressed);
91
+
92
+ expect(decompressed).toBeNull();
93
+ });
94
+ });
95
+
96
+ describe("round-trip compression", () => {
97
+ it("should preserve data through compress-decompress cycle", () => {
98
+ const original = {
99
+ id: 123,
100
+ name: "Test User",
101
+ tags: ["tag1", "tag2", "tag3"],
102
+ metadata: {
103
+ created: "2024-01-01",
104
+ updated: "2024-01-02",
105
+ },
106
+ };
107
+
108
+ const compressed = compressJsonToB64(original);
109
+ const decompressed =
110
+ decompressJsonFromB64<typeof original>(compressed);
111
+
112
+ expect(decompressed).toEqual(original);
113
+ });
114
+
115
+ it("should handle empty object round-trip", () => {
116
+ const original = {};
117
+ const compressed = compressJsonToB64(original);
118
+ const decompressed =
119
+ decompressJsonFromB64<typeof original>(compressed);
120
+
121
+ expect(decompressed).toEqual(original);
122
+ });
123
+
124
+ it("should handle empty array round-trip", () => {
125
+ const original: unknown[] = [];
126
+ const compressed = compressJsonToB64(original);
127
+ const decompressed =
128
+ decompressJsonFromB64<typeof original>(compressed);
129
+
130
+ expect(decompressed).toEqual(original);
131
+ });
132
+ });
133
+
134
+ describe("edge cases", () => {
135
+ it("should handle empty object round-trip gracefully", () => {
136
+ // Empty objects should compress and decompress correctly
137
+ const original = {};
138
+ const compressed = compressJsonToB64(original);
139
+ const decompressed =
140
+ decompressJsonFromB64<typeof original>(compressed);
141
+
142
+ expect(decompressed).toEqual(original);
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,11 @@
1
+ import { decompressJson } from "@frak-labs/frame-connector";
2
+ import { base64urlDecode } from "./b64";
3
+
4
+ /**
5
+ * Decompress json data
6
+ * @param data
7
+ * @ignore
8
+ */
9
+ export function decompressJsonFromB64<T>(data: string): T | null {
10
+ return decompressJson(base64urlDecode(data));
11
+ }
@@ -0,0 +1,3 @@
1
+ export { base64urlDecode, base64urlEncode } from "./b64";
2
+ export { compressJsonToB64 } from "./compress";
3
+ export { decompressJsonFromB64 } from "./decompress";