@neus/sdk 1.0.1 → 1.0.3

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/cjs/utils.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -44,9 +45,15 @@ __export(utils_exports, {
44
45
  isSupportedChain: () => isSupportedChain,
45
46
  isTerminalStatus: () => isTerminalStatus,
46
47
  normalizeAddress: () => normalizeAddress,
48
+ resolveDID: () => resolveDID,
49
+ resolveZkPassportConfig: () => resolveZkPassportConfig,
50
+ signMessage: () => signMessage,
51
+ standardizeVerificationRequest: () => standardizeVerificationRequest,
52
+ toHexUtf8: () => toHexUtf8,
47
53
  validateQHash: () => validateQHash,
48
54
  validateSignatureComponents: () => validateSignatureComponents,
49
55
  validateTimestamp: () => validateTimestamp,
56
+ validateUniversalAddress: () => validateUniversalAddress,
50
57
  validateVerifierPayload: () => validateVerifierPayload,
51
58
  validateWalletAddress: () => validateWalletAddress,
52
59
  withRetry: () => withRetry
@@ -75,8 +82,95 @@ var SDKError = class _SDKError extends Error {
75
82
  };
76
83
  }
77
84
  };
85
+ var ApiError = class _ApiError extends SDKError {
86
+ constructor(message, statusCode = 500, code = "API_ERROR", response = null) {
87
+ super(message, code);
88
+ this.name = "ApiError";
89
+ this.statusCode = statusCode;
90
+ this.response = response;
91
+ this.isClientError = statusCode >= 400 && statusCode < 500;
92
+ this.isServerError = statusCode >= 500;
93
+ this.isRetryable = this.isServerError || statusCode === 429;
94
+ }
95
+ static fromResponse(response, responseData) {
96
+ const statusCode = response.status;
97
+ const message = responseData?.error?.message || responseData?.message || `API request failed with status ${statusCode}`;
98
+ const code = responseData?.error?.code || "API_ERROR";
99
+ return new _ApiError(message, statusCode, code, responseData);
100
+ }
101
+ toJSON() {
102
+ return {
103
+ ...super.toJSON(),
104
+ statusCode: this.statusCode,
105
+ isClientError: this.isClientError,
106
+ isServerError: this.isServerError,
107
+ isRetryable: this.isRetryable
108
+ };
109
+ }
110
+ };
111
+ var ValidationError = class extends SDKError {
112
+ constructor(message, field = null, value = null) {
113
+ super(message, "VALIDATION_ERROR");
114
+ this.name = "ValidationError";
115
+ this.field = field;
116
+ this.value = value;
117
+ this.isRetryable = false;
118
+ }
119
+ toJSON() {
120
+ return {
121
+ ...super.toJSON(),
122
+ field: this.field,
123
+ value: this.value,
124
+ isRetryable: this.isRetryable
125
+ };
126
+ }
127
+ };
78
128
 
79
129
  // utils.js
