@fenelabs/fene-sdk 0.3.7 → 0.4.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/dist/index.d.mts CHANGED
@@ -207,6 +207,7 @@ declare class ResonanceClient {
207
207
  private onRequest?;
208
208
  private onResponse?;
209
209
  private onError?;
210
+ private isRefreshing;
210
211
  constructor(config?: ResonanceClientConfig);
211
212
  setToken(token: string): void;
212
213
  clearToken(): void;
@@ -214,6 +215,19 @@ declare class ResonanceClient {
214
215
  private executeRequest;
215
216
  getNonce(address: Address): Promise<NonceResponse>;
216
217
  verify(params: AuthVerifyRequest): Promise<AuthResponse>;
218
+ /**
219
+ * Refresh the access token using the refresh token cookie.
220
+ * Returns true if refresh was successful.
221
+ */
222
+ refresh(): Promise<boolean>;
223
+ /**
224
+ * Logout and revoke current session.
225
+ */
226
+ logout(): Promise<void>;
227
+ /**
228
+ * Logout from all devices.
229
+ */
230
+ logoutAll(): Promise<void>;
217
231
  getValidators(): Promise<ValidatorSummary[]>;
218
232
  getActiveValidators(): Promise<ValidatorSummary[]>;
219
233
  getCandidates(): Promise<ValidatorSummary[]>;
@@ -310,4 +324,14 @@ declare function isAuthError(error: unknown): boolean;
310
324
  */
311
325
  declare function isNotFoundError(error: unknown): boolean;
312
326
 
313
- 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 ReferralKey, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
327
+ type EventCallback = (data?: any) => void;
328
+ declare class EventBus {
329
+ private listeners;
330
+ on(event: string, callback: EventCallback): void;
331
+ off(event: string, callback: EventCallback): void;
332
+ emit(event: string, data?: any): void;
333
+ clear(): void;
334
+ }
335
+ declare const eventBus: EventBus;
336
+
337
+ 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 ReferralKey, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, 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
@@ -207,6 +207,7 @@ declare class ResonanceClient {
207
207
  private onRequest?;
208
208
  private onResponse?;
209
209
  private onError?;
210
+ private isRefreshing;
210
211
  constructor(config?: ResonanceClientConfig);
211
212
  setToken(token: string): void;
212
213
  clearToken(): void;
@@ -214,6 +215,19 @@ declare class ResonanceClient {
214
215
  private executeRequest;
215
216
  getNonce(address: Address): Promise<NonceResponse>;
216
217
  verify(params: AuthVerifyRequest): Promise<AuthResponse>;
218
+ /**
219
+ * Refresh the access token using the refresh token cookie.
220
+ * Returns true if refresh was successful.
221
+ */
222
+ refresh(): Promise<boolean>;
223
+ /**
224
+ * Logout and revoke current session.
225
+ */
226
+ logout(): Promise<void>;
227
+ /**
228
+ * Logout from all devices.
229
+ */
230
+ logoutAll(): Promise<void>;
217
231
  getValidators(): Promise<ValidatorSummary[]>;
218
232
  getActiveValidators(): Promise<ValidatorSummary[]>;
219
233
  getCandidates(): Promise<ValidatorSummary[]>;
@@ -310,4 +324,14 @@ declare function isAuthError(error: unknown): boolean;
310
324
  */
311
325
  declare function isNotFoundError(error: unknown): boolean;
312
326
 
313
- 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 ReferralKey, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, type UploadResponse, type Validator, type ValidatorAPR, ValidatorStatus, type ValidatorSummary, type WhitelistCheckRequest, type WhitelistCheckResponse, createResonanceClient, hasErrorCode, isAuthError, isNetworkError, isNotFoundError, isResonanceError };
327
+ type EventCallback = (data?: any) => void;
328
+ declare class EventBus {
329
+ private listeners;
330
+ on(event: string, callback: EventCallback): void;
331
+ off(event: string, callback: EventCallback): void;
332
+ emit(event: string, data?: any): void;
333
+ clear(): void;
334
+ }
335
+ declare const eventBus: EventBus;
336
+
337
+ 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 ReferralKey, type RequestContext, ResonanceClient, type ResonanceClientConfig, ResonanceError, type ResponseContext, type RewardHistory, 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
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  ResonanceClient: () => ResonanceClient,
25
25
  ResonanceError: () => ResonanceError,
26
26
  createResonanceClient: () => createResonanceClient,
27
+ eventBus: () => eventBus,
27
28
  hasErrorCode: () => hasErrorCode,
28
29
  isAuthError: () => isAuthError,
29
30
  isNetworkError: () => isNetworkError,
@@ -112,6 +113,35 @@ function isNotFoundError(error) {
112
113
  return false;
113
114
  }
114
115
 
116
+ // src/eventBus.ts
117
+ var EventBus = class {
118
+ constructor() {
119
+ this.listeners = /* @__PURE__ */ new Map();
120
+ }
121
+ on(event, callback) {
122
+ if (!this.listeners.has(event)) {
123
+ this.listeners.set(event, /* @__PURE__ */ new Set());
124
+ }
125
+ this.listeners.get(event).add(callback);
126
+ }
127
+ off(event, callback) {
128
+ this.listeners.get(event)?.delete(callback);
129
+ }
130
+ emit(event, data) {
131
+ this.listeners.get(event)?.forEach((cb) => {
132
+ try {
133
+ cb(data);
134
+ } catch (error) {
135
+ console.error(`EventBus error in ${event} handler:`, error);
136
+ }
137
+ });
138
+ }
139
+ clear() {
140
+ this.listeners.clear();
141
+ }
142
+ };
143
+ var eventBus = new EventBus();
144
+
115
145
  // src/client.ts
116
146
  var import_meta = {};
117
147
  var DEFAULT_CONFIG = {
@@ -146,6 +176,7 @@ function getEnv(key) {
146
176
  }
147
177
  var ResonanceClient = class {
148
178
  constructor(config = {}) {
179
+ this.isRefreshing = false;
149
180
  validateConfig(config);
150
181
  const defaultUrl = "http://localhost:8080";
151
182
  const envUrl = getEnv(ENV_API_URL);
@@ -219,11 +250,20 @@ var ResonanceClient = class {
219
250
  const response = await fetch(url, {
220
251
  ...options,
221
252
  headers,
253
+ credentials: "include",
254
+ // ✅ Send httpOnly cookies automatically
222
255
  signal: controller.signal
223
256
  });
224
257
  clearTimeout(timeoutId);
225
258
  const duration = Date.now() - startTime;
226
- if (response.status === 401) {
259
+ if (response.status === 401 && !this.isRefreshing) {
260
+ this.isRefreshing = true;
261
+ const refreshed = await this.refresh();
262
+ this.isRefreshing = false;
263
+ if (refreshed) {
264
+ return this.executeRequest(path, options, attemptNumber);
265
+ }
266
+ eventBus.emit("api:unauthorized", { url, method: options.method || "GET" });
227
267
  this.onTokenExpired?.();
228
268
  }
229
269
  if (!response.ok) {
@@ -295,7 +335,7 @@ var ResonanceClient = class {
295
335
  return this.request(`/eth/v1/auth/nonce?address=${address}`);
296
336
  }
297
337
  async verify(params) {
298
- const result = await this.request("/eth/v1/auth/verify", {
338
+ return this.request("/eth/v1/auth/verify", {
299
339
  method: "POST",
300
340
  body: JSON.stringify({
301
341
  address: params.address,
@@ -303,8 +343,55 @@ var ResonanceClient = class {
303
343
  nonce: params.nonce
304
344
  })
305
345
  });
306
- this.token = result.token;
307
- return result;
346
+ }
347
+ /**
348
+ * Refresh the access token using the refresh token cookie.
349
+ * Returns true if refresh was successful.
350
+ */
351
+ async refresh() {
352
+ try {
353
+ const response = await fetch(`${this.baseUrl}/eth/v1/auth/refresh`, {
354
+ method: "POST",
355
+ credentials: "include",
356
+ // Send refresh_token cookie
357
+ headers: { "Content-Type": "application/json" }
358
+ });
359
+ if (response.ok) {
360
+ const data = await response.json();
361
+ return data.success === true;
362
+ }
363
+ return false;
364
+ } catch {
365
+ return false;
366
+ }
367
+ }
368
+ /**
369
+ * Logout and revoke current session.
370
+ */
371
+ async logout() {
372
+ try {
373
+ await fetch(`${this.baseUrl}/eth/v1/auth/logout`, {
374
+ method: "POST",
375
+ credentials: "include",
376
+ headers: { "Content-Type": "application/json" }
377
+ });
378
+ } catch {
379
+ }
380
+ this.token = void 0;
381
+ }
382
+ /**
383
+ * Logout from all devices.
384
+ */
385
+ async logoutAll() {
386
+ try {
387
+ await fetch(`${this.baseUrl}/eth/v1/auth/logout-all`, {
388
+ method: "POST",
389
+ credentials: "include",
390
+ headers: { "Content-Type": "application/json" }
391
+ });
392
+ } catch {
393
+ }
394
+ this.token = void 0;
308
395
  }
309
396
  // ============================================
310
397
  // Validators
@@ -430,6 +517,7 @@ function createResonanceClient(config) {
430
517
  ResonanceClient,
431
518
  ResonanceError,
432
519
  createResonanceClient,
520
+ eventBus,
433
521
  hasErrorCode,
434
522
  isAuthError,
435
523
  isNetworkError,
package/dist/index.mjs CHANGED
@@ -78,6 +78,35 @@ function isNotFoundError(error) {
78
78
  return false;
79
79
  }
80
80
 
81
+ // src/eventBus.ts
82
+ var EventBus = class {
83
+ constructor() {
84
+ this.listeners = /* @__PURE__ */ new Map();
85
+ }
86
+ on(event, callback) {
87
+ if (!this.listeners.has(event)) {
88
+ this.listeners.set(event, /* @__PURE__ */ new Set());
89
+ }
90
+ this.listeners.get(event).add(callback);
91
+ }
92
+ off(event, callback) {
93
+ this.listeners.get(event)?.delete(callback);
94
+ }
95
+ emit(event, data) {
96
+ this.listeners.get(event)?.forEach((cb) => {
97
+ try {
98
+ cb(data);
99
+ } catch (error) {
100
+ console.error(`EventBus error in ${event} handler:`, error);
101
+ }
102
+ });
103
+ }
104
+ clear() {
105
+ this.listeners.clear();
106
+ }
107
+ };
108
+ var eventBus = new EventBus();
109
+
81
110
  // src/client.ts
82
111
  var DEFAULT_CONFIG = {
83
112
  timeout: 3e4,
@@ -111,6 +140,7 @@ function getEnv(key) {
111
140
  }
112
141
  var ResonanceClient = class {
113
142
  constructor(config = {}) {
143
+ this.isRefreshing = false;
114
144
  validateConfig(config);
115
145
  const defaultUrl = "http://localhost:8080";
116
146
  const envUrl = getEnv(ENV_API_URL);
@@ -184,11 +214,20 @@ var ResonanceClient = class {
184
214
  const response = await fetch(url, {
185
215
  ...options,
186
216
  headers,
217
+ credentials: "include",
218
+ // ✅ Send httpOnly cookies automatically
187
219
  signal: controller.signal
188
220
  });
189
221
  clearTimeout(timeoutId);
190
222
  const duration = Date.now() - startTime;
191
- if (response.status === 401) {
223
+ if (response.status === 401 && !this.isRefreshing) {
224
+ this.isRefreshing = true;
225
+ const refreshed = await this.refresh();
226
+ this.isRefreshing = false;
227
+ if (refreshed) {
228
+ return this.executeRequest(path, options, attemptNumber);
229
+ }
230
+ eventBus.emit("api:unauthorized", { url, method: options.method || "GET" });
192
231
  this.onTokenExpired?.();
193
232
  }
194
233
  if (!response.ok) {
@@ -260,7 +299,7 @@ var ResonanceClient = class {
260
299
  return this.request(`/eth/v1/auth/nonce?address=${address}`);
261
300
  }
262
301
  async verify(params) {
263
- const result = await this.request("/eth/v1/auth/verify", {
302
+ return this.request("/eth/v1/auth/verify", {
264
303
  method: "POST",
265
304
  body: JSON.stringify({
266
305
  address: params.address,
@@ -268,8 +307,55 @@ var ResonanceClient = class {
268
307
  nonce: params.nonce
269
308
  })
270
309
  });
271
- this.token = result.token;
272
- return result;
310
+ }
311
+ /**
312
+ * Refresh the access token using the refresh token cookie.
313
+ * Returns true if refresh was successful.
314
+ */
315
+ async refresh() {
316
+ try {
317
+ const response = await fetch(`${this.baseUrl}/eth/v1/auth/refresh`, {
318
+ method: "POST",
319
+ credentials: "include",
320
+ // Send refresh_token cookie
321
+ headers: { "Content-Type": "application/json" }
322
+ });
323
+ if (response.ok) {
324
+ const data = await response.json();
325
+ return data.success === true;
326
+ }
327
+ return false;
328
+ } catch {
329
+ return false;
330
+ }
331
+ }
332
+ /**
333
+ * Logout and revoke current session.
334
+ */
335
+ async logout() {
336
+ try {
337
+ await fetch(`${this.baseUrl}/eth/v1/auth/logout`, {
338
+ method: "POST",
339
+ credentials: "include",
340
+ headers: { "Content-Type": "application/json" }
341
+ });
342
+ } catch {
343
+ }
344
+ this.token = void 0;
345
+ }
346
+ /**
347
+ * Logout from all devices.
348
+ */
349
+ async logoutAll() {
350
+ try {
351
+ await fetch(`${this.baseUrl}/eth/v1/auth/logout-all`, {
352
+ method: "POST",
353
+ credentials: "include",
354
+ headers: { "Content-Type": "application/json" }
355
+ });
356
+ } catch {
357
+ }
358
+ this.token = void 0;
273
359
  }
274
360
  // ============================================
275
361
  // Validators
@@ -394,6 +480,7 @@ export {
394
480
  ResonanceClient,
395
481
  ResonanceError,
396
482
  createResonanceClient,
483
+ eventBus,
397
484
  hasErrorCode,
398
485
  isAuthError,
399
486
  isNetworkError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenelabs/fene-sdk",
3
- "version": "0.3.7",
3
+ "version": "0.4.0",
4
4
  "description": "TypeScript SDK for Fene API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",