@frak-labs/core-sdk 1.0.2-beta.9985efe7 → 1.0.2-beta.9d4f564a
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 +0 -1
- package/package.json +4 -4
- package/src/actions/ensureIdentity.ts +3 -3
- package/src/actions/openSso.ts +4 -4
- package/src/actions/referral/processReferral.test.ts +38 -22
- package/src/actions/referral/processReferral.ts +6 -4
- package/src/actions/referral/referralInteraction.test.ts +7 -7
- package/src/actions/referral/referralInteraction.ts +1 -1
- package/src/actions/sendInteraction.ts +1 -1
- package/src/actions/trackPurchaseStatus.test.ts +4 -4
- package/src/actions/trackPurchaseStatus.ts +3 -3
- package/src/actions/wrapper/siweAuthenticate.test.ts +1 -5
- package/src/actions/wrapper/siweAuthenticate.ts +25 -1
- package/src/clients/createIFrameFrakClient.ts +5 -21
- package/src/clients/index.ts +0 -1
- package/src/clients/transports/iframeLifecycleManager.test.ts +10 -10
- package/src/clients/transports/iframeLifecycleManager.ts +3 -3
- package/src/config/index.ts +3 -0
- package/src/{utils → config}/sdkConfigStore.ts +1 -1
- package/src/{utils/constants.test.ts → constants.test.ts} +1 -6
- package/src/constants.ts +15 -0
- package/src/context/address.ts +76 -0
- package/src/{utils/FrakContext.test.ts → context/frakContext.test.ts} +4 -2
- package/src/{utils/FrakContext.ts → context/frakContext.ts} +4 -4
- package/src/{utils → context}/frakContextV2Codec.test.ts +1 -1
- package/src/{utils → context}/frakContextV2Codec.ts +6 -6
- package/src/context/index.ts +6 -0
- package/src/index.ts +16 -22
- package/src/stubs/rrweb.ts +8 -4
- package/src/types/client.ts +0 -3
- package/src/utils/{deepLinkWithFallback.ts → browser/deepLinkWithFallback.ts} +10 -5
- package/src/utils/{inAppBrowser.ts → browser/inAppBrowser.ts} +13 -0
- package/src/utils/browser/index.ts +13 -0
- package/src/utils/{formatAmount.test.ts → format/formatAmount.test.ts} +1 -1
- package/src/utils/{formatAmount.ts → format/formatAmount.ts} +1 -1
- package/src/utils/{getCurrencyAmountKey.test.ts → format/getCurrencyAmountKey.test.ts} +2 -2
- package/src/utils/{getCurrencyAmountKey.ts → format/getCurrencyAmountKey.ts} +1 -1
- package/src/utils/{getSupportedCurrency.test.ts → format/getSupportedCurrency.test.ts} +2 -2
- package/src/utils/{getSupportedCurrency.ts → format/getSupportedCurrency.ts} +2 -2
- package/src/utils/{getSupportedLocale.test.ts → format/getSupportedLocale.test.ts} +3 -3
- package/src/utils/{getSupportedLocale.ts → format/getSupportedLocale.ts} +2 -2
- package/src/utils/format/index.ts +4 -0
- package/src/utils/{iframeHelper.test.ts → iframe/iframeHelper.test.ts} +3 -3
- package/src/utils/{iframeHelper.ts → iframe/iframeHelper.ts} +3 -3
- package/src/utils/iframe/index.ts +6 -0
- package/src/utils/index.ts +31 -24
- package/src/utils/sso/index.ts +6 -0
- package/src/utils/{sso.ts → sso/sso.ts} +2 -2
- package/cdn/bundle.js +0 -14
- package/dist/actions-DihYM-OG.js +0 -1
- package/dist/actions-cYbmqewX.cjs +0 -1
- package/dist/actions.cjs +0 -1
- package/dist/actions.d.cts +0 -3
- package/dist/actions.d.ts +0 -3
- package/dist/actions.js +0 -1
- package/dist/bundle.cjs +0 -1
- package/dist/bundle.d.cts +0 -4
- package/dist/bundle.d.ts +0 -4
- package/dist/bundle.js +0 -1
- package/dist/index-BsBbSMxk.d.cts +0 -646
- package/dist/index-DzVPSUQq.d.ts +0 -716
- package/dist/index-quaxtKRh.d.ts +0 -646
- package/dist/index-s1vE3jLz.d.cts +0 -716
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +0 -3
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -1
- package/dist/openSso-DyUQew2K.d.ts +0 -1392
- package/dist/openSso-rQhLhPbq.d.cts +0 -1392
- package/dist/sdkConfigStore-BXzz5PlK.js +0 -1
- package/dist/sdkConfigStore-DDL_fjYX.cjs +0 -1
- package/dist/src-BfqUdz3x.js +0 -13
- package/dist/src-x06nhpns.cjs +0 -13
- package/src/clients/DebugInfo.test.ts +0 -418
- package/src/clients/DebugInfo.ts +0 -182
- package/src/utils/computeLegacyProductId.ts +0 -11
- package/src/utils/constants.ts +0 -9
- /package/src/{utils → clients}/ssoUrlListener.test.ts +0 -0
- /package/src/{utils → clients}/ssoUrlListener.ts +0 -0
- /package/src/{utils → config}/backendUrl.test.ts +0 -0
- /package/src/{utils → config}/backendUrl.ts +0 -0
- /package/src/{utils → config}/clientId.test.ts +0 -0
- /package/src/{utils → config}/clientId.ts +0 -0
- /package/src/{utils → config}/sdkConfigStore.test.ts +0 -0
- /package/src/{utils → context}/mergeAttribution.test.ts +0 -0
- /package/src/{utils → context}/mergeAttribution.ts +0 -0
- /package/src/utils/{deepLinkWithFallback.test.ts → browser/deepLinkWithFallback.test.ts} +0 -0
package/README.md
CHANGED
|
@@ -42,7 +42,6 @@ The Core SDK exports 111 functions, types, and utilities organized into four cat
|
|
|
42
42
|
| Export | Purpose |
|
|
43
43
|
|--------|---------|
|
|
44
44
|
| `sdkConfigStore` | Reactive config singleton — resolve, cache, and subscribe to merchant config |
|
|
45
|
-
| `computeLegacyProductId` | Convert product ID to legacy format |
|
|
46
45
|
| `triggerDeepLinkWithFallback` | Open deep link with mobile fallback |
|
|
47
46
|
| `base64urlEncode` / `base64urlDecode` | URL-safe base64 encoding/decoding |
|
|
48
47
|
| `compressJsonToB64` / `decompressJsonFromB64` | JSON compression utilities |
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"url": "https://twitter.com/QNivelais"
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
-
"version": "1.0.2-beta.
|
|
14
|
+
"version": "1.0.2-beta.9d4f564a",
|
|
15
15
|
"description": "Core SDK of the Frak wallet, low level library to interact directly with the frak ecosystem.",
|
|
16
16
|
"repository": {
|
|
17
17
|
"url": "https://github.com/frak-id/wallet",
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"viem": "^2.x"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@frak-labs/frame-connector": "0.2.0-beta.
|
|
95
|
-
"@openpanel/web": "^1.
|
|
94
|
+
"@frak-labs/frame-connector": "0.2.0-beta.9d4f564a",
|
|
95
|
+
"@openpanel/web": "^1.4.1"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"@vitest/coverage-v8": "^4.1.4",
|
|
104
104
|
"@vitest/ui": "^4.1.4",
|
|
105
105
|
"jsdom": "^29.0.0",
|
|
106
|
-
"tsdown": "^0.
|
|
106
|
+
"tsdown": "^0.22.0",
|
|
107
107
|
"typescript": "^6.0.2",
|
|
108
108
|
"viem": "^2.47.16",
|
|
109
109
|
"vitest": "^4.1.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getBackendUrl } from "../
|
|
2
|
-
import { getClientId } from "../
|
|
3
|
-
import { sdkConfigStore } from "../
|
|
1
|
+
import { getBackendUrl } from "../config/backendUrl";
|
|
2
|
+
import { getClientId } from "../config/clientId";
|
|
3
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
4
4
|
|
|
5
5
|
const ENSURE_STORAGE_PREFIX = "frak-identity-ensured-";
|
|
6
6
|
|
package/src/actions/openSso.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { getClientId } from "../config/clientId";
|
|
2
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
1
3
|
import type {
|
|
2
4
|
FrakClient,
|
|
3
5
|
OpenSsoParamsType,
|
|
4
6
|
OpenSsoReturnType,
|
|
5
7
|
} from "../types";
|
|
6
|
-
import {
|
|
7
|
-
import { computeLegacyProductId } from "../utils/computeLegacyProductId";
|
|
8
|
-
import { generateSsoUrl } from "../utils/sso";
|
|
8
|
+
import { generateSsoUrl } from "../utils/sso/sso";
|
|
9
9
|
|
|
10
10
|
// SSO popup configuration
|
|
11
11
|
export const ssoPopupFeatures =
|
|
@@ -92,7 +92,7 @@ export async function openSso(
|
|
|
92
92
|
generateSsoUrl(
|
|
93
93
|
walletUrl ?? "https://wallet.frak.id",
|
|
94
94
|
args,
|
|
95
|
-
|
|
95
|
+
(await sdkConfigStore.resolveMerchantId()) ?? "",
|
|
96
96
|
metadata.name,
|
|
97
97
|
getClientId(),
|
|
98
98
|
customizations?.css
|
|
@@ -19,15 +19,20 @@ vi.mock("../sendInteraction", () => ({
|
|
|
19
19
|
sendInteraction: vi.fn().mockResolvedValue(undefined),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
vi.mock("../../
|
|
22
|
+
vi.mock("../../context", () => ({
|
|
23
23
|
FrakContextManager: {
|
|
24
24
|
replaceUrl: vi.fn(),
|
|
25
25
|
},
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock("../../config/clientId", () => ({
|
|
28
29
|
getClientId: vi.fn().mockReturnValue("test-client-id"),
|
|
29
30
|
}));
|
|
30
31
|
|
|
32
|
+
vi.mock("../../utils", () => ({
|
|
33
|
+
trackEvent: vi.fn(),
|
|
34
|
+
}));
|
|
35
|
+
|
|
31
36
|
describe("processReferral", () => {
|
|
32
37
|
let mockClient: FrakClient;
|
|
33
38
|
let mockAddress: Address;
|
|
@@ -116,8 +121,10 @@ describe("processReferral", () => {
|
|
|
116
121
|
});
|
|
117
122
|
|
|
118
123
|
it("should return 'self-referral' when v2 context has same clientId as current user", async () => {
|
|
119
|
-
const
|
|
120
|
-
vi.mocked(
|
|
124
|
+
const clientIdMod = await import("../../config/clientId");
|
|
125
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(
|
|
126
|
+
"referrer-client-id"
|
|
127
|
+
);
|
|
121
128
|
|
|
122
129
|
const v2SelfReferralContext: FrakContextV2 = {
|
|
123
130
|
v: 2,
|
|
@@ -132,7 +139,9 @@ describe("processReferral", () => {
|
|
|
132
139
|
});
|
|
133
140
|
|
|
134
141
|
expect(result).toBe("self-referral");
|
|
135
|
-
vi.mocked(
|
|
142
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(
|
|
143
|
+
"test-client-id"
|
|
144
|
+
);
|
|
136
145
|
});
|
|
137
146
|
|
|
138
147
|
it("should successfully process v2 referral with wallet only (no clientId)", async () => {
|
|
@@ -180,9 +189,11 @@ describe("processReferral", () => {
|
|
|
180
189
|
});
|
|
181
190
|
|
|
182
191
|
it("should prefer wallet over clientId for self-referral when both are present", async () => {
|
|
183
|
-
const
|
|
192
|
+
const clientIdMod = await import("../../config/clientId");
|
|
184
193
|
// clientId does NOT match current user, but wallet does → still self-referral
|
|
185
|
-
vi.mocked(
|
|
194
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(
|
|
195
|
+
"some-other-client"
|
|
196
|
+
);
|
|
186
197
|
|
|
187
198
|
const v2Hybrid: FrakContextV2 = {
|
|
188
199
|
v: 2,
|
|
@@ -198,7 +209,9 @@ describe("processReferral", () => {
|
|
|
198
209
|
});
|
|
199
210
|
|
|
200
211
|
expect(result).toBe("self-referral");
|
|
201
|
-
vi.mocked(
|
|
212
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(
|
|
213
|
+
"test-client-id"
|
|
214
|
+
);
|
|
202
215
|
});
|
|
203
216
|
});
|
|
204
217
|
|
|
@@ -240,7 +253,8 @@ describe("processReferral", () => {
|
|
|
240
253
|
});
|
|
241
254
|
|
|
242
255
|
it("should update URL context when alwaysAppendUrl is true", async () => {
|
|
243
|
-
const
|
|
256
|
+
const clientIdMod = await import("../../config/clientId");
|
|
257
|
+
const contextMod = await import("../../context");
|
|
244
258
|
|
|
245
259
|
const v2Context: FrakContextV2 = {
|
|
246
260
|
v: 2,
|
|
@@ -257,9 +271,9 @@ describe("processReferral", () => {
|
|
|
257
271
|
},
|
|
258
272
|
});
|
|
259
273
|
|
|
260
|
-
expect(
|
|
274
|
+
expect(clientIdMod.getClientId()).toBe("test-client-id");
|
|
261
275
|
|
|
262
|
-
expect(
|
|
276
|
+
expect(contextMod.FrakContextManager.replaceUrl).toHaveBeenCalledWith({
|
|
263
277
|
url: window.location.href,
|
|
264
278
|
context: expect.objectContaining({
|
|
265
279
|
v: 2,
|
|
@@ -271,7 +285,7 @@ describe("processReferral", () => {
|
|
|
271
285
|
});
|
|
272
286
|
|
|
273
287
|
it("should remove URL context when alwaysAppendUrl is false", async () => {
|
|
274
|
-
const
|
|
288
|
+
const contextMod = await import("../../context");
|
|
275
289
|
|
|
276
290
|
const v2Context: FrakContextV2 = {
|
|
277
291
|
v: 2,
|
|
@@ -288,15 +302,16 @@ describe("processReferral", () => {
|
|
|
288
302
|
},
|
|
289
303
|
});
|
|
290
304
|
|
|
291
|
-
expect(
|
|
305
|
+
expect(contextMod.FrakContextManager.replaceUrl).toHaveBeenCalledWith({
|
|
292
306
|
url: window.location.href,
|
|
293
307
|
context: null,
|
|
294
308
|
});
|
|
295
309
|
});
|
|
296
310
|
|
|
297
311
|
it("should emit wallet in replacement context when alwaysAppendUrl is true and user is connected", async () => {
|
|
298
|
-
const
|
|
299
|
-
|
|
312
|
+
const clientIdMod = await import("../../config/clientId");
|
|
313
|
+
const contextMod = await import("../../context");
|
|
314
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(null as never);
|
|
300
315
|
|
|
301
316
|
const v2Context: FrakContextV2 = {
|
|
302
317
|
v: 2,
|
|
@@ -312,7 +327,7 @@ describe("processReferral", () => {
|
|
|
312
327
|
});
|
|
313
328
|
|
|
314
329
|
// clientId is null, but wallet is available — should still emit {w, m}
|
|
315
|
-
expect(
|
|
330
|
+
expect(contextMod.FrakContextManager.replaceUrl).toHaveBeenCalledWith({
|
|
316
331
|
url: window.location.href,
|
|
317
332
|
context: expect.objectContaining({
|
|
318
333
|
v: 2,
|
|
@@ -321,12 +336,13 @@ describe("processReferral", () => {
|
|
|
321
336
|
}),
|
|
322
337
|
});
|
|
323
338
|
|
|
324
|
-
vi.mocked(
|
|
339
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue("test-client-id");
|
|
325
340
|
});
|
|
326
341
|
|
|
327
342
|
it("should return null replacement context when both clientId and wallet are missing", async () => {
|
|
328
|
-
const
|
|
329
|
-
|
|
343
|
+
const clientIdMod = await import("../../config/clientId");
|
|
344
|
+
const contextMod = await import("../../context");
|
|
345
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue(null as never);
|
|
330
346
|
|
|
331
347
|
const v2Context: FrakContextV2 = {
|
|
332
348
|
v: 2,
|
|
@@ -341,11 +357,11 @@ describe("processReferral", () => {
|
|
|
341
357
|
options: { alwaysAppendUrl: true },
|
|
342
358
|
});
|
|
343
359
|
|
|
344
|
-
expect(
|
|
360
|
+
expect(contextMod.FrakContextManager.replaceUrl).toHaveBeenCalledWith({
|
|
345
361
|
url: window.location.href,
|
|
346
362
|
context: null,
|
|
347
363
|
});
|
|
348
364
|
|
|
349
|
-
vi.mocked(
|
|
365
|
+
vi.mocked(clientIdMod.getClientId).mockReturnValue("test-client-id");
|
|
350
366
|
});
|
|
351
367
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getClientId } from "../../config/clientId";
|
|
2
|
+
import { FrakContextManager } from "../../context";
|
|
3
|
+
import { areAddressesEqual } from "../../context/address";
|
|
2
4
|
import type {
|
|
3
5
|
FrakClient,
|
|
4
6
|
FrakContext,
|
|
@@ -6,7 +8,7 @@ import type {
|
|
|
6
8
|
WalletStatusReturnType,
|
|
7
9
|
} from "../../types";
|
|
8
10
|
import { isV1Context, isV2Context } from "../../types";
|
|
9
|
-
import {
|
|
11
|
+
import { trackEvent } from "../../utils";
|
|
10
12
|
import { sendInteraction } from "../sendInteraction";
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -113,7 +115,7 @@ function isSelfReferral(
|
|
|
113
115
|
if (isV2Context(frakContext)) {
|
|
114
116
|
// Wallet match takes precedence — it's the strongest signal we have.
|
|
115
117
|
if (frakContext.w && walletStatus?.wallet) {
|
|
116
|
-
return
|
|
118
|
+
return areAddressesEqual(frakContext.w, walletStatus.wallet);
|
|
117
119
|
}
|
|
118
120
|
if (frakContext.c) {
|
|
119
121
|
return getClientId() === frakContext.c;
|
|
@@ -121,7 +123,7 @@ function isSelfReferral(
|
|
|
121
123
|
return false;
|
|
122
124
|
}
|
|
123
125
|
if (isV1Context(frakContext) && walletStatus?.wallet) {
|
|
124
|
-
return
|
|
126
|
+
return areAddressesEqual(frakContext.r, walletStatus.wallet);
|
|
125
127
|
}
|
|
126
128
|
return false;
|
|
127
129
|
}
|
|
@@ -2,7 +2,7 @@ import type { Hex } from "viem";
|
|
|
2
2
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
3
|
import { referralInteraction } from "./referralInteraction";
|
|
4
4
|
|
|
5
|
-
vi.mock("../../
|
|
5
|
+
vi.mock("../../context", () => ({
|
|
6
6
|
FrakContextManager: {
|
|
7
7
|
parse: vi.fn(),
|
|
8
8
|
},
|
|
@@ -30,7 +30,7 @@ describe("referralInteraction", () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
test("should parse context from window location", async () => {
|
|
33
|
-
const { FrakContextManager } = await import("../../
|
|
33
|
+
const { FrakContextManager } = await import("../../context");
|
|
34
34
|
const { watchWalletStatus } = await import("../index");
|
|
35
35
|
const { processReferral } = await import("./processReferral");
|
|
36
36
|
|
|
@@ -46,7 +46,7 @@ describe("referralInteraction", () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
test("should get current wallet status", async () => {
|
|
49
|
-
const { FrakContextManager } = await import("../../
|
|
49
|
+
const { FrakContextManager } = await import("../../context");
|
|
50
50
|
const { watchWalletStatus } = await import("../index");
|
|
51
51
|
const { processReferral } = await import("./processReferral");
|
|
52
52
|
|
|
@@ -63,7 +63,7 @@ describe("referralInteraction", () => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test("should call processReferral with all parameters", async () => {
|
|
66
|
-
const { FrakContextManager } = await import("../../
|
|
66
|
+
const { FrakContextManager } = await import("../../context");
|
|
67
67
|
const { watchWalletStatus } = await import("../index");
|
|
68
68
|
const { processReferral } = await import("./processReferral");
|
|
69
69
|
|
|
@@ -87,7 +87,7 @@ describe("referralInteraction", () => {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
test("should return result from processReferral", async () => {
|
|
90
|
-
const { FrakContextManager } = await import("../../
|
|
90
|
+
const { FrakContextManager } = await import("../../context");
|
|
91
91
|
const { watchWalletStatus } = await import("../index");
|
|
92
92
|
const { processReferral } = await import("./processReferral");
|
|
93
93
|
|
|
@@ -101,7 +101,7 @@ describe("referralInteraction", () => {
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
test("should return undefined on error", async () => {
|
|
104
|
-
const { FrakContextManager } = await import("../../
|
|
104
|
+
const { FrakContextManager } = await import("../../context");
|
|
105
105
|
const { watchWalletStatus } = await import("../index");
|
|
106
106
|
const { processReferral } = await import("./processReferral");
|
|
107
107
|
|
|
@@ -124,7 +124,7 @@ describe("referralInteraction", () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
test("should work with empty options", async () => {
|
|
127
|
-
const { FrakContextManager } = await import("../../
|
|
127
|
+
const { FrakContextManager } = await import("../../context");
|
|
128
128
|
const { watchWalletStatus } = await import("../index");
|
|
129
129
|
const { processReferral } = await import("./processReferral");
|
|
130
130
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { getClientId } from "../config/clientId";
|
|
1
2
|
import type { FrakClient } from "../types";
|
|
2
3
|
import type { SendInteractionParamsType } from "../types/rpc/interaction";
|
|
3
|
-
import { getClientId } from "../utils/clientId";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Send an interaction to the backend via the listener RPC.
|
|
@@ -7,18 +7,18 @@ import {
|
|
|
7
7
|
test,
|
|
8
8
|
} from "../../tests/vitest-fixtures";
|
|
9
9
|
|
|
10
|
-
vi.mock("../
|
|
10
|
+
vi.mock("../config/clientId", () => ({
|
|
11
11
|
getClientId: vi.fn().mockReturnValue("test-client-id"),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
-
vi.mock("../
|
|
14
|
+
vi.mock("../config/sdkConfigStore", () => ({
|
|
15
15
|
sdkConfigStore: {
|
|
16
16
|
resolveMerchantId: vi.fn().mockResolvedValue(undefined),
|
|
17
17
|
},
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
import { getClientId } from "../
|
|
21
|
-
import { sdkConfigStore } from "../
|
|
20
|
+
import { getClientId } from "../config/clientId";
|
|
21
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
22
22
|
import { trackPurchaseStatus } from "./trackPurchaseStatus";
|
|
23
23
|
|
|
24
24
|
describe.sequential("trackPurchaseStatus", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getBackendUrl } from "../
|
|
2
|
-
import { getClientId } from "../
|
|
3
|
-
import { sdkConfigStore } from "../
|
|
1
|
+
import { getBackendUrl } from "../config/backendUrl";
|
|
2
|
+
import { getClientId } from "../config/clientId";
|
|
3
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Function used to track the status of a purchase
|
|
@@ -4,10 +4,6 @@ vi.mock("../displayModal", () => ({
|
|
|
4
4
|
displayModal: vi.fn(),
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
|
-
vi.mock("viem/siwe", () => ({
|
|
8
|
-
generateSiweNonce: vi.fn(() => "mock-nonce-123456"),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
7
|
import type { Address, Hex } from "viem";
|
|
12
8
|
import { describe, expect, it } from "../../../tests/vitest-fixtures";
|
|
13
9
|
import type { FrakClient } from "../../types";
|
|
@@ -65,7 +61,7 @@ describe("siweAuthenticate", () => {
|
|
|
65
61
|
siweAuthenticate: {
|
|
66
62
|
siwe: expect.objectContaining({
|
|
67
63
|
domain: "example.com",
|
|
68
|
-
nonce:
|
|
64
|
+
nonce: expect.stringMatching(/^[0-9a-f]{96}$/),
|
|
69
65
|
uri: "https://example.com",
|
|
70
66
|
version: "1",
|
|
71
67
|
}),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { generateSiweNonce } from "viem/siwe";
|
|
2
1
|
import type {
|
|
3
2
|
FrakClient,
|
|
4
3
|
ModalRpcMetadata,
|
|
@@ -7,6 +6,31 @@ import type {
|
|
|
7
6
|
} from "../../types";
|
|
8
7
|
import { displayModal } from "../displayModal";
|
|
9
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Generate a random EIP-4361-compatible nonce.
|
|
11
|
+
*
|
|
12
|
+
* Matches viem/siwe's `generateSiweNonce` shape (96 alphanumeric chars) but
|
|
13
|
+
* inlined here to avoid pulling viem's runtime utilities into the bundle.
|
|
14
|
+
* Uses `crypto.getRandomValues` when available (browsers, modern Node) and
|
|
15
|
+
* falls back to `Math.random` for the rare environment without WebCrypto.
|
|
16
|
+
*/
|
|
17
|
+
function generateSiweNonce(length = 96): string {
|
|
18
|
+
const byteCount = Math.ceil(length / 2);
|
|
19
|
+
let hex = "";
|
|
20
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
21
|
+
const bytes = new Uint8Array(byteCount);
|
|
22
|
+
crypto.getRandomValues(bytes);
|
|
23
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
24
|
+
hex += bytes[i].toString(16).padStart(2, "0");
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
for (let i = 0; i < byteCount; i++) {
|
|
28
|
+
hex += ((Math.random() * 256) | 0).toString(16).padStart(2, "0");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return hex.substring(0, length);
|
|
32
|
+
}
|
|
33
|
+
|
|
10
34
|
/**
|
|
11
35
|
* Parameter used to directly show a modal used to authenticate with SIWE
|
|
12
36
|
* @inline
|
|
@@ -6,17 +6,16 @@ import {
|
|
|
6
6
|
RpcErrorCodes,
|
|
7
7
|
} from "@frak-labs/frame-connector";
|
|
8
8
|
import { OpenPanel } from "@openpanel/web";
|
|
9
|
+
import { getClientId } from "../config/clientId";
|
|
10
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
11
|
+
import { BACKUP_KEY } from "../constants";
|
|
9
12
|
import type { FrakLifecycleEvent } from "../types";
|
|
10
13
|
import type { FrakClient } from "../types/client";
|
|
11
14
|
import type { FrakWalletSdkConfig } from "../types/config";
|
|
12
15
|
import type { SdkResolvedConfig } from "../types/resolvedConfig";
|
|
13
16
|
import type { IFrameRpcSchema } from "../types/rpc";
|
|
14
|
-
import { getClientId } from "../utils";
|
|
15
17
|
import { clearAllCache } from "../utils/cache";
|
|
16
|
-
import {
|
|
17
|
-
import { sdkConfigStore } from "../utils/sdkConfigStore";
|
|
18
|
-
import { setupSsoUrlListener } from "../utils/ssoUrlListener";
|
|
19
|
-
import { DebugInfoGatherer } from "./DebugInfo";
|
|
18
|
+
import { setupSsoUrlListener } from "./ssoUrlListener";
|
|
20
19
|
import {
|
|
21
20
|
createIFrameLifecycleManager,
|
|
22
21
|
type IframeLifecycleManager,
|
|
@@ -84,9 +83,6 @@ export function createIFrameFrakClient({
|
|
|
84
83
|
// lifecycle manager resolves the `isConnected` promise.
|
|
85
84
|
const handshakeStartedAt = Date.now();
|
|
86
85
|
|
|
87
|
-
// Create our debug info gatherer
|
|
88
|
-
const debugInfo = new DebugInfoGatherer(config, iframe);
|
|
89
|
-
|
|
90
86
|
// Validate iframe
|
|
91
87
|
if (!iframe.contentWindow) {
|
|
92
88
|
throw new FrakRpcError(
|
|
@@ -115,17 +111,6 @@ export function createIFrameFrakClient({
|
|
|
115
111
|
return ctx;
|
|
116
112
|
},
|
|
117
113
|
},
|
|
118
|
-
// Save debug info
|
|
119
|
-
{
|
|
120
|
-
onRequest(message, ctx) {
|
|
121
|
-
debugInfo.setLastRequest(message);
|
|
122
|
-
return ctx;
|
|
123
|
-
},
|
|
124
|
-
onResponse(message, response) {
|
|
125
|
-
debugInfo.setLastResponse(message, response);
|
|
126
|
-
return response;
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
114
|
],
|
|
130
115
|
// Add lifecycle handlers to process iframe lifecycle events
|
|
131
116
|
lifecycleHandlers: {
|
|
@@ -227,7 +212,7 @@ export function createIFrameFrakClient({
|
|
|
227
212
|
contextSent,
|
|
228
213
|
openPanel,
|
|
229
214
|
})
|
|
230
|
-
.then(() =>
|
|
215
|
+
.then(() => {})
|
|
231
216
|
.catch((err) => {
|
|
232
217
|
contextSent.reject(err);
|
|
233
218
|
throw err;
|
|
@@ -235,7 +220,6 @@ export function createIFrameFrakClient({
|
|
|
235
220
|
|
|
236
221
|
return {
|
|
237
222
|
config,
|
|
238
|
-
debugInfo,
|
|
239
223
|
waitForConnection: lifecycleManager.isConnected,
|
|
240
224
|
waitForSetup,
|
|
241
225
|
request: rpcClient.request,
|
package/src/clients/index.ts
CHANGED
|
@@ -16,19 +16,19 @@ vi.mock("@frak-labs/frame-connector", () => ({
|
|
|
16
16
|
},
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
|
-
vi.mock("../../
|
|
19
|
+
vi.mock("../../constants", () => ({
|
|
20
20
|
BACKUP_KEY: "frak-backup-key",
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
|
-
vi.mock("../../utils/iframeHelper", () => ({
|
|
23
|
+
vi.mock("../../utils/iframe/iframeHelper", () => ({
|
|
24
24
|
changeIframeVisibility: vi.fn(),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
vi.mock("../../
|
|
27
|
+
vi.mock("../../config/clientId", () => ({
|
|
28
28
|
getClientId: vi.fn(() => "mock-client-id"),
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
-
vi.mock("../../utils/deepLinkWithFallback", () => ({
|
|
31
|
+
vi.mock("../../utils/browser/deepLinkWithFallback", () => ({
|
|
32
32
|
isFrakDeepLink: vi.fn((url: string) => url.startsWith("frakwallet://")),
|
|
33
33
|
triggerDeepLinkWithFallback: vi.fn(),
|
|
34
34
|
}));
|
|
@@ -185,7 +185,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
185
185
|
"./iframeLifecycleManager"
|
|
186
186
|
);
|
|
187
187
|
const { changeIframeVisibility } = await import(
|
|
188
|
-
"../../utils/iframeHelper"
|
|
188
|
+
"../../utils/iframe/iframeHelper"
|
|
189
189
|
);
|
|
190
190
|
|
|
191
191
|
const mockIframe = document.createElement("iframe");
|
|
@@ -211,7 +211,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
211
211
|
"./iframeLifecycleManager"
|
|
212
212
|
);
|
|
213
213
|
const { changeIframeVisibility } = await import(
|
|
214
|
-
"../../utils/iframeHelper"
|
|
214
|
+
"../../utils/iframe/iframeHelper"
|
|
215
215
|
);
|
|
216
216
|
|
|
217
217
|
const mockIframe = document.createElement("iframe");
|
|
@@ -301,7 +301,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
301
301
|
"./iframeLifecycleManager"
|
|
302
302
|
);
|
|
303
303
|
const { triggerDeepLinkWithFallback } = await import(
|
|
304
|
-
"../../utils/deepLinkWithFallback"
|
|
304
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
305
305
|
);
|
|
306
306
|
|
|
307
307
|
Object.defineProperty(window, "location", {
|
|
@@ -340,7 +340,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
340
340
|
"./iframeLifecycleManager"
|
|
341
341
|
);
|
|
342
342
|
const { triggerDeepLinkWithFallback } = await import(
|
|
343
|
-
"../../utils/deepLinkWithFallback"
|
|
343
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
344
344
|
);
|
|
345
345
|
|
|
346
346
|
Object.defineProperty(window, "location", {
|
|
@@ -396,7 +396,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
396
396
|
"./iframeLifecycleManager"
|
|
397
397
|
);
|
|
398
398
|
const { triggerDeepLinkWithFallback } = await import(
|
|
399
|
-
"../../utils/deepLinkWithFallback"
|
|
399
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
400
400
|
);
|
|
401
401
|
|
|
402
402
|
Object.defineProperty(window, "location", {
|
|
@@ -453,7 +453,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
453
453
|
"./iframeLifecycleManager"
|
|
454
454
|
);
|
|
455
455
|
const { changeIframeVisibility } = await import(
|
|
456
|
-
"../../utils/iframeHelper"
|
|
456
|
+
"../../utils/iframe/iframeHelper"
|
|
457
457
|
);
|
|
458
458
|
|
|
459
459
|
const mockIframe = document.createElement("iframe");
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Deferred } from "@frak-labs/frame-connector";
|
|
2
|
+
import { BACKUP_KEY } from "../../constants";
|
|
2
3
|
import type { FrakLifecycleEvent } from "../../types";
|
|
3
|
-
import { BACKUP_KEY } from "../../utils/constants";
|
|
4
4
|
import {
|
|
5
5
|
isFrakDeepLink,
|
|
6
6
|
triggerDeepLinkWithFallback,
|
|
7
|
-
} from "../../utils/deepLinkWithFallback";
|
|
8
|
-
import { changeIframeVisibility } from "../../utils/iframeHelper";
|
|
7
|
+
} from "../../utils/browser/deepLinkWithFallback";
|
|
8
|
+
import { changeIframeVisibility } from "../../utils/iframe/iframeHelper";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Detect iOS in-app browsers (Instagram, Facebook) where server-side
|
|
@@ -14,8 +14,8 @@ import type {
|
|
|
14
14
|
MerchantConfigResponse,
|
|
15
15
|
SdkResolvedConfig,
|
|
16
16
|
} from "../types/resolvedConfig";
|
|
17
|
+
import { clearAllCache, withCache } from "../utils/cache";
|
|
17
18
|
import { getBackendUrl } from "./backendUrl";
|
|
18
|
-
import { clearAllCache, withCache } from "./cache";
|
|
19
19
|
|
|
20
20
|
const GLOBAL_KEY = "__frakSdkConfig";
|
|
21
21
|
const CACHE_TTL = 30_000; // 30 seconds
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Tests for constants
|
|
3
|
-
* Tests backup key constant value
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, expect, it } from "../../tests/vitest-fixtures";
|
|
1
|
+
import { describe, expect, it } from "../tests/vitest-fixtures";
|
|
7
2
|
import { BACKUP_KEY } from "./constants";
|
|
8
3
|
|
|
9
4
|
describe("constants", () => {
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backup key for client side backup if needed
|
|
3
|
+
*/
|
|
4
|
+
export const BACKUP_KEY = "nexus-wallet-backup";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deep link scheme for Frak Wallet mobile app.
|
|
8
|
+
*
|
|
9
|
+
* Replaced at build time via tsdown/Vite `define`. Defaults to the prod scheme;
|
|
10
|
+
* in-monorepo dev builds (listener at wallet-dev.frak.id) override this with
|
|
11
|
+
* `frakwallet-dev://` so deep links open the dev wallet variant (id.frak.wallet.dev).
|
|
12
|
+
* External integrators consuming the published NPM/CDN bundle always see the prod scheme.
|
|
13
|
+
*/
|
|
14
|
+
export const DEEP_LINK_SCHEME: string =
|
|
15
|
+
process.env.DEEP_LINK_SCHEME ?? "frakwallet://";
|