@neus/sdk 1.0.12 → 1.1.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/client.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ApiError, ValidationError, NetworkError, ConfigurationError } from './errors.js';
2
+ import { fetchSponsorGrant } from './sponsor.js';
2
3
  import {
3
4
  PORTABLE_PROOF_SIGNER_HEADER,
4
5
  constructVerificationMessage,
@@ -239,6 +240,17 @@ const validateVerifierData = (verifierId, data) => {
239
240
  if (data.agentType && !['ai', 'bot', 'service', 'automation', 'agent'].includes(data.agentType)) {
240
241
  return { valid: false, error: 'agentType must be one of: ai, bot, service, automation, agent' };
241
242
  }
243
+ if (data.defaultRuntime && typeof data.defaultRuntime === 'object') {
244
+ if (data.defaultRuntime.provider && typeof data.defaultRuntime.provider === 'string' && data.defaultRuntime.provider.length > 64) {
245
+ return { valid: false, error: 'defaultRuntime.provider must be 64 chars or less' };
246
+ }
247
+ if (data.defaultRuntime.model && typeof data.defaultRuntime.model === 'string' && data.defaultRuntime.model.length > 128) {
248
+ return { valid: false, error: 'defaultRuntime.model must be 128 chars or less' };
249
+ }
250
+ if (data.defaultRuntime.mode && typeof data.defaultRuntime.mode === 'string' && data.defaultRuntime.mode.length > 64) {
251
+ return { valid: false, error: 'defaultRuntime.mode must be 64 chars or less' };
252
+ }
253
+ }
242
254
  break;
243
255
  case 'agent-delegation':
244
256
  if (!data.controllerWallet || !validateWalletAddress(data.controllerWallet)) {
@@ -253,6 +265,18 @@ const validateVerifierData = (verifierId, data) => {
253
265
  if (data.expiresAt && (typeof data.expiresAt !== 'number' || data.expiresAt < Date.now())) {
254
266
  return { valid: false, error: 'expiresAt must be a future timestamp' };
255
267
  }
268
+ if (data.model && typeof data.model === 'string' && data.model.length > 128) {
269
+ return { valid: false, error: 'model must be 128 chars or less' };
270
+ }
271
+ if (data.provider && typeof data.provider === 'string' && data.provider.length > 64) {
272
+ return { valid: false, error: 'provider must be 64 chars or less' };
273
+ }
274
+ if (data.allowedActions && Array.isArray(data.allowedActions) && data.allowedActions.length > 32) {
275
+ return { valid: false, error: 'allowedActions must have 32 items or less' };
276
+ }
277
+ if (data.deniedActions && Array.isArray(data.deniedActions) && data.deniedActions.length > 32) {
278
+ return { valid: false, error: 'deniedActions must have 32 items or less' };
279
+ }
256
280
  break;
257
281
  case 'ai-content-moderation':
258
282
  if (!data.content || typeof data.content !== 'string') {
@@ -373,6 +397,77 @@ export class NeusClient {
373
397
  } catch {
374
398
  void 0;
375
399
  }
400
+
401
+ /** @type {{ token: string, expMs: number, key: string } | null} */
402
+ this._sponsorGrantCache = null;
403
+ }
404
+
405
+ _getBillingWallet() {
406
+ const raw =
407
+ this.config.billingWallet ||
408
+ this.config.sponsorOrgWallet ||
409
+ this.config.orgWallet ||
410
+ null;
411
+ if (typeof raw !== 'string') return null;
412
+ const trimmed = raw.trim().toLowerCase();
413
+ return /^0x[a-f0-9]{40}$/.test(trimmed) ? trimmed : null;
414
+ }
415
+
416
+ _resolveIntegratorOrigin() {
417
+ if (typeof this.config.appOrigin === 'string' && this.config.appOrigin.trim()) {
418
+ return this.config.appOrigin.trim();
419
+ }
420
+ try {
421
+ if (typeof window !== 'undefined' && window.location?.origin) {
422
+ return window.location.origin;
423
+ }
424
+ } catch {
425
+ void 0;
426
+ }
427
+ return null;
428
+ }
429
+
430
+ async _resolveSponsorGrantHeaders(verifierIds = []) {
431
+ const appId = typeof this.config.appId === 'string' ? this.config.appId.trim() : '';
432
+ const orgWallet = this._getBillingWallet();
433
+ if (!appId || !orgWallet) {
434
+ return {};
435
+ }
436
+
437
+ const normalizedVerifierIds = Array.isArray(verifierIds)
438
+ ? verifierIds.map((v) => String(v || '').trim()).filter(Boolean).slice(0, 25)
439
+ : [];
440
+ const cacheKey = `${appId}:${orgWallet}:${normalizedVerifierIds.join(',')}`;
441
+ const now = Date.now();
442
+ if (
443
+ this._sponsorGrantCache &&
444
+ this._sponsorGrantCache.key === cacheKey &&
445
+ this._sponsorGrantCache.expMs > now + 30_000
446
+ ) {
447
+ return { 'X-Sponsor-Grant': this._sponsorGrantCache.token };
448
+ }
449
+
450
+ const origin = this._resolveIntegratorOrigin();
451
+ const grant = await fetchSponsorGrant({
452
+ apiUrl: this.baseUrl,
453
+ appId,
454
+ orgWallet,
455
+ verifierIds: normalizedVerifierIds,
456
+ origin
457
+ });
458
+
459
+ const expSeconds = Number(grant?.exp);
460
+ const expMs = Number.isFinite(expSeconds) && expSeconds > 0
461
+ ? expSeconds * 1000
462
+ : now + 15 * 60 * 1000;
463
+
464
+ this._sponsorGrantCache = {
465
+ key: cacheKey,
466
+ token: grant.sponsorGrant,
467
+ expMs
468
+ };
469
+
470
+ return { 'X-Sponsor-Grant': grant.sponsorGrant };
376
471
  }
377
472
 
378
473
  _getHubChainId() {
@@ -457,12 +552,12 @@ export class NeusClient {
457
552
  throw new ConfigurationError('Invalid wallet provider');
458
553
  }
459
554
 
460
- _getDefaultBrowserWallet() {
461
- if (typeof window === 'undefined') return null;
462
- // Legacy convenience fallback only. Non-EVM wallets must be passed explicitly
463
- // with CAIP-2 chain context so the SDK does not route them through EVM RPC.
464
- return window.ethereum || null;
465
- }
555
+ _getDefaultBrowserWallet() {
556
+ if (typeof window === 'undefined') return null;
557
+ // Legacy convenience fallback only. Non-EVM wallets must be passed explicitly
558
+ // with CAIP-2 chain context so the SDK does not route them through EVM RPC.
559
+ return window.ethereum || null;
560
+ }
466
561
 
467
562
  async _buildPrivateGateAuth({ address, wallet, chain, signatureMethod } = {}) {
468
563
  const providerWallet = wallet || this._getDefaultBrowserWallet();
@@ -732,10 +827,10 @@ export class NeusClient {
732
827
  walletAddress = Array.isArray(accounts) && accounts.length > 0 ? accounts[0] : null;
733
828
  }
734
829
  }
735
- } else {
736
- if (typeof window === 'undefined' || !window.ethereum) {
737
- throw new ConfigurationError('No EVM browser wallet detected. Provide wallet explicitly for non-EVM flows and include chain as a CAIP-2 value.');
738
- }
830
+ } else {
831
+ if (typeof window === 'undefined' || !window.ethereum) {
832
+ throw new ConfigurationError('No EVM browser wallet detected. Provide wallet explicitly for non-EVM flows and include chain as a CAIP-2 value.');
833
+ }
739
834
  await window.ethereum.request({ method: 'eth_requestAccounts' });
740
835
  provider = window.ethereum;
741
836
  const accounts = await provider.request({ method: 'eth_accounts' });
@@ -852,9 +947,13 @@ export class NeusClient {
852
947
  verificationData = {
853
948
  agentId: data.agentId,
854
949
  agentWallet: data?.agentWallet || walletAddress,
950
+ ...(data?.agentChainRef && { agentChainRef: data.agentChainRef }),
951
+ ...(data?.agentAccountId && { agentAccountId: data.agentAccountId }),
855
952
  ...(data?.agentLabel && { agentLabel: data.agentLabel }),
856
953
  ...(data?.agentType && { agentType: data.agentType }),
954
+ ...(data?.avatar && { avatar: data.avatar }),
857
955
  ...(data?.description && { description: data.description }),
956
+ ...(data?.defaultRuntime && { defaultRuntime: data.defaultRuntime }),
858
957
  ...(data?.capabilities && { capabilities: data.capabilities }),
859
958
  ...(data?.instructions && { instructions: data.instructions }),
860
959
  ...(data?.skills && { skills: data.skills }),
@@ -866,7 +965,11 @@ export class NeusClient {
866
965
  }
867
966
  verificationData = {
868
967
  controllerWallet: data?.controllerWallet || walletAddress,
968
+ ...(data?.controllerChainRef && { controllerChainRef: data.controllerChainRef }),
869
969
  agentWallet: data.agentWallet,
970
+ ...(data?.agentChainRef && { agentChainRef: data.agentChainRef }),
971
+ ...(data?.controllerAccountId && { controllerAccountId: data.controllerAccountId }),
972
+ ...(data?.agentAccountId && { agentAccountId: data.agentAccountId }),
870
973
  ...(data?.agentId && { agentId: data.agentId }),
871
974
  ...(data?.scope && { scope: data.scope }),
872
975
  ...(data?.permissions && { permissions: data.permissions }),
@@ -875,7 +978,13 @@ export class NeusClient {
875
978
  ...(data?.receiptDisclosure && { receiptDisclosure: data.receiptDisclosure }),
876
979
  ...(data?.expiresAt && { expiresAt: data.expiresAt }),
877
980
  ...(data?.instructions && { instructions: data.instructions }),
878
- ...(data?.skills && { skills: data.skills })
981
+ ...(data?.skills && { skills: data.skills }),
982
+ ...(data?.model && { model: data.model }),
983
+ ...(data?.provider && { provider: data.provider }),
984
+ ...(data?.runtimePolicy && { runtimePolicy: data.runtimePolicy }),
985
+ ...(data?.allowedActions && { allowedActions: data.allowedActions }),
986
+ ...(data?.deniedActions && { deniedActions: data.deniedActions }),
987
+ ...(data?.approvalPolicy && { approvalPolicy: data.approvalPolicy })
879
988
  };
880
989
  } else if (verifier === 'ai-content-moderation') {
881
990
  if (!data?.content) {
@@ -1110,7 +1219,8 @@ export class NeusClient {
1110
1219
  options: optionsPayload
1111
1220
  };
1112
1221
 
1113
- const response = await this._makeRequest('POST', '/api/v1/verification', requestData);
1222
+ const sponsorHeaders = await this._resolveSponsorGrantHeaders(normalizedVerifierIds);
1223
+ const response = await this._makeRequest('POST', '/api/v1/verification', requestData, sponsorHeaders);
1114
1224
 
1115
1225
  if (!response.success) {
1116
1226
  throw new ApiError(`Verification failed: ${response.error?.message || 'Unknown error'}`, response.error);
@@ -1120,11 +1230,10 @@ export class NeusClient {
1120
1230
  }
1121
1231
 
1122
1232
  async getProof(qHash) {
1123
- const resolvedQHash = qHash; // Legacy input compatibility only. Do not expose or store proofId.
1124
- if (!resolvedQHash || typeof resolvedQHash !== 'string') {
1233
+ if (!qHash || typeof qHash !== 'string') {
1125
1234
  throw new ValidationError('qHash is required');
1126
1235
  }
1127
- const response = await this._makeRequest('GET', `/api/v1/proofs/${resolvedQHash}`);
1236
+ const response = await this._makeRequest('GET', `/api/v1/proofs/${qHash}`);
1128
1237
 
1129
1238
  if (!response.success) {
1130
1239
  throw new ApiError(`Failed to get proof: ${response.error?.message || 'Unknown error'}`, response.error);
@@ -1134,8 +1243,7 @@ export class NeusClient {
1134
1243
  }
1135
1244
 
1136
1245
  async getPrivateProof(qHash, wallet = null) {
1137
- const resolvedQHash = qHash; // Legacy input compatibility only. Do not expose or store proofId.
1138
- if (!resolvedQHash || typeof resolvedQHash !== 'string') {
1246
+ if (!qHash || typeof qHash !== 'string') {
1139
1247
  throw new ValidationError('qHash is required');
1140
1248
  }
1141
1249
 
@@ -1153,7 +1261,7 @@ export class NeusClient {
1153
1261
  ...(typeof auth.chain === 'string' && auth.chain.trim() ? { 'x-chain': auth.chain.trim() } : {}),
1154
1262
  ...(typeof auth.signatureMethod === 'string' && auth.signatureMethod.trim() ? { 'x-signature-method': auth.signatureMethod.trim() } : {})
1155
1263
  };
1156
- const response = await this._makeRequest('GET', `/api/v1/proofs/${resolvedQHash}`, null, headers);
1264
+ const response = await this._makeRequest('GET', `/api/v1/proofs/${qHash}`, null, headers);
1157
1265
  if (!response.success) {
1158
1266
  throw new ApiError(
1159
1267
  `Failed to access private proof: ${response.error?.message || 'Unauthorized'}`,
@@ -1177,7 +1285,7 @@ export class NeusClient {
1177
1285
  const message = constructVerificationMessage({
1178
1286
  walletAddress,
1179
1287
  signedTimestamp,
1180
- data: { action: 'access_private_proof', qHash: resolvedQHash },
1288
+ data: { action: 'access_private_proof', qHash: qHash },
1181
1289
  verifierIds: ['ownership-basic'],
1182
1290
  ...(signerIsEvm ? { chainId: this._getHubChainId() } : { chain })
1183
1291
  });
@@ -1197,7 +1305,7 @@ export class NeusClient {
1197
1305
  throw new ValidationError(`Failed to sign message: ${error.message}`);
1198
1306
  }
1199
1307
 
1200
- const response = await this._makeRequest('GET', `/api/v1/proofs/${resolvedQHash}`, null, {
1308
+ const response = await this._makeRequest('GET', `/api/v1/proofs/${qHash}`, null, {
1201
1309
  'x-wallet-address': walletAddress,
1202
1310
  'x-signature': signature,
1203
1311
  'x-signed-timestamp': signedTimestamp.toString(),
@@ -1247,14 +1355,13 @@ export class NeusClient {
1247
1355
  }
1248
1356
 
1249
1357
  async pollProofStatus(qHash, options = {}) {
1250
- const resolvedQHash = qHash; // Legacy input compatibility only. Do not expose or store proofId.
1251
1358
  const {
1252
1359
  interval = 5000,
1253
1360
  timeout = 120000,
1254
1361
  onProgress
1255
1362
  } = options;
1256
1363
 
1257
- if (!resolvedQHash || typeof resolvedQHash !== 'string') {
1364
+ if (!qHash || typeof qHash !== 'string') {
1258
1365
  throw new ValidationError('qHash is required');
1259
1366
  }
1260
1367
 
@@ -1263,7 +1370,7 @@ export class NeusClient {
1263
1370
 
1264
1371
  while (Date.now() - startTime < timeout) {
1265
1372
  try {
1266
- const status = await this.getProof(resolvedQHash);
1373
+ const status = await this.getProof(qHash);
1267
1374
  consecutiveRateLimits = 0;
1268
1375
 
1269
1376
  if (onProgress && typeof onProgress === 'function') {
@@ -1316,8 +1423,7 @@ export class NeusClient {
1316
1423
  }
1317
1424
 
1318
1425
  async revokeOwnProof(qHash, wallet) {
1319
- const resolvedQHash = qHash; // Legacy input compatibility only. Do not expose or store proofId.
1320
- if (!resolvedQHash || typeof resolvedQHash !== 'string') {
1426
+ if (!qHash || typeof qHash !== 'string') {
1321
1427
  throw new ValidationError('qHash is required');
1322
1428
  }
1323
1429
  const providerWallet = wallet || this._getDefaultBrowserWallet();
@@ -1333,7 +1439,7 @@ export class NeusClient {
1333
1439
  const message = constructVerificationMessage({
1334
1440
  walletAddress: address,
1335
1441
  signedTimestamp,
1336
- data: { action: 'revoke_proof', qHash: resolvedQHash },
1442
+ data: { action: 'revoke_proof', qHash: qHash },
1337
1443
  verifierIds: ['ownership-basic'],
1338
1444
  ...(signerIsEvm ? { chainId: this._getHubChainId() } : { chain })
1339
1445
  });
@@ -1353,7 +1459,7 @@ export class NeusClient {
1353
1459
  throw new ValidationError(`Failed to sign revocation: ${error.message}`);
1354
1460
  }
1355
1461
 
1356
- const res = await this._makeRequest('POST', `/api/v1/proofs/revoke-self/${resolvedQHash}`, {
1462
+ const res = await this._makeRequest('POST', `/api/v1/proofs/revoke-self/${qHash}`, {
1357
1463
  walletAddress: address,
1358
1464
  signature,
1359
1465
  signedTimestamp,
@@ -1586,7 +1692,23 @@ export class NeusClient {
1586
1692
  }
1587
1693
  }
1588
1694
 
1589
- const response = await this._makeRequest('GET', `/api/v1/proofs/check?${qs.toString()}`, null, headersOverride);
1695
+ let mergedHeaders = headersOverride;
1696
+ if (!mergedHeaders) {
1697
+ try {
1698
+ const sponsorHeaders = await this._resolveSponsorGrantHeaders(
1699
+ Array.isArray(params.verifierIds)
1700
+ ? params.verifierIds
1701
+ : (params.verifierIds ? [params.verifierIds] : [])
1702
+ );
1703
+ if (sponsorHeaders && Object.keys(sponsorHeaders).length > 0) {
1704
+ mergedHeaders = sponsorHeaders;
1705
+ }
1706
+ } catch (error) {
1707
+ this._log('Sponsor grant unavailable for gateCheck (continuing without)', error?.message || String(error));
1708
+ }
1709
+ }
1710
+
1711
+ const response = await this._makeRequest('GET', `/api/v1/proofs/check?${qs.toString()}`, null, mergedHeaders);
1590
1712
  if (!response.success) {
1591
1713
  throw new ApiError(`Gate check failed: ${response.error?.message || 'Unknown error'}`, response.error);
1592
1714
  }
@@ -1848,9 +1970,6 @@ export class NeusClient {
1848
1970
  const qHash = response?.data?.qHash ||
1849
1971
  response?.qHash ||
1850
1972
  response?.data?.resource?.qHash ||
1851
- response?.data?.proofId || // Legacy input compatibility only. Do not expose or store proofId.
1852
- response?.proofId || // Legacy input compatibility only. Do not expose or store proofId.
1853
- response?.data?.resource?.proofId || // Legacy input compatibility only. Do not expose or store proofId.
1854
1973
  response?.data?.id;
1855
1974
 
1856
1975
  const status = response?.data?.status ||
package/errors.js CHANGED
@@ -1,189 +1,154 @@
1
- export class SDKError extends Error {
2
- constructor(message, code = 'SDK_ERROR', details = {}) {
3
- super(message);
4
- this.name = 'SDKError';
5
- this.code = code;
6
- this.details = details;
7
- this.timestamp = Date.now();
8
-
9
- if (Error.captureStackTrace) {
10
- Error.captureStackTrace(this, SDKError);
11
- }
12
- }
13
-
14
- toJSON() {
15
- return {
16
- name: this.name,
17
- message: this.message,
18
- code: this.code,
19
- details: this.details,
20
- timestamp: this.timestamp
21
- };
22
- }
23
- }
24
-
25
- export class ApiError extends SDKError {
26
- constructor(message, statusCode = 500, code = 'API_ERROR', response = null) {
27
- super(message, code);
28
- this.name = 'ApiError';
29
- this.statusCode = statusCode;
30
- this.response = response;
31
-
32
- this.isClientError = statusCode >= 400 && statusCode < 500;
33
- this.isServerError = statusCode >= 500;
34
- this.isRetryable = this.isServerError || statusCode === 429; // Server errors or rate limit
35
- }
36
-
37
- static fromResponse(response, responseData) {
38
- const statusCode = response.status;
39
- const message = responseData?.error?.message ||
40
- responseData?.message ||
41
- `API request failed with status ${statusCode}`;
42
- const code = responseData?.error?.code || 'API_ERROR';
43
-
44
- return new ApiError(message, statusCode, code, responseData);
45
- }
46
-
47
- toJSON() {
48
- return {
49
- ...super.toJSON(),
50
- statusCode: this.statusCode,
51
- isClientError: this.isClientError,
52
- isServerError: this.isServerError,
53
- isRetryable: this.isRetryable
54
- };
55
- }
56
- }
57
-
58
- export class ValidationError extends SDKError {
59
- constructor(message, field = null, value = null) {
60
- super(message, 'VALIDATION_ERROR');
61
- this.name = 'ValidationError';
62
- this.field = field;
63
- this.value = value;
64
- this.isRetryable = false; // Validation errors are not retryable
65
- }
66
-
67
- toJSON() {
68
- return {
69
- ...super.toJSON(),
70
- field: this.field,
71
- value: this.value,
72
- isRetryable: this.isRetryable
73
- };
74
- }
75
- }
76
-
77
- export class NetworkError extends SDKError {
78
- constructor(message, code = 'NETWORK_ERROR', originalError = null) {
79
- super(message, code);
80
- this.name = 'NetworkError';
81
- this.originalError = originalError;
82
- this.isRetryable = true; // Network errors are retryable
83
- }
84
-
85
- static isNetworkError(error) {
86
- return error instanceof NetworkError ||
87
- error.name === 'TypeError' && error.message.includes('fetch') ||
88
- error.name === 'AbortError' ||
89
- error.code === 'ENOTFOUND' ||
90
- error.code === 'ECONNREFUSED' ||
91
- error.code === 'ETIMEDOUT';
92
- }
93
-
94
- toJSON() {
95
- return {
96
- ...super.toJSON(),
97
- isRetryable: this.isRetryable,
98
- originalError: this.originalError ? {
99
- name: this.originalError.name,
100
- message: this.originalError.message,
101
- code: this.originalError.code
102
- } : null
103
- };
104
- }
105
- }
106
-
107
- export class ConfigurationError extends SDKError {
108
- constructor(message, configKey = null) {
109
- super(message, 'CONFIGURATION_ERROR');
110
- this.name = 'ConfigurationError';
111
- this.configKey = configKey;
112
- this.isRetryable = false; // Config errors require user intervention
113
- }
114
-
115
- toJSON() {
116
- return {
117
- ...super.toJSON(),
118
- configKey: this.configKey,
119
- isRetryable: this.isRetryable
120
- };
121
- }
122
- }
123
-
124
- export class VerificationError extends SDKError {
125
- constructor(message, verifierId = null, code = 'VERIFICATION_ERROR') {
126
- super(message, code);
127
- this.name = 'VerificationError';
128
- this.verifierId = verifierId;
129
- this.isRetryable = true; // Some verification errors might be retryable
130
- }
131
-
132
- toJSON() {
133
- return {
134
- ...super.toJSON(),
135
- verifierId: this.verifierId,
136
- isRetryable: this.isRetryable
137
- };
138
- }
139
- }
140
-
141
- export class AuthenticationError extends SDKError {
142
- constructor(message, code = 'AUTHENTICATION_ERROR') {
143
- super(message, code);
144
- this.name = 'AuthenticationError';
145
- this.isRetryable = false; // Auth errors require user intervention
146
- }
147
-
148
- toJSON() {
149
- return {
150
- ...super.toJSON(),
151
- isRetryable: this.isRetryable
152
- };
153
- }
154
- }
155
-
156
- export function createErrorFromGeneric(error, context = {}) {
157
- if (error instanceof SDKError) {
158
- return error;
159
- }
160
-
161
- if (NetworkError.isNetworkError(error)) {
162
- return new NetworkError(
163
- error.message || 'Network error occurred',
164
- error.code || 'NETWORK_ERROR',
165
- error
166
- );
167
- }
168
-
169
- if (error.name === 'AbortError' || error.message.includes('timeout')) {
170
- return new NetworkError('Request timeout', 'TIMEOUT', error);
171
- }
172
-
173
- return new SDKError(
174
- error.message || 'Unknown error occurred',
175
- error.code || 'UNKNOWN_ERROR',
176
- { originalError: error, context }
177
- );
178
- }
179
-
180
- export default {
181
- SDKError,
182
- ApiError,
183
- ValidationError,
184
- NetworkError,
185
- ConfigurationError,
186
- VerificationError,
187
- AuthenticationError,
188
- createErrorFromGeneric
189
- };
1
+ export class SDKError extends Error {
2
+ constructor(message, code = 'SDK_ERROR', details = {}) {
3
+ super(message);
4
+ this.name = 'SDKError';
5
+ this.code = code;
6
+ this.details = details;
7
+ this.timestamp = Date.now();
8
+
9
+ if (Error.captureStackTrace) {
10
+ Error.captureStackTrace(this, SDKError);
11
+ }
12
+ }
13
+
14
+ toJSON() {
15
+ return {
16
+ name: this.name,
17
+ message: this.message,
18
+ code: this.code,
19
+ details: this.details,
20
+ timestamp: this.timestamp
21
+ };
22
+ }
23
+ }
24
+
25
+ export class ApiError extends SDKError {
26
+ constructor(message, statusCode = 500, code = 'API_ERROR', response = null) {
27
+ super(message, code);
28
+ this.name = 'ApiError';
29
+ this.statusCode = statusCode;
30
+ this.response = response;
31
+
32
+ this.isClientError = statusCode >= 400 && statusCode < 500;
33
+ this.isServerError = statusCode >= 500;
34
+ this.isRetryable = this.isServerError || statusCode === 429; // Server errors or rate limit
35
+ }
36
+
37
+ static fromResponse(response, responseData) {
38
+ const statusCode = response.status;
39
+ const message = responseData?.error?.message ||
40
+ responseData?.message ||
41
+ `API request failed with status ${statusCode}`;
42
+ const code = responseData?.error?.code || 'API_ERROR';
43
+
44
+ return new ApiError(message, statusCode, code, responseData);
45
+ }
46
+
47
+ toJSON() {
48
+ return {
49
+ ...super.toJSON(),
50
+ statusCode: this.statusCode,
51
+ isClientError: this.isClientError,
52
+ isServerError: this.isServerError,
53
+ isRetryable: this.isRetryable
54
+ };
55
+ }
56
+ }
57
+
58
+ export class ValidationError extends SDKError {
59
+ constructor(message, field = null, value = null) {
60
+ super(message, 'VALIDATION_ERROR');
61
+ this.name = 'ValidationError';
62
+ this.field = field;
63
+ this.value = value;
64
+ this.isRetryable = false; // Validation errors are not retryable
65
+ }
66
+
67
+ toJSON() {
68
+ return {
69
+ ...super.toJSON(),
70
+ field: this.field,
71
+ value: this.value,
72
+ isRetryable: this.isRetryable
73
+ };
74
+ }
75
+ }
76
+
77
+ export class NetworkError extends SDKError {
78
+ constructor(message, code = 'NETWORK_ERROR', originalError = null) {
79
+ super(message, code);
80
+ this.name = 'NetworkError';
81
+ this.originalError = originalError;
82
+ this.isRetryable = true; // Network errors are retryable
83
+ }
84
+
85
+ static isNetworkError(error) {
86
+ return error instanceof NetworkError ||
87
+ error.name === 'TypeError' && error.message.includes('fetch') ||
88
+ error.name === 'AbortError' ||
89
+ error.code === 'ENOTFOUND' ||
90
+ error.code === 'ECONNREFUSED' ||
91
+ error.code === 'ETIMEDOUT';
92
+ }
93
+
94
+ toJSON() {
95
+ return {
96
+ ...super.toJSON(),
97
+ isRetryable: this.isRetryable,
98
+ originalError: this.originalError ? {
99
+ name: this.originalError.name,
100
+ message: this.originalError.message,
101
+ code: this.originalError.code
102
+ } : null
103
+ };
104
+ }
105
+ }
106
+
107
+ export class ConfigurationError extends SDKError {
108
+ constructor(message, configKey = null) {
109
+ super(message, 'CONFIGURATION_ERROR');
110
+ this.name = 'ConfigurationError';
111
+ this.configKey = configKey;
112
+ this.isRetryable = false; // Config errors require user intervention
113
+ }
114
+
115
+ toJSON() {
116
+ return {
117
+ ...super.toJSON(),
118
+ configKey: this.configKey,
119
+ isRetryable: this.isRetryable
120
+ };
121
+ }
122
+ }
123
+
124
+ export class VerificationError extends SDKError {
125
+ constructor(message, verifierId = null, code = 'VERIFICATION_ERROR') {
126
+ super(message, code);
127
+ this.name = 'VerificationError';
128
+ this.verifierId = verifierId;
129
+ this.isRetryable = true; // Some verification errors might be retryable
130
+ }
131
+
132
+ toJSON() {
133
+ return {
134
+ ...super.toJSON(),
135
+ verifierId: this.verifierId,
136
+ isRetryable: this.isRetryable
137
+ };
138
+ }
139
+ }
140
+
141
+ export class AuthenticationError extends SDKError {
142
+ constructor(message, code = 'AUTHENTICATION_ERROR') {
143
+ super(message, code);
144
+ this.name = 'AuthenticationError';
145
+ this.isRetryable = false; // Auth errors require user intervention
146
+ }
147
+
148
+ toJSON() {
149
+ return {
150
+ ...super.toJSON(),
151
+ isRetryable: this.isRetryable
152
+ };
153
+ }
154
+ }