@mitumba/sdk 1.11.0 → 1.13.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
@@ -1,3 +1,29 @@
1
+ interface TokenStore {
2
+ getAccessToken(): Promise<string | null>;
3
+ getRefreshToken(): Promise<string | null>;
4
+ setTokens(access: string, refresh: string): Promise<void>;
5
+ clear(): Promise<void>;
6
+ }
7
+ declare class MemoryTokenStore implements TokenStore {
8
+ private accessToken;
9
+ private refreshToken;
10
+ getAccessToken(): Promise<string | null>;
11
+ getRefreshToken(): Promise<string | null>;
12
+ setTokens(access: string, refresh: string): Promise<void>;
13
+ clear(): Promise<void>;
14
+ }
15
+ declare class IDBTokenStore implements TokenStore {
16
+ private dbPromise;
17
+ private openDB;
18
+ private get;
19
+ getAccessToken(): Promise<string | null>;
20
+ getRefreshToken(): Promise<string | null>;
21
+ setTokens(access: string, refresh: string): Promise<void>;
22
+ clear(): Promise<void>;
23
+ }
24
+ /** Creates IDBTokenStore if indexedDB is available, otherwise MemoryTokenStore. */
25
+ declare function createTokenStore(): TokenStore;
26
+
1
27
  interface AuthTokens {
2
28
  access_token: string;
3
29
  refresh_token: string;
@@ -628,6 +654,20 @@ interface UpdateBusinessInput {
628
654
  price_range_max?: number;
629
655
  }
630
656
 
657
+ interface BlockedIp {
658
+ ip: string;
659
+ blocked_at: string;
660
+ expires_at: string;
661
+ }
662
+ interface SystemStats {
663
+ total_users: number;
664
+ total_sellers: number;
665
+ total_listings: number;
666
+ total_orders: number;
667
+ total_revenue: number;
668
+ active_today: number;
669
+ }
670
+
631
671
  interface MitumbaClientConfig {
632
672
  baseUrl: string;
633
673
  debug?: boolean;
@@ -649,6 +689,16 @@ interface MitumbaClientConfig {
649
689
  token: string;
650
690
  refreshToken: string;
651
691
  }) => void;
692
+ /**
693
+ * Callback invoked when the refresh token is invalid/expired and the user
694
+ * needs to re-authenticate. Only fires when session is truly dead.
695
+ */
696
+ onAuthExpired?: () => void;
697
+ /**
698
+ * Custom token store for persisting tokens. Defaults to IndexedDB in browsers,
699
+ * in-memory elsewhere. Pass your own implementation for custom persistence.
700
+ */
701
+ tokenStore?: TokenStore;
652
702
  }
