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

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 (85) hide show
  1. package/cdn/bundle.js +5 -5
  2. package/dist/actions.cjs +1 -1
  3. package/dist/actions.d.cts +154 -73
  4. package/dist/actions.d.ts +154 -73
  5. package/dist/actions.js +1 -1
  6. package/dist/bundle.cjs +2 -2
  7. package/dist/bundle.d.cts +257 -192
  8. package/dist/bundle.d.ts +257 -192
  9. package/dist/bundle.js +2 -2
  10. package/dist/index.cjs +11 -11
  11. package/dist/index.d.cts +199 -185
  12. package/dist/index.d.ts +199 -185
  13. package/dist/index.js +2 -2
  14. package/package.json +7 -2
  15. package/src/actions/displayEmbeddedWallet.ts +20 -0
  16. package/src/actions/displayModal.ts +131 -0
  17. package/src/actions/getProductInformation.ts +14 -0
  18. package/src/actions/index.ts +29 -0
  19. package/src/actions/openSso.ts +116 -0
  20. package/src/actions/prepareSso.ts +48 -0
  21. package/src/actions/referral/processReferral.ts +230 -0
  22. package/src/actions/referral/referralInteraction.ts +57 -0
  23. package/src/actions/sendInteraction.ts +32 -0
  24. package/src/actions/trackPurchaseStatus.ts +53 -0
  25. package/src/actions/watchWalletStatus.ts +94 -0
  26. package/src/actions/wrapper/modalBuilder.ts +212 -0
  27. package/src/actions/wrapper/sendTransaction.ts +62 -0
  28. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  29. package/src/bundle.ts +3 -0
  30. package/src/clients/DebugInfo.ts +182 -0
  31. package/src/clients/createIFrameFrakClient.ts +287 -0
  32. package/src/clients/index.ts +3 -0
  33. package/src/clients/setupClient.ts +71 -0
  34. package/src/clients/transports/iframeLifecycleManager.ts +88 -0
  35. package/src/constants/interactionTypes.ts +44 -0
  36. package/src/constants/locales.ts +14 -0
  37. package/src/constants/productTypes.ts +33 -0
  38. package/src/index.ts +103 -0
  39. package/src/interactions/index.ts +5 -0
  40. package/src/interactions/pressEncoder.ts +53 -0
  41. package/src/interactions/purchaseEncoder.ts +94 -0
  42. package/src/interactions/referralEncoder.ts +47 -0
  43. package/src/interactions/retailEncoder.ts +37 -0
  44. package/src/interactions/webshopEncoder.ts +30 -0
  45. package/src/types/client.ts +14 -0
  46. package/src/types/compression.ts +22 -0
  47. package/src/types/config.ts +111 -0
  48. package/src/types/context.ts +13 -0
  49. package/src/types/index.ts +70 -0
  50. package/src/types/lifecycle/client.ts +46 -0
  51. package/src/types/lifecycle/iframe.ts +35 -0
  52. package/src/types/lifecycle/index.ts +2 -0
  53. package/src/types/rpc/displayModal.ts +84 -0
  54. package/src/types/rpc/embedded/index.ts +68 -0
  55. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  56. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  57. package/src/types/rpc/interaction.ts +43 -0
  58. package/src/types/rpc/modal/final.ts +46 -0
  59. package/src/types/rpc/modal/generic.ts +46 -0
  60. package/src/types/rpc/modal/index.ts +20 -0
  61. package/src/types/rpc/modal/login.ts +32 -0
  62. package/src/types/rpc/modal/openSession.ts +25 -0
  63. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  64. package/src/types/rpc/modal/transaction.ts +33 -0
  65. package/src/types/rpc/productInformation.ts +59 -0
  66. package/src/types/rpc/sso.ts +80 -0
  67. package/src/types/rpc/walletStatus.ts +35 -0
  68. package/src/types/rpc.ts +158 -0
  69. package/src/types/transport.ts +34 -0
  70. package/src/utils/FrakContext.ts +152 -0
  71. package/src/utils/compression/b64.ts +29 -0
  72. package/src/utils/compression/compress.ts +11 -0
  73. package/src/utils/compression/decompress.ts +11 -0
  74. package/src/utils/compression/index.ts +3 -0
  75. package/src/utils/computeProductId.ts +11 -0
  76. package/src/utils/constants.ts +4 -0
  77. package/src/utils/formatAmount.ts +18 -0
  78. package/src/utils/getCurrencyAmountKey.ts +15 -0
  79. package/src/utils/getSupportedCurrency.ts +14 -0
  80. package/src/utils/getSupportedLocale.ts +16 -0
  81. package/src/utils/iframeHelper.ts +142 -0
  82. package/src/utils/index.ts +21 -0
  83. package/src/utils/sso.ts +119 -0
  84. package/src/utils/ssoUrlListener.ts +60 -0
  85. package/src/utils/trackEvent.ts +26 -0
