@neus/sdk 1.0.1 → 1.0.2
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 +4 -32
- package/cjs/client.cjs +89 -48
- package/cjs/index.cjs +115 -56
- package/cjs/utils.cjs +70 -9
- package/client.js +1729 -1693
- package/package.json +1 -1
- package/types.d.ts +171 -59
- package/utils.js +36 -10
- package/widgets/verify-gate/dist/VerifyGate.js +1 -1
package/README.md
CHANGED
|
@@ -36,9 +36,6 @@ const client = new NeusClient({
|
|
|
36
36
|
apiUrl: 'https://api.neus.network',
|
|
37
37
|
// Optional: request timeout (ms)
|
|
38
38
|
timeout: 30000,
|
|
39
|
-
// Optional: server-side enterprise API key for higher limits on eligible endpoints
|
|
40
|
-
// (Do not embed API keys in browser apps.)
|
|
41
|
-
apiKey: process.env.NEUS_API_KEY
|
|
42
39
|
});
|
|
43
40
|
```
|
|
44
41
|
|
|
@@ -90,36 +87,11 @@ if (!res.data?.eligible) {
|
|
|
90
87
|
|
|
91
88
|
Note: `gateCheck` evaluates **existing public/discoverable proofs**. For strict real-time decisions, create a new proof via `client.verify(...)` (or `POST /api/v1/verification`) and use the final status.
|
|
92
89
|
|
|
93
|
-
##
|
|
90
|
+
## Resilience & Polling
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
- **
|
|
98
|
-
- **Auth:** `Authorization: Bearer sk_live_...` (or `sk_test_...`)
|
|
99
|
-
- **Semantics:** runs `external_lookup` verifiers only; **does not** create a proof record
|
|
100
|
-
|
|
101
|
-
Note: lookup mode is deployment-dependent and is not part of the stable public integrator OpenAPI (`docs/api/public-api.json`).
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
const res = await client.lookup({
|
|
105
|
-
// Premium API key (keep server-side only)
|
|
106
|
-
apiKey: process.env.NEUS_API_KEY,
|
|
107
|
-
verifierIds: ['wallet-risk'],
|
|
108
|
-
targetWalletAddress: '0x...',
|
|
109
|
-
data: { chainId: 1 }
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (!res.data?.verified) {
|
|
113
|
-
throw new Error('Rejected');
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Note: `gateCheck()` and `lookup()` are different tools.
|
|
118
|
-
- Use **`gateCheck()`** when you want to gate based on **existing public/discoverable proofs** (fast, minimal response).
|
|
119
|
-
- Use **`lookup()`** when you want a **fresh, non-persistent** decision (typically billable in hosted deployments).
|
|
120
|
-
- Only combine them if you are intentionally doing a cache-first strategy (gate-check first to avoid unnecessary lookups).
|
|
121
|
-
|
|
122
|
-
## Private proof reads (owner)
|
|
92
|
+
The SDK is designed for production stability:
|
|
93
|
+
- **Automatic Backoff**: `pollProofStatus()` automatically detects rate limiting (`429`) and applies jittered exponential backoff.
|
|
94
|
+
- **Wallet Identification**: Automatically attaches headers to preferred wallet-based limiting for higher reliability behind shared IPs (NATs).
|
|
123
95
|
|
|
124
96
|
- Private proof by Proof ID (qHash): `client.getPrivateStatus(qHash, wallet)`
|
|
125
97
|
- Private proofs by wallet/DID: `client.getPrivateProofsByWallet(walletOrDid, { limit, offset }, wallet)`
|
package/cjs/client.cjs
CHANGED
|
@@ -159,7 +159,7 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
|
|
|
159
159
|
}
|
|
160
160
|
const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
|
|
161
161
|
if (!chainContext) {
|
|
162
|
-
throw new SDKError("chainId is required (or provide chain for
|
|
162
|
+
throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
|
|
163
163
|
}
|
|
164
164
|
if (chainContext === chainId && typeof chainId !== "number") {
|
|
165
165
|
throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
|
|
@@ -230,6 +230,67 @@ var validateVerifierData = (verifierId, data) => {
|
|
|
230
230
|
if (!data.owner || !validateWalletAddress(data.owner)) {
|
|
231
231
|
return { valid: false, error: "owner (wallet address) is required" };
|
|
232
232
|
}
|
|
233
|
+
if (data.content !== void 0 && data.content !== null) {
|
|
234
|
+
if (typeof data.content !== "string") {
|
|
235
|
+
return { valid: false, error: "content must be a string when provided" };
|
|
236
|
+
}
|
|
237
|
+
if (data.content.length > 5e4) {
|
|
238
|
+
return { valid: false, error: "content exceeds 50KB inline limit" };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (data.contentHash !== void 0 && data.contentHash !== null) {
|
|
242
|
+
if (typeof data.contentHash !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(data.contentHash)) {
|
|
243
|
+
return { valid: false, error: "contentHash must be a 32-byte hex string (0x + 64 hex chars) when provided" };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (data.contentType !== void 0 && data.contentType !== null) {
|
|
247
|
+
if (typeof data.contentType !== "string" || data.contentType.length > 100) {
|
|
248
|
+
return { valid: false, error: "contentType must be a string (max 100 chars) when provided" };
|
|
249
|
+
}
|
|
250
|
+
const base = String(data.contentType).split(";")[0].trim().toLowerCase();
|
|
251
|
+
if (!base || base.includes(" ") || !base.includes("/")) {
|
|
252
|
+
return { valid: false, error: "contentType must be a valid MIME type when provided" };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (data.provenance !== void 0 && data.provenance !== null) {
|
|
256
|
+
if (!data.provenance || typeof data.provenance !== "object" || Array.isArray(data.provenance)) {
|
|
257
|
+
return { valid: false, error: "provenance must be an object when provided" };
|
|
258
|
+
}
|
|
259
|
+
const dk = data.provenance.declaredKind;
|
|
260
|
+
if (dk !== void 0 && dk !== null) {
|
|
261
|
+
const allowed = ["human", "ai", "mixed", "unknown"];
|
|
262
|
+
if (typeof dk !== "string" || !allowed.includes(dk)) {
|
|
263
|
+
return { valid: false, error: `provenance.declaredKind must be one of: ${allowed.join(", ")}` };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const ai = data.provenance.aiContext;
|
|
267
|
+
if (ai !== void 0 && ai !== null) {
|
|
268
|
+
if (typeof ai !== "object" || Array.isArray(ai)) {
|
|
269
|
+
return { valid: false, error: "provenance.aiContext must be an object when provided" };
|
|
270
|
+
}
|
|
271
|
+
if (ai.generatorType !== void 0 && ai.generatorType !== null) {
|
|
272
|
+
const allowed = ["local", "saas", "agent"];
|
|
273
|
+
if (typeof ai.generatorType !== "string" || !allowed.includes(ai.generatorType)) {
|
|
274
|
+
return { valid: false, error: `provenance.aiContext.generatorType must be one of: ${allowed.join(", ")}` };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (ai.provider !== void 0 && ai.provider !== null) {
|
|
278
|
+
if (typeof ai.provider !== "string" || ai.provider.length > 64) {
|
|
279
|
+
return { valid: false, error: "provenance.aiContext.provider must be a string (max 64 chars) when provided" };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (ai.model !== void 0 && ai.model !== null) {
|
|
283
|
+
if (typeof ai.model !== "string" || ai.model.length > 128) {
|
|
284
|
+
return { valid: false, error: "provenance.aiContext.model must be a string (max 128 chars) when provided" };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (ai.runId !== void 0 && ai.runId !== null) {
|
|
288
|
+
if (typeof ai.runId !== "string" || ai.runId.length > 128) {
|
|
289
|
+
return { valid: false, error: "provenance.aiContext.runId must be a string (max 128 chars) when provided" };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
233
294
|
if (data.reference !== void 0) {
|
|
234
295
|
if (!data.reference || typeof data.reference !== "object") {
|
|
235
296
|
return { valid: false, error: "reference must be an object when provided" };
|
|
@@ -368,6 +429,17 @@ var validateVerifierData = (verifierId, data) => {
|
|
|
368
429
|
if (!validTypes.includes(contentType)) {
|
|
369
430
|
return { valid: false, error: `contentType must be one of: ${validTypes.join(", ")}` };
|
|
370
431
|
}
|
|
432
|
+
const isTextual = contentType.startsWith("text/") || contentType.includes("markdown");
|
|
433
|
+
if (isTextual) {
|
|
434
|
+
try {
|
|
435
|
+
const maxBytes = 50 * 1024;
|
|
436
|
+
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(data.content).length : String(data.content).length;
|
|
437
|
+
if (bytes > maxBytes) {
|
|
438
|
+
return { valid: false, error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)` };
|
|
439
|
+
}
|
|
440
|
+
} catch {
|
|
441
|
+
}
|
|
442
|
+
}
|
|
371
443
|
}
|
|
372
444
|
if (data.content.length > 13653334) {
|
|
373
445
|
return { valid: false, error: "content exceeds 10MB limit" };
|
|
@@ -455,7 +527,7 @@ var NeusClient = class {
|
|
|
455
527
|
* data: {
|
|
456
528
|
* content: "My content",
|
|
457
529
|
* owner: walletAddress, // or ownerAddress for nft-ownership/token-holding
|
|
458
|
-
* reference: { type: '
|
|
530
|
+
* reference: { type: 'other', id: 'my-unique-identifier' }
|
|
459
531
|
* },
|
|
460
532
|
* walletAddress: '0x...',
|
|
461
533
|
* signature: '0x...',
|
|
@@ -543,6 +615,7 @@ var NeusClient = class {
|
|
|
543
615
|
reference: data2.reference,
|
|
544
616
|
...data2.content && { content: data2.content },
|
|
545
617
|
...data2.contentHash && { contentHash: data2.contentHash },
|
|
618
|
+
...data2.contentType && { contentType: data2.contentType },
|
|
546
619
|
...data2.provenance && { provenance: data2.provenance }
|
|
547
620
|
};
|
|
548
621
|
} else {
|
|
@@ -1037,9 +1110,11 @@ ${bytes.length}`;
|
|
|
1037
1110
|
throw new ValidationError("qHash is required");
|
|
1038
1111
|
}
|
|
1039
1112
|
const startTime = Date.now();
|
|
1113
|
+
let consecutiveRateLimits = 0;
|
|
1040
1114
|
while (Date.now() - startTime < timeout) {
|
|
1041
1115
|
try {
|
|
1042
1116
|
const status = await this.getStatus(qHash);
|
|
1117
|
+
consecutiveRateLimits = 0;
|
|
1043
1118
|
if (onProgress && typeof onProgress === "function") {
|
|
1044
1119
|
onProgress(status.data || status);
|
|
1045
1120
|
}
|
|
@@ -1054,7 +1129,17 @@ ${bytes.length}`;
|
|
|
1054
1129
|
if (error instanceof ValidationError) {
|
|
1055
1130
|
throw error;
|
|
1056
1131
|
}
|
|
1057
|
-
|
|
1132
|
+
let nextDelay = interval;
|
|
1133
|
+
if (error instanceof ApiError && Number(error.statusCode) === 429) {
|
|
1134
|
+
consecutiveRateLimits += 1;
|
|
1135
|
+
const exp = Math.min(6, consecutiveRateLimits);
|
|
1136
|
+
const base = Math.max(500, Number(interval) || 0);
|
|
1137
|
+
const max = 3e4;
|
|
1138
|
+
const backoff = Math.min(max, base * Math.pow(2, exp));
|
|
1139
|
+
const jitter = Math.floor(backoff * (0.5 + Math.random() * 0.5));
|
|
1140
|
+
nextDelay = jitter;
|
|
1141
|
+
}
|
|
1142
|
+
await new Promise((resolve) => setTimeout(resolve, nextDelay));
|
|
1058
1143
|
}
|
|
1059
1144
|
}
|
|
1060
1145
|
throw new NetworkError(`Polling timeout after ${timeout}ms`, "POLLING_TIMEOUT");
|
|
@@ -1148,7 +1233,7 @@ ${bytes.length}`;
|
|
|
1148
1233
|
return true;
|
|
1149
1234
|
}
|
|
1150
1235
|
// ============================================================================
|
|
1151
|
-
//
|
|
1236
|
+
// PROOFS & GATING METHODS
|
|
1152
1237
|
// ============================================================================
|
|
1153
1238
|
/**
|
|
1154
1239
|
* GET PROOFS BY WALLET - Fetch proofs for a wallet address
|
|
@@ -1281,45 +1366,6 @@ ${bytes.length}`;
|
|
|
1281
1366
|
nextOffset: response.data?.nextOffset ?? null
|
|
1282
1367
|
};
|
|
1283
1368
|
}
|
|
1284
|
-
/**
|
|
1285
|
-
* LOOKUP MODE (API) - Non-persistent server-to-server checks
|
|
1286
|
-
*
|
|
1287
|
-
* Runs `external_lookup` verifiers without minting/storing a proof.
|
|
1288
|
-
* Requires an enterprise API key (server-side only).
|
|
1289
|
-
*
|
|
1290
|
-
* @param {Object} params
|
|
1291
|
-
* @param {string} params.apiKey - Enterprise API key (sk_live_... or sk_test_...)
|
|
1292
|
-
* @param {Array<string>} params.verifierIds - Verifiers to run (external_lookup only)
|
|
1293
|
-
* @param {string} params.targetWalletAddress - Wallet to evaluate
|
|
1294
|
-
* @param {Object} [params.data] - Verifier input data (e.g., contractAddress/tokenId/chainId)
|
|
1295
|
-
* @returns {Promise<Object>} API response ({ success, data })
|
|
1296
|
-
*/
|
|
1297
|
-
async lookup(params = {}) {
|
|
1298
|
-
const apiKey = (params.apiKey || "").toString().trim();
|
|
1299
|
-
if (!apiKey || !(apiKey.startsWith("sk_live_") || apiKey.startsWith("sk_test_"))) {
|
|
1300
|
-
throw new ValidationError("lookup requires apiKey (sk_live_* or sk_test_*)");
|
|
1301
|
-
}
|
|
1302
|
-
const verifierIds = Array.isArray(params.verifierIds) ? params.verifierIds.map((v) => String(v).trim()).filter(Boolean) : [];
|
|
1303
|
-
if (verifierIds.length === 0) {
|
|
1304
|
-
throw new ValidationError("lookup requires verifierIds (non-empty array)");
|
|
1305
|
-
}
|
|
1306
|
-
const targetWalletAddress = (params.targetWalletAddress || "").toString().trim();
|
|
1307
|
-
if (!targetWalletAddress || !/^0x[a-fA-F0-9]{40}$/i.test(targetWalletAddress)) {
|
|
1308
|
-
throw new ValidationError("lookup requires a valid targetWalletAddress (0x...)");
|
|
1309
|
-
}
|
|
1310
|
-
const body = {
|
|
1311
|
-
verifierIds,
|
|
1312
|
-
targetWalletAddress,
|
|
1313
|
-
data: params.data && typeof params.data === "object" ? params.data : {}
|
|
1314
|
-
};
|
|
1315
|
-
const response = await this._makeRequest("POST", "/api/v1/verification/lookup", body, {
|
|
1316
|
-
Authorization: `Bearer ${apiKey}`
|
|
1317
|
-
});
|
|
1318
|
-
if (!response.success) {
|
|
1319
|
-
throw new ApiError(`Lookup failed: ${response.error?.message || "Unknown error"}`, response.error);
|
|
1320
|
-
}
|
|
1321
|
-
return response;
|
|
1322
|
-
}
|
|
1323
1369
|
/**
|
|
1324
1370
|
* GATE CHECK (API) - Minimal eligibility check
|
|
1325
1371
|
*
|
|
@@ -1337,7 +1383,6 @@ ${bytes.length}`;
|
|
|
1337
1383
|
* @param {number} [params.sinceDays] - Optional time window in days
|
|
1338
1384
|
* @param {number} [params.since] - Optional unix timestamp in ms (lower bound)
|
|
1339
1385
|
* @param {number} [params.limit] - Max rows to scan (server may clamp)
|
|
1340
|
-
* @param {string} [params.select] - Comma-separated projections (handle,provider,profileUrl,traits.<key>)
|
|
1341
1386
|
* @returns {Promise<Object>} API response ({ success, data })
|
|
1342
1387
|
*/
|
|
1343
1388
|
async gateCheck(params = {}) {
|
|
@@ -1377,7 +1422,6 @@ ${bytes.length}`;
|
|
|
1377
1422
|
setIfPresent("sinceDays", params.sinceDays);
|
|
1378
1423
|
setIfPresent("since", params.since);
|
|
1379
1424
|
setIfPresent("limit", params.limit);
|
|
1380
|
-
setCsvIfPresent("select", params.select);
|
|
1381
1425
|
setIfPresent("referenceType", params.referenceType);
|
|
1382
1426
|
setIfPresent("referenceId", params.referenceId);
|
|
1383
1427
|
setIfPresent("tag", params.tag);
|
|
@@ -1391,7 +1435,6 @@ ${bytes.length}`;
|
|
|
1391
1435
|
setIfPresent("domain", params.domain);
|
|
1392
1436
|
setIfPresent("minBalance", params.minBalance);
|
|
1393
1437
|
setIfPresent("provider", params.provider);
|
|
1394
|
-
setIfPresent("handle", params.handle);
|
|
1395
1438
|
setIfPresent("ownerAddress", params.ownerAddress);
|
|
1396
1439
|
setIfPresent("riskLevel", params.riskLevel);
|
|
1397
1440
|
setBoolIfPresent("sanctioned", params.sanctioned);
|
|
@@ -1399,8 +1442,6 @@ ${bytes.length}`;
|
|
|
1399
1442
|
setIfPresent("primaryWalletAddress", params.primaryWalletAddress);
|
|
1400
1443
|
setIfPresent("secondaryWalletAddress", params.secondaryWalletAddress);
|
|
1401
1444
|
setIfPresent("verificationMethod", params.verificationMethod);
|
|
1402
|
-
setIfPresent("traitPath", params.traitPath);
|
|
1403
|
-
setIfPresent("traitGte", params.traitGte);
|
|
1404
1445
|
const response = await this._makeRequest("GET", `/api/v1/proofs/gate/check?${qs.toString()}`);
|
|
1405
1446
|
if (!response.success) {
|
|
1406
1447
|
throw new ApiError(`Gate check failed: ${response.error?.message || "Unknown error"}`, response.error);
|
package/cjs/index.cjs
CHANGED
|
@@ -197,7 +197,7 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
|
|
|
197
197
|
}
|
|
198
198
|
const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
|
|
199
199
|
if (!chainContext) {
|
|
200
|
-
throw new SDKError("chainId is required (or provide chain for
|
|
200
|
+
throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
|
|
201
201
|
}
|
|
202
202
|
if (chainContext === chainId && typeof chainId !== "number") {
|
|
203
203
|
throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
|
|
@@ -472,10 +472,14 @@ function validateVerifierPayload(verifierId, data) {
|
|
|
472
472
|
result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
|
|
473
473
|
}
|
|
474
474
|
} else if (id === "ownership-basic") {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
475
|
+
if (!("owner" in data))
|
|
476
|
+
result.missing.push("owner");
|
|
477
|
+
const hasContent = typeof data.content === "string" && data.content.length > 0;
|
|
478
|
+
const hasContentHash = typeof data.contentHash === "string" && data.contentHash.length > 0;
|
|
479
|
+
const hasRefId = typeof data.reference?.id === "string" && data.reference.id.length > 0;
|
|
480
|
+
if (!hasContent && !hasContentHash && !hasRefId) {
|
|
481
|
+
result.missing.push("content (or contentHash or reference.id)");
|
|
482
|
+
}
|
|
479
483
|
}
|
|
480
484
|
if (result.missing.length > 0) {
|
|
481
485
|
result.valid = false;
|
|
@@ -648,10 +652,24 @@ var init_utils = __esm({
|
|
|
648
652
|
}
|
|
649
653
|
setTimeout(pollAttempt, this.currentInterval);
|
|
650
654
|
} catch (error) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
+
if (error instanceof ValidationError) {
|
|
656
|
+
reject(error);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (error instanceof ApiError && error.statusCode === 429 || error?.isRetryable === true) {
|
|
660
|
+
if (this.options.exponentialBackoff) {
|
|
661
|
+
const next = Math.min(this.currentInterval * 2, this.options.maxInterval);
|
|
662
|
+
const jitter = next * (0.5 + Math.random() * 0.5);
|
|
663
|
+
this.currentInterval = Math.max(250, Math.floor(jitter));
|
|
664
|
+
}
|
|
665
|
+
if (this.attempt >= this.options.maxAttempts) {
|
|
666
|
+
reject(new SDKError("Verification polling timeout", "POLLING_TIMEOUT"));
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
setTimeout(pollAttempt, this.currentInterval);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
reject(new SDKError(`Polling failed: ${error.message}`, "POLLING_ERROR"));
|
|
655
673
|
}
|
|
656
674
|
};
|
|
657
675
|
pollAttempt();
|
|
@@ -714,6 +732,67 @@ var init_client = __esm({
|
|
|
714
732
|
if (!data.owner || !validateWalletAddress(data.owner)) {
|
|
715
733
|
return { valid: false, error: "owner (wallet address) is required" };
|
|
716
734
|
}
|
|
735
|
+
if (data.content !== void 0 && data.content !== null) {
|
|
736
|
+
if (typeof data.content !== "string") {
|
|
737
|
+
return { valid: false, error: "content must be a string when provided" };
|
|
738
|
+
}
|
|
739
|
+
if (data.content.length > 5e4) {
|
|
740
|
+
return { valid: false, error: "content exceeds 50KB inline limit" };
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (data.contentHash !== void 0 && data.contentHash !== null) {
|
|
744
|
+
if (typeof data.contentHash !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(data.contentHash)) {
|
|
745
|
+
return { valid: false, error: "contentHash must be a 32-byte hex string (0x + 64 hex chars) when provided" };
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (data.contentType !== void 0 && data.contentType !== null) {
|
|
749
|
+
if (typeof data.contentType !== "string" || data.contentType.length > 100) {
|
|
750
|
+
return { valid: false, error: "contentType must be a string (max 100 chars) when provided" };
|
|
751
|
+
}
|
|
752
|
+
const base = String(data.contentType).split(";")[0].trim().toLowerCase();
|
|
753
|
+
if (!base || base.includes(" ") || !base.includes("/")) {
|
|
754
|
+
return { valid: false, error: "contentType must be a valid MIME type when provided" };
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (data.provenance !== void 0 && data.provenance !== null) {
|
|
758
|
+
if (!data.provenance || typeof data.provenance !== "object" || Array.isArray(data.provenance)) {
|
|
759
|
+
return { valid: false, error: "provenance must be an object when provided" };
|
|
760
|
+
}
|
|
761
|
+
const dk = data.provenance.declaredKind;
|
|
762
|
+
if (dk !== void 0 && dk !== null) {
|
|
763
|
+
const allowed = ["human", "ai", "mixed", "unknown"];
|
|
764
|
+
if (typeof dk !== "string" || !allowed.includes(dk)) {
|
|
765
|
+
return { valid: false, error: `provenance.declaredKind must be one of: ${allowed.join(", ")}` };
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const ai = data.provenance.aiContext;
|
|
769
|
+
if (ai !== void 0 && ai !== null) {
|
|
770
|
+
if (typeof ai !== "object" || Array.isArray(ai)) {
|
|
771
|
+
return { valid: false, error: "provenance.aiContext must be an object when provided" };
|
|
772
|
+
}
|
|
773
|
+
if (ai.generatorType !== void 0 && ai.generatorType !== null) {
|
|
774
|
+
const allowed = ["local", "saas", "agent"];
|
|
775
|
+
if (typeof ai.generatorType !== "string" || !allowed.includes(ai.generatorType)) {
|
|
776
|
+
return { valid: false, error: `provenance.aiContext.generatorType must be one of: ${allowed.join(", ")}` };
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (ai.provider !== void 0 && ai.provider !== null) {
|
|
780
|
+
if (typeof ai.provider !== "string" || ai.provider.length > 64) {
|
|
781
|
+
return { valid: false, error: "provenance.aiContext.provider must be a string (max 64 chars) when provided" };
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (ai.model !== void 0 && ai.model !== null) {
|
|
785
|
+
if (typeof ai.model !== "string" || ai.model.length > 128) {
|
|
786
|
+
return { valid: false, error: "provenance.aiContext.model must be a string (max 128 chars) when provided" };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (ai.runId !== void 0 && ai.runId !== null) {
|
|
790
|
+
if (typeof ai.runId !== "string" || ai.runId.length > 128) {
|
|
791
|
+
return { valid: false, error: "provenance.aiContext.runId must be a string (max 128 chars) when provided" };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
717
796
|
if (data.reference !== void 0) {
|
|
718
797
|
if (!data.reference || typeof data.reference !== "object") {
|
|
719
798
|
return { valid: false, error: "reference must be an object when provided" };
|
|
@@ -852,6 +931,17 @@ var init_client = __esm({
|
|
|
852
931
|
if (!validTypes.includes(contentType)) {
|
|
853
932
|
return { valid: false, error: `contentType must be one of: ${validTypes.join(", ")}` };
|
|
854
933
|
}
|
|
934
|
+
const isTextual = contentType.startsWith("text/") || contentType.includes("markdown");
|
|
935
|
+
if (isTextual) {
|
|
936
|
+
try {
|
|
937
|
+
const maxBytes = 50 * 1024;
|
|
938
|
+
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(data.content).length : String(data.content).length;
|
|
939
|
+
if (bytes > maxBytes) {
|
|
940
|
+
return { valid: false, error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)` };
|
|
941
|
+
}
|
|
942
|
+
} catch {
|
|
943
|
+
}
|
|
944
|
+
}
|
|
855
945
|
}
|
|
856
946
|
if (data.content.length > 13653334) {
|
|
857
947
|
return { valid: false, error: "content exceeds 10MB limit" };
|
|
@@ -939,7 +1029,7 @@ var init_client = __esm({
|
|
|
939
1029
|
* data: {
|
|
940
1030
|
* content: "My content",
|
|
941
1031
|
* owner: walletAddress, // or ownerAddress for nft-ownership/token-holding
|
|
942
|
-
* reference: { type: '
|
|
1032
|
+
* reference: { type: 'other', id: 'my-unique-identifier' }
|
|
943
1033
|
* },
|
|
944
1034
|
* walletAddress: '0x...',
|
|
945
1035
|
* signature: '0x...',
|
|
@@ -1027,6 +1117,7 @@ var init_client = __esm({
|
|
|
1027
1117
|
reference: data2.reference,
|
|
1028
1118
|
...data2.content && { content: data2.content },
|
|
1029
1119
|
...data2.contentHash && { contentHash: data2.contentHash },
|
|
1120
|
+
...data2.contentType && { contentType: data2.contentType },
|
|
1030
1121
|
...data2.provenance && { provenance: data2.provenance }
|
|
1031
1122
|
};
|
|
1032
1123
|
} else {
|
|
@@ -1521,9 +1612,11 @@ ${bytes.length}`;
|
|
|
1521
1612
|
throw new ValidationError("qHash is required");
|
|
1522
1613
|
}
|
|
1523
1614
|
const startTime = Date.now();
|
|
1615
|
+
let consecutiveRateLimits = 0;
|
|
1524
1616
|
while (Date.now() - startTime < timeout) {
|
|
1525
1617
|
try {
|
|
1526
1618
|
const status = await this.getStatus(qHash);
|
|
1619
|
+
consecutiveRateLimits = 0;
|
|
1527
1620
|
if (onProgress && typeof onProgress === "function") {
|
|
1528
1621
|
onProgress(status.data || status);
|
|
1529
1622
|
}
|
|
@@ -1538,7 +1631,17 @@ ${bytes.length}`;
|
|
|
1538
1631
|
if (error instanceof ValidationError) {
|
|
1539
1632
|
throw error;
|
|
1540
1633
|
}
|
|
1541
|
-
|
|
1634
|
+
let nextDelay = interval;
|
|
1635
|
+
if (error instanceof ApiError && Number(error.statusCode) === 429) {
|
|
1636
|
+
consecutiveRateLimits += 1;
|
|
1637
|
+
const exp = Math.min(6, consecutiveRateLimits);
|
|
1638
|
+
const base = Math.max(500, Number(interval) || 0);
|
|
1639
|
+
const max = 3e4;
|
|
1640
|
+
const backoff = Math.min(max, base * Math.pow(2, exp));
|
|
1641
|
+
const jitter = Math.floor(backoff * (0.5 + Math.random() * 0.5));
|
|
1642
|
+
nextDelay = jitter;
|
|
1643
|
+
}
|
|
1644
|
+
await new Promise((resolve) => setTimeout(resolve, nextDelay));
|
|
1542
1645
|
}
|
|
1543
1646
|
}
|
|
1544
1647
|
throw new NetworkError(`Polling timeout after ${timeout}ms`, "POLLING_TIMEOUT");
|
|
@@ -1632,7 +1735,7 @@ ${bytes.length}`;
|
|
|
1632
1735
|
return true;
|
|
1633
1736
|
}
|
|
1634
1737
|
// ============================================================================
|
|
1635
|
-
//
|
|
1738
|
+
// PROOFS & GATING METHODS
|
|
1636
1739
|
// ============================================================================
|
|
1637
1740
|
/**
|
|
1638
1741
|
* GET PROOFS BY WALLET - Fetch proofs for a wallet address
|
|
@@ -1765,45 +1868,6 @@ ${bytes.length}`;
|
|
|
1765
1868
|
nextOffset: response.data?.nextOffset ?? null
|
|
1766
1869
|
};
|
|
1767
1870
|
}
|
|
1768
|
-
/**
|
|
1769
|
-
* LOOKUP MODE (API) - Non-persistent server-to-server checks
|
|
1770
|
-
*
|
|
1771
|
-
* Runs `external_lookup` verifiers without minting/storing a proof.
|
|
1772
|
-
* Requires an enterprise API key (server-side only).
|
|
1773
|
-
*
|
|
1774
|
-
* @param {Object} params
|
|
1775
|
-
* @param {string} params.apiKey - Enterprise API key (sk_live_... or sk_test_...)
|
|
1776
|
-
* @param {Array<string>} params.verifierIds - Verifiers to run (external_lookup only)
|
|
1777
|
-
* @param {string} params.targetWalletAddress - Wallet to evaluate
|
|
1778
|
-
* @param {Object} [params.data] - Verifier input data (e.g., contractAddress/tokenId/chainId)
|
|
1779
|
-
* @returns {Promise<Object>} API response ({ success, data })
|
|
1780
|
-
*/
|
|
1781
|
-
async lookup(params = {}) {
|
|
1782
|
-
const apiKey = (params.apiKey || "").toString().trim();
|
|
1783
|
-
if (!apiKey || !(apiKey.startsWith("sk_live_") || apiKey.startsWith("sk_test_"))) {
|
|
1784
|
-
throw new ValidationError("lookup requires apiKey (sk_live_* or sk_test_*)");
|
|
1785
|
-
}
|
|
1786
|
-
const verifierIds = Array.isArray(params.verifierIds) ? params.verifierIds.map((v) => String(v).trim()).filter(Boolean) : [];
|
|
1787
|
-
if (verifierIds.length === 0) {
|
|
1788
|
-
throw new ValidationError("lookup requires verifierIds (non-empty array)");
|
|
1789
|
-
}
|
|
1790
|
-
const targetWalletAddress = (params.targetWalletAddress || "").toString().trim();
|
|
1791
|
-
if (!targetWalletAddress || !/^0x[a-fA-F0-9]{40}$/i.test(targetWalletAddress)) {
|
|
1792
|
-
throw new ValidationError("lookup requires a valid targetWalletAddress (0x...)");
|
|
1793
|
-
}
|
|
1794
|
-
const body = {
|
|
1795
|
-
verifierIds,
|
|
1796
|
-
targetWalletAddress,
|
|
1797
|
-
data: params.data && typeof params.data === "object" ? params.data : {}
|
|
1798
|
-
};
|
|
1799
|
-
const response = await this._makeRequest("POST", "/api/v1/verification/lookup", body, {
|
|
1800
|
-
Authorization: `Bearer ${apiKey}`
|
|
1801
|
-
});
|
|
1802
|
-
if (!response.success) {
|
|
1803
|
-
throw new ApiError(`Lookup failed: ${response.error?.message || "Unknown error"}`, response.error);
|
|
1804
|
-
}
|
|
1805
|
-
return response;
|
|
1806
|
-
}
|
|
1807
1871
|
/**
|
|
1808
1872
|
* GATE CHECK (API) - Minimal eligibility check
|
|
1809
1873
|
*
|
|
@@ -1821,7 +1885,6 @@ ${bytes.length}`;
|
|
|
1821
1885
|
* @param {number} [params.sinceDays] - Optional time window in days
|
|
1822
1886
|
* @param {number} [params.since] - Optional unix timestamp in ms (lower bound)
|
|
1823
1887
|
* @param {number} [params.limit] - Max rows to scan (server may clamp)
|
|
1824
|
-
* @param {string} [params.select] - Comma-separated projections (handle,provider,profileUrl,traits.<key>)
|
|
1825
1888
|
* @returns {Promise<Object>} API response ({ success, data })
|
|
1826
1889
|
*/
|
|
1827
1890
|
async gateCheck(params = {}) {
|
|
@@ -1861,7 +1924,6 @@ ${bytes.length}`;
|
|
|
1861
1924
|
setIfPresent("sinceDays", params.sinceDays);
|
|
1862
1925
|
setIfPresent("since", params.since);
|
|
1863
1926
|
setIfPresent("limit", params.limit);
|
|
1864
|
-
setCsvIfPresent("select", params.select);
|
|
1865
1927
|
setIfPresent("referenceType", params.referenceType);
|
|
1866
1928
|
setIfPresent("referenceId", params.referenceId);
|
|
1867
1929
|
setIfPresent("tag", params.tag);
|
|
@@ -1875,7 +1937,6 @@ ${bytes.length}`;
|
|
|
1875
1937
|
setIfPresent("domain", params.domain);
|
|
1876
1938
|
setIfPresent("minBalance", params.minBalance);
|
|
1877
1939
|
setIfPresent("provider", params.provider);
|
|
1878
|
-
setIfPresent("handle", params.handle);
|
|
1879
1940
|
setIfPresent("ownerAddress", params.ownerAddress);
|
|
1880
1941
|
setIfPresent("riskLevel", params.riskLevel);
|
|
1881
1942
|
setBoolIfPresent("sanctioned", params.sanctioned);
|
|
@@ -1883,8 +1944,6 @@ ${bytes.length}`;
|
|
|
1883
1944
|
setIfPresent("primaryWalletAddress", params.primaryWalletAddress);
|
|
1884
1945
|
setIfPresent("secondaryWalletAddress", params.secondaryWalletAddress);
|
|
1885
1946
|
setIfPresent("verificationMethod", params.verificationMethod);
|
|
1886
|
-
setIfPresent("traitPath", params.traitPath);
|
|
1887
|
-
setIfPresent("traitGte", params.traitGte);
|
|
1888
1947
|
const response = await this._makeRequest("GET", `/api/v1/proofs/gate/check?${qs.toString()}`);
|
|
1889
1948
|
if (!response.success) {
|
|
1890
1949
|
throw new ApiError(`Gate check failed: ${response.error?.message || "Unknown error"}`, response.error);
|
package/cjs/utils.cjs
CHANGED
|
@@ -75,6 +75,49 @@ var SDKError = class _SDKError extends Error {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
|
+
var ApiError = class _ApiError extends SDKError {
|
|
79
|
+
constructor(message, statusCode = 500, code = "API_ERROR", response = null) {
|
|
80
|
+
super(message, code);
|
|
81
|
+
this.name = "ApiError";
|
|
82
|
+
this.statusCode = statusCode;
|
|
83
|
+
this.response = response;
|
|
84
|
+
this.isClientError = statusCode >= 400 && statusCode < 500;
|
|
85
|
+
this.isServerError = statusCode >= 500;
|
|
86
|
+
this.isRetryable = this.isServerError || statusCode === 429;
|
|
87
|
+
}
|
|
88
|
+
static fromResponse(response, responseData) {
|
|
89
|
+
const statusCode = response.status;
|
|
90
|
+
const message = responseData?.error?.message || responseData?.message || `API request failed with status ${statusCode}`;
|
|
91
|
+
const code = responseData?.error?.code || "API_ERROR";
|
|
92
|
+
return new _ApiError(message, statusCode, code, responseData);
|
|
93
|
+
}
|
|
94
|
+
toJSON() {
|
|
95
|
+
return {
|
|
96
|
+
...super.toJSON(),
|
|
97
|
+
statusCode: this.statusCode,
|
|
98
|
+
isClientError: this.isClientError,
|
|
99
|
+
isServerError: this.isServerError,
|
|
100
|
+
isRetryable: this.isRetryable
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var ValidationError = class extends SDKError {
|
|
105
|
+
constructor(message, field = null, value = null) {
|
|
106
|
+
super(message, "VALIDATION_ERROR");
|
|
107
|
+
this.name = "ValidationError";
|
|
108
|
+
this.field = field;
|
|
109
|
+
this.value = value;
|
|
110
|
+
this.isRetryable = false;
|
|
111
|
+
}
|
|
112
|
+
toJSON() {
|
|
113
|
+
return {
|
|
114
|
+
...super.toJSON(),
|
|
115
|
+
field: this.field,
|
|
116
|
+
value: this.value,
|
|
117
|
+
isRetryable: this.isRetryable
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
};
|
|
78
121
|
|
|
79
122
|
// utils.js
|
|
80
123
|
function deterministicStringify(obj) {
|
|
@@ -108,7 +151,7 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
|
|
|
108
151
|
}
|
|
109
152
|
const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
|
|
110
153
|
if (!chainContext) {
|
|
111
|
-
throw new SDKError("chainId is required (or provide chain for
|
|
154
|
+
throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
|
|
112
155
|
}
|
|
113
156
|
if (chainContext === chainId && typeof chainId !== "number") {
|
|
114
157
|
throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
|
|
@@ -384,10 +427,24 @@ var StatusPoller = class {
|
|
|
384
427
|
}
|
|
385
428
|
setTimeout(pollAttempt, this.currentInterval);
|
|
386
429
|
} catch (error) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
430
|
+
if (error instanceof ValidationError) {
|
|
431
|
+
reject(error);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (error instanceof ApiError && error.statusCode === 429 || error?.isRetryable === true) {
|
|
435
|
+
if (this.options.exponentialBackoff) {
|
|
436
|
+
const next = Math.min(this.currentInterval * 2, this.options.maxInterval);
|
|
437
|
+
const jitter = next * (0.5 + Math.random() * 0.5);
|
|
438
|
+
this.currentInterval = Math.max(250, Math.floor(jitter));
|
|
439
|
+
}
|
|
440
|
+
if (this.attempt >= this.options.maxAttempts) {
|
|
441
|
+
reject(new SDKError("Verification polling timeout", "POLLING_TIMEOUT"));
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
setTimeout(pollAttempt, this.currentInterval);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
reject(new SDKError(`Polling failed: ${error.message}`, "POLLING_ERROR"));
|
|
391
448
|
}
|
|
392
449
|
};
|
|
393
450
|
pollAttempt();
|
|
@@ -464,10 +521,14 @@ function validateVerifierPayload(verifierId, data) {
|
|
|
464
521
|
result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
|
|
465
522
|
}
|
|
466
523
|
} else if (id === "ownership-basic") {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
524
|
+
if (!("owner" in data))
|
|
525
|
+
result.missing.push("owner");
|
|
526
|
+
const hasContent = typeof data.content === "string" && data.content.length > 0;
|
|
527
|
+
const hasContentHash = typeof data.contentHash === "string" && data.contentHash.length > 0;
|
|
528
|
+
const hasRefId = typeof data.reference?.id === "string" && data.reference.id.length > 0;
|
|
529
|
+
if (!hasContent && !hasContentHash && !hasRefId) {
|
|
530
|
+
result.missing.push("content (or contentHash or reference.id)");
|
|
531
|
+
}
|
|
471
532
|
}
|
|
472
533
|
if (result.missing.length > 0) {
|
|
473
534
|
result.valid = false;
|