130
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
131
+ function encodeBase58Bytes(input) {
132
+ let source;
133
+ if (input instanceof Uint8Array) {
134
+ source = input;
135
+ } else if (input instanceof ArrayBuffer) {
136
+ source = new Uint8Array(input);
137
+ } else if (ArrayBuffer.isView(input)) {
138
+ source = new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
139
+ } else if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(input)) {
140
+ source = new Uint8Array(input);
141
+ } else {
142
+ throw new SDKError("Unsupported non-EVM signature byte format", "INVALID_SIGNATURE_FORMAT");
143
+ }
144
+ if (source.length === 0)
145
+ return "";
146
+ let zeroes = 0;
147
+ while (zeroes < source.length && source[zeroes] === 0) {
148
+ zeroes++;
149
+ }
150
+ const iFactor = Math.log(256) / Math.log(58);
151
+ const size = (source.length - zeroes) * iFactor + 1 >>> 0;
152
+ const b58 = new Uint8Array(size);
153
+ let length = 0;
154
+ for (let i = zeroes; i < source.length; i++) {
155
+ let carry = source[i];
156
+ let j = 0;
157
+ for (let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++) {
158
+ carry += 256 * b58[k];
159
+ b58[k] = carry % 58;
160
+ carry = carry / 58 | 0;
161
+ }
162
+ length = j;
163
+ }
164
+ let it = size - length;
165
+ while (it < size && b58[it] === 0) {
166
+ it++;
167
+ }
168
+ let out = BASE58_ALPHABET[0].repeat(zeroes);
169
+ for (; it < size; it++) {
170
+ out += BASE58_ALPHABET[b58[it]];
171
+ }
172
+ return out;
173
+ }
80
174
  function deterministicStringify(obj) {
81
175
  if (obj === null || obj === void 0) {
82
176
  return JSON.stringify(obj);
@@ -108,7 +202,7 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
108
202
  }
109
203
  const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
110
204
  if (!chainContext) {
111
- throw new SDKError("chainId is required (or provide chain for preview mode)", "INVALID_CHAIN_CONTEXT");
205
+ throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
112
206
  }
113
207
  if (chainContext === chainId && typeof chainId !== "number") {
114
208
  throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
@@ -135,6 +229,29 @@ function validateWalletAddress(address) {
135
229
  }
136
230
  return /^0x[a-fA-F0-9]{40}$/.test(address);
137
231
  }
232
+ function validateUniversalAddress(address, chain) {
233
+ if (!address || typeof address !== "string")
234
+ return false;
235
+ const value = address.trim();
236
+ if (!value)
237
+ return false;
238
+ const chainRef = typeof chain === "string" ? chain.trim().toLowerCase() : "";
239
+ const namespace = chainRef.includes(":") ? chainRef.split(":")[0] : "";
240
+ if (validateWalletAddress(value))
241
+ return true;
242
+ if (namespace === "eip155")
243
+ return false;
244
+ if (namespace === "solana") {
245
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
246
+ }
247
+ if (namespace === "bip122") {
248
+ return /^(bc1|tb1|bcrt1)[a-z0-9]{11,87}$/.test(value.toLowerCase()) || /^[13mn2][a-km-zA-HJ-NP-Z1-9]{25,62}$/.test(value);
249
+ }
250
+ if (namespace === "near") {
251
+ return /^[a-z0-9._-]{2,64}$/.test(value);
252
+ }
253
+ return /^[A-Za-z0-9][A-Za-z0-9._:-]{1,127}$/.test(value);
254
+ }
138
255
  function validateTimestamp(timestamp, maxAgeMs = 5 * 60 * 1e3) {
139
256
  if (!timestamp || typeof timestamp !== "number") {
140
257
  return false;
@@ -156,7 +273,7 @@ function createVerificationData(content, owner, reference = null) {
156
273
  };
157
274
  return {
158
275
  content,
159
- owner: owner.toLowerCase(),
276
+ owner: validateWalletAddress(owner) ? owner.toLowerCase() : owner,
160
277
  reference: reference || {
161
278
  // Must be a valid backend enum value; 'content' is not supported.
162
279
  type: "other",
@@ -181,6 +298,261 @@ function deriveDid(address, chainIdOrChain) {
181
298
  return `did:pkh:eip155:${chainContext}:${address.toLowerCase()}`;
182
299
  }
183
300
  }
301
+ async function resolveDID(params, options = {}) {
302
+ const endpointPath = options.endpoint || "/api/v1/profile/did/resolve";
303
+ const apiUrl = typeof options.apiUrl === "string" ? options.apiUrl.trim() : "";
304
+ const resolveEndpoint = (path) => {
305
+ if (!path || typeof path !== "string")
306
+ return null;
307
+ const trimmedPath = path.trim();
308
+ if (!trimmedPath)
309
+ return null;
310
+ if (/^https?:\/\//i.test(trimmedPath))
311
+ return trimmedPath;
312
+ if (trimmedPath.startsWith("/")) {
313
+ if (!apiUrl)
314
+ return trimmedPath;
315
+ try {
316
+ return new URL(trimmedPath, apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`).toString();
317
+ } catch {
318
+ return null;
319
+ }
320
+ }
321
+ const base = apiUrl || NEUS_CONSTANTS.API_BASE_URL;
322
+ if (!base || typeof base !== "string")
323
+ return null;
324
+ try {
325
+ return new URL(trimmedPath, base.endsWith("/") ? base : `${base}/`).toString();
326
+ } catch {
327
+ return null;
328
+ }
329
+ };
330
+ const endpoint = resolveEndpoint(endpointPath);
331
+ if (!endpoint) {
332
+ throw new SDKError("resolveDID requires a valid endpoint", "INVALID_ENDPOINT");
333
+ }
334
+ const payload = {
335
+ walletAddress: params?.walletAddress,
336
+ chainId: params?.chainId,
337
+ chain: params?.chain
338
+ };
339
+ const isRelative = endpoint.startsWith("/") || !/^https?:\/\//i.test(endpoint);
340
+ const credentialsMode = options.credentials !== void 0 ? options.credentials : isRelative ? "same-origin" : "omit";
341
+ try {
342
+ const response = await fetch(endpoint, {
343
+ method: "POST",
344
+ headers: {
345
+ "Content-Type": "application/json",
346
+ Accept: "application/json",
347
+ ...options.headers || {}
348
+ },
349
+ body: JSON.stringify(payload),
350
+ credentials: credentialsMode
351
+ });
352
+ const json = await response.json().catch(() => null);
353
+ if (!response.ok) {
354
+ const msg = json?.error?.message || json?.error || json?.message || "DID resolution failed";
355
+ throw new SDKError(msg, "DID_RESOLVE_FAILED", json);
356
+ }
357
+ const did = json?.data?.did || json?.did;
358
+ if (!did || typeof did !== "string") {
359
+ throw new SDKError("DID resolution missing DID", "DID_RESOLVE_MISSING", json);
360
+ }
361
+ return { did, data: json?.data || null, raw: json };
362
+ } catch (error) {
363
+ if (error instanceof SDKError)
364
+ throw error;
365
+ throw new SDKError(`DID resolution failed: ${error?.message || error}`, "DID_RESOLVE_FAILED");
366
+ }
367
+ }
368
+ async function standardizeVerificationRequest(params, options = {}) {
369
+ const endpointPath = options.endpoint || "/api/v1/verification/standardize";
370
+ const apiUrl = typeof options.apiUrl === "string" ? options.apiUrl.trim() : "";
371
+ const resolveEndpoint = (path) => {
372
+ if (!path || typeof path !== "string")
373
+ return null;
374
+ const trimmedPath = path.trim();
375
+ if (!trimmedPath)
376
+ return null;
377
+ if (/^https?:\/\//i.test(trimmedPath))
378
+ return trimmedPath;
379
+ if (trimmedPath.startsWith("/")) {
380
+ if (!apiUrl)
381
+ return trimmedPath;
382
+ try {
383
+ return new URL(trimmedPath, apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`).toString();
384
+ } catch {
385
+ return null;
386
+ }
387
+ }
388
+ const base = apiUrl || NEUS_CONSTANTS.API_BASE_URL;
389
+ if (!base || typeof base !== "string")
390
+ return null;
391
+ try {
392
+ return new URL(trimmedPath, base.endsWith("/") ? base : `${base}/`).toString();
393
+ } catch {
394
+ return null;
395
+ }
396
+ };
397
+ const endpoint = resolveEndpoint(endpointPath);
398
+ if (!endpoint) {
399
+ throw new SDKError("standardizeVerificationRequest requires a valid endpoint", "INVALID_ENDPOINT");
400
+ }
401
+ const isRelative = endpoint.startsWith("/") || !/^https?:\/\//i.test(endpoint);
402
+ const credentialsMode = options.credentials !== void 0 ? options.credentials : isRelative ? "same-origin" : "omit";
403
+ try {
404
+ const response = await fetch(endpoint, {
405
+ method: "POST",
406
+ headers: {
407
+ "Content-Type": "application/json",
408
+ Accept: "application/json",
409
+ ...options.headers || {}
410
+ },
411
+ body: JSON.stringify(params || {}),
412
+ credentials: credentialsMode
413
+ });
414
+ const json = await response.json().catch(() => null);
415
+ if (!response.ok) {
416
+ const msg = json?.error?.message || json?.error || json?.message || "Standardize request failed";
417
+ throw new SDKError(msg, "STANDARDIZE_FAILED", json);
418
+ }
419
+ return json?.data || json;
420
+ } catch (error) {
421
+ if (error instanceof SDKError)
422
+ throw error;
423
+ throw new SDKError(`Standardize request failed: ${error?.message || error}`, "STANDARDIZE_FAILED");
424
+ }
425
+ }
426
+ function resolveZkPassportConfig(overrides = {}) {
427
+ const defaults = {
428
+ provider: "zkpassport",
429
+ scope: "basic_kyc",
430
+ checkSanctions: true,
431
+ requireFaceMatch: true,
432
+ faceMatchMode: "strict"
433
+ };
434
+ return {
435
+ ...defaults,
436
+ ...overrides && typeof overrides === "object" ? overrides : {}
437
+ };
438
+ }
439
+ function toHexUtf8(value) {
440
+ const input = typeof value === "string" ? value : String(value || "");
441
+ const bytes = new TextEncoder().encode(input);
442
+ return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
443
+ }
444
+ async function signMessage({ provider, message, walletAddress, chain } = {}) {
445
+ const msg = typeof message === "string" ? message : String(message || "");
446
+ if (!msg) {
447
+ throw new SDKError("signMessage: message is required", "INVALID_ARGUMENT");
448
+ }
449
+ const resolvedProvider = provider || (typeof window !== "undefined" && window?.ethereum ? window.ethereum : null);
450
+ if (!resolvedProvider) {
451
+ throw new SDKError("signMessage: provider is required", "SIGNER_UNAVAILABLE");
452
+ }
453
+ const chainStr = typeof chain === "string" && chain.trim().length > 0 ? chain.trim() : "eip155";
454
+ const namespace = chainStr.includes(":") ? chainStr.split(":")[0] || "eip155" : "eip155";
455
+ const resolveAddress = async () => {
456
+ if (typeof walletAddress === "string" && walletAddress.trim().length > 0)
457
+ return walletAddress;
458
+ if (namespace === "solana") {
459
+ if (resolvedProvider?.publicKey && typeof resolvedProvider.publicKey.toBase58 === "function") {
460
+ const pk = resolvedProvider.publicKey.toBase58();
461
+ if (typeof pk === "string" && pk)
462
+ return pk;
463
+ }
464
+ if (typeof resolvedProvider.getAddress === "function") {
465
+ const addr = await resolvedProvider.getAddress().catch(() => null);
466
+ if (typeof addr === "string" && addr)
467
+ return addr;
468
+ }
469
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
470
+ return resolvedProvider.address;
471
+ return null;
472
+ }
473
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
474
+ return resolvedProvider.address;
475
+ if (typeof resolvedProvider.getAddress === "function")
476
+ return await resolvedProvider.getAddress();
477
+ if (typeof resolvedProvider.request === "function") {
478
+ let accounts = await resolvedProvider.request({ method: "eth_accounts" }).catch(() => []);
479
+ if (!Array.isArray(accounts) || accounts.length === 0) {
480
+ accounts = await resolvedProvider.request({ method: "eth_requestAccounts" }).catch(() => []);
481
+ }
482
+ if (Array.isArray(accounts) && accounts[0])
483
+ return accounts[0];
484
+ }
485
+ return null;
486
+ };
487
+ if (namespace !== "eip155") {
488
+ if (typeof resolvedProvider.signMessage === "function") {
489
+ const encoded = typeof msg === "string" ? new TextEncoder().encode(msg) : msg;
490
+ const result = await resolvedProvider.signMessage(encoded);
491
+ if (typeof result === "string" && result)
492
+ return result;
493
+ if (result instanceof Uint8Array)
494
+ return encodeBase58Bytes(result);
495
+ if (result instanceof ArrayBuffer)
496
+ return encodeBase58Bytes(new Uint8Array(result));
497
+ if (ArrayBuffer.isView(result))
498
+ return encodeBase58Bytes(result);
499
+ if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(result))
500
+ return encodeBase58Bytes(result);
501
+ }
502
+ throw new SDKError("Non-EVM signing requires provider.signMessage", "SIGNER_UNAVAILABLE");
503
+ }
504
+ const address = await resolveAddress();
505
+ if (typeof resolvedProvider.request === "function" && address) {
506
+ let firstPersonalSignError = null;
507
+ try {
508
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [msg, address] });
509
+ if (typeof sig === "string" && sig)
510
+ return sig;
511
+ } catch (error) {
512
+ firstPersonalSignError = error;
513
+ }
514
+ let secondPersonalSignError = null;
515
+ try {
516
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [address, msg] });
517
+ if (typeof sig === "string" && sig)
518
+ return sig;
519
+ } catch (error) {
520
+ secondPersonalSignError = error;
521
+ const signatureErrorMessage = String(
522
+ error?.message || error?.reason || firstPersonalSignError?.message || firstPersonalSignError?.reason || ""
523
+ ).toLowerCase();
524
+ const needsHex = /byte|bytes|invalid byte sequence|encoding|non-hex/i.test(signatureErrorMessage);
525
+ if (needsHex) {
526
+ try {
527
+ const hexMsg = toHexUtf8(msg);
528
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [hexMsg, address] });
529
+ if (typeof sig === "string" && sig)
530
+ return sig;
531
+ } catch {
532
+ }
533
+ }
534
+ }
535
+ try {
536
+ const sig = await resolvedProvider.request({ method: "eth_sign", params: [address, msg] });
537
+ if (typeof sig === "string" && sig)
538
+ return sig;
539
+ } catch {
540
+ }
541
+ if (secondPersonalSignError || firstPersonalSignError) {
542
+ const lastError = secondPersonalSignError || firstPersonalSignError;
543
+ const isUserRejection = [4001, "ACTION_REJECTED"].includes(lastError?.code);
544
+ if (isUserRejection) {
545
+ throw lastError;
546
+ }
547
+ }
548
+ }
549
+ if (typeof resolvedProvider.signMessage === "function") {
550
+ const result = await resolvedProvider.signMessage(msg);
551
+ if (typeof result === "string" && result)
552
+ return result;
553
+ }
554
+ throw new SDKError("Unable to sign message with provided wallet/provider", "SIGNER_UNAVAILABLE");
555
+ }
184
556
  function isTerminalStatus(status) {
185
557
  if (!status || typeof status !== "string")
186
558
  return false;
@@ -384,10 +756,24 @@ var StatusPoller = class {
384
756
  }
385
757
  setTimeout(pollAttempt, this.currentInterval);
386
758
  } catch (error) {
387
- reject(new SDKError(
388
- `Polling failed: ${error.message}`,
389
- "POLLING_ERROR"
390
- ));
759
+ if (error instanceof ValidationError) {
760
+ reject(error);
761
+ return;
762
+ }
763
+ if (error instanceof ApiError && error.statusCode === 429 || error?.isRetryable === true) {
764
+ if (this.options.exponentialBackoff) {
765
+ const next = Math.min(this.currentInterval * 2, this.options.maxInterval);
766
+ const jitter = next * (0.5 + Math.random() * 0.5);
767
+ this.currentInterval = Math.max(250, Math.floor(jitter));
768
+ }
769
+ if (this.attempt >= this.options.maxAttempts) {
770
+ reject(new SDKError("Verification polling timeout", "POLLING_TIMEOUT"));
771
+ return;
772
+ }
773
+ setTimeout(pollAttempt, this.currentInterval);
774
+ return;
775
+ }
776
+ reject(new SDKError(`Polling failed: ${error.message}`, "POLLING_ERROR"));
391
777
  }