@@ -0,0 +1,32 @@
1
+ import type { Address } from "viem";
2
+ import type { SsoMetadata } from "../sso";
3
+ import type { GenericModalStepType } from "./generic";
4
+
5
+ /** @inline */
6
+ type LoginWithSso = {
7
+ allowSso: true;
8
+ // Optional metadata for the SSO (if not provided, will be recomputed from the top level wallet sdk)
9
+ ssoMetadata?: SsoMetadata;
10
+ };
11
+
12
+ /** @inline */
13
+ type LoginWithoutSso = {
14
+ allowSso?: false;
15
+ ssoMetadata?: never;
16
+ };
17
+
18
+ /**
19
+ * The login step for a Modal
20
+ *
21
+ * **Input**: Do we allow SSO or not? Is yes then the SSO metadata
22
+ * **Output**: The logged in wallet address
23
+ *
24
+ * @group Modal Display
25
+ */
26
+ export type LoginModalStepType = GenericModalStepType<
27
+ "login",
28
+ LoginWithSso | LoginWithoutSso,
29
+ {
30
+ wallet: Address;
31
+ }
32
+ >;
@@ -0,0 +1,25 @@
1
+ import type { GenericModalStepType } from "./generic";
2
+
3
+ /**
4
+ * Return type of the open session modal step
5
+ * @inline
6
+ * @ignore
7
+ */
8
+ export type OpenInteractionSessionReturnType = {
9
+ startTimestamp: number;
10
+ endTimestamp: number;
11
+ };
12
+
13
+ /**
14
+ * The open interaction session step for a Modal
15
+ *
16
+ * **Input**: None
17
+ * **Output**: The interactions session period (start and end timestamp)
18
+ *
19
+ * @group Modal Display
20
+ */
21
+ export type OpenInteractionSessionModalStepType = GenericModalStepType<
22
+ "openSession",
23
+ object,
24
+ OpenInteractionSessionReturnType
25
+ >;
@@ -0,0 +1,37 @@
1
+ import type { Hex } from "viem";
2
+ import type { SiweMessage } from "viem/siwe";
3
+ import type { GenericModalStepType } from "./generic";
4
+
5
+ /**
6
+ * Parameters used send a SIWE rpc request
7
+ */
8
+ export type SiweAuthenticationParams = Omit<
9
+ SiweMessage,
10
+ "address" | "chainId" | "expirationTime" | "issuedAt" | "notBefore"
11
+ > & {
12
+ expirationTimeTimestamp?: number;
13
+ notBeforeTimestamp?: number;
14
+ };
15
+
16
+ /**
17
+ * Return type of the Siwe transaction rpc request
18
+ * @inline
19
+ */
20
+ export type SiweAuthenticateReturnType = {
21
+ signature: Hex;
22
+ message: string;
23
+ };
24
+
25
+ /**
26
+ * The SIWE authentication step for a Modal
27
+ *
28
+ * **Input**: SIWE message parameters
29
+ * **Output**: SIWE result (message signed and wallet signature)
30
+ *
31
+ * @group Modal Display
32
+ */
33
+ export type SiweAuthenticateModalStepType = GenericModalStepType<
34
+ "siweAuthenticate",
35
+ { siwe: SiweAuthenticationParams },
36
+ SiweAuthenticateReturnType
37
+ >;
@@ -0,0 +1,33 @@
1
+ import type { Address, Hex } from "viem";
2
+ import type { GenericModalStepType } from "./generic";
3
+
4
+ /**
5
+ * Generic format representing a tx to be sent
6
+ */
7
+ export type SendTransactionTxType = {
8
+ to: Address;
9
+ data?: Hex;
10
+ value?: Hex;
11
+ };
12
+
13
+ /**
14
+ * Return type of the send transaction rpc request
15
+ * @inline
16
+ */
17
+ export type SendTransactionReturnType = {
18
+ hash: Hex;
19
+ };
20
+
21
+ /**
22
+ * The send transaction step for a Modal
23
+ *
24
+ * **Input**: Either a single tx or an array of tx to be sent
25
+ * **Output**: The hash of the tx(s) hash (in case of multiple tx, still returns a single hash because it's bundled on the wallet level)
26
+ *
27
+ * @group Modal Display
28
+ */
29
+ export type SendTransactionModalStepType = GenericModalStepType<
30
+ "sendTransaction",
31
+ { tx: SendTransactionTxType | SendTransactionTxType[] },
32
+ SendTransactionReturnType
33
+ >;
@@ -0,0 +1,59 @@
1
+ import type { Address, Hex } from "viem";
2
+ import type { FullInteractionTypesKey } from "../../constants/interactionTypes";
3
+ import type { ProductTypesKey } from "../../constants/productTypes";
4
+
5
+ /**
6
+ * The type for the amount of tokens
7
+ */
8
+ export type TokenAmountType = {
9
+ amount: number;
10
+ eurAmount: number;
11
+ usdAmount: number;
12
+ gbpAmount: number;
13
+ };
14
+
15
+ /**
16
+ * Response of the `frak_getProductInformation` RPC method
17
+ * @group RPC Schema
18
+ */
19
+ export type GetProductInformationReturnType = {
20
+ /**
21
+ * Current product id
22
+ */
23
+ id: Hex;
24
+ /**
25
+ * Some metadata
26
+ */
27
+ onChainMetadata: {
28
+ /**
29
+ * Name of the product on-chain
30
+ */
31
+ name: string;
32
+ /**
33
+ * Domain of the product on-chain
34
+ */
35
+ domain: string;
36
+ /**
37
+ * The supported product types
38
+ */
39
+ productTypes: ProductTypesKey[];
40
+ };
41
+ /**
42
+ * The max potential reward for the referrer
43
+ */
44
+ maxReferrer?: TokenAmountType;
45
+ /**
46
+ * The max potential reward for the referee
47
+ */
48
+ maxReferee?: TokenAmountType;
49
+ /**
50
+ * List of all the potentials reward arround this product
51
+ */
52
+ rewards: {
53
+ token: Address;
54
+ campaign: Address;
55
+ interactionTypeKey: FullInteractionTypesKey;
56
+ referrer: TokenAmountType;
57
+ referee: TokenAmountType;
58
+ }[];
59
+ };
@@ -0,0 +1,80 @@
1
+ import type { Hex } from "viem";
2
+
3
+ /**
4
+ * SSO Metadata
5
+ */
6
+ export type SsoMetadata = {
7
+ /**
8
+ * URL to your client, if provided will be displayed in the SSO header
9
+ */
10
+ logoUrl?: string;
11
+ /**
12
+ * Link to your homepage, if referenced your app name will contain a link on the sso page
13
+ */
14
+ homepageLink?: string;
15
+ };
16
+
17
+ /**
18
+ * Params for preparing SSO (generating URL)
19
+ * Same as OpenSsoParamsType but without openInSameWindow (popup-only operation)
20
+ * @group RPC Schema
21
+ */
22
+ export type PrepareSsoParamsType = {
23
+ /**
24
+ * Redirect URL after the SSO (optional)
25
+ */
26
+ redirectUrl?: string;
27
+ /**
28
+ * If the SSO should directly exit after completion
29
+ * @defaultValue true
30
+ */
31
+ directExit?: boolean;
32
+ /**
33
+ * Language of the SSO page (optional)
34
+ * It will default to the current user language (or "en" if unsupported language)
35
+ */
36
+ lang?: "en" | "fr";
37
+ /**
38
+ * Custom SSO metadata
39
+ */
40
+ metadata?: SsoMetadata;
41
+ };
42
+
43
+ /**
44
+ * Response after preparing SSO
45
+ * @group RPC Schema
46
+ */
47
+ export type PrepareSsoReturnType = {
48
+ /**
49
+ * The SSO URL that should be opened in a popup
50
+ */
51
+ ssoUrl: string;
52
+ };
53
+
54
+ /**
55
+ * Response after an SSO has been openned
56
+ */
57
+ export type OpenSsoReturnType = {
58
+ /**
59
+ * Optional wallet address, returned when SSO completes via postMessage
60
+ * Note: Only present when SSO flow completes (not immediately on open)
61
+ */
62
+ wallet?: Hex;
63
+ };
64
+
65
+ /**
66
+ * Params to start a SSO
67
+ * @group RPC Schema
68
+ */
69
+ export type OpenSsoParamsType = PrepareSsoParamsType & {
70
+ /**
71
+ * Indicate whether we want todo the flow within the same window context, or if we want to do it with an external popup window openned
72
+ * Note: Default true if redirectUrl is present, otherwise, false
73
+ */
74
+ openInSameWindow?: boolean;
75
+
76
+ /**
77
+ * Custom SSO popup url if user want additionnal customisation
78
+ */
79
+ ssoPopupUrl?: string;
80
+ };
@@ -0,0 +1,35 @@
1
+ import type { Address } from "viem";
2
+
3
+ /**
4
+ * RPC Response for the method `frak_listenToWalletStatus`
5
+ * @group RPC Schema
6
+ */
7
+ export type WalletStatusReturnType = WalletConnected | WalletNotConnected;
8
+
9
+ /**
10
+ * @ignore
11
+ * @inline
12
+ */
13
+ export type WalletConnected = {
14
+ key: "connected";
15
+ // The user wallet address
16
+ wallet: Address;
17
+ // The interaction token, used to push interactions to the delegator if needed
18
+ interactionToken?: string;
19
+ // The current onchain interaction session of the user
20
+ interactionSession?: {
21
+ startTimestamp: number;
22
+ endTimestamp: number;
23
+ };
24
+ };
25
+
26
+ /**
27
+ * @ignore
28
+ * @inline
29
+ */
30
+ export type WalletNotConnected = {
31
+ key: "not-connected";
32
+ wallet?: never;
33
+ interactionToken?: never;
34
+ interactionSession?: never;
35
+ };
@@ -0,0 +1,158 @@
1
+ import type { Hex } from "viem";
2
+ import type { FrakWalletSdkConfig } from "./config";
3
+ import type {
4
+ ModalRpcMetadata,
5
+ ModalRpcStepsInput,
6
+ ModalRpcStepsResultType,
7
+ } from "./rpc/displayModal";
8
+ import type {
9
+ DisplayEmbeddedWalletParamsType,
10
+ DisplayEmbeddedWalletResultType,
11
+ } from "./rpc/embedded";
12
+ import type {
13
+ PreparedInteraction,
14
+ SendInteractionReturnType,
15
+ } from "./rpc/interaction";
16
+ import type { GetProductInformationReturnType } from "./rpc/productInformation";
17
+ import type {
18
+ OpenSsoParamsType,
19
+ OpenSsoReturnType,
20
+ PrepareSsoParamsType,
21
+ PrepareSsoReturnType,
22
+ } from "./rpc/sso";
23
+ import type { WalletStatusReturnType } from "./rpc/walletStatus";
24
+
25
+ /**
26
+ * RPC interface that's used for the iframe communication
27
+ *
28
+ * Define all the methods available within the iFrame RPC client with response type annotations
29
+ *
30
+ * @group RPC Schema
31
+ *
32
+ * @remarks
33
+ * Each method in the schema now includes a ResponseType field that indicates:
34
+ * - "promise": One-shot request that resolves once
35
+ * - "stream": Streaming request that can emit multiple values
36
+ *
37
+ * ### Methods:
38
+ *
39
+ * #### frak_listenToWalletStatus
40
+ * - Params: None
41
+ * - Returns: {@link WalletStatusReturnType}
42
+ * - Response Type: stream (emits updates when wallet status changes)
43
+ *
44
+ * #### frak_displayModal
45
+ * - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
46
+ * - Returns: {@link ModalRpcStepsResultType}
47
+ * - Response Type: promise (one-shot)
48
+ *
49
+ * #### frak_sendInteraction
50
+ * - Params: [productId: Hex, interaction: {@link PreparedInteraction}, signature?: Hex]
51
+ * - Returns: {@link SendInteractionReturnType}
52
+ * - Response Type: promise (one-shot)
53
+ *
54
+ * #### frak_sso
55
+ * - Params: [params: {@link OpenSsoParamsType}, name: string, customCss?: string]
56
+ * - Returns: {@link OpenSsoReturnType}
57
+ * - Response Type: promise (one-shot)
58
+ *
59
+ * #### frak_getProductInformation
60
+ * - Params: None
61
+ * - Returns: {@link GetProductInformationReturnType}
62
+ * - Response Type: promise (one-shot)
63
+ *
64
+ * #### frak_displayEmbeddedWallet
65
+ * - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
66
+ * - Returns: {@link DisplayEmbeddedWalletResultType}
67
+ * - Response Type: promise (one-shot)
68
+ */
69
+ export type IFrameRpcSchema = [
70
+ /**
71
+ * Method used to listen to the wallet status
72
+ * This is a streaming method that emits updates when wallet status changes
73
+ */
74
+ {
75
+ Method: "frak_listenToWalletStatus";
76
+ Parameters?: undefined;
77
+ ReturnType: WalletStatusReturnType;
78
+ },
79
+ /**
80
+ * Method to display a modal with the provided steps
81
+ * This is a one-shot request
82
+ */
83
+ {
84
+ Method: "frak_displayModal";
85
+ Parameters: [
86
+ requests: ModalRpcStepsInput,
87
+ metadata: ModalRpcMetadata | undefined,
88
+ configMetadata: FrakWalletSdkConfig["metadata"],
89
+ ];
90
+ ReturnType: ModalRpcStepsResultType;
91
+ },
92
+ /**
93
+ * Method to transmit a user interaction
94
+ * This is a one-shot request
95
+ */
96
+ {
97
+ Method: "frak_sendInteraction";
98
+ Parameters: [
99
+ productId: Hex,
100
+ interaction: PreparedInteraction,
101
+ signature?: Hex,
102
+ ];
103
+ ReturnType: SendInteractionReturnType;
104
+ },
105
+ /**
106
+ * Method to prepare SSO (generate URL for popup)
107
+ * Returns the SSO URL that should be opened in a popup
108
+ * Only used for popup flows (not redirect flows)
109
+ */
110
+ {
111
+ Method: "frak_prepareSso";
112
+ Parameters: [
113
+ params: PrepareSsoParamsType,
114
+ name: string,
115
+ customCss?: string,
116
+ ];
117
+ ReturnType: PrepareSsoReturnType;
118
+ },
119
+ /**
120
+ * Method to open/trigger SSO
121
+ * Either triggers redirect (if openInSameWindow/redirectUrl)
122
+ * Or waits for popup completion (if popup mode)
123
+ * This method handles BOTH redirect and popup flows
124
+ */
125
+ {
126
+ Method: "frak_openSso";
127
+ Parameters: [
128
+ params: OpenSsoParamsType,
129
+ name: string,
130
+ customCss?: string,
131
+ ];
132
+ ReturnType: OpenSsoReturnType;
133
+ },
134
+ /**
135
+ * Method to get current product information's
136
+ * - Is product minted?
137
+ * - Does it have running campaign?
138
+ * - Estimated reward on actions
139
+ * This is a one-shot request
140
+ */
141
+ {
142
+ Method: "frak_getProductInformation";
143
+ Parameters?: undefined;
144
+ ReturnType: GetProductInformationReturnType;
145
+ },
146
+ /**
147
+ * Method to show the embedded wallet, with potential customization
148
+ * This is a one-shot request
149
+ */
150
+ {
151
+ Method: "frak_displayEmbeddedWallet";
152
+ Parameters: [
153
+ request: DisplayEmbeddedWalletParamsType,
154
+ metadata: FrakWalletSdkConfig["metadata"],
155
+ ];
156
+ ReturnType: DisplayEmbeddedWalletResultType;
157
+ },
158
+ ];
@@ -0,0 +1,34 @@
1
+ import type { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
2
+ import type { ClientLifecycleEvent, IFrameLifecycleEvent } from "./lifecycle";
3
+ import type { IFrameRpcSchema } from "./rpc";
4
+
5
+ /**
6
+ * IFrame transport interface
7
+ */
8
+ export type IFrameTransport = {
9
+ /**
10
+ * Wait for the connection to be established
11
+ */
12
+ waitForConnection: Promise<boolean>;
13
+ /**
14
+ * Wait for the setup to be done
15
+ */
16
+ waitForSetup: Promise<void>;
17
+ /**
18
+ * Function used to perform a single request via the iframe transport
19
+ */
20
+ request: RpcClient<IFrameRpcSchema, LifecycleMessage>["request"];
21
+ /**
22
+ * Function used to listen to a request response via the iframe transport
23
+ */
24
+ listenerRequest: RpcClient<IFrameRpcSchema, LifecycleMessage>["listen"];
25
+ /**
26
+ * Function used to destroy the iframe transport
27
+ */
28
+ destroy: () => Promise<void>;
29
+ };
30
+
31
+ /**
32
+ * Represent an iframe event
33
+ */
34
+ export type FrakLifecycleEvent = IFrameLifecycleEvent | ClientLifecycleEvent;
@@ -0,0 +1,152 @@
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
+ }: { url?: string; context: Partial<FrakContext> }) {
71
+ if (!url) return null;
72
+
73
+ // Parse the current context
74
+ const currentContext = parse({ url });
75
+
76
+ // Merge the current context with the new context
77
+ const mergedContext = currentContext
78
+ ? { ...currentContext, ...context }
79
+ : context;
80
+
81
+ // If we don't have a referrer, early exit
82
+ if (!mergedContext.r) return null;
83
+
84
+ // Compress it
85
+ const compressedContext = compress(mergedContext);
86
+ if (!compressedContext) return null;
87
+
88
+ // Build the new url and return it
89
+ const urlObj = new URL(url);
90
+ urlObj.searchParams.set(contextKey, compressedContext);
91
+ return urlObj.toString();
92
+ }
93
+
94
+ /**
95
+ * Remove Frak context from current url
96
+ * @param url - The url to update
97
+ * @returns The new url without the Frak context
98
+ */
99
+ function remove(url: string) {
100
+ const urlObj = new URL(url);
101
+ urlObj.searchParams.delete(contextKey);
102
+ return urlObj.toString();
103
+ }
104
+
105
+ /**
106
+ * Replace the current url with the given Frak context
107
+ * @param args
108
+ * @param args.url - The url to update
109
+ * @param args.context - The context to update
110
+ */
111
+ function replaceUrl({
112
+ url: baseUrl,
113
+ context,
114
+ }: { url?: string; context: Partial<FrakContext> | null }) {
115
+ // If no window here early exit
116
+ if (!window.location?.href || typeof window === "undefined") {
117
+ console.error("No window found, can't update context");
118
+ return;
119
+ }
120
+
121
+ // If no url, try to use the current one
122
+ const url = baseUrl ?? window.location.href;
123
+
124
+ // Get our new url with the frak context
125
+ let newUrl: string | null;
126
+ if (context !== null) {
127
+ newUrl = update({
128
+ url,
129
+ context,
130
+ });
131
+ } else {
132
+ newUrl = remove(url);
133
+ }
134
+
135
+ // If no new url, early exit
136
+ if (!newUrl) return;
137
+
138
+ // Update the url
139
+ window.history.replaceState(null, "", newUrl.toString());
140
+ }
141
+
142
+ /**
143
+ * Export our frak context
144
+ */
145
+ export const FrakContextManager = {
146
+ compress,
147
+ decompress,
148
+ parse,
149
+ update,
150
+ remove,
151
+ replaceUrl,
152
+ };
@@ -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,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
+ }