@mitumba/sdk 1.11.0 → 1.12.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 +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +159 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +157 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,82 @@
|
|
|
1
|
+
// src/token-store.ts
|
|
2
|
+
var MemoryTokenStore = class {
|
|
3
|
+
accessToken = null;
|
|
4
|
+
refreshToken = null;
|
|
5
|
+
async getAccessToken() {
|
|
6
|
+
return this.accessToken;
|
|
7
|
+
}
|
|
8
|
+
async getRefreshToken() {
|
|
9
|
+
return this.refreshToken;
|
|
10
|
+
}
|
|
11
|
+
async setTokens(access, refresh) {
|
|
12
|
+
this.accessToken = access;
|
|
13
|
+
this.refreshToken = refresh;
|
|
14
|
+
}
|
|
15
|
+
async clear() {
|
|
16
|
+
this.accessToken = null;
|
|
17
|
+
this.refreshToken = null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var DB_NAME = "mitumba_sdk";
|
|
21
|
+
var STORE_NAME = "tokens";
|
|
22
|
+
var KEY = "auth";
|
|
23
|
+
var IDBTokenStore = class {
|
|
24
|
+
dbPromise = null;
|
|
25
|
+
openDB() {
|
|
26
|
+
if (this.dbPromise) return this.dbPromise;
|
|
27
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
28
|
+
const request = indexedDB.open(DB_NAME, 1);
|
|
29
|
+
request.onupgradeneeded = () => {
|
|
30
|
+
request.result.createObjectStore(STORE_NAME);
|
|
31
|
+
};
|
|
32
|
+
request.onsuccess = () => resolve(request.result);
|
|
33
|
+
request.onerror = () => reject(request.error);
|
|
34
|
+
});
|
|
35
|
+
return this.dbPromise;
|
|
36
|
+
}
|
|
37
|
+
async get() {
|
|
38
|
+
const db = await this.openDB();
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const tx = db.transaction(STORE_NAME, "readonly");
|
|
41
|
+
const req = tx.objectStore(STORE_NAME).get(KEY);
|
|
42
|
+
req.onsuccess = () => resolve(req.result || null);
|
|
43
|
+
req.onerror = () => reject(req.error);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async getAccessToken() {
|
|
47
|
+
const data = await this.get();
|
|
48
|
+
return data?.access ?? null;
|
|
49
|
+
}
|
|
50
|
+
async getRefreshToken() {
|
|
51
|
+
const data = await this.get();
|
|
52
|
+
return data?.refresh ?? null;
|
|
53
|
+
}
|
|
54
|
+
async setTokens(access, refresh) {
|
|
55
|
+
const db = await this.openDB();
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
58
|
+
const req = tx.objectStore(STORE_NAME).put({ access, refresh }, KEY);
|
|
59
|
+
req.onsuccess = () => resolve();
|
|
60
|
+
req.onerror = () => reject(req.error);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async clear() {
|
|
64
|
+
const db = await this.openDB();
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
67
|
+
const req = tx.objectStore(STORE_NAME).delete(KEY);
|
|
68
|
+
req.onsuccess = () => resolve();
|
|
69
|
+
req.onerror = () => reject(req.error);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
function createTokenStore() {
|
|
74
|
+
if (typeof indexedDB !== "undefined") {
|
|
75
|
+
return new IDBTokenStore();
|
|
76
|
+
}
|
|
77
|
+
return new MemoryTokenStore();
|
|
78
|
+
}
|
|
79
|
+
|
|
1
80
|
// src/client.ts
|
|
2
81
|
var APIError = class extends Error {
|
|
3
82
|
code;
|
|
@@ -11,18 +90,33 @@ var APIError = class extends Error {
|
|
|
11
90
|
this.details = data.details;
|
|
12
91
|
}
|
|
13
92
|
};
|
|
93
|
+
function isExpiringSoon(token, thresholdSeconds) {
|
|
94
|
+
try {
|
|
95
|
+
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
96
|
+
const exp = payload.exp;
|
|
97
|
+
return exp - Date.now() / 1e3 < thresholdSeconds;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
14
102
|
var APIClient = class {
|
|
15
103
|
config;
|
|
104
|
+
tokenStore;
|
|
16
105
|
isRefreshing = false;
|
|
17
106
|
refreshPromise = null;
|
|
18
107
|
constructor(config) {
|
|
19
108
|
this.config = config;
|
|
109
|
+
this.tokenStore = config.tokenStore ?? createTokenStore();
|
|
110
|
+
if (config.token) {
|
|
111
|
+
this.tokenStore.setTokens(config.token, config.refreshToken ?? "");
|
|
112
|
+
}
|
|
20
113
|
}
|
|
21
|
-
setToken(token, refreshToken) {
|
|
114
|
+
async setToken(token, refreshToken) {
|
|
22
115
|
this.config.token = token;
|
|
23
116
|
if (refreshToken) {
|
|
24
117
|
this.config.refreshToken = refreshToken;
|
|
25
118
|
}
|
|
119
|
+
await this.tokenStore.setTokens(token, refreshToken ?? this.config.refreshToken ?? "");
|
|
26
120
|
}
|
|
27
121
|
getToken() {
|
|
28
122
|
return this.config.token;
|
|
@@ -30,11 +124,44 @@ var APIClient = class {
|
|
|
30
124
|
getBaseUrl() {
|
|
31
125
|
return this.config.baseUrl;
|
|
32
126
|
}
|
|
33
|
-
clearToken() {
|
|
127
|
+
async clearToken() {
|
|
34
128
|
this.config.token = void 0;
|
|
35
129
|
this.config.refreshToken = void 0;
|
|
130
|
+
await this.tokenStore.clear();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if user has a stored session (access or refresh token available).
|
|
134
|
+
*/
|
|
135
|
+
async isAuthenticated() {
|
|
136
|
+
const access = this.config.token ?? await this.tokenStore.getAccessToken();
|
|
137
|
+
if (access) return true;
|
|
138
|
+
const refresh = await this.tokenStore.getRefreshToken();
|
|
139
|
+
return !!refresh;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Hydrate tokens from the store into memory (call on app boot).
|
|
143
|
+
*/
|
|
144
|
+
async hydrate() {
|
|
145
|
+
const access = await this.tokenStore.getAccessToken();
|
|
146
|
+
const refresh = await this.tokenStore.getRefreshToken();
|
|
147
|
+
if (access) this.config.token = access;
|
|
148
|
+
if (refresh) this.config.refreshToken = refresh;
|
|
149
|
+
}
|
|
150
|
+
async ensureFreshToken() {
|
|
151
|
+
if (!this.config.token) {
|
|
152
|
+
const stored = await this.tokenStore.getAccessToken();
|
|
153
|
+
if (stored) this.config.token = stored;
|
|
154
|
+
const refresh = await this.tokenStore.getRefreshToken();
|
|
155
|
+
if (refresh) this.config.refreshToken = refresh;
|
|
156
|
+
}
|
|
157
|
+
if (this.config.token && isExpiringSoon(this.config.token, 60) && this.config.refreshToken) {
|
|
158
|
+
await this.handleTokenRefresh();
|
|
159
|
+
}
|
|
36
160
|
}
|
|
37
161
|
async request(method, path, body, params, options) {
|
|
162
|
+
if (!path.includes("/auth/refresh") && !path.includes("/auth/login") && !path.includes("/auth/register")) {
|
|
163
|
+
await this.ensureFreshToken();
|
|
164
|
+
}
|
|
38
165
|
const url = new URL(path, this.config.baseUrl);
|
|
39
166
|
if (params) {
|
|
40
167
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -94,9 +221,16 @@ var APIClient = class {
|
|
|
94
221
|
break;
|
|
95
222
|
}
|
|
96
223
|
if (response.status === 401 && this.config.refreshToken && !path.includes("/auth/refresh")) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
224
|
+
try {
|
|
225
|
+
await this.handleTokenRefresh();
|
|
226
|
+
headers.set("Authorization", `Bearer ${this.config.token}`);
|
|
227
|
+
response = await fetch(url.toString(), { ...init, headers });
|
|
228
|
+
} catch {
|
|
229
|
+
if (this.config.onAuthExpired) {
|
|
230
|
+
this.config.onAuthExpired();
|
|
231
|
+
}
|
|
232
|
+
throw new APIError(401, { error: "session_expired", message: "Session expired. Please log in again." });
|
|
233
|
+
}
|
|
100
234
|
}
|
|
101
235
|
if (!response.ok) {
|
|
102
236
|
let errorData;
|
|
@@ -129,13 +263,15 @@ var APIClient = class {
|
|
|
129
263
|
throw new Error("Refresh failed");
|
|
130
264
|
}
|
|
131
265
|
const data = await response.json();
|
|
132
|
-
this.
|
|
266
|
+
this.config.token = data.access_token;
|
|
267
|
+
this.config.refreshToken = data.refresh_token;
|
|
268
|
+
await this.tokenStore.setTokens(data.access_token, data.refresh_token);
|
|
133
269
|
if (this.config.onTokenRefresh) {
|
|
134
270
|
this.config.onTokenRefresh({ token: data.access_token, refreshToken: data.refresh_token });
|
|
135
271
|
}
|
|
136
272
|
} catch (err) {
|
|
137
|
-
this.clearToken();
|
|
138
|
-
throw
|
|
273
|
+
await this.clearToken();
|
|
274
|
+
throw err;
|
|
139
275
|
} finally {
|
|
140
276
|
this.isRefreshing = false;
|
|
141
277
|
this.refreshPromise = null;
|
|
@@ -1006,8 +1142,20 @@ var MitumbaClient = class {
|
|
|
1006
1142
|
clearToken() {
|
|
1007
1143
|
this.api.clearToken();
|
|
1008
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Hydrate tokens from persistent store (call on app boot).
|
|
1147
|
+
*/
|
|
1148
|
+
hydrate() {
|
|
1149
|
+
return this.api.hydrate();
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Check if user has a stored session.
|
|
1153
|
+
*/
|
|
1154
|
+
isAuthenticated() {
|
|
1155
|
+
return this.api.isAuthenticated();
|
|
1156
|
+
}
|
|
1009
1157
|
};
|
|
1010
1158
|
|
|
1011
|
-
export { APIClient, APIError, AuthModule, BusinessesModule, CONDITIONS, CartModule, LISTING_STATUSES, ListingsModule, MailerModule, MessagesModule, MitumbaClient, NotificationsModule, ORDER_STATUSES, OrdersModule, PAYMENT_STATUSES, PayModule, ReviewsModule, SearchModule, SettingsModule, StoresModule, VaziModule, WishlistsModule };
|
|
1159
|
+
export { APIClient, APIError, AuthModule, BusinessesModule, CONDITIONS, CartModule, IDBTokenStore, LISTING_STATUSES, ListingsModule, MailerModule, MemoryTokenStore, MessagesModule, MitumbaClient, NotificationsModule, ORDER_STATUSES, OrdersModule, PAYMENT_STATUSES, PayModule, ReviewsModule, SearchModule, SettingsModule, StoresModule, VaziModule, WishlistsModule, createTokenStore };
|
|
1012
1160
|
//# sourceMappingURL=index.mjs.map
|
|
1013
1161
|
//# sourceMappingURL=index.mjs.map
|