@fenelabs/fene-sdk 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,14 +40,22 @@ const client = createResonanceClient({
40
40
  });
41
41
 
42
42
  // 1. Get nonce
43
- const { message } = await client.getNonce("0x...");
43
+ const { nonce, message } = await client.getNonce("0x...");
44
44
 
45
45
  // 2. Sign message (using viem or wagmi)
46
46
  const signature = await signMessage({ message });
47
47
 
48
- // 3. Verify and get JWT
49
- const auth = await client.verify("0x...", signature);
50
- console.log(`Logged in as: ${auth.role}`);
48
+ // 3. Verify signature (API sets secure httpOnly cookies)
49
+ const auth = await client.verify({
50
+ address: "0x...",
51
+ signature,
52
+ nonce,
53
+ });
54
+ console.log(`Session expires at: ${auth.expires_at}`);
55
+
56
+ // 4. Optional: fetch current session metadata
57
+ const session = await client.getSession();
58
+ console.log(`Logged in as: ${session.role}`);
51
59
 
52
60
  // Now authenticated requests work
53
61
  await client.updateGeoLocation({ latitude: 0, longitude: 0, node_type: "rpc" });
@@ -120,16 +128,17 @@ The SDK uses a standard Web3 authentication flow:
120
128
  1. Request a nonce from the API (`getNonce`)
121
129
  2. Sign the message with your wallet (using viem, wagmi, etc.)
122
130
  3. Submit signature for verification (`verify`)
123
- 4. Receive JWT token for authenticated requests
131
+ 4. API stores auth in secure httpOnly cookies
124
132
 
125
- The token is automatically included in all subsequent requests and managed by the client.
133
+ The SDK sends credentials automatically (`credentials: include`) and handles refresh flow for 401 responses.
126
134
 
127
135
  ## API Reference
128
136
 
129
137
  ### Auth
130
138
 
131
139
  - `getNonce(address)` - Get authentication nonce
132
- - `verify(address, signature)` - Verify signature and get JWT
140
+ - `verify({ address, signature, nonce })` - Verify signature and establish cookie-based session
141
+ - `getSession()` - Get current session metadata
133
142
 
134
143
  ### Validators
135
144
 
package/dist/index.d.mts CHANGED
@@ -18,13 +18,21 @@ interface AuthVerifyRequest {
18
18
  nonce: string;
19
19
  }
20
20
  interface AuthResponse {
21
- token: string;
22
- role: "validator" | "delegator";
21
+ success: boolean;
22
+ address: string;
23
23
  expires_at: number;
24
24
  }
25
25
  interface RefreshResponse {
26
26
  success: boolean;
27
27
  expires_at?: number;
28
+ error?: string;
29
+ requires_login?: boolean;
30
+ }
31
+ interface SessionResponse {
32
+ authenticated: boolean;
33
+ address: string;
34
+ role: "validator" | "delegator";
35
+ expires_at: number;
28
36
  }