392
778
  };
393
779
  pollAttempt();
@@ -453,7 +839,7 @@ function validateVerifierPayload(verifierId, data) {
453
839
  result.missing.push(key);
454
840
  });
455
841
  if (!("ownerAddress" in data)) {
456
- result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
842
+ result.warnings.push("ownerAddress omitted (defaults to the signed walletAddress)");
457
843
  }
458
844
  } else if (id === "token-holding") {
459
845
  ["contractAddress", "minBalance", "chainId"].forEach((key) => {
@@ -461,13 +847,17 @@ function validateVerifierPayload(verifierId, data) {
461
847
  result.missing.push(key);
462
848
  });
463
849
  if (!("ownerAddress" in data)) {
464
- result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
850
+ result.warnings.push("ownerAddress omitted (defaults to the signed walletAddress)");
465
851
  }
466
852
  } else if (id === "ownership-basic") {
467
- ["content"].forEach((key) => {
468
- if (!(key in data))
469
- result.missing.push(key);
470
- });
853
+ if (!("owner" in data))
854
+ result.missing.push("owner");
855
+ const hasContent = typeof data.content === "string" && data.content.length > 0;
856
+ const hasContentHash = typeof data.contentHash === "string" && data.contentHash.length > 0;
857
+ const hasRefId = typeof data.reference?.id === "string" && data.reference.id.length > 0;
858
+ if (!hasContent && !hasContentHash && !hasRefId) {
859
+ result.missing.push("content (or contentHash or reference.id)");
860
+ }
471
861
  }
472
862
  if (result.missing.length > 0) {
473
863
  result.valid = false;
@@ -611,9 +1001,15 @@ function validateSignatureComponents({ walletAddress, signature, signedTimestamp
611
1001
  isSupportedChain,
612
1002
  isTerminalStatus,
613
1003
  normalizeAddress,
1004
+ resolveDID,
1005
+ resolveZkPassportConfig,
1006
+ signMessage,
1007
+ standardizeVerificationRequest,
1008
+ toHexUtf8,
614
1009
  validateQHash,
615
1010
  validateSignatureComponents,
616
1011
  validateTimestamp,
1012
+ validateUniversalAddress,
617
1013
  validateVerifierPayload,
618
1014
  validateWalletAddress,
619
1015
  withRetry