@zerohash-sdk/crypto-account-link-react 1.2.6 → 1.3.1
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/dist/index.js
CHANGED
|
@@ -1,43 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
const
|
|
1
|
+
import k, { useRef as O, useEffect as h } from "react";
|
|
2
|
+
const m = (t) => {
|
|
3
|
+
if (!t || typeof t != "string")
|
|
4
|
+
return null;
|
|
5
|
+
const e = t.split(".");
|
|
6
|
+
if (e.length < 2)
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
const r = e[1].replace(/-/g, "+").replace(/_/g, "/"), u = r + "===".slice(0, (4 - r.length % 4) % 4), i = typeof atob < "u" ? atob(u) : Buffer.from(u, "base64").toString("utf-8"), c = JSON.parse(i)?.payload?.region;
|
|
10
|
+
if (typeof c != "string")
|
|
11
|
+
return null;
|
|
12
|
+
const o = c.toLowerCase();
|
|
13
|
+
return o === "us" || o === "eu" ? o : null;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}, b = (t, e) => m(e) !== "eu" ? t : t === "cert" ? "eu-cert" : t === "prod" ? "eu-prod" : t;
|
|
18
|
+
var y;
|
|
19
|
+
(function(t) {
|
|
20
|
+
t.NETWORK_ERROR = "network_error", t.AUTH_ERROR = "auth_error", t.NOT_FOUND_ERROR = "not_found_error", t.VALIDATION_ERROR = "validation_error", t.SERVER_ERROR = "server_error", t.CLIENT_ERROR = "client_error", t.UNKNOWN_ERROR = "unknown_error";
|
|
21
|
+
})(y || (y = {}));
|
|
22
|
+
const a = {
|
|
3
23
|
local: "http://localhost:5173/crypto-account-link-web/index.js",
|
|
4
24
|
dev: "https://connect-sdk.dev.0hash.com/crypto-account-link-web/index.js",
|
|
5
25
|
cert: "https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js",
|
|
6
|
-
prod: "https://sdk.connect.xyz/crypto-account-link-web/index.js"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
26
|
+
prod: "https://sdk.connect.xyz/crypto-account-link-web/index.js",
|
|
27
|
+
"eu-cert": "https://sdk-cdn.cert.zerohash.eu/crypto-account-link-web/index.js",
|
|
28
|
+
"eu-prod": "https://sdk-cdn.zerohash.eu/crypto-account-link-web/index.js"
|
|
29
|
+
}, E = "zerohash-crypto-account-link-script", _ = "zerohash-crypto-account-link", N = (t = "prod", e) => {
|
|
30
|
+
const r = b(t, e);
|
|
31
|
+
return a[r] ?? a[t] ?? a.prod;
|
|
32
|
+
}, T = ({
|
|
33
|
+
jwt: t,
|
|
34
|
+
env: e = "prod",
|
|
10
35
|
theme: r,
|
|
11
|
-
isPayouts:
|
|
12
|
-
onCompleted:
|
|
13
|
-
onError:
|
|
14
|
-
onClose:
|
|
15
|
-
onLoaded:
|
|
16
|
-
onExternalAccountCreated:
|
|
17
|
-
onEvent:
|
|
18
|
-
...
|
|
36
|
+
isPayouts: u = !1,
|
|
37
|
+
onCompleted: i,
|
|
38
|
+
onError: l,
|
|
39
|
+
onClose: c,
|
|
40
|
+
onLoaded: o,
|
|
41
|
+
onExternalAccountCreated: p,
|
|
42
|
+
onEvent: d,
|
|
43
|
+
...g
|
|
19
44
|
}) => {
|
|
20
|
-
const
|
|
21
|
-
return
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
}, [r,
|
|
25
|
-
const
|
|
26
|
-
if (document.getElementById(
|
|
45
|
+
const f = O(null);
|
|
46
|
+
return h(() => {
|
|
47
|
+
const n = f.current;
|
|
48
|
+
n && (r !== void 0 && (n.theme = r), i && (n.onCompleted = i), l && (n.onError = l), c && (n.onClose = c), o && (n.onLoaded = o), p && (n.onExternalAccountCreated = p), d && (n.onEvent = d));
|
|
49
|
+
}, [r, i, l, c, o, p, d]), h(() => {
|
|
50
|
+
const n = N(e, t), R = `${E}-${e}`;
|
|
51
|
+
if (document.getElementById(R))
|
|
27
52
|
return;
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
console.error(`Failed to load the script for ${
|
|
31
|
-
}, document.head.appendChild(
|
|
32
|
-
}, [
|
|
33
|
-
ref:
|
|
34
|
-
jwt:
|
|
35
|
-
env:
|
|
53
|
+
const s = document.createElement("script");
|
|
54
|
+
s.id = R, s.src = n, s.type = "module", s.async = !0, s.onerror = () => {
|
|
55
|
+
console.error(`Failed to load the script for ${_} from ${e} environment.`);
|
|
56
|
+
}, document.head.appendChild(s);
|
|
57
|
+
}, [e, t]), k.createElement(_, {
|
|
58
|
+
ref: f,
|
|
59
|
+
jwt: t,
|
|
60
|
+
env: e,
|
|
36
61
|
theme: r,
|
|
37
|
-
"is-payouts":
|
|
38
|
-
...
|
|
62
|
+
"is-payouts": u,
|
|
63
|
+
...g
|
|
39
64
|
});
|
|
40
65
|
};
|
|
41
66
|
export {
|
|
42
|
-
|
|
67
|
+
T as CryptoAccountLink
|
|
43
68
|
};
|
package/package.json
CHANGED
|
@@ -2,9 +2,111 @@ import { render } from '@testing-library/react';
|
|
|
2
2
|
|
|
3
3
|
import { CryptoAccountLink } from './crypto-account-link-react';
|
|
4
4
|
|
|
5
|
+
// Build a fake JWT whose `payload.region` claim matches the value passed in.
|
|
6
|
+
const makeJwt = (region: 'us' | 'eu' | null): string => {
|
|
7
|
+
const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
|
|
8
|
+
const body = btoa(JSON.stringify(region === null ? { sub: 'test' } : { payload: { region } }));
|
|
9
|
+
return `${header}.${body}.sig`;
|
|
10
|
+
};
|
|
11
|
+
|
|
5
12
|
describe('CryptoAccountLink', () => {
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
document.head.innerHTML = '';
|
|
15
|
+
});
|
|
16
|
+
|
|
6
17
|
it('should render successfully', () => {
|
|
7
18
|
const { baseElement } = render(<CryptoAccountLink jwt="test-token" />);
|
|
8
19
|
expect(baseElement).toBeTruthy();
|
|
9
20
|
});
|
|
21
|
+
|
|
22
|
+
describe('script loading', () => {
|
|
23
|
+
it('loads the dev CDN script for dev env', () => {
|
|
24
|
+
render(
|
|
25
|
+
<CryptoAccountLink
|
|
26
|
+
jwt="test-jwt"
|
|
27
|
+
env="dev"
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
const script = document.head.querySelector('script');
|
|
31
|
+
expect(script?.src).toBe('https://connect-sdk.dev.0hash.com/crypto-account-link-web/index.js');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('loads the cert CDN script for cert env', () => {
|
|
35
|
+
render(
|
|
36
|
+
<CryptoAccountLink
|
|
37
|
+
jwt="test-jwt"
|
|
38
|
+
env="cert"
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
const script = document.head.querySelector('script');
|
|
42
|
+
expect(script?.src).toBe('https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('loads the production CDN script for prod env', () => {
|
|
46
|
+
render(<CryptoAccountLink jwt="test-jwt" />);
|
|
47
|
+
const script = document.head.querySelector('script');
|
|
48
|
+
expect(script?.src).toBe('https://sdk.connect.xyz/crypto-account-link-web/index.js');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('loads the EU cert CDN script when JWT region is "eu" and env is "cert"', () => {
|
|
52
|
+
render(
|
|
53
|
+
<CryptoAccountLink
|
|
54
|
+
jwt={makeJwt('eu')}
|
|
55
|
+
env="cert"
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
const script = document.head.querySelector('script');
|
|
59
|
+
expect(script?.src).toBe('https://sdk-cdn.cert.zerohash.eu/crypto-account-link-web/index.js');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('loads the EU prod CDN script when JWT region is "eu" and env is "prod"', () => {
|
|
63
|
+
render(<CryptoAccountLink jwt={makeJwt('eu')} />);
|
|
64
|
+
const script = document.head.querySelector('script');
|
|
65
|
+
expect(script?.src).toBe('https://sdk-cdn.zerohash.eu/crypto-account-link-web/index.js');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('keeps US partners on the original cert host when JWT region is "us"', () => {
|
|
69
|
+
render(
|
|
70
|
+
<CryptoAccountLink
|
|
71
|
+
jwt={makeJwt('us')}
|
|
72
|
+
env="cert"
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
const script = document.head.querySelector('script');
|
|
76
|
+
expect(script?.src).toBe('https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('falls back to the US host when the JWT has no region claim', () => {
|
|
80
|
+
render(
|
|
81
|
+
<CryptoAccountLink
|
|
82
|
+
jwt={makeJwt(null)}
|
|
83
|
+
env="cert"
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
const script = document.head.querySelector('script');
|
|
87
|
+
expect(script?.src).toBe('https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('falls back to the US host when the JWT is malformed', () => {
|
|
91
|
+
render(
|
|
92
|
+
<CryptoAccountLink
|
|
93
|
+
jwt="not-a-jwt"
|
|
94
|
+
env="cert"
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
const script = document.head.querySelector('script');
|
|
98
|
+
expect(script?.src).toBe('https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('falls back to the prod connect.xyz host when env is an unknown string', () => {
|
|
102
|
+
render(
|
|
103
|
+
<CryptoAccountLink
|
|
104
|
+
jwt="test-jwt"
|
|
105
|
+
env={'staging' as unknown as 'prod'}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
const script = document.head.querySelector('script');
|
|
109
|
+
expect(script?.src).toBe('https://sdk.connect.xyz/crypto-account-link-web/index.js');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
10
112
|
});
|
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
CryptoAccountLinkEvent,
|
|
4
4
|
ErrorPayload,
|
|
5
5
|
} from '@zerohash/callbacks';
|
|
6
|
+
import { resolveEnvByRegion } from '@zerohash/js-sdk-base';
|
|
6
7
|
import React, { useEffect, useRef } from 'react';
|
|
7
8
|
|
|
9
|
+
// US partners (and JWTs without a region claim) stay on the original
|
|
10
|
+
// connect.xyz CDN — that host has no Cloudflare Rocket Loader, which was the
|
|
11
|
+
// root cause of the SRE-18031 blank-screen incident. EU partners
|
|
12
|
+
// (`region: 'eu'` in the JWT payload) are routed to the EU CDN. The two will
|
|
13
|
+
// be unified once Rocket Loader is dropped on the .com host.
|
|
8
14
|
const SCRIPT_URLS = {
|
|
9
15
|
local: 'http://localhost:5173/crypto-account-link-web/index.js',
|
|
10
16
|
dev: 'https://connect-sdk.dev.0hash.com/crypto-account-link-web/index.js',
|
|
11
17
|
cert: 'https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js',
|
|
12
18
|
prod: 'https://sdk.connect.xyz/crypto-account-link-web/index.js',
|
|
19
|
+
'eu-cert': 'https://sdk-cdn.cert.zerohash.eu/crypto-account-link-web/index.js',
|
|
20
|
+
'eu-prod': 'https://sdk-cdn.zerohash.eu/crypto-account-link-web/index.js',
|
|
13
21
|
} as const;
|
|
14
22
|
|
|
15
23
|
const SCRIPT_ID = 'zerohash-crypto-account-link-script';
|
|
@@ -34,8 +42,12 @@ type Environment = 'local' | 'dev' | 'cert' | 'prod';
|
|
|
34
42
|
*/
|
|
35
43
|
type Theme = 'auto' | 'light' | 'dark';
|
|
36
44
|
|
|
37
|
-
const getScriptUrl = (env: Environment = 'prod'): string => {
|
|
38
|
-
|
|
45
|
+
const getScriptUrl = (env: Environment = 'prod', jwt?: string): string => {
|
|
46
|
+
const effectiveEnv = resolveEnvByRegion(env, jwt) as keyof typeof SCRIPT_URLS;
|
|
47
|
+
// Final fallback to prod guarantees script.src is never undefined — even if
|
|
48
|
+
// a partner passes a garbage env at runtime (TS won't catch it from JS
|
|
49
|
+
// consumers). Matches the IFRAME_URLS pattern in crypto-account-link-web.
|
|
50
|
+
return SCRIPT_URLS[effectiveEnv] ?? SCRIPT_URLS[env] ?? SCRIPT_URLS['prod'];
|
|
39
51
|
};
|
|
40
52
|
|
|
41
53
|
interface CryptoAccountLinkProps {
|
|
@@ -305,7 +317,7 @@ export const CryptoAccountLink: React.FC<CryptoAccountLinkProps> = ({
|
|
|
305
317
|
|
|
306
318
|
// Load connect script to render the web component.
|
|
307
319
|
useEffect(() => {
|
|
308
|
-
const scriptUrl = getScriptUrl(env);
|
|
320
|
+
const scriptUrl = getScriptUrl(env, jwt);
|
|
309
321
|
const scriptId = `${SCRIPT_ID}-${env}`;
|
|
310
322
|
|
|
311
323
|
if (document.getElementById(scriptId)) {
|
|
@@ -326,7 +338,7 @@ export const CryptoAccountLink: React.FC<CryptoAccountLinkProps> = ({
|
|
|
326
338
|
// Note: We don't return a cleanup function to remove the script.
|
|
327
339
|
// The script defines the custom element globally for the entire application,
|
|
328
340
|
// so it should remain even if all component instances are unmounted.
|
|
329
|
-
}, [env]); // Re-run if environment changes
|
|
341
|
+
}, [env, jwt]); // Re-run if environment or JWT (region) changes
|
|
330
342
|
|
|
331
343
|
return React.createElement(WEB_COMPONENT_TAG, {
|
|
332
344
|
ref,
|