29
37
  declare enum ValidatorStatus {
30
38
  NOT_EXIST = 0,
@@ -298,6 +306,10 @@ declare class ResonanceClient {
298
306
  private executeRequest;
299
307
  getNonce(address: Address): Promise<NonceResponse>;
300
308
  verify(params: AuthVerifyRequest): Promise<AuthResponse>;
309
+ /**
310
+ * Get current authenticated session metadata from server.
311
+ */
312
+ getSession(): Promise<SessionResponse>;
301
313
  /**
302
314
  * Refresh the access token using the refresh token cookie.
303
315
  * Returns { success: boolean, expires_at?: number }
@@ -446,4 +458,4 @@ declare class EventBus {
446
458
  }
447
459
  declare const eventBus: EventBus;
448
460
 
449
- export { type APIErrorResponse, type Address, type AuthResponse, type AuthVerifyRequest, type AvatarResponse, type DailyBlockStats, type Delegator, type DelegatorRewards, type DelegatorStake, DelegatorStatus, type EpochInfo, ErrorCode, type ErrorCodeType, type GeoNode, type GeoStats, type GeoUpdateRequest, type NetworkAPR, type NetworkStats, type NonceResponse, type PaginatedResponse, type PriceResponse, type ReferralKey, type RefreshResponse, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type Transaction, type TransactionStats, type TransactionsList, type TransactionsParams, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, eventBus, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
461
+ export { type APIErrorResponse, type Address, type AuthResponse, type AuthVerifyRequest, type AvatarResponse, type DailyBlockStats, type Delegator, type DelegatorRewards, type DelegatorStake, DelegatorStatus, type EpochInfo, ErrorCode, type ErrorCodeType, type GeoNode, type GeoStats, type GeoUpdateRequest, type NetworkAPR, type NetworkStats, type NonceResponse, type PaginatedResponse, type PriceResponse, type ReferralKey, type RefreshResponse, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type SessionResponse, type Transaction, type TransactionStats, type TransactionsList, type TransactionsParams, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, eventBus, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
package/dist/index.d.ts CHANGED
@@ -18,13 +18,21 @@ interface AuthVerifyRequest {
18
18
  nonce: string;
19
19
  }
20
20
  interface AuthResponse {
21
- token: string;
22
- role: "validator" | "delegator";
21
+ success: boolean;
22
+ address: string;
23
23
  expires_at: number;
24
24
  }
25
25
  interface RefreshResponse {
26
26
  success: boolean;
27
27
  expires_at?: number;
28
+ error?: string;
29
+ requires_login?: boolean;
30
+ }
31
+ interface SessionResponse {
32
+ authenticated: boolean;
33
+ address: string;
34
+ role: "validator" | "delegator";
35
+ expires_at: number;
28
36
  }
29
37
  declare enum ValidatorStatus {
30
38
  NOT_EXIST = 0,
@@ -298,6 +306,10 @@ declare class ResonanceClient {
298
306
  private executeRequest;
299
307
  getNonce(address: Address): Promise<NonceResponse>;
300
308
  verify(params: AuthVerifyRequest): Promise<AuthResponse>;
309
+ /**
310
+ * Get current authenticated session metadata from server.
311
+ */
312
+ getSession(): Promise<SessionResponse>;
301
313
  /**
302
314
  * Refresh the access token using the refresh token cookie.
303
315
  * Returns { success: boolean, expires_at?: number }
@@ -446,4 +458,4 @@ declare class EventBus {
446
458
  }
447
459
  declare const eventBus: EventBus;
448
460
 
449
- export { type APIErrorResponse, type Address, type AuthResponse, type AuthVerifyRequest, type AvatarResponse, type DailyBlockStats, type Delegator, type DelegatorRewards, type DelegatorStake, DelegatorStatus, type EpochInfo, ErrorCode, type ErrorCodeType, type GeoNode, type GeoStats, type GeoUpdateRequest, type NetworkAPR, type NetworkStats, type NonceResponse, type PaginatedResponse, type PriceResponse, type ReferralKey, type RefreshResponse, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type Transaction, type TransactionStats, type TransactionsList, type TransactionsParams, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, eventBus, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
461
+ export { type APIErrorResponse, type Address, type AuthResponse, type AuthVerifyRequest, type AvatarResponse, type DailyBlockStats, type Delegator, type DelegatorRewards, type DelegatorStake, DelegatorStatus, type EpochInfo, ErrorCode, type ErrorCodeType, type GeoNode, type GeoStats, type GeoUpdateRequest, type NetworkAPR, type NetworkStats, type NonceResponse, type PaginatedResponse, type PriceResponse, type ReferralKey, type RefreshResponse, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type SessionResponse, type Transaction, type TransactionStats, type TransactionsList, type TransactionsParams, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, eventBus, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
package/dist/index.js CHANGED
@@ -215,7 +215,7 @@ var ResonanceClient = class {
215
215
  if (attempt >= this.retries) {
216
216
  throw error;
217
217
  }
218
- const delay = this.retryDelay * Math.pow(2, attempt);
218
+ const delay = this.retryDelay * 2 ** attempt;
219
219
  await new Promise((resolve) => setTimeout(resolve, delay));
220
220
  }
221
221
  }
@@ -239,28 +239,53 @@ var ResonanceClient = class {
239
239
  timestamp: startTime,
240
240
  attemptNumber
241
241
  };
242
+ const reportError = (error) => {
243
+ try {
244
+ this.onError?.(error, requestContext);
245
+ } catch (hookError) {
246
+ console.error("onError hook error:", hookError);
247
+ }
248
+ };
242
249
  try {
243
250
  this.onRequest?.(requestContext);
244
251
  } catch (hookError) {
245
252
  console.error("onRequest hook error:", hookError);
246
253
  }
247
254
  const controller = new AbortController();
248
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
255
+ let timeoutId = null;
256
+ const timeoutPromise = new Promise((_, reject) => {
257
+ timeoutId = setTimeout(() => {
258
+ controller.abort();
259
+ reject(
260
+ new ResonanceError(
261
+ ErrorCode.TIMEOUT,
262
+ `Request timed out after ${this.timeout}ms`,
263
+ { url, timeout: this.timeout },
264
+ 408
265
+ )
266
+ );
267
+ }, this.timeout);
268
+ });
249
269
  try {
250
- const response = await fetch(url, {
251
- ...options,
252
- headers,
253
- credentials: "include",
254
- // ✅ Send httpOnly cookies automatically
255
- signal: controller.signal
256
- });
257
- clearTimeout(timeoutId);
270
+ const response = await Promise.race([
271
+ fetch(url, {
272
+ ...options,
273
+ headers,
274
+ credentials: "include",
275
+ // Send httpOnly cookies automatically
276
+ signal: controller.signal
277
+ }),
278
+ timeoutPromise
279
+ ]);
280
+ if (timeoutId) {
281
+ clearTimeout(timeoutId);
282
+ }
258
283
  const duration = Date.now() - startTime;
259
284
  if (response.status === 401 && !this.isRefreshing) {
260
285
  this.isRefreshing = true;
261
- const refreshed = await this.refresh();
286
+ const refreshResult = await this.refresh();
262
287
  this.isRefreshing = false;
263
- if (refreshed) {
288
+ if (refreshResult.success) {
264
289
  return this.executeRequest(path, options, attemptNumber);
265
290
  }
266
291
  eventBus.emit("api:unauthorized", {
@@ -271,22 +296,13 @@ var ResonanceClient = class {
271
296
  }
272
297
  if (!response.ok) {
273
298
  const error = await response.json().catch(() => null);
274
- const resonanceError = error && typeof error === "object" && "code" in error && "message" in error ? new ResonanceError(
275
- error.code,
276
- error.message,
277
- error.details,
278
- response.status
279
- ) : new ResonanceError(
299
+ const resonanceError = error && typeof error === "object" && "code" in error && "message" in error ? new ResonanceError(error.code, error.message, error.details, response.status) : new ResonanceError(
280
300
  "INTERNAL_ERROR",
281
301
  error?.error || `HTTP ${response.status}`,
282
302
  void 0,
283
303
  response.status
284
304
  );
285
- try {
286
- this.onError?.(resonanceError, requestContext);
287
- } catch (hookError) {
288
- console.error("onError hook error:", hookError);
289
- }
305
+ reportError(resonanceError);
290
306
  throw resonanceError;
291
307
  }
292
308
  const data = await response.json();
@@ -304,28 +320,38 @@ var ResonanceClient = class {
304
320
  }
305
321
  return data;
306
322
  } catch (error) {
307
- clearTimeout(timeoutId);
323
+ if (timeoutId) {
324
+ clearTimeout(timeoutId);
325
+ }
326
+ if (error instanceof ResonanceError) {
327
+ if (error.code === ErrorCode.TIMEOUT) {
328
+ reportError(error);
329
+ }
330
+ throw error;
331
+ }
308
332
  if (error instanceof Error && error.name === "AbortError") {
309
333
  const timeoutError = new ResonanceError(
310
- "TIMEOUT",
334
+ ErrorCode.TIMEOUT,
311
335
  `Request timed out after ${this.timeout}ms`,
312
336
  { url, timeout: this.timeout },
313
337
  408
314
338
  );
315
- try {
316
- this.onError?.(timeoutError, requestContext);
317
- } catch (hookError) {
318
- console.error("onError hook error:", hookError);
319
- }
339
+ reportError(timeoutError);
340
+ throw timeoutError;
341
+ }
342
+ if (controller.signal.aborted) {
343
+ const timeoutError = new ResonanceError(
344
+ ErrorCode.TIMEOUT,
345
+ `Request timed out after ${this.timeout}ms`,
346
+ { url, timeout: this.timeout },
347
+ 408
348
+ );
349
+ reportError(timeoutError);
320
350
  throw timeoutError;
321
351
  }
322
352
  if (error instanceof Error) {
323
- const networkError = error instanceof ResonanceError ? error : new ResonanceError("NETWORK_ERROR", error.message, void 0, 0);
324
- try {
325
- this.onError?.(networkError, requestContext);
326
- } catch (hookError) {
327
- console.error("onError hook error:", hookError);
328
- }
353
+ const networkError = new ResonanceError(ErrorCode.NETWORK_ERROR, error.message, void 0, 0);
354
+ reportError(networkError);
329
355
  throw networkError;
330
356
  }
331
357
  throw error;
@@ -347,6 +373,12 @@ var ResonanceClient = class {
347
373
  })
348
374
  });
349
375
  }
376
+ /**
377
+ * Get current authenticated session metadata from server.
378
+ */
379
+ async getSession() {
380
+ return this.request("/eth/v1/auth/session");
381
+ }
350
382
  /**
351
383
  * Refresh the access token using the refresh token cookie.
352
384
  * Returns { success: boolean, expires_at?: number }
@@ -360,13 +392,18 @@ var ResonanceClient = class {
360
392
  headers: { "Content-Type": "application/json" }
361
393
  });
362
394
  if (response.ok) {
363
- const data = await response.json();
395
+ const data2 = await response.json();
364
396
  return {
365
- success: data.success === true,
366
- expires_at: data.expires_at
397
+ success: data2.success === true,
398
+ expires_at: data2.expires_at
367
399
  };
368
400
  }
369
- return { success: false };
401
+ const data = await response.json().catch(() => ({}));
402
+ return {
403
+ success: false,
404
+ error: typeof data.error === "string" ? data.error : "refresh_failed",
405
+ requires_login: data.requires_login === true
406
+ };
370
407
  } catch {
371
408
  return { success: false };
372
409
  }
@@ -436,7 +473,7 @@ var ResonanceClient = class {
436
473
  return this.request(`/eth/v1/referral/key/${key}`);
437
474
  }
438
475
  async getValidatorKeys(address) {
439
- return this.request(`/eth/v1/referral/validator/${address}`);
476
+ return this.request(`/eth/v1/referral/validator/${address}/keys`);
440
477
  }
441
478
  async checkWhitelist(data) {
442
479
  return this.request("/eth/v1/referral/whitelist", {
@@ -469,23 +506,15 @@ var ResonanceClient = class {
469
506
  async uploadUserAvatar(address, file) {
470
507
  const formData = new FormData();
471
508
  formData.append("file", file);
472
- const response = await fetch(
473
- `${this.baseUrl}/eth/v1/user/${address}/avatar`,
474
- {
475
- method: "POST",
476
- credentials: "include",
477
- // Send httpOnly cookies
478
- body: formData
479
- }
480
- );
509
+ const response = await fetch(`${this.baseUrl}/eth/v1/user/${address}/avatar`, {
510
+ method: "POST",
511
+ credentials: "include",
512
+ // Send httpOnly cookies
513
+ body: formData
514
+ });
481
515
  if (!response.ok) {
482
516
  const error = await response.json().catch(() => ({ error: "Upload failed" }));
483
- throw new ResonanceError(
484
- "UPLOAD_FAILED",
485
- error.error || "Failed to upload avatar",
486
- void 0,
487
- response.status
488
- );
517
+ throw new ResonanceError("UPLOAD_FAILED", error.error || "Failed to upload avatar", void 0, response.status);
489
518
  }
490
519
  return response.json();
491
520
  }
package/dist/index.mjs CHANGED
@@ -179,7 +179,7 @@ var ResonanceClient = class {
179
179
  if (attempt >= this.retries) {
180
180
  throw error;
181
181
  }
182
- const delay = this.retryDelay * Math.pow(2, attempt);
182
+ const delay = this.retryDelay * 2 ** attempt;
183
183
  await new Promise((resolve) => setTimeout(resolve, delay));
184
184
  }
185
185
  }
@@ -203,28 +203,53 @@ var ResonanceClient = class {
203
203
  timestamp: startTime,
204
204
  attemptNumber
205
205
  };
206
+ const reportError = (error) => {
207
+ try {
208
+ this.onError?.(error, requestContext);
209
+ } catch (hookError) {
210
+ console.error("onError hook error:", hookError);
211
+ }
212
+ };
206
213
  try {
207
214
  this.onRequest?.(requestContext);
208
215
  } catch (hookError) {
209
216
  console.error("onRequest hook error:", hookError);
210
217
  }
211
218
  const controller = new AbortController();
212
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
219
+ let timeoutId = null;
220
+ const timeoutPromise = new Promise((_, reject) => {
221
+ timeoutId = setTimeout(() => {
222
+ controller.abort();
223
+ reject(
224
+ new ResonanceError(
225
+ ErrorCode.TIMEOUT,
226
+ `Request timed out after ${this.timeout}ms`,
227
+ { url, timeout: this.timeout },
228
+ 408
229
+ )
230
+ );
231
+ }, this.timeout);
232
+ });
213
233
  try {
214
- const response = await fetch(url, {
215
- ...options,
216
- headers,
217
- credentials: "include",
218
- // ✅ Send httpOnly cookies automatically
219
- signal: controller.signal
220
- });
221
- clearTimeout(timeoutId);
234
+ const response = await Promise.race([
235
+ fetch(url, {
236
+ ...options,
237
+ headers,
238
+ credentials: "include",
239
+ // Send httpOnly cookies automatically
240
+ signal: controller.signal
241
+ }),
242
+ timeoutPromise
243
+ ]);
244
+ if (timeoutId) {
245
+ clearTimeout(timeoutId);
246
+ }
222
247
  const duration = Date.now() - startTime;
223
248
  if (response.status === 401 && !this.isRefreshing) {
224
249
  this.isRefreshing = true;
225
- const refreshed = await this.refresh();
250
+ const refreshResult = await this.refresh();
226
251
  this.isRefreshing = false;
227
- if (refreshed) {
252
+ if (refreshResult.success) {
228
253
  return this.executeRequest(path, options, attemptNumber);
229
254
  }
230
255
  eventBus.emit("api:unauthorized", {
@@ -235,22 +260,13 @@ var ResonanceClient = class {
235
260
  }
236
261
  if (!response.ok) {
237
262
  const error = await response.json().catch(() => null);
238
- const resonanceError = error && typeof error === "object" && "code" in error && "message" in error ? new ResonanceError(
239
- error.code,
240
- error.message,
241
- error.details,
242
- response.status
243
- ) : new ResonanceError(
263
+ const resonanceError = error && typeof error === "object" && "code" in error && "message" in error ? new ResonanceError(error.code, error.message, error.details, response.status) : new ResonanceError(
244
264
  "INTERNAL_ERROR",
245
265
  error?.error || `HTTP ${response.status}`,
246
266
  void 0,
247
267
  response.status
248
268
  );
249
- try {
250
- this.onError?.(resonanceError, requestContext);
251
- } catch (hookError) {
252
- console.error("onError hook error:", hookError);
253
- }
269
+ reportError(resonanceError);
254
270
  throw resonanceError;
255
271
  }
256
272
  const data = await response.json();
@@ -268,28 +284,38 @@ var ResonanceClient = class {
268
284
  }
269
285
  return data;
270
286
  } catch (error) {
271
- clearTimeout(timeoutId);
287
+ if (timeoutId) {
288
+ clearTimeout(timeoutId);
289
+ }
290
+ if (error instanceof ResonanceError) {
291
+ if (error.code === ErrorCode.TIMEOUT) {
292
+ reportError(error);
293
+ }
294
+ throw error;
295
+ }
272
296
  if (error instanceof Error && error.name === "AbortError") {
273
297
  const timeoutError = new ResonanceError(
274
- "TIMEOUT",
298
+ ErrorCode.TIMEOUT,
275
299
  `Request timed out after ${this.timeout}ms`,
276
300
  { url, timeout: this.timeout },
277
301
  408
278
302
  );
279
- try {
280
- this.onError?.(timeoutError, requestContext);
281
- } catch (hookError) {
282
- console.error("onError hook error:", hookError);
283
- }
303
+ reportError(timeoutError);
304
+ throw timeoutError;
305
+ }
306
+ if (controller.signal.aborted) {
307
+ const timeoutError = new ResonanceError(
308
+ ErrorCode.TIMEOUT,
309
+ `Request timed out after ${this.timeout}ms`,
310
+ { url, timeout: this.timeout },
311
+ 408
312
+ );
313
+ reportError(timeoutError);
284
314
  throw timeoutError;
285
315
  }
286
316
  if (error instanceof Error) {
287
- const networkError = error instanceof ResonanceError ? error : new ResonanceError("NETWORK_ERROR", error.message, void 0, 0);
288
- try {
289
- this.onError?.(networkError, requestContext);
290
- } catch (hookError) {
291
- console.error("onError hook error:", hookError);
292
- }
317
+ const networkError = new ResonanceError(ErrorCode.NETWORK_ERROR, error.message, void 0, 0);
318
+ reportError(networkError);
293
319
  throw networkError;
294
320
  }
295
321
  throw error;
@@ -311,6 +337,12 @@ var ResonanceClient = class {
311
337
  })
312
338
  });
313
339
  }
340
+ /**
341
+ * Get current authenticated session metadata from server.
342
+ */
343
+ async getSession() {
344
+ return this.request("/eth/v1/auth/session");
345
+ }
314
346
  /**
315
347
  * Refresh the access token using the refresh token cookie.
316
348
  * Returns { success: boolean, expires_at?: number }
@@ -324,13 +356,18 @@ var ResonanceClient = class {
324
356
  headers: { "Content-Type": "application/json" }
325
357
  });
326
358
  if (response.ok) {
327
- const data = await response.json();
359
+ const data2 = await response.json();
328
360
  return {
329
- success: data.success === true,
330
- expires_at: data.expires_at
361
+ success: data2.success === true,
362
+ expires_at: data2.expires_at
331
363
  };
332
364
  }
333
- return { success: false };
365
+ const data = await response.json().catch(() => ({}));
366
+ return {
367
+ success: false,
368
+ error: typeof data.error === "string" ? data.error : "refresh_failed",
369
+ requires_login: data.requires_login === true
370
+ };
334
371
  } catch {
335
372
  return { success: false };
336
373
  }
@@ -400,7 +437,7 @@ var ResonanceClient = class {
400
437
  return this.request(`/eth/v1/referral/key/${key}`);
401
438
  }
402
439
  async getValidatorKeys(address) {
403
- return this.request(`/eth/v1/referral/validator/${address}`);
440
+ return this.request(`/eth/v1/referral/validator/${address}/keys`);
404
441
  }
405
442
  async checkWhitelist(data) {
406
443
  return this.request("/eth/v1/referral/whitelist", {
@@ -433,23 +470,15 @@ var ResonanceClient = class {
433
470
  async uploadUserAvatar(address, file) {
434
471
  const formData = new FormData();
435
472
  formData.append("file", file);
436
- const response = await fetch(
437
- `${this.baseUrl}/eth/v1/user/${address}/avatar`,
438
- {
439
- method: "POST",
440
- credentials: "include",
441
- // Send httpOnly cookies
442
- body: formData
443
- }
444
- );
473
+ const response = await fetch(`${this.baseUrl}/eth/v1/user/${address}/avatar`, {
474
+ method: "POST",
475
+ credentials: "include",
476
+ // Send httpOnly cookies
477
+ body: formData
478
+ });
445
479
  if (!response.ok) {
446
480
  const error = await response.json().catch(() => ({ error: "Upload failed" }));
447
- throw new ResonanceError(
448
- "UPLOAD_FAILED",
449
- error.error || "Failed to upload avatar",
450
- void 0,
451
- response.status
452
- );
481
+ throw new ResonanceError("UPLOAD_FAILED", error.error || "Failed to upload avatar", void 0, response.status);
453
482
  }
454
483
  return response.json();
455
484
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenelabs/fene-sdk",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript SDK for Fene API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -12,9 +12,7 @@
12
12
  "types": "./dist/index.d.ts"
13
13
  }
14
14
  },
15
- "files": [
16
- "dist"
17
- ],
15
+ "files": ["dist"],
18
16
  "scripts": {
19
17
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
20
18
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
@@ -23,13 +21,7 @@
23
21
  "watch": "esbuild src/app.ts --bundle --sourcemap --outdir=www --servedir=www --watch",
24
22
  "prepublishOnly": "pnpm run build"
25
23
  },
26
- "keywords": [
27
- "resonance",
28
- "api",
29
- "sdk",
30
- "blockchain",
31
- "fene"
32
- ],
24
+ "keywords": ["resonance", "api", "sdk", "blockchain", "fene"],
33
25
  "author": "Fene Labs",
34
26
  "license": "MIT",
35
27
  "repository": {