@zerohash-sdk/crypto-account-link-react 1.3.1 → 1.4.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/dist/index.js +226 -50
- package/package.json +1 -1
- package/src/lib/crypto-account-link-react.tsx +17 -46
package/dist/index.js
CHANGED
|
@@ -1,68 +1,244 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { jsxs as A, jsx as h } from "react/jsx-runtime";
|
|
2
|
+
import U, { useEffect as B, useState as T, useCallback as W, useRef as M } from "react";
|
|
3
|
+
const _ = {
|
|
4
|
+
evergreen: "#00381D",
|
|
5
|
+
green: "#CCFFD0",
|
|
6
|
+
offBlack: "#1C1B1C",
|
|
7
|
+
grayLight: "#EBEBE9"
|
|
8
|
+
}, D = ({ onRetry: e }) => A("div", { role: "alert", style: {
|
|
9
|
+
display: "flex",
|
|
10
|
+
flexDirection: "column",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
gap: "8px",
|
|
13
|
+
maxWidth: "360px",
|
|
14
|
+
margin: "0 auto",
|
|
15
|
+
padding: "32px 24px",
|
|
16
|
+
textAlign: "center",
|
|
17
|
+
fontFamily: "inherit",
|
|
18
|
+
color: _.offBlack,
|
|
19
|
+
background: "#FFFFFF",
|
|
20
|
+
border: `1px solid ${_.grayLight}`,
|
|
21
|
+
borderRadius: "12px"
|
|
22
|
+
}, children: [h("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: h("path", { d: "M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: _.evergreen, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }), h("p", { style: { margin: 0, fontSize: "16px", fontWeight: 600 }, children: "Something went wrong" }), h("p", { style: { margin: 0, fontSize: "14px", opacity: 0.75 }, children: "We couldn't load this content. Please check your internet connection and try again." }), h("button", { type: "button", onClick: e, style: {
|
|
23
|
+
marginTop: "8px",
|
|
24
|
+
padding: "8px 20px",
|
|
25
|
+
fontSize: "14px",
|
|
26
|
+
fontWeight: 600,
|
|
27
|
+
fontFamily: "inherit",
|
|
28
|
+
color: _.green,
|
|
29
|
+
background: _.evergreen,
|
|
30
|
+
border: "none",
|
|
31
|
+
borderRadius: "8px",
|
|
32
|
+
cursor: "pointer"
|
|
33
|
+
}, children: "Try again" })] }), V = (e, t) => {
|
|
34
|
+
B(() => {
|
|
35
|
+
const o = e.current;
|
|
36
|
+
if (o)
|
|
37
|
+
for (const [n, r] of Object.entries(t))
|
|
38
|
+
r !== void 0 && (o[n] = r);
|
|
39
|
+
});
|
|
40
|
+
}, P = {
|
|
41
|
+
dev: "https://grafana-faro-collector.dev.0hash.com/collect",
|
|
42
|
+
cert: "https://grafana-faro-collector.cert.zerohash.com/collect",
|
|
43
|
+
prod: "https://grafana-faro-collector.zerohash.com/collect",
|
|
44
|
+
"eu-cert": "https://grafana-faro-collector.cert.zerohash.eu/collect",
|
|
45
|
+
"eu-prod": "https://grafana-faro-collector.zerohash.eu/collect",
|
|
46
|
+
sandbox: "https://grafana-faro-collector.cert.zerohash.com/collect",
|
|
47
|
+
production: "https://grafana-faro-collector.zerohash.com/collect"
|
|
48
|
+
}, J = (e) => {
|
|
49
|
+
if (!e || typeof e != "string")
|
|
4
50
|
return null;
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
51
|
+
const t = e.split(".");
|
|
52
|
+
if (t.length < 2)
|
|
7
53
|
return null;
|
|
8
54
|
try {
|
|
9
|
-
const
|
|
10
|
-
if (typeof
|
|
55
|
+
const o = t[1].replace(/-/g, "+").replace(/_/g, "/"), n = o + "===".slice(0, (4 - o.length % 4) % 4), r = typeof atob < "u" ? atob(n) : Buffer.from(n, "base64").toString("utf-8"), a = JSON.parse(r)?.payload?.region;
|
|
56
|
+
if (typeof a != "string")
|
|
11
57
|
return null;
|
|
12
|
-
const
|
|
13
|
-
return
|
|
58
|
+
const c = a.toLowerCase();
|
|
59
|
+
return c === "us" || c === "eu" ? c : null;
|
|
14
60
|
} catch {
|
|
15
61
|
return null;
|
|
16
62
|
}
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
63
|
+
}, I = (e, t) => J(t) !== "eu" ? e : e === "cert" ? "eu-cert" : e === "prod" ? "eu-prod" : e, $ = (e) => {
|
|
64
|
+
if (!e || typeof e != "string")
|
|
65
|
+
return {};
|
|
66
|
+
const t = e.split(".");
|
|
67
|
+
if (t.length < 2)
|
|
68
|
+
return {};
|
|
69
|
+
try {
|
|
70
|
+
const o = t[1].replace(/-/g, "+").replace(/_/g, "/"), n = o + "===".slice(0, (4 - o.length % 4) % 4), r = typeof atob < "u" ? atob(n) : Buffer.from(n, "base64").toString("utf-8"), i = JSON.parse(r), a = i?.payload ?? {}, c = (s) => typeof s == "string" && s.length > 0 ? s : void 0;
|
|
71
|
+
return {
|
|
72
|
+
participantCode: c(a.participant_code),
|
|
73
|
+
platformName: c(a.platform_name),
|
|
74
|
+
platformCode: c(i.platform_code),
|
|
75
|
+
region: c(a.region)
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
}, L = (e, t, o) => {
|
|
81
|
+
const n = I(t, o);
|
|
82
|
+
return e[n] ?? e[t] ?? e.prod;
|
|
83
|
+
}, K = (e, t, o) => {
|
|
84
|
+
const n = I(t, o);
|
|
85
|
+
return e[n] ?? e[t];
|
|
86
|
+
}, v = () => {
|
|
87
|
+
}, H = () => {
|
|
88
|
+
const e = new Uint8Array(8);
|
|
89
|
+
return globalThis.crypto.getRandomValues(e), Array.from(e, (t) => t.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
}, G = (e, t) => {
|
|
91
|
+
const o = H(), n = globalThis.__ZH_WEB_SDK_VERSION__, r = {};
|
|
92
|
+
e.claims.participantCode && (r.participant_code = e.claims.participantCode), e.claims.platformName && (r.platform_name = e.claims.platformName), e.claims.platformCode && (r.platform_code = e.claims.platformCode), e.claims.region && (r.region = e.claims.region), n && (r.zh_web_sdk_version = n);
|
|
93
|
+
const i = {
|
|
94
|
+
meta: {
|
|
95
|
+
app: { name: e.appName, version: t ?? "unknown", environment: e.env },
|
|
96
|
+
session: { id: o, attributes: r },
|
|
97
|
+
browser: typeof navigator < "u" ? { userAgent: navigator.userAgent } : void 0,
|
|
98
|
+
page: typeof window < "u" ? { url: window.location.origin } : void 0
|
|
99
|
+
},
|
|
100
|
+
logs: [
|
|
101
|
+
{
|
|
102
|
+
message: `Failed to load the script for ${e.webComponentTag} from ${e.env} environment.`,
|
|
103
|
+
level: "error",
|
|
104
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
105
|
+
context: {
|
|
106
|
+
web_component: e.webComponentTag,
|
|
107
|
+
script_url: e.scriptUrl,
|
|
108
|
+
reason: e.reason,
|
|
109
|
+
tried_fallback: String(e.triedFallback),
|
|
110
|
+
elapsed_ms: String(e.elapsedMs)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
return { sessionId: o, payload: i };
|
|
116
|
+
}, Z = (e, t, o) => {
|
|
117
|
+
if (!e || typeof fetch > "u")
|
|
118
|
+
return;
|
|
119
|
+
const { sessionId: n, payload: r } = G(t, o);
|
|
120
|
+
try {
|
|
121
|
+
fetch(e, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: { "Content-Type": "application/json", "x-faro-session-id": n },
|
|
124
|
+
body: JSON.stringify(r),
|
|
125
|
+
keepalive: !0
|
|
126
|
+
}).catch(() => {
|
|
127
|
+
});
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}, q = (e) => {
|
|
131
|
+
const { webComponentTag: t, appName: o, appVersion: n, scriptUrls: r, fallbackScriptUrls: i, collectorUrls: a, env: c, jwt: s, timeoutMs: m = 15e3, onLoad: y, onError: u } = e;
|
|
132
|
+
if (typeof document > "u")
|
|
133
|
+
return v;
|
|
134
|
+
const d = `${t}-script-${c}`;
|
|
135
|
+
if (customElements.get(t) || document.getElementById(d))
|
|
136
|
+
return v;
|
|
137
|
+
const b = $(s), k = e.report ?? ((p) => Z(a && K(a, c, s), p, n));
|
|
138
|
+
let f = !1, C = 0, x;
|
|
139
|
+
const R = () => {
|
|
140
|
+
x !== void 0 && clearTimeout(x);
|
|
141
|
+
}, S = (p, g, l) => {
|
|
142
|
+
if (f)
|
|
143
|
+
return;
|
|
144
|
+
const j = Math.round(performance.now() - C), N = {
|
|
145
|
+
webComponentTag: t,
|
|
146
|
+
appName: o,
|
|
147
|
+
env: c,
|
|
148
|
+
scriptUrl: g,
|
|
149
|
+
reason: p,
|
|
150
|
+
triedFallback: l,
|
|
151
|
+
elapsedMs: j,
|
|
152
|
+
claims: b
|
|
153
|
+
};
|
|
154
|
+
k(N);
|
|
155
|
+
const w = l ? void 0 : L(i ?? {}, c, s);
|
|
156
|
+
if (w && w !== g) {
|
|
157
|
+
O(w, !0);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
f = !0, R(), document.getElementById(d)?.remove(), u?.(N);
|
|
161
|
+
}, O = (p, g) => {
|
|
162
|
+
R(), C = performance.now();
|
|
163
|
+
const l = document.createElement("script");
|
|
164
|
+
l.id = d, l.src = p, l.type = "module", l.async = !0, l.onload = () => {
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
f || (customElements.get(t) ? (f = !0, R(), y?.()) : S("not-defined", p, g));
|
|
167
|
+
}, 0);
|
|
168
|
+
}, l.onerror = () => S("network", p, g), x = setTimeout(() => S("timeout", p, g), m), document.getElementById(d)?.remove(), document.head.appendChild(l);
|
|
169
|
+
}, E = L(r, c, s);
|
|
170
|
+
return E ? (O(E, !1), () => {
|
|
171
|
+
f = !0, R();
|
|
172
|
+
}) : v;
|
|
173
|
+
};
|
|
174
|
+
var z;
|
|
175
|
+
(function(e) {
|
|
176
|
+
e.NETWORK_ERROR = "network_error", e.AUTH_ERROR = "auth_error", e.NOT_FOUND_ERROR = "not_found_error", e.VALIDATION_ERROR = "validation_error", e.SERVER_ERROR = "server_error", e.CLIENT_ERROR = "client_error", e.UNKNOWN_ERROR = "unknown_error";
|
|
177
|
+
})(z || (z = {}));
|
|
178
|
+
const Q = (e) => {
|
|
179
|
+
const { webComponentTag: t, appName: o, appVersion: n, scriptUrls: r, fallbackScriptUrls: i, collectorUrls: a, env: c, jwt: s, timeoutMs: m } = e, [y, u] = T(!1), [d, b] = T(0);
|
|
180
|
+
B(() => (u(!1), q({
|
|
181
|
+
webComponentTag: t,
|
|
182
|
+
appName: o ?? t,
|
|
183
|
+
appVersion: n,
|
|
184
|
+
scriptUrls: r,
|
|
185
|
+
fallbackScriptUrls: i,
|
|
186
|
+
collectorUrls: a ?? P,
|
|
187
|
+
env: c,
|
|
188
|
+
jwt: s,
|
|
189
|
+
timeoutMs: m,
|
|
190
|
+
onError: () => u(!0)
|
|
191
|
+
})), [
|
|
192
|
+
t,
|
|
193
|
+
o,
|
|
194
|
+
n,
|
|
195
|
+
r,
|
|
196
|
+
i,
|
|
197
|
+
a,
|
|
198
|
+
c,
|
|
199
|
+
s,
|
|
200
|
+
m,
|
|
201
|
+
d
|
|
202
|
+
]);
|
|
203
|
+
const k = W(() => b((f) => f + 1), []);
|
|
204
|
+
return { loadFailed: y, retry: k };
|
|
205
|
+
}, X = {
|
|
23
206
|
local: "http://localhost:5173/crypto-account-link-web/index.js",
|
|
24
207
|
dev: "https://connect-sdk.dev.0hash.com/crypto-account-link-web/index.js",
|
|
25
208
|
cert: "https://sdk.sandbox.connect.xyz/crypto-account-link-web/index.js",
|
|
26
209
|
prod: "https://sdk.connect.xyz/crypto-account-link-web/index.js",
|
|
27
210
|
"eu-cert": "https://sdk-cdn.cert.zerohash.eu/crypto-account-link-web/index.js",
|
|
28
211
|
"eu-prod": "https://sdk-cdn.zerohash.eu/crypto-account-link-web/index.js"
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
onExternalAccountCreated: p,
|
|
42
|
-
onEvent: d,
|
|
43
|
-
...g
|
|
212
|
+
}, F = "zerohash-crypto-account-link", te = ({
|
|
213
|
+
jwt: e,
|
|
214
|
+
env: t = "prod",
|
|
215
|
+
theme: o,
|
|
216
|
+
isPayouts: n = !1,
|
|
217
|
+
onCompleted: r,
|
|
218
|
+
onError: i,
|
|
219
|
+
onClose: a,
|
|
220
|
+
onLoaded: c,
|
|
221
|
+
onExternalAccountCreated: s,
|
|
222
|
+
onEvent: m,
|
|
223
|
+
...y
|
|
44
224
|
}) => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
env: e,
|
|
61
|
-
theme: r,
|
|
62
|
-
"is-payouts": u,
|
|
63
|
-
...g
|
|
225
|
+
const u = M(null);
|
|
226
|
+
V(u, { theme: o, onCompleted: r, onError: i, onClose: a, onLoaded: c, onExternalAccountCreated: s, onEvent: m });
|
|
227
|
+
const { loadFailed: d, retry: b } = Q({
|
|
228
|
+
webComponentTag: F,
|
|
229
|
+
scriptUrls: X,
|
|
230
|
+
env: t,
|
|
231
|
+
jwt: e
|
|
232
|
+
});
|
|
233
|
+
return d ? /* @__PURE__ */ h(D, { onRetry: b }) : U.createElement(F, {
|
|
234
|
+
ref: u,
|
|
235
|
+
jwt: e,
|
|
236
|
+
env: t,
|
|
237
|
+
theme: o,
|
|
238
|
+
"is-payouts": n,
|
|
239
|
+
...y
|
|
64
240
|
});
|
|
65
241
|
};
|
|
66
242
|
export {
|
|
67
|
-
|
|
243
|
+
te as CryptoAccountLink
|
|
68
244
|
};
|
package/package.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
CryptoAccountLinkEvent,
|
|
4
4
|
ErrorPayload,
|
|
5
5
|
} from '@zerohash/callbacks';
|
|
6
|
-
import {
|
|
7
|
-
import React, {
|
|
6
|
+
import { ScriptLoadFallback, useSyncWebComponentProps, useWebComponentScript } from '@zerohash/react-sdk-base';
|
|
7
|
+
import React, { useRef } from 'react';
|
|
8
8
|
|
|
9
9
|
// US partners (and JWTs without a region claim) stay on the original
|
|
10
10
|
// connect.xyz CDN — that host has no Cloudflare Rocket Loader, which was the
|
|
@@ -20,7 +20,6 @@ const SCRIPT_URLS = {
|
|
|
20
20
|
'eu-prod': 'https://sdk-cdn.zerohash.eu/crypto-account-link-web/index.js',
|
|
21
21
|
} as const;
|
|
22
22
|
|
|
23
|
-
const SCRIPT_ID = 'zerohash-crypto-account-link-script';
|
|
24
23
|
const WEB_COMPONENT_TAG = 'zerohash-crypto-account-link';
|
|
25
24
|
|
|
26
25
|
/**
|
|
@@ -42,14 +41,6 @@ type Environment = 'local' | 'dev' | 'cert' | 'prod';
|
|
|
42
41
|
*/
|
|
43
42
|
type Theme = 'auto' | 'light' | 'dark';
|
|
44
43
|
|
|
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'];
|
|
51
|
-
};
|
|
52
|
-
|
|
53
44
|
interface CryptoAccountLinkProps {
|
|
54
45
|
/**
|
|
55
46
|
* JWT token used for authentication with the Connect Crypto Account Link service.
|
|
@@ -302,43 +293,23 @@ export const CryptoAccountLink: React.FC<CryptoAccountLinkProps> = ({
|
|
|
302
293
|
}) => {
|
|
303
294
|
const ref = useRef<CryptoAccountLinkElement | null>(null);
|
|
304
295
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (theme !== undefined) element.theme = theme;
|
|
310
|
-
if (onCompleted) element.onCompleted = onCompleted;
|
|
311
|
-
if (onError) element.onError = onError;
|
|
312
|
-
if (onClose) element.onClose = onClose;
|
|
313
|
-
if (onLoaded) element.onLoaded = onLoaded;
|
|
314
|
-
if (onExternalAccountCreated) element.onExternalAccountCreated = onExternalAccountCreated;
|
|
315
|
-
if (onEvent) element.onEvent = onEvent;
|
|
316
|
-
}, [theme, onCompleted, onError, onClose, onLoaded, onExternalAccountCreated, onEvent]);
|
|
317
|
-
|
|
318
|
-
// Load connect script to render the web component.
|
|
319
|
-
useEffect(() => {
|
|
320
|
-
const scriptUrl = getScriptUrl(env, jwt);
|
|
321
|
-
const scriptId = `${SCRIPT_ID}-${env}`;
|
|
322
|
-
|
|
323
|
-
if (document.getElementById(scriptId)) {
|
|
324
|
-
return; // Script already loaded, do nothing.
|
|
325
|
-
}
|
|
296
|
+
// Mirror props/callbacks onto the element (re-attached if the node remounts,
|
|
297
|
+
// e.g. after a successful "Try again" retry).
|
|
298
|
+
useSyncWebComponentProps(ref, { theme, onCompleted, onError, onClose, onLoaded, onExternalAccountCreated, onEvent });
|
|
326
299
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
document.head.appendChild(script);
|
|
300
|
+
// Load the connect script that registers the web component. The shared hook
|
|
301
|
+
// injects it, reports failures to Faro (this wrapper has no Faro of its own),
|
|
302
|
+
// and surfaces a final failure as a visible fallback instead of a blank screen.
|
|
303
|
+
const { loadFailed, retry } = useWebComponentScript({
|
|
304
|
+
webComponentTag: WEB_COMPONENT_TAG,
|
|
305
|
+
scriptUrls: SCRIPT_URLS,
|
|
306
|
+
env,
|
|
307
|
+
jwt,
|
|
308
|
+
});
|
|
337
309
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}, [env, jwt]); // Re-run if environment or JWT (region) changes
|
|
310
|
+
if (loadFailed) {
|
|
311
|
+
return <ScriptLoadFallback onRetry={retry} />;
|
|
312
|
+
}
|
|
342
313
|
|
|
343
314
|
return React.createElement(WEB_COMPONENT_TAG, {
|
|
344
315
|
ref,
|