653
703
  interface APIErrorResponse {
654
704
  error: string;
@@ -675,13 +725,23 @@ declare class APIError extends Error {
675
725
  }
676
726
  declare class APIClient {
677
727
  private config;
728
+ private tokenStore;
678
729
  private isRefreshing;
679
730
  private refreshPromise;
680
731
  constructor(config: MitumbaClientConfig);
681
- setToken(token: string, refreshToken?: string): void;
732
+ setToken(token: string, refreshToken?: string): Promise<void>;
682
733
  getToken(): string | undefined;
683
734
  getBaseUrl(): string;
684
- clearToken(): void;
735
+ clearToken(): Promise<void>;
736
+ /**
737
+ * Check if user has a stored session (access or refresh token available).
738
+ */
739
+ isAuthenticated(): Promise<boolean>;
740
+ /**
741
+ * Hydrate tokens from the store into memory (call on app boot).
742
+ */
743
+ hydrate(): Promise<void>;
744
+ private ensureFreshToken;
685
745
  private request;
686
746
  private handleTokenRefresh;
687
747
  get<T>(path: string, params?: Record<string, string | number | boolean | undefined>, options?: RequestOptions): Promise<T>;
@@ -1319,6 +1379,44 @@ declare class BusinessesModule {
1319
1379
  }>;
1320
1380
  }
1321
1381
 
1382
+ declare class AdminModule {
1383
+ private readonly client;
1384
+ constructor(client: APIClient);
1385
+ /** Block an IP address. Default duration: 24 hours. */
1386
+ blockIp(ip: string, durationHours?: number, options?: RequestOptions): Promise<{
1387
+ ok: true;
1388
+ blocked: string;
1389
+ hours: number;
1390
+ }>;
1391
+ /** Unblock an IP address. */
1392
+ unblockIp(ip: string, options?: RequestOptions): Promise<{
1393
+ ok: true;
1394
+ unblocked: string;
1395
+ }>;
1396
+ /** List currently blocked IPs. */
1397
+ getBlockedIps(options?: RequestOptions): Promise<{
1398
+ data: BlockedIp[];
1399
+ }>;
1400
+ /** Get platform-wide statistics. */
1401
+ getSystemStats(options?: RequestOptions): Promise<SystemStats>;
1402
+ /** Suspend a user account. */
1403
+ suspendUser(userId: string, reason: string, options?: RequestOptions): Promise<{
1404
+ ok: true;
1405
+ }>;
1406
+ /** Unsuspend a user account. */
1407
+ unsuspendUser(userId: string, options?: RequestOptions): Promise<{
1408
+ ok: true;
1409
+ }>;
1410
+ /** Grant verified badge to a store. */
1411
+ verifyStore(storeId: string, options?: RequestOptions): Promise<{
1412
+ ok: true;
1413
+ }>;
1414
+ /** Revoke verified badge from a store. */
1415
+ unverifyStore(storeId: string, options?: RequestOptions): Promise<{
1416
+ ok: true;
1417
+ }>;
1418
+ }
1419
+
1322
1420
  declare class MitumbaClient {
1323
1421
  readonly api: APIClient;
1324
1422
  readonly auth: AuthModule;
@@ -1336,6 +1434,7 @@ declare class MitumbaClient {
1336
1434
  readonly settings: SettingsModule;
1337
1435
  readonly mailer: MailerModule;
1338
1436
  readonly businesses: BusinessesModule;
1437
+ readonly admin: AdminModule;
1339
1438
  constructor(config: MitumbaClientConfig);
1340
1439
  /**
1341
1440
  * Set the access token for authenticated requests.
@@ -1350,6 +1449,14 @@ declare class MitumbaClient {
1350
1449
  * Clear the current tokens.
1351
1450
  */
1352
1451
  clearToken(): void;
1452
+ /**
1453
+ * Hydrate tokens from persistent store (call on app boot).
1454
+ */
1455
+ hydrate(): Promise<void>;
1456
+ /**
1457
+ * Check if user has a stored session.
1458
+ */
1459
+ isAuthenticated(): Promise<boolean>;
1353
1460
  }
1354
1461
 
1355
- export { APIClient, APIError, type APIErrorResponse, type AddAddressInput, type AddPaymentMethodInput, type Address, AuthModule, type AuthTokens, type BecomeSellerInput, type Business, BusinessesModule, CONDITIONS, type CartItem, CartModule, type Category, type ChangePasswordInput, type City, type CompleteOnboardingInput, type Condition, type Conversation, type CreateListingInput, type CreateOrderInput, type CreateReviewInput, type CreateStoreInput, type DeliveryAddress, type EmailLoginInput, type EmailRegisterInput, type ForgotPasswordInput, LISTING_STATUSES, type LinkedAccount, type LinkedAccountProvider, type Listing, type ListingImage, type ListingStatus, type ListingsFeedParams, ListingsModule, type LoginInput, MailerModule, type MailerTemplate, type Message, type MessageResponse, MessagesModule, MitumbaClient, type MitumbaClientConfig, type MpesaInput, type Notification, type NotificationPref, type NotificationType, NotificationsModule, ORDER_STATUSES, type Order, type OrderEvent, type OrderHistoryParams, type OrderStatus, OrdersModule, PAYMENT_STATUSES, type PaginatedResponse, PayModule, type PaymentMethod, type PaymentMethodType, type PaymentStatus, type PaymentStatusResponse, type PaystackInitResponse, type PaystackInput, type PhoneLoginInput, type PhoneRegisterInput, type PresignImageResponse, type RegisterInput, type RequestOptions, type ResetPasswordInput, type Review, type ReviewableOrder, ReviewsModule, type SaveSearchInput, type SearchHistoryItem, SearchModule, type SearchParams, type SearchResult, type SellerProfile, type SellerStorefront, type SendEmailInput, type SendMessageInput, type SendOtpInput, type Session, SettingsModule, type SimilarListing, type StkPushInput, type StkPushResponse, type Store, type StoreAnalytics, type StoreSettings, type StoreStats, StoresModule, type SubscriptionTier, type TransitionOrderInput, type TrendingTerm, type TwoFactorRequired, type UpdateBusinessInput, type UpdateListingInput, type UpdateProfileInput, type UpdateStoreInput, type UserProfile, type VAZIOutfit, type VAZIOutfitDetail, type VAZIOutfitDetailItem, type VAZIOutfitItem, type VaziFeedParams, type VaziFeedResponse, VaziModule, type Verify2FAInput, type VerifyOtpInput, type WishlistListing, WishlistsModule };
1462
+ export { APIClient, APIError, type APIErrorResponse, type AddAddressInput, type AddPaymentMethodInput, type Address, AdminModule, AuthModule, type AuthTokens, type BecomeSellerInput, type BlockedIp, type Business, BusinessesModule, CONDITIONS, type CartItem, CartModule, type Category, type ChangePasswordInput, type City, type CompleteOnboardingInput, type Condition, type Conversation, type CreateListingInput, type CreateOrderInput, type CreateReviewInput, type CreateStoreInput, type DeliveryAddress, type EmailLoginInput, type EmailRegisterInput, type ForgotPasswordInput, IDBTokenStore, LISTING_STATUSES, type LinkedAccount, type LinkedAccountProvider, type Listing, type ListingImage, type ListingStatus, type ListingsFeedParams, ListingsModule, type LoginInput, MailerModule, type MailerTemplate, MemoryTokenStore, type Message, type MessageResponse, MessagesModule, MitumbaClient, type MitumbaClientConfig, type MpesaInput, type Notification, type NotificationPref, type NotificationType, NotificationsModule, ORDER_STATUSES, type Order, type OrderEvent, type OrderHistoryParams, type OrderStatus, OrdersModule, PAYMENT_STATUSES, type PaginatedResponse, PayModule, type PaymentMethod, type PaymentMethodType, type PaymentStatus, type PaymentStatusResponse, type PaystackInitResponse, type PaystackInput, type PhoneLoginInput, type PhoneRegisterInput, type PresignImageResponse, type RegisterInput, type RequestOptions, type ResetPasswordInput, type Review, type ReviewableOrder, ReviewsModule, type SaveSearchInput, type SearchHistoryItem, SearchModule, type SearchParams, type SearchResult, type SellerProfile, type SellerStorefront, type SendEmailInput, type SendMessageInput, type SendOtpInput, type Session, SettingsModule, type SimilarListing, type StkPushInput, type StkPushResponse, type Store, type StoreAnalytics, type StoreSettings, type StoreStats, StoresModule, type SubscriptionTier, type SystemStats, type TokenStore, type TransitionOrderInput, type TrendingTerm, type TwoFactorRequired, type UpdateBusinessInput, type UpdateListingInput, type UpdateProfileInput, type UpdateStoreInput, type UserProfile, type VAZIOutfit, type VAZIOutfitDetail, type VAZIOutfitDetailItem, type VAZIOutfitItem, type VaziFeedParams, type VaziFeedResponse, VaziModule, type Verify2FAInput, type VerifyOtpInput, type WishlistListing, WishlistsModule, createTokenStore };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,29 @@
1
+ interface TokenStore {
2
+ getAccessToken(): Promise<string | null>;
3
+ getRefreshToken(): Promise<string | null>;
4
+ setTokens(access: string, refresh: string): Promise<void>;
5
+ clear(): Promise<void>;
6
+ }
7
+ declare class MemoryTokenStore implements TokenStore {
8
+ private accessToken;
9
+ private refreshToken;
10
+ getAccessToken(): Promise<string | null>;
11
+ getRefreshToken(): Promise<string | null>;
12
+ setTokens(access: string, refresh: string): Promise<void>;
13
+ clear(): Promise<void>;
14
+ }
15
+ declare class IDBTokenStore implements TokenStore {
16
+ private dbPromise;
17
+ private openDB;
18
+ private get;
19
+ getAccessToken(): Promise<string | null>;
20
+ getRefreshToken(): Promise<string | null>;
21
+ setTokens(access: string, refresh: string): Promise<void>;
22
+ clear(): Promise<void>;
23
+ }
24
+ /** Creates IDBTokenStore if indexedDB is available, otherwise MemoryTokenStore. */
25
+ declare function createTokenStore(): TokenStore;
26
+
1
27
  interface AuthTokens {
2
28
  access_token: string;
3
29
  refresh_token: string;
@@ -628,6 +654,20 @@ interface UpdateBusinessInput {
628
654
  price_range_max?: number;
629
655
  }
630
656
 
657
+ interface BlockedIp {
658
+ ip: string;
659
+ blocked_at: string;
660
+ expires_at: string;
661
+ }
662
+ interface SystemStats {
663
+ total_users: number;
664
+ total_sellers: number;
665
+ total_listings: number;
666
+ total_orders: number;
667
+ total_revenue: number;
668
+ active_today: number;
669
+ }
670
+
631
671
  interface MitumbaClientConfig {
632
672
  baseUrl: string;
633
673
  debug?: boolean;
@@ -649,6 +689,16 @@ interface MitumbaClientConfig {
649
689
  token: string;
650
690
  refreshToken: string;
651
691
  }) => void;
692
+ /**
693
+ * Callback invoked when the refresh token is invalid/expired and the user
694
+ * needs to re-authenticate. Only fires when session is truly dead.
695
+ */
696
+ onAuthExpired?: () => void;
697
+ /**
698
+ * Custom token store for persisting tokens. Defaults to IndexedDB in browsers,
699
+ * in-memory elsewhere. Pass your own implementation for custom persistence.
700
+ */
701
+ tokenStore?: TokenStore;
652
702
  }
653
703
  interface APIErrorResponse {
654
704
  error: string;
@@ -675,13 +725,23 @@ declare class APIError extends Error {
675
725
  }
676
726
  declare class APIClient {
677
727
  private config;
728
+ private tokenStore;
678
729
  private isRefreshing;
679
730
  private refreshPromise;
680
731
  constructor(config: MitumbaClientConfig);
681
- setToken(token: string, refreshToken?: string): void;
732
+ setToken(token: string, refreshToken?: string): Promise<void>;
682
733
  getToken(): string | undefined;
683
734
  getBaseUrl(): string;
684
- clearToken(): void;
735
+ clearToken(): Promise<void>;
736
+ /**
737
+ * Check if user has a stored session (access or refresh token available).
738
+ */
739
+ isAuthenticated(): Promise<boolean>;
740
+ /**
741
+ * Hydrate tokens from the store into memory (call on app boot).
742
+ */
743
+ hydrate(): Promise<void>;
744
+ private ensureFreshToken;
685
745
  private request;
686
746
  private handleTokenRefresh;
687
747
  get<T>(path: string, params?: Record<string, string | number | boolean | undefined>, options?: RequestOptions): Promise<T>;
@@ -1319,6 +1379,44 @@ declare class BusinessesModule {
1319
1379
  }>;
1320
1380
  }
1321
1381
 
1382
+ declare class AdminModule {
1383
+ private readonly client;
1384
+ constructor(client: APIClient);
1385
+ /** Block an IP address. Default duration: 24 hours. */
1386
+ blockIp(ip: string, durationHours?: number, options?: RequestOptions): Promise<{
1387
+ ok: true;
1388
+ blocked: string;
1389
+ hours: number;
1390
+ }>;
1391
+ /** Unblock an IP address. */
1392
+ unblockIp(ip: string, options?: RequestOptions): Promise<{
1393
+ ok: true;
1394
+ unblocked: string;
1395
+ }>;
1396
+ /** List currently blocked IPs. */
1397
+ getBlockedIps(options?: RequestOptions): Promise<{
1398
+ data: BlockedIp[];
1399
+ }>;
1400
+ /** Get platform-wide statistics. */
1401
+ getSystemStats(options?: RequestOptions): Promise<SystemStats>;
1402
+ /** Suspend a user account. */
1403
+ suspendUser(userId: string, reason: string, options?: RequestOptions): Promise<{
1404
+ ok: true;
1405
+ }>;
1406
+ /** Unsuspend a user account. */
1407
+ unsuspendUser(userId: string, options?: RequestOptions): Promise<{
1408
+ ok: true;
1409
+ }>;
1410
+ /** Grant verified badge to a store. */
1411
+ verifyStore(storeId: string, options?: RequestOptions): Promise<{
1412
+ ok: true;
1413
+ }>;
1414
+ /** Revoke verified badge from a store. */
1415
+ unverifyStore(storeId: string, options?: RequestOptions): Promise<{
1416
+ ok: true;
1417
+ }>;
1418
+ }
1419
+
1322
1420
  declare class MitumbaClient {
1323
1421
  readonly api: APIClient;
1324
1422
  readonly auth: AuthModule;
@@ -1336,6 +1434,7 @@ declare class MitumbaClient {
1336
1434
  readonly settings: SettingsModule;
1337
1435
  readonly mailer: MailerModule;
1338
1436
  readonly businesses: BusinessesModule;
1437
+ readonly admin: AdminModule;
1339
1438
  constructor(config: MitumbaClientConfig);
1340
1439
  /**
1341
1440
  * Set the access token for authenticated requests.
@@ -1350,6 +1449,14 @@ declare class MitumbaClient {
1350
1449
  * Clear the current tokens.
1351
1450
  */
1352
1451
  clearToken(): void;
1452
+ /**
1453
+ * Hydrate tokens from persistent store (call on app boot).
1454
+ */
1455
+ hydrate(): Promise<void>;
1456
+ /**
1457
+ * Check if user has a stored session.
1458
+ */
1459
+ isAuthenticated(): Promise<boolean>;
1353
1460
  }
1354
1461
 
1355
- export { APIClient, APIError, type APIErrorResponse, type AddAddressInput, type AddPaymentMethodInput, type Address, AuthModule, type AuthTokens, type BecomeSellerInput, type Business, BusinessesModule, CONDITIONS, type CartItem, CartModule, type Category, type ChangePasswordInput, type City, type CompleteOnboardingInput, type Condition, type Conversation, type CreateListingInput, type CreateOrderInput, type CreateReviewInput, type CreateStoreInput, type DeliveryAddress, type EmailLoginInput, type EmailRegisterInput, type ForgotPasswordInput, LISTING_STATUSES, type LinkedAccount, type LinkedAccountProvider, type Listing, type ListingImage, type ListingStatus, type ListingsFeedParams, ListingsModule, type LoginInput, MailerModule, type MailerTemplate, type Message, type MessageResponse, MessagesModule, MitumbaClient, type MitumbaClientConfig, type MpesaInput, type Notification, type NotificationPref, type NotificationType, NotificationsModule, ORDER_STATUSES, type Order, type OrderEvent, type OrderHistoryParams, type OrderStatus, OrdersModule, PAYMENT_STATUSES, type PaginatedResponse, PayModule, type PaymentMethod, type PaymentMethodType, type PaymentStatus, type PaymentStatusResponse, type PaystackInitResponse, type PaystackInput, type PhoneLoginInput, type PhoneRegisterInput, type PresignImageResponse, type RegisterInput, type RequestOptions, type ResetPasswordInput, type Review, type ReviewableOrder, ReviewsModule, type SaveSearchInput, type SearchHistoryItem, SearchModule, type SearchParams, type SearchResult, type SellerProfile, type SellerStorefront, type SendEmailInput, type SendMessageInput, type SendOtpInput, type Session, SettingsModule, type SimilarListing, type StkPushInput, type StkPushResponse, type Store, type StoreAnalytics, type StoreSettings, type StoreStats, StoresModule, type SubscriptionTier, type TransitionOrderInput, type TrendingTerm, type TwoFactorRequired, type UpdateBusinessInput, type UpdateListingInput, type UpdateProfileInput, type UpdateStoreInput, type UserProfile, type VAZIOutfit, type VAZIOutfitDetail, type VAZIOutfitDetailItem, type VAZIOutfitItem, type VaziFeedParams, type VaziFeedResponse, VaziModule, type Verify2FAInput, type VerifyOtpInput, type WishlistListing, WishlistsModule };
1462
+ export { APIClient, APIError, type APIErrorResponse, type AddAddressInput, type AddPaymentMethodInput, type Address, AdminModule, AuthModule, type AuthTokens, type BecomeSellerInput, type BlockedIp, type Business, BusinessesModule, CONDITIONS, type CartItem, CartModule, type Category, type ChangePasswordInput, type City, type CompleteOnboardingInput, type Condition, type Conversation, type CreateListingInput, type CreateOrderInput, type CreateReviewInput, type CreateStoreInput, type DeliveryAddress, type EmailLoginInput, type EmailRegisterInput, type ForgotPasswordInput, IDBTokenStore, LISTING_STATUSES, type LinkedAccount, type LinkedAccountProvider, type Listing, type ListingImage, type ListingStatus, type ListingsFeedParams, ListingsModule, type LoginInput, MailerModule, type MailerTemplate, MemoryTokenStore, type Message, type MessageResponse, MessagesModule, MitumbaClient, type MitumbaClientConfig, type MpesaInput, type Notification, type NotificationPref, type NotificationType, NotificationsModule, ORDER_STATUSES, type Order, type OrderEvent, type OrderHistoryParams, type OrderStatus, OrdersModule, PAYMENT_STATUSES, type PaginatedResponse, PayModule, type PaymentMethod, type PaymentMethodType, type PaymentStatus, type PaymentStatusResponse, type PaystackInitResponse, type PaystackInput, type PhoneLoginInput, type PhoneRegisterInput, type PresignImageResponse, type RegisterInput, type RequestOptions, type ResetPasswordInput, type Review, type ReviewableOrder, ReviewsModule, type SaveSearchInput, type SearchHistoryItem, SearchModule, type SearchParams, type SearchResult, type SellerProfile, type SellerStorefront, type SendEmailInput, type SendMessageInput, type SendOtpInput, type Session, SettingsModule, type SimilarListing, type StkPushInput, type StkPushResponse, type Store, type StoreAnalytics, type StoreSettings, type StoreStats, StoresModule, type SubscriptionTier, type SystemStats, type TokenStore, type TransitionOrderInput, type TrendingTerm, type TwoFactorRequired, type UpdateBusinessInput, type UpdateListingInput, type UpdateProfileInput, type UpdateStoreInput, type UserProfile, type VAZIOutfit, type VAZIOutfitDetail, type VAZIOutfitDetailItem, type VAZIOutfitItem, type VaziFeedParams, type VaziFeedResponse, VaziModule, type Verify2FAInput, type VerifyOtpInput, type WishlistListing, WishlistsModule, createTokenStore };
package/dist/index.js CHANGED
@@ -1,5 +1,84 @@
1
1
  'use strict';
2
2
 
3
+ // src/token-store.ts
4
+ var MemoryTokenStore = class {
5
+ accessToken = null;
6
+ refreshToken = null;
7
+ async getAccessToken() {
8
+ return this.accessToken;
9
+ }
10
+ async getRefreshToken() {
11
+ return this.refreshToken;
12
+ }
13
+ async setTokens(access, refresh) {
14
+ this.accessToken = access;
15
+ this.refreshToken = refresh;
16
+ }
17
+ async clear() {
18
+ this.accessToken = null;
19
+ this.refreshToken = null;
20
+ }
21
+ };
22
+ var DB_NAME = "mitumba_sdk";
23
+ var STORE_NAME = "tokens";
24
+ var KEY = "auth";
25
+ var IDBTokenStore = class {
26
+ dbPromise = null;
27
+ openDB() {
28
+ if (this.dbPromise) return this.dbPromise;
29
+ this.dbPromise = new Promise((resolve, reject) => {
30
+ const request = indexedDB.open(DB_NAME, 1);
31
+ request.onupgradeneeded = () => {
32
+ request.result.createObjectStore(STORE_NAME);
33
+ };
34
+ request.onsuccess = () => resolve(request.result);
35
+ request.onerror = () => reject(request.error);
36
+ });
37
+ return this.dbPromise;
38
+ }
39
+ async get() {
40
+ const db = await this.openDB();
41
+ return new Promise((resolve, reject) => {
42
+ const tx = db.transaction(STORE_NAME, "readonly");
43
+ const req = tx.objectStore(STORE_NAME).get(KEY);
44
+ req.onsuccess = () => resolve(req.result || null);
45
+ req.onerror = () => reject(req.error);
46
+ });
47
+ }
48
+ async getAccessToken() {
49
+ const data = await this.get();
50
+ return data?.access ?? null;
51
+ }
52
+ async getRefreshToken() {
53
+ const data = await this.get();
54
+ return data?.refresh ?? null;
55
+ }
56
+ async setTokens(access, refresh) {
57
+ const db = await this.openDB();
58
+ return new Promise((resolve, reject) => {
59
+ const tx = db.transaction(STORE_NAME, "readwrite");
60
+ const req = tx.objectStore(STORE_NAME).put({ access, refresh }, KEY);
61
+ req.onsuccess = () => resolve();
62
+ req.onerror = () => reject(req.error);
63
+ });
64
+ }
65
+ async clear() {
66
+ const db = await this.openDB();
67
+ return new Promise((resolve, reject) => {
68
+ const tx = db.transaction(STORE_NAME, "readwrite");
69
+ const req = tx.objectStore(STORE_NAME).delete(KEY);
70
+ req.onsuccess = () => resolve();
71
+ req.onerror = () => reject(req.error);
72
+ });
73
+ }
74
+ };
75
+ function createTokenStore() {
76
+ if (typeof indexedDB !== "undefined") {
77
+ return new IDBTokenStore();
78
+ }
79
+ return new MemoryTokenStore();
80
+ }
81
+
3
82
  // src/client.ts
4
83
  var APIError = class extends Error {
5
84
  code;
@@ -13,18 +92,33 @@ var APIError = class extends Error {
13
92
  this.details = data.details;
14
93
  }
15
94
  };
95
+ function isExpiringSoon(token, thresholdSeconds) {
96
+ try {
97
+ const payload = JSON.parse(atob(token.split(".")[1]));
98
+ const exp = payload.exp;
99
+ return exp - Date.now() / 1e3 < thresholdSeconds;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
16
104
  var APIClient = class {
17
105
  config;
106
+ tokenStore;
18
107
  isRefreshing = false;
19
108
  refreshPromise = null;
20
109
  constructor(config) {
21
110
  this.config = config;
111
+ this.tokenStore = config.tokenStore ?? createTokenStore();
112
+ if (config.token) {
113
+ this.tokenStore.setTokens(config.token, config.refreshToken ?? "");
114
+ }
22
115
  }
23
- setToken(token, refreshToken) {
116
+ async setToken(token, refreshToken) {
24
117
  this.config.token = token;
25
118
  if (refreshToken) {
26
119
  this.config.refreshToken = refreshToken;
27
120
  }
121
+ await this.tokenStore.setTokens(token, refreshToken ?? this.config.refreshToken ?? "");
28
122
  }
29
123
  getToken() {
30
124
  return this.config.token;
@@ -32,11 +126,44 @@ var APIClient = class {
32
126
  getBaseUrl() {
33
127
  return this.config.baseUrl;
34
128
  }
35
- clearToken() {
129
+ async clearToken() {
36
130
  this.config.token = void 0;
37
131
  this.config.refreshToken = void 0;
132
+ await this.tokenStore.clear();
133
+ }
134
+ /**
135
+ * Check if user has a stored session (access or refresh token available).
136
+ */
137
+ async isAuthenticated() {
138
+ const access = this.config.token ?? await this.tokenStore.getAccessToken();
139
+ if (access) return true;
140
+ const refresh = await this.tokenStore.getRefreshToken();
141
+ return !!refresh;
142
+ }
143
+ /**
144
+ * Hydrate tokens from the store into memory (call on app boot).
145
+ */
146
+ async hydrate() {
147
+ const access = await this.tokenStore.getAccessToken();
148
+ const refresh = await this.tokenStore.getRefreshToken();
149
+ if (access) this.config.token = access;
150
+ if (refresh) this.config.refreshToken = refresh;
151
+ }
152
+ async ensureFreshToken() {
153
+ if (!this.config.token) {
154
+ const stored = await this.tokenStore.getAccessToken();
155
+ if (stored) this.config.token = stored;
156
+ const refresh = await this.tokenStore.getRefreshToken();
157
+ if (refresh) this.config.refreshToken = refresh;
158
+ }
159
+ if (this.config.token && isExpiringSoon(this.config.token, 60) && this.config.refreshToken) {
160
+ await this.handleTokenRefresh();
161
+ }
38
162
  }
39
163
  async request(method, path, body, params, options) {
164
+ if (!path.includes("/auth/refresh") && !path.includes("/auth/login") && !path.includes("/auth/register")) {
165
+ await this.ensureFreshToken();
166
+ }
40
167
  const url = new URL(path, this.config.baseUrl);
41
168
  if (params) {
42
169
  for (const [key, value] of Object.entries(params)) {
@@ -96,9 +223,16 @@ var APIClient = class {
96
223
  break;
97
224
  }
98
225
  if (response.status === 401 && this.config.refreshToken && !path.includes("/auth/refresh")) {
99
- await this.handleTokenRefresh();
100
- headers.set("Authorization", `Bearer ${this.config.token}`);
101
- response = await fetch(url.toString(), { ...init, headers });
226
+ try {
227
+ await this.handleTokenRefresh();
228
+ headers.set("Authorization", `Bearer ${this.config.token}`);
229
+ response = await fetch(url.toString(), { ...init, headers });
230
+ } catch {
231
+ if (this.config.onAuthExpired) {
232
+ this.config.onAuthExpired();
233
+ }
234
+ throw new APIError(401, { error: "session_expired", message: "Session expired. Please log in again." });
235
+ }
102
236
  }
103
237
  if (!response.ok) {
104
238
  let errorData;
@@ -131,13 +265,15 @@ var APIClient = class {
131
265
  throw new Error("Refresh failed");
132
266
  }
133
267
  const data = await response.json();
134
- this.setToken(data.access_token, data.refresh_token);
268
+ this.config.token = data.access_token;
269
+ this.config.refreshToken = data.refresh_token;
270
+ await this.tokenStore.setTokens(data.access_token, data.refresh_token);
135
271
  if (this.config.onTokenRefresh) {
136
272
  this.config.onTokenRefresh({ token: data.access_token, refreshToken: data.refresh_token });
137
273
  }
138
274
  } catch (err) {
139
- this.clearToken();
140
- throw new APIError(401, { error: "token_expired", message: "Session expired. Please log in again." });
275
+ await this.clearToken();
276
+ throw err;
141
277
  } finally {
142
278
  this.isRefreshing = false;
143
279
  this.refreshPromise = null;
@@ -933,6 +1069,46 @@ var BusinessesModule = class {
933
1069
  }
934
1070
  };
935
1071
 
1072
+ // src/modules/admin.ts
1073
+ var AdminModule = class {
1074
+ constructor(client) {
1075
+ this.client = client;
1076
+ }
1077
+ client;
1078
+ /** Block an IP address. Default duration: 24 hours. */
1079
+ async blockIp(ip, durationHours, options) {
1080
+ return this.client.post("/admin/block-ip", { ip, duration_hours: durationHours ?? 24 }, options);
1081
+ }
1082
+ /** Unblock an IP address. */
1083
+ async unblockIp(ip, options) {
1084
+ return this.client.post("/admin/unblock-ip", { ip }, options);
1085
+ }
1086
+ /** List currently blocked IPs. */
1087
+ async getBlockedIps(options) {
1088
+ return this.client.get("/admin/blocked-ips", void 0, options);
1089
+ }
1090
+ /** Get platform-wide statistics. */
1091
+ async getSystemStats(options) {
1092
+ return this.client.get("/admin/stats", void 0, options);
1093
+ }
1094
+ /** Suspend a user account. */
1095
+ async suspendUser(userId, reason, options) {
1096
+ return this.client.post(`/admin/users/${userId}/suspend`, { reason }, options);
1097
+ }
1098
+ /** Unsuspend a user account. */
1099
+ async unsuspendUser(userId, options) {
1100
+ return this.client.post(`/admin/users/${userId}/unsuspend`, void 0, options);
1101
+ }
1102
+ /** Grant verified badge to a store. */
1103
+ async verifyStore(storeId, options) {
1104
+ return this.client.post(`/admin/stores/${storeId}/verify`, void 0, options);
1105
+ }
1106
+ /** Revoke verified badge from a store. */
1107
+ async unverifyStore(storeId, options) {
1108
+ return this.client.post(`/admin/stores/${storeId}/unverify`, void 0, options);
1109
+ }
1110
+ };
1111
+
936
1112
  // src/types/listings.ts
937
1113
  var CONDITIONS = ["new", "like_new", "good", "fair"];
938
1114
  var LISTING_STATUSES = ["draft", "active", "sold", "removed"];
@@ -971,6 +1147,7 @@ var MitumbaClient = class {
971
1147
  settings;
972
1148
  mailer;
973
1149
  businesses;
1150
+ admin;
974
1151
  constructor(config) {
975
1152
  this.api = new APIClient(config);
976
1153
  this.auth = new AuthModule(this.api);
@@ -988,6 +1165,7 @@ var MitumbaClient = class {
988
1165
  this.settings = new SettingsModule(this.api);
989
1166
  this.mailer = new MailerModule(this.api);
990
1167
  this.businesses = new BusinessesModule(this.api);
1168
+ this.admin = new AdminModule(this.api);
991
1169
  }
992
1170
  /**
993
1171
  * Set the access token for authenticated requests.
@@ -1008,17 +1186,32 @@ var MitumbaClient = class {
1008
1186
  clearToken() {
1009
1187
  this.api.clearToken();
1010
1188
  }
1189
+ /**
1190
+ * Hydrate tokens from persistent store (call on app boot).
1191
+ */
1192
+ hydrate() {
1193
+ return this.api.hydrate();
1194
+ }
1195
+ /**
1196
+ * Check if user has a stored session.
1197
+ */
1198
+ isAuthenticated() {
1199
+ return this.api.isAuthenticated();
1200
+ }
1011
1201
  };
1012
1202
 
1013
1203
  exports.APIClient = APIClient;
1014
1204
  exports.APIError = APIError;
1205
+ exports.AdminModule = AdminModule;
1015
1206
  exports.AuthModule = AuthModule;
1016
1207
  exports.BusinessesModule = BusinessesModule;
1017
1208
  exports.CONDITIONS = CONDITIONS;
1018
1209
  exports.CartModule = CartModule;
1210
+ exports.IDBTokenStore = IDBTokenStore;
1019
1211
  exports.LISTING_STATUSES = LISTING_STATUSES;
1020
1212
  exports.ListingsModule = ListingsModule;
1021
1213
  exports.MailerModule = MailerModule;
1214
+ exports.MemoryTokenStore = MemoryTokenStore;
1022
1215
  exports.MessagesModule = MessagesModule;
1023
1216
  exports.MitumbaClient = MitumbaClient;
1024
1217
  exports.NotificationsModule = NotificationsModule;
@@ -1032,5 +1225,6 @@ exports.SettingsModule = SettingsModule;
1032
1225
  exports.StoresModule = StoresModule;
1033
1226
  exports.VaziModule = VaziModule;
1034
1227
  exports.WishlistsModule = WishlistsModule;
1228
+ exports.createTokenStore = createTokenStore;
1035
1229
  //# sourceMappingURL=index.js.map
1036
1230
  //# sourceMappingURL=index.js.map