@neus/sdk 1.0.0 → 1.0.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.
@@ -0,0 +1,53 @@
1
+ # NEUS Widgets (VerifyGate + ProofBadge)
2
+
3
+ React components for NEUS verification and access gating.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @neus/sdk react react-dom
9
+ ```
10
+
11
+ ## VerifyGate
12
+
13
+ Gate UI behind verification requirements.
14
+
15
+ ```jsx
16
+ import { VerifyGate } from '@neus/sdk/widgets';
17
+
18
+ export function Page() {
19
+ return (
20
+ <VerifyGate
21
+ requiredVerifiers={['nft-ownership']}
22
+ verifierData={{
23
+ 'nft-ownership': { contractAddress: '0x...', tokenId: '1', chainId: 1 }
24
+ }}
25
+ >
26
+ <div>Unlocked</div>
27
+ </VerifyGate>
28
+ );
29
+ }
30
+ ```
31
+
32
+ ### Key props
33
+
34
+ - `requiredVerifiers`: `string[]` (default: `['ownership-basic']`)
35
+ - `verifierData`: object keyed by verifier id
36
+ - `strategy`: `'reuse-or-create' | 'reuse' | 'fresh'` (default: `'reuse-or-create'`)
37
+ - `proofOptions`: `{ privacyLevel, publicDisplay, storeOriginalContent, enableIpfs? }` (defaults: private)
38
+ - `mode`: `'create' | 'access'` (default: `'create'`)
39
+ - `qHash`: string (required for `mode="access"`)
40
+
41
+ Notes:
42
+ - Reuse without prompting can only see **public + discoverable** proofs.
43
+ - Reusing private proofs requires an **owner signature** (wallet grants read access).
44
+
45
+ ## ProofBadge
46
+
47
+ Display verification status by Proof ID (`qHash`).
48
+
49
+ ```jsx
50
+ import { ProofBadge } from '@neus/sdk/widgets';
51
+
52
+ <ProofBadge qHash="0x..." showChains />
53
+ ```
@@ -0,0 +1,9 @@
1
+ /**
2
+ * NEUS Widgets
3
+ *
4
+ * Core Widgets:
5
+ * - VerifyGate: Universal verification gate component
6
+ * - ProofBadge: Display verification status
7
+ */
8
+
9
+ export * from './verify-gate/index.js';
@@ -0,0 +1,355 @@
1
+ "use client";
2
+
3
+ // widgets/verify-gate/ProofBadge.jsx
4
+ import React, { useEffect, useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ var DEFAULT_API_BASE = "https://api.neus.network";
7
+ var NeusLogo = ({ size = 12 }) => /* @__PURE__ */ jsxs(
8
+ "svg",
9
+ {
10
+ width: size,
11
+ height: size,
12
+ viewBox: "0 0 24 24",
13
+ "aria-hidden": "true",
14
+ focusable: "false",
15
+ style: {
16
+ width: size,
17
+ height: size,
18
+ display: "block",
19
+ borderRadius: 2,
20
+ flexShrink: 0
21
+ },
22
+ children: [
23
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "20", height: "20", rx: "5", fill: "currentColor", opacity: "0.18" }),
24
+ /* @__PURE__ */ jsx(
25
+ "path",
26
+ {
27
+ d: "M7 16V8h2.1l4.9 5.9V8H17v8h-2.1L10 10.1V16H7z",
28
+ fill: "currentColor",
29
+ opacity: "0.9"
30
+ }
31
+ )
32
+ ]
33
+ }
34
+ );
35
+ function ProofBadge({
36
+ qHash,
37
+ size = "sm",
38
+ uiLinkBase = "https://neus.network",
39
+ apiUrl = DEFAULT_API_BASE,
40
+ proof = void 0,
41
+ showChains = false,
42
+ showLabel = true,
43
+ onClick = void 0,
44
+ className = ""
45
+ }) {
46
+ const [status, setStatus] = useState(() => {
47
+ if (proof) {
48
+ const proofStatus = proof.status || "";
49
+ return proofStatus.includes("verified") ? "verified" : proofStatus.includes("pending") || proofStatus.includes("processing") ? "pending" : "failed";
50
+ }
51
+ return qHash ? "pending" : "unknown";
52
+ });
53
+ const [chainCount, setChainCount] = useState(() => {
54
+ if (proof?.crosschain) {
55
+ const total = proof.crosschain.totalChains || 0;
56
+ const relayResults = proof.crosschain.relayResults || {};
57
+ return total > 0 ? total : Object.keys(relayResults).length + (proof.crosschain.hubTxHash ? 1 : 0);
58
+ }
59
+ return 0;
60
+ });
61
+ useEffect(() => {
62
+ if (!qHash || proof)
63
+ return;
64
+ let cancelled = false;
65
+ async function checkStatus() {
66
+ try {
67
+ const res = await fetch(`${apiUrl}/api/v1/verification/status/${qHash}`, {
68
+ headers: { Accept: "application/json" }
69
+ });
70
+ if (!res.ok) {
71
+ if (!cancelled)
72
+ setStatus("failed");
73
+ return;
74
+ }
75
+ const json = await res.json();
76
+ if (cancelled)
77
+ return;
78
+ const proofStatus = json?.data?.status || "";
79
+ const isVerified = proofStatus.toLowerCase().includes("verified");
80
+ const isPending = proofStatus.toLowerCase().includes("processing") || proofStatus.toLowerCase().includes("pending");
81
+ setStatus(isVerified ? "verified" : isPending ? "pending" : "failed");
82
+ if (showChains && json?.data?.crosschain) {
83
+ const cc = json.data.crosschain;
84
+ const total = cc.totalChains || 0;
85
+ const relayResults = cc.relayResults || {};
86
+ const count = total > 0 ? total : Object.keys(relayResults).length + (cc.hubTxHash ? 1 : 0);
87
+ setChainCount(count);
88
+ }
89
+ } catch (_) {
90
+ if (!cancelled)
91
+ setStatus("failed");
92
+ }
93
+ }
94
+ checkStatus();
95
+ return () => {
96
+ cancelled = true;
97
+ };
98
+ }, [qHash, proof, apiUrl, showChains]);
99
+ const href = `${String(uiLinkBase).replace(/\/$/, "")}/proof/${qHash}`;
100
+ const isSm = size === "sm";
101
+ const logoSize = isSm ? 12 : 14;
102
+ const fontSize = isSm ? 10 : 11;
103
+ const gap = isSm ? 4 : 5;
104
+ const padY = isSm ? 2 : 3;
105
+ const padX = isSm ? 6 : 8;
106
+ const label = status === "verified" ? "Verified" : status === "pending" ? "Pending" : status === "unknown" ? "Unknown" : "Unverified";
107
+ const style = {
108
+ display: "inline-flex",
109
+ alignItems: "center",
110
+ gap,
111
+ textDecoration: "none",
112
+ padding: `${padY}px ${padX}px`,
113
+ borderRadius: 9999,
114
+ // rounded-full
115
+ border: "1px solid var(--neus-badge-border, rgba(148, 163, 184, 0.2))",
116
+ background: "var(--neus-badge-bg, rgba(148, 163, 184, 0.06))",
117
+ color: "var(--neus-badge-text, #94a3b8)",
118
+ fontFamily: "var(--neus-badge-font, inherit)",
119
+ fontWeight: 500,
120
+ fontSize,
121
+ whiteSpace: "nowrap",
122
+ lineHeight: 1,
123
+ cursor: "pointer",
124
+ transition: "opacity 0.15s ease"
125
+ };
126
+ const handleClick = (e) => {
127
+ if (onClick) {
128
+ e.preventDefault();
129
+ onClick({ qHash, status, chainCount });
130
+ }
131
+ };
132
+ const title = showChains && chainCount > 0 ? `${label} on ${chainCount} chain${chainCount === 1 ? "" : "s"}` : label;
133
+ return /* @__PURE__ */ jsxs(
134
+ "a",
135
+ {
136
+ href,
137
+ target: "_blank",
138
+ rel: "noreferrer",
139
+ style,
140
+ className,
141
+ "aria-label": title,
142
+ title,
143
+ onClick: handleClick,
144
+ children: [
145
+ /* @__PURE__ */ jsx(NeusLogo, { size: logoSize }),
146
+ showLabel && /* @__PURE__ */ jsx("span", { children: label }),
147
+ showChains && chainCount > 0 && /* @__PURE__ */ jsxs("span", { style: { opacity: 0.7, fontSize: fontSize - 1 }, children: [
148
+ "\xB7 ",
149
+ chainCount
150
+ ] })
151
+ ]
152
+ }
153
+ );
154
+ }
155
+ function SimpleProofBadge({
156
+ qHash,
157
+ uiLinkBase = "https://neus.network",
158
+ apiUrl = DEFAULT_API_BASE,
159
+ size = "sm",
160
+ label = "Verified",
161
+ proof = void 0,
162
+ onClick = void 0,
163
+ className = ""
164
+ }) {
165
+ const [status, setStatus] = useState(() => {
166
+ if (proof) {
167
+ const proofStatus = proof.status || "";
168
+ return proofStatus.includes("verified") ? "verified" : "failed";
169
+ }
170
+ return qHash ? "pending" : "unknown";
171
+ });
172
+ useEffect(() => {
173
+ if (!qHash || proof)
174
+ return;
175
+ let cancelled = false;
176
+ async function checkStatus() {
177
+ try {
178
+ const res = await fetch(`${apiUrl}/api/v1/verification/status/${qHash}`, {
179
+ headers: { Accept: "application/json" }
180
+ });
181
+ if (!res.ok) {
182
+ if (!cancelled)
183
+ setStatus("failed");
184
+ return;
185
+ }
186
+ const json = await res.json();
187
+ if (cancelled)
188
+ return;
189
+ const isVerified = json?.success === true || json?.data?.status?.toLowerCase()?.includes("verified");
190
+ setStatus(isVerified ? "verified" : "failed");
191
+ } catch (_) {
192
+ if (!cancelled)
193
+ setStatus("failed");
194
+ }
195
+ }
196
+ checkStatus();
197
+ return () => {
198
+ cancelled = true;
199
+ };
200
+ }, [qHash, proof, apiUrl]);
201
+ const href = `${String(uiLinkBase).replace(/\/$/, "")}/proof/${qHash}`;
202
+ const isSm = size === "sm";
203
+ const logoSize = isSm ? 12 : 14;
204
+ const fontSize = isSm ? 10 : 11;
205
+ const style = {
206
+ display: "inline-flex",
207
+ alignItems: "center",
208
+ gap: 4,
209
+ textDecoration: "none",
210
+ padding: "2px 6px",
211
+ borderRadius: 9999,
212
+ // rounded-full
213
+ border: "1px solid var(--neus-badge-border, rgba(148, 163, 184, 0.2))",
214
+ background: "var(--neus-badge-bg, transparent)",
215
+ color: "var(--neus-badge-text, #94a3b8)",
216
+ fontFamily: "var(--neus-badge-font, inherit)",
217
+ fontWeight: 500,
218
+ fontSize,
219
+ whiteSpace: "nowrap",
220
+ lineHeight: 1,
221
+ cursor: "pointer",
222
+ transition: "opacity 0.15s ease"
223
+ };
224
+ const handleClick = (e) => {
225
+ if (onClick) {
226
+ e.preventDefault();
227
+ onClick({ qHash, status });
228
+ }
229
+ };
230
+ const displayLabel = status === "verified" ? label : status === "pending" ? "Pending" : status === "unknown" ? "Unknown" : "Unverified";
231
+ return /* @__PURE__ */ jsxs(
232
+ "a",
233
+ {
234
+ href,
235
+ target: "_blank",
236
+ rel: "noreferrer",
237
+ style,
238
+ className,
239
+ "aria-label": displayLabel,
240
+ title: displayLabel,
241
+ onClick: handleClick,
242
+ children: [
243
+ /* @__PURE__ */ jsx(NeusLogo, { size: logoSize }),
244
+ /* @__PURE__ */ jsx("span", { children: displayLabel })
245
+ ]
246
+ }
247
+ );
248
+ }
249
+ function NeusPillLink({
250
+ qHash,
251
+ uiLinkBase = "https://neus.network",
252
+ label = "View",
253
+ size = "sm",
254
+ onClick = void 0,
255
+ className = ""
256
+ }) {
257
+ const base = String(uiLinkBase).replace(/\/$/, "");
258
+ const href = qHash ? `${base}/proof/${qHash}` : base;
259
+ const isSm = size === "sm";
260
+ const logoSize = isSm ? 12 : 14;
261
+ const fontSize = isSm ? 10 : 11;
262
+ const style = {
263
+ display: "inline-flex",
264
+ alignItems: "center",
265
+ gap: 4,
266
+ textDecoration: "none",
267
+ padding: "2px 6px",
268
+ borderRadius: 9999,
269
+ // rounded-full
270
+ border: "1px solid var(--neus-badge-border, rgba(148, 163, 184, 0.2))",
271
+ background: "var(--neus-badge-bg, transparent)",
272
+ color: "var(--neus-badge-text, #94a3b8)",
273
+ fontFamily: "var(--neus-badge-font, inherit)",
274
+ fontWeight: 500,
275
+ fontSize,
276
+ whiteSpace: "nowrap",
277
+ lineHeight: 1,
278
+ cursor: "pointer",
279
+ transition: "opacity 0.15s ease"
280
+ };
281
+ const handleClick = (e) => {
282
+ if (onClick) {
283
+ e.preventDefault();
284
+ onClick({ qHash });
285
+ }
286
+ };
287
+ return /* @__PURE__ */ jsxs(
288
+ "a",
289
+ {
290
+ href,
291
+ target: "_blank",
292
+ rel: "noreferrer",
293
+ style,
294
+ className,
295
+ "aria-label": label,
296
+ title: label,
297
+ onClick: handleClick,
298
+ children: [
299
+ /* @__PURE__ */ jsx(NeusLogo, { size: logoSize }),
300
+ /* @__PURE__ */ jsx("span", { children: label })
301
+ ]
302
+ }
303
+ );
304
+ }
305
+ function VerifiedIcon({
306
+ qHash,
307
+ uiLinkBase = "https://neus.network",
308
+ size = 14,
309
+ tooltip = "Proof",
310
+ onClick = void 0,
311
+ className = ""
312
+ }) {
313
+ const href = qHash ? `${String(uiLinkBase).replace(/\/$/, "")}/proof/${qHash}` : void 0;
314
+ const handleClick = (e) => {
315
+ if (onClick) {
316
+ e.preventDefault();
317
+ onClick({ qHash });
318
+ }
319
+ };
320
+ const icon = /* @__PURE__ */ jsx(
321
+ "span",
322
+ {
323
+ title: tooltip,
324
+ className,
325
+ style: {
326
+ display: "inline-flex",
327
+ cursor: href || onClick ? "pointer" : "default",
328
+ opacity: 0.85,
329
+ transition: "opacity 0.15s ease"
330
+ },
331
+ children: /* @__PURE__ */ jsx(NeusLogo, { size })
332
+ }
333
+ );
334
+ if (href) {
335
+ return /* @__PURE__ */ jsx(
336
+ "a",
337
+ {
338
+ href,
339
+ target: "_blank",
340
+ rel: "noreferrer",
341
+ onClick: handleClick,
342
+ style: { display: "inline-flex", textDecoration: "none" },
343
+ "aria-label": tooltip,
344
+ children: icon
345
+ }
346
+ );
347
+ }
348
+ return icon;
349
+ }
350
+ export {
351
+ NeusPillLink,
352
+ ProofBadge,
353
+ SimpleProofBadge,
354
+ VerifiedIcon
355
+ };