@frak-labs/core-sdk 0.1.1 → 0.2.0
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.
- package/README.md +58 -0
- package/cdn/bundle.js +14 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.d.cts +3 -3
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +1 -1
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.cts +4 -6
- package/dist/bundle.d.ts +4 -6
- package/dist/bundle.js +1 -1
- package/dist/{index-CRsQWnTs.d.cts → computeLegacyProductId-BkyJ4rEY.d.ts} +197 -10
- package/dist/{index-Ck1hudEi.d.ts → computeLegacyProductId-Raks6FXg.d.cts} +197 -10
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -1
- package/dist/{openSso-D--Airj6.d.cts → openSso-BCJGchIb.d.cts} +135 -131
- package/dist/{openSso-DsKJ4y0j.d.ts → openSso-DG-_9CED.d.ts} +135 -131
- package/dist/setupClient-CQrMDGyZ.js +13 -0
- package/dist/setupClient-Ccv3XxwL.cjs +13 -0
- package/dist/{index-d8xS4ryI.d.ts → siweAuthenticate-BH7Dn7nZ.d.cts} +90 -65
- package/dist/siweAuthenticate-BJHbtty4.js +1 -0
- package/dist/{index-C6FxkWPC.d.cts → siweAuthenticate-Btem4QHs.d.ts} +90 -65
- package/dist/siweAuthenticate-Cwj3HP0m.cjs +1 -0
- package/dist/trackEvent-M2RLTQ2p.js +1 -0
- package/dist/trackEvent-T_R9ER2S.cjs +1 -0
- package/package.json +11 -22
- package/src/actions/displayEmbeddedWallet.ts +1 -0
- package/src/actions/displayModal.test.ts +12 -11
- package/src/actions/displayModal.ts +7 -18
- package/src/actions/ensureIdentity.ts +68 -0
- package/src/actions/{getProductInformation.test.ts → getMerchantInformation.test.ts} +33 -50
- package/src/actions/getMerchantInformation.ts +16 -0
- package/src/actions/index.ts +3 -2
- package/src/actions/openSso.ts +4 -2
- package/src/actions/referral/processReferral.test.ts +42 -151
- package/src/actions/referral/processReferral.ts +18 -42
- package/src/actions/referral/referralInteraction.test.ts +1 -7
- package/src/actions/referral/referralInteraction.ts +1 -6
- package/src/actions/sendInteraction.ts +46 -22
- package/src/actions/trackPurchaseStatus.test.ts +354 -141
- package/src/actions/trackPurchaseStatus.ts +48 -11
- package/src/actions/watchWalletStatus.ts +2 -3
- package/src/actions/wrapper/modalBuilder.test.ts +0 -14
- package/src/actions/wrapper/modalBuilder.ts +3 -12
- package/src/bundle.ts +0 -1
- package/src/clients/createIFrameFrakClient.ts +10 -5
- package/src/clients/transports/iframeLifecycleManager.test.ts +163 -4
- package/src/clients/transports/iframeLifecycleManager.ts +172 -33
- package/src/constants/interactionTypes.ts +12 -41
- package/src/index.ts +24 -16
- package/src/types/config.ts +6 -0
- package/src/types/index.ts +13 -10
- package/src/types/lifecycle/client.ts +24 -1
- package/src/types/lifecycle/iframe.ts +6 -0
- package/src/types/rpc/displayModal.ts +2 -4
- package/src/types/rpc/embedded/index.ts +2 -2
- package/src/types/rpc/interaction.ts +26 -39
- package/src/types/rpc/merchantInformation.ts +77 -0
- package/src/types/rpc/modal/index.ts +0 -4
- package/src/types/rpc/modal/login.ts +5 -1
- package/src/types/rpc/walletStatus.ts +1 -7
- package/src/types/rpc.ts +22 -30
- package/src/types/tracking.ts +60 -0
- package/src/utils/backendUrl.test.ts +83 -0
- package/src/utils/backendUrl.ts +62 -0
- package/src/utils/clientId.test.ts +41 -0
- package/src/utils/clientId.ts +43 -0
- package/src/utils/compression/compress.test.ts +1 -1
- package/src/utils/compression/compress.ts +2 -2
- package/src/utils/compression/decompress.test.ts +8 -4
- package/src/utils/compression/decompress.ts +2 -2
- package/src/utils/{computeProductId.ts → computeLegacyProductId.ts} +2 -2
- package/src/utils/constants.ts +5 -0
- package/src/utils/deepLinkWithFallback.test.ts +243 -0
- package/src/utils/deepLinkWithFallback.ts +103 -0
- package/src/utils/formatAmount.ts +6 -0
- package/src/utils/iframeHelper.test.ts +18 -5
- package/src/utils/iframeHelper.ts +10 -3
- package/src/utils/index.ts +16 -1
- package/src/utils/merchantId.test.ts +653 -0
- package/src/utils/merchantId.ts +143 -0
- package/src/utils/sso.ts +18 -11
- package/src/utils/trackEvent.test.ts +23 -5
- package/src/utils/trackEvent.ts +13 -0
- package/cdn/bundle.iife.js +0 -14
- package/dist/actions-B5j-i1p0.cjs +0 -1
- package/dist/actions-q090Z0oR.js +0 -1
- package/dist/index-7OZ39x1U.d.ts +0 -195
- package/dist/index-zDq-VlKx.d.cts +0 -195
- package/dist/interaction-DMJ3ZfaF.d.cts +0 -45
- package/dist/interaction-KX1h9a7V.d.ts +0 -45
- package/dist/interactions-DnfM3oe0.js +0 -1
- package/dist/interactions-EIXhNLf6.cjs +0 -1
- package/dist/interactions.cjs +0 -1
- package/dist/interactions.d.cts +0 -2
- package/dist/interactions.d.ts +0 -2
- package/dist/interactions.js +0 -1
- package/dist/productTypes-BUkXJKZ7.cjs +0 -1
- package/dist/productTypes-CGb1MmBF.js +0 -1
- package/dist/src-1LQ4eLq5.js +0 -13
- package/dist/src-hW71KjPN.cjs +0 -13
- package/dist/trackEvent-CHnYa85W.js +0 -1
- package/dist/trackEvent-GuQm_1Nm.cjs +0 -1
- package/src/actions/getProductInformation.ts +0 -14
- package/src/actions/openSso.test.ts +0 -407
- package/src/actions/sendInteraction.test.ts +0 -219
- package/src/constants/interactionTypes.test.ts +0 -128
- package/src/constants/productTypes.test.ts +0 -130
- package/src/constants/productTypes.ts +0 -33
- package/src/interactions/index.ts +0 -5
- package/src/interactions/pressEncoder.test.ts +0 -215
- package/src/interactions/pressEncoder.ts +0 -53
- package/src/interactions/purchaseEncoder.test.ts +0 -291
- package/src/interactions/purchaseEncoder.ts +0 -99
- package/src/interactions/referralEncoder.test.ts +0 -170
- package/src/interactions/referralEncoder.ts +0 -47
- package/src/interactions/retailEncoder.test.ts +0 -107
- package/src/interactions/retailEncoder.ts +0 -37
- package/src/interactions/webshopEncoder.test.ts +0 -56
- package/src/interactions/webshopEncoder.ts +0 -30
- package/src/types/rpc/modal/openSession.ts +0 -25
- package/src/types/rpc/productInformation.ts +0 -59
- package/src/utils/computeProductId.test.ts +0 -80
- package/src/utils/sso.test.ts +0 -361
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
import type { InteractionTypeKey } from "../../constants/interactionTypes";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The type for the amount of tokens
|
|
6
|
+
*/
|
|
7
|
+
export type TokenAmountType = {
|
|
8
|
+
amount: number;
|
|
9
|
+
eurAmount: number;
|
|
10
|
+
usdAmount: number;
|
|
11
|
+
gbpAmount: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A tier definition for tiered rewards
|
|
16
|
+
*/
|
|
17
|
+
export type RewardTier = {
|
|
18
|
+
minValue: number;
|
|
19
|
+
maxValue?: number;
|
|
20
|
+
amount: TokenAmountType;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Estimated reward amount — discriminated union by payout type
|
|
25
|
+
*
|
|
26
|
+
* - `fixed`: A known token amount (with fiat equivalents)
|
|
27
|
+
* - `percentage`: A percent of a purchase field (e.g. 5% of purchase_amount), with optional min/max caps
|
|
28
|
+
* - `tiered`: Amount depends on a field value matching tier brackets
|
|
29
|
+
*/
|
|
30
|
+
export type EstimatedReward =
|
|
31
|
+
| {
|
|
32
|
+
payoutType: "fixed";
|
|
33
|
+
amount: TokenAmountType;
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
payoutType: "percentage";
|
|
37
|
+
percent: number;
|
|
38
|
+
percentOf: string;
|
|
39
|
+
maxAmount?: TokenAmountType;
|
|
40
|
+
minAmount?: TokenAmountType;
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
payoutType: "tiered";
|
|
44
|
+
tierField: string;
|
|
45
|
+
tiers: RewardTier[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Response of the `frak_getMerchantInformation` RPC method
|
|
50
|
+
* @group RPC Schema
|
|
51
|
+
*/
|
|
52
|
+
export type GetMerchantInformationReturnType = {
|
|
53
|
+
/**
|
|
54
|
+
* Current merchant id
|
|
55
|
+
*/
|
|
56
|
+
id: string;
|
|
57
|
+
/**
|
|
58
|
+
* Some metadata
|
|
59
|
+
*/
|
|
60
|
+
onChainMetadata: {
|
|
61
|
+
/**
|
|
62
|
+
* Name of the merchant on-chain
|
|
63
|
+
*/
|
|
64
|
+
name: string;
|
|
65
|
+
/**
|
|
66
|
+
* Domain of the merchant on-chain
|
|
67
|
+
*/
|
|
68
|
+
domain: string;
|
|
69
|
+
};
|
|
70
|
+
rewards: {
|
|
71
|
+
token?: Address;
|
|
72
|
+
campaignId: string;
|
|
73
|
+
interactionTypeKey: InteractionTypeKey;
|
|
74
|
+
referrer?: EstimatedReward;
|
|
75
|
+
referee?: EstimatedReward;
|
|
76
|
+
}[];
|
|
77
|
+
};
|
|
@@ -4,10 +4,6 @@ export type {
|
|
|
4
4
|
} from "./final";
|
|
5
5
|
export type { ModalStepMetadata } from "./generic";
|
|
6
6
|
export type { LoginModalStepType } from "./login";
|
|
7
|
-
export type {
|
|
8
|
-
OpenInteractionSessionModalStepType,
|
|
9
|
-
OpenInteractionSessionReturnType,
|
|
10
|
-
} from "./openSession";
|
|
11
7
|
export type {
|
|
12
8
|
SiweAuthenticateModalStepType,
|
|
13
9
|
SiweAuthenticateReturnType,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Address } from "viem";
|
|
1
|
+
import type { Address, Hex } from "viem";
|
|
2
2
|
import type { SsoMetadata } from "../sso";
|
|
3
3
|
import type { GenericModalStepType } from "./generic";
|
|
4
4
|
|
|
@@ -28,5 +28,9 @@ export type LoginModalStepType = GenericModalStepType<
|
|
|
28
28
|
LoginWithSso | LoginWithoutSso,
|
|
29
29
|
{
|
|
30
30
|
wallet: Address;
|
|
31
|
+
webauthnProof?: {
|
|
32
|
+
challenge: Hex;
|
|
33
|
+
authenticatorResponse: string;
|
|
34
|
+
};
|
|
31
35
|
}
|
|
32
36
|
>;
|
|
@@ -14,13 +14,8 @@ export type WalletConnected = {
|
|
|
14
14
|
key: "connected";
|
|
15
15
|
// The user wallet address
|
|
16
16
|
wallet: Address;
|
|
17
|
-
// The interaction token, used to push interactions to the
|
|
17
|
+
// The interaction token, used to push interactions to the backend
|
|
18
18
|
interactionToken?: string;
|
|
19
|
-
// The current onchain interaction session of the user
|
|
20
|
-
interactionSession?: {
|
|
21
|
-
startTimestamp: number;
|
|
22
|
-
endTimestamp: number;
|
|
23
|
-
};
|
|
24
19
|
};
|
|
25
20
|
|
|
26
21
|
/**
|
|
@@ -31,5 +26,4 @@ export type WalletNotConnected = {
|
|
|
31
26
|
key: "not-connected";
|
|
32
27
|
wallet?: never;
|
|
33
28
|
interactionToken?: never;
|
|
34
|
-
interactionSession?: never;
|
|
35
29
|
};
|
package/src/types/rpc.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Hex } from "viem";
|
|
2
1
|
import type { FrakWalletSdkConfig } from "./config";
|
|
3
2
|
import type {
|
|
4
3
|
ModalRpcMetadata,
|
|
@@ -9,11 +8,8 @@ import type {
|
|
|
9
8
|
DisplayEmbeddedWalletParamsType,
|
|
10
9
|
DisplayEmbeddedWalletResultType,
|
|
11
10
|
} from "./rpc/embedded";
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
SendInteractionReturnType,
|
|
15
|
-
} from "./rpc/interaction";
|
|
16
|
-
import type { GetProductInformationReturnType } from "./rpc/productInformation";
|
|
11
|
+
import type { SendInteractionParamsType } from "./rpc/interaction";
|
|
12
|
+
import type { GetMerchantInformationReturnType } from "./rpc/merchantInformation";
|
|
17
13
|
import type {
|
|
18
14
|
OpenSsoParamsType,
|
|
19
15
|
OpenSsoReturnType,
|
|
@@ -46,19 +42,14 @@ import type { WalletStatusReturnType } from "./rpc/walletStatus";
|
|
|
46
42
|
* - Returns: {@link ModalRpcStepsResultType}
|
|
47
43
|
* - Response Type: promise (one-shot)
|
|
48
44
|
*
|
|
49
|
-
* #### frak_sendInteraction
|
|
50
|
-
* - Params: [productId: Hex, interaction: {@link PreparedInteraction}, signature?: Hex]
|
|
51
|
-
* - Returns: {@link SendInteractionReturnType}
|
|
52
|
-
* - Response Type: promise (one-shot)
|
|
53
|
-
*
|
|
54
45
|
* #### frak_sso
|
|
55
46
|
* - Params: [params: {@link OpenSsoParamsType}, name: string, customCss?: string]
|
|
56
47
|
* - Returns: {@link OpenSsoReturnType}
|
|
57
48
|
* - Response Type: promise (one-shot)
|
|
58
49
|
*
|
|
59
|
-
* ####
|
|
50
|
+
* #### frak_getMerchantInformation
|
|
60
51
|
* - Params: None
|
|
61
|
-
* - Returns: {@link
|
|
52
|
+
* - Returns: {@link GetMerchantInformationReturnType}
|
|
62
53
|
* - Response Type: promise (one-shot)
|
|
63
54
|
*
|
|
64
55
|
* #### frak_displayEmbeddedWallet
|
|
@@ -89,19 +80,6 @@ export type IFrameRpcSchema = [
|
|
|
89
80
|
];
|
|
90
81
|
ReturnType: ModalRpcStepsResultType;
|
|
91
82
|
},
|
|
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
83
|
/**
|
|
106
84
|
* Method to prepare SSO (generate URL for popup)
|
|
107
85
|
* Returns the SSO URL that should be opened in a popup
|
|
@@ -132,16 +110,16 @@ export type IFrameRpcSchema = [
|
|
|
132
110
|
ReturnType: OpenSsoReturnType;
|
|
133
111
|
},
|
|
134
112
|
/**
|
|
135
|
-
* Method to get current
|
|
136
|
-
* - Is
|
|
113
|
+
* Method to get current merchant information
|
|
114
|
+
* - Is merchant registered?
|
|
137
115
|
* - Does it have running campaign?
|
|
138
116
|
* - Estimated reward on actions
|
|
139
117
|
* This is a one-shot request
|
|
140
118
|
*/
|
|
141
119
|
{
|
|
142
|
-
Method: "
|
|
120
|
+
Method: "frak_getMerchantInformation";
|
|
143
121
|
Parameters?: undefined;
|
|
144
|
-
ReturnType:
|
|
122
|
+
ReturnType: GetMerchantInformationReturnType;
|
|
145
123
|
},
|
|
146
124
|
/**
|
|
147
125
|
* Method to show the embedded wallet, with potential customization
|
|
@@ -155,4 +133,18 @@ export type IFrameRpcSchema = [
|
|
|
155
133
|
];
|
|
156
134
|
ReturnType: DisplayEmbeddedWalletResultType;
|
|
157
135
|
},
|
|
136
|
+
/**
|
|
137
|
+
* Method to send interactions (arrival, sharing, custom events)
|
|
138
|
+
* Fire-and-forget method - no return value expected
|
|
139
|
+
* merchantId is resolved from context
|
|
140
|
+
* clientId is passed via metadata as safeguard against handshake race condition
|
|
141
|
+
*/
|
|
142
|
+
{
|
|
143
|
+
Method: "frak_sendInteraction";
|
|
144
|
+
Parameters: [
|
|
145
|
+
interaction: SendInteractionParamsType,
|
|
146
|
+
metadata?: { clientId?: string },
|
|
147
|
+
];
|
|
148
|
+
ReturnType: undefined;
|
|
149
|
+
},
|
|
158
150
|
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for arrival tracking and referral attribution
|
|
3
|
+
* @category Tracking
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Address } from "viem";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* UTM parameters for marketing attribution
|
|
10
|
+
* @category Tracking
|
|
11
|
+
*/
|
|
12
|
+
export type UtmParams = {
|
|
13
|
+
source?: string;
|
|
14
|
+
medium?: string;
|
|
15
|
+
campaign?: string;
|
|
16
|
+
term?: string;
|
|
17
|
+
content?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parameters for tracking an arrival event
|
|
22
|
+
* @category Tracking
|
|
23
|
+
*/
|
|
24
|
+
export type TrackArrivalParams = {
|
|
25
|
+
/**
|
|
26
|
+
* The referrer wallet address (from fCtx URL param)
|
|
27
|
+
*/
|
|
28
|
+
referrerWallet?: Address;
|
|
29
|
+
/**
|
|
30
|
+
* The landing page URL (defaults to current page)
|
|
31
|
+
*/
|
|
32
|
+
landingUrl?: string;
|
|
33
|
+
/**
|
|
34
|
+
* UTM parameters for marketing attribution
|
|
35
|
+
*/
|
|
36
|
+
utmParams?: UtmParams;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Result from tracking an arrival event
|
|
41
|
+
* @category Tracking
|
|
42
|
+
*/
|
|
43
|
+
export type TrackArrivalResult = {
|
|
44
|
+
success: boolean;
|
|
45
|
+
identityGroupId?: string;
|
|
46
|
+
touchpointId?: string;
|
|
47
|
+
error?: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Internal params passed to the trackArrival action
|
|
52
|
+
* Includes merchantId resolved from config or fetch
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export type TrackArrivalInternalParams = TrackArrivalParams & {
|
|
56
|
+
/**
|
|
57
|
+
* The merchant ID (UUID from dashboard)
|
|
58
|
+
*/
|
|
59
|
+
merchantId: string;
|
|
60
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { getBackendUrl } from "./backendUrl";
|
|
3
|
+
|
|
4
|
+
describe("getBackendUrl", () => {
|
|
5
|
+
const originalWindow = globalThis.window;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.stubGlobal("window", { ...originalWindow });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.unstubAllGlobals();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("with explicit walletUrl", () => {
|
|
16
|
+
test("should return localhost backend for localhost:3000", () => {
|
|
17
|
+
expect(getBackendUrl("https://localhost:3000")).toBe(
|
|
18
|
+
"http://localhost:3030"
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("should return localhost backend for localhost:3010", () => {
|
|
23
|
+
expect(getBackendUrl("https://localhost:3010")).toBe(
|
|
24
|
+
"http://localhost:3030"
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("should return dev backend for wallet-dev.frak.id", () => {
|
|
29
|
+
expect(getBackendUrl("https://wallet-dev.frak.id")).toBe(
|
|
30
|
+
"https://backend.gcp-dev.frak.id"
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("should return dev backend for wallet.gcp-dev.frak.id", () => {
|
|
35
|
+
expect(getBackendUrl("https://wallet.gcp-dev.frak.id")).toBe(
|
|
36
|
+
"https://backend.gcp-dev.frak.id"
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("should return production backend for wallet.frak.id", () => {
|
|
41
|
+
expect(getBackendUrl("https://wallet.frak.id")).toBe(
|
|
42
|
+
"https://backend.frak.id"
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("should return production backend for unknown URLs", () => {
|
|
47
|
+
expect(getBackendUrl("https://some-other-url.com")).toBe(
|
|
48
|
+
"https://backend.frak.id"
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("with FrakSetup global config", () => {
|
|
54
|
+
test("should derive from window.FrakSetup.client.config.walletUrl", () => {
|
|
55
|
+
vi.stubGlobal("window", {
|
|
56
|
+
FrakSetup: {
|
|
57
|
+
client: {
|
|
58
|
+
config: {
|
|
59
|
+
walletUrl: "https://wallet-dev.frak.id",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(getBackendUrl()).toBe("https://backend.gcp-dev.frak.id");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("should fall back to production when FrakSetup has no walletUrl", () => {
|
|
69
|
+
vi.stubGlobal("window", {
|
|
70
|
+
FrakSetup: { client: { config: {} } },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(getBackendUrl()).toBe("https://backend.frak.id");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("fallback", () => {
|
|
78
|
+
test("should return production URL when no walletUrl and no FrakSetup", () => {
|
|
79
|
+
vi.stubGlobal("window", {});
|
|
80
|
+
expect(getBackendUrl()).toBe("https://backend.frak.id");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default production backend URL
|
|
3
|
+
*/
|
|
4
|
+
const DEFAULT_BACKEND_URL = "https://backend.frak.id";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if wallet URL is local development (port 3000 or 3010)
|
|
8
|
+
*/
|
|
9
|
+
function isLocalDevelopment(walletUrl: string): boolean {
|
|
10
|
+
return (
|
|
11
|
+
walletUrl.includes("localhost:3000") ||
|
|
12
|
+
walletUrl.includes("localhost:3010")
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Derive backend URL from wallet URL
|
|
18
|
+
* Maps wallet URLs to their corresponding backend URLs
|
|
19
|
+
*/
|
|
20
|
+
function deriveBackendUrl(walletUrl: string): string {
|
|
21
|
+
if (isLocalDevelopment(walletUrl)) {
|
|
22
|
+
return "http://localhost:3030";
|
|
23
|
+
}
|
|
24
|
+
// Dev environment
|
|
25
|
+
if (
|
|
26
|
+
walletUrl.includes("wallet-dev.frak.id") ||
|
|
27
|
+
walletUrl.includes("wallet.gcp-dev.frak.id")
|
|
28
|
+
) {
|
|
29
|
+
return "https://backend.gcp-dev.frak.id";
|
|
30
|
+
}
|
|
31
|
+
// Production
|
|
32
|
+
return DEFAULT_BACKEND_URL;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the backend URL for API calls
|
|
37
|
+
* Tries to derive from SDK config, falls back to production
|
|
38
|
+
*
|
|
39
|
+
* @param walletUrl - Optional wallet URL to derive from (overrides global config)
|
|
40
|
+
*/
|
|
41
|
+
export function getBackendUrl(walletUrl?: string): string {
|
|
42
|
+
// If explicit walletUrl provided, derive from it
|
|
43
|
+
if (walletUrl) {
|
|
44
|
+
return deriveBackendUrl(walletUrl);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try to get from global FrakSetup config
|
|
48
|
+
if (typeof window !== "undefined") {
|
|
49
|
+
const configWalletUrl = (
|
|
50
|
+
window as {
|
|
51
|
+
FrakSetup?: { client?: { config?: { walletUrl?: string } } };
|
|
52
|
+
}
|
|
53
|
+
).FrakSetup?.client?.config?.walletUrl;
|
|
54
|
+
|
|
55
|
+
if (configWalletUrl) {
|
|
56
|
+
return deriveBackendUrl(configWalletUrl);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Fallback to production
|
|
61
|
+
return DEFAULT_BACKEND_URL;
|
|
62
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { getClientId } from "./clientId";
|
|
3
|
+
|
|
4
|
+
describe("clientId", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
// Clear localStorage before each test
|
|
7
|
+
localStorage.clear();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("getClientId", () => {
|
|
15
|
+
it("should generate and store a new client ID when none exists", () => {
|
|
16
|
+
const clientId = getClientId();
|
|
17
|
+
|
|
18
|
+
expect(clientId).toBeDefined();
|
|
19
|
+
expect(clientId).toMatch(
|
|
20
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
21
|
+
);
|
|
22
|
+
expect(localStorage.getItem("frak-client-id")).toBe(clientId);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should return existing client ID from localStorage", () => {
|
|
26
|
+
const existingId = "existing-uuid-1234";
|
|
27
|
+
localStorage.setItem("frak-client-id", existingId);
|
|
28
|
+
|
|
29
|
+
const clientId = getClientId();
|
|
30
|
+
|
|
31
|
+
expect(clientId).toBe(existingId);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should generate consistent UUIDs", () => {
|
|
35
|
+
const id1 = getClientId();
|
|
36
|
+
const id2 = getClientId();
|
|
37
|
+
|
|
38
|
+
expect(id1).toBe(id2);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client ID utilities for anonymous tracking
|
|
3
|
+
* Generates and persists a UUID fingerprint for referral attribution
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const CLIENT_ID_KEY = "frak-client-id";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate a UUID v4
|
|
10
|
+
* Uses crypto.randomUUID if available, otherwise falls back to a manual implementation
|
|
11
|
+
*/
|
|
12
|
+
function generateUUID(): string {
|
|
13
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
14
|
+
return crypto.randomUUID();
|
|
15
|
+
}
|
|
16
|
+
// Fallback for older browsers
|
|
17
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
18
|
+
const r = (Math.random() * 16) | 0;
|
|
19
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
20
|
+
return v.toString(16);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the client ID from localStorage, creating one if it doesn't exist
|
|
26
|
+
* @returns The client ID (UUID format)
|
|
27
|
+
*/
|
|
28
|
+
export function getClientId(): string {
|
|
29
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
30
|
+
// SSR or no localStorage - generate ephemeral ID
|
|
31
|
+
console.warn(
|
|
32
|
+
"[Frak SDK] No Window / localStorage available to save the clientId"
|
|
33
|
+
);
|
|
34
|
+
return generateUUID();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let clientId = localStorage.getItem(CLIENT_ID_KEY);
|
|
38
|
+
if (!clientId) {
|
|
39
|
+
clientId = generateUUID();
|
|
40
|
+
localStorage.setItem(CLIENT_ID_KEY, clientId);
|
|
41
|
+
}
|
|
42
|
+
return clientId;
|
|
43
|
+
}
|
|
@@ -7,7 +7,7 @@ import { vi } from "vitest";
|
|
|
7
7
|
|
|
8
8
|
// Mock the frame-connector module - must be before imports
|
|
9
9
|
vi.mock("@frak-labs/frame-connector", () => ({
|
|
10
|
-
|
|
10
|
+
jsonEncode: vi.fn((data: unknown) => {
|
|
11
11
|
// Simple mock: convert JSON to Uint8Array
|
|
12
12
|
const jsonString = JSON.stringify(data);
|
|
13
13
|
return new TextEncoder().encode(jsonString);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsonEncode } from "@frak-labs/frame-connector";
|
|
2
2
|
import { base64urlEncode } from "./b64";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,5 +7,5 @@ import { base64urlEncode } from "./b64";
|
|
|
7
7
|
* @ignore
|
|
8
8
|
*/
|
|
9
9
|
export function compressJsonToB64(data: unknown): string {
|
|
10
|
-
return base64urlEncode(
|
|
10
|
+
return base64urlEncode(jsonEncode(data));
|
|
11
11
|
}
|
|
@@ -7,15 +7,19 @@ import { vi } from "vitest";
|
|
|
7
7
|
|
|
8
8
|
// Mock the frame-connector module - must be before imports
|
|
9
9
|
vi.mock("@frak-labs/frame-connector", () => ({
|
|
10
|
-
|
|
10
|
+
jsonEncode: vi.fn((data: unknown) => {
|
|
11
11
|
// Simple mock: convert JSON to Uint8Array
|
|
12
12
|
const jsonString = JSON.stringify(data);
|
|
13
13
|
return new TextEncoder().encode(jsonString);
|
|
14
14
|
}),
|
|
15
|
-
|
|
15
|
+
jsonDecode: vi.fn((data: Uint8Array) => {
|
|
16
16
|
// Simple mock: convert Uint8Array back to JSON
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
try {
|
|
18
|
+
const jsonString = new TextDecoder().decode(data);
|
|
19
|
+
return JSON.parse(jsonString);
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
19
23
|
}),
|
|
20
24
|
}));
|
|
21
25
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsonDecode } from "@frak-labs/frame-connector";
|
|
2
2
|
import { base64urlDecode } from "./b64";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,5 +7,5 @@ import { base64urlDecode } from "./b64";
|
|
|
7
7
|
* @ignore
|
|
8
8
|
*/
|
|
9
9
|
export function decompressJsonFromB64<T>(data: string): T | null {
|
|
10
|
-
return
|
|
10
|
+
return jsonDecode<T>(base64urlDecode(data));
|
|
11
11
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { keccak256, toHex } from "viem";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Compute the product id from a domain
|
|
4
|
+
* Compute the legacy product id from a domain
|
|
5
5
|
* @ignore
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
7
|
+
export function computeLegacyProductId({ domain }: { domain?: string } = {}) {
|
|
8
8
|
const effectiveDomain = domain ?? window.location.host;
|
|
9
9
|
const normalizedDomain = effectiveDomain.replace("www.", "");
|
|
10
10
|
return keccak256(toHex(normalizedDomain));
|