@wheeparam/library 0.0.4 → 0.0.6
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/api/index.d.cts +13 -1
- package/dist/api/index.d.ts +13 -1
- package/dist/client/index.cjs +239 -102
- package/dist/client/index.d.cts +95 -82
- package/dist/client/index.d.ts +95 -82
- package/dist/client/index.js +242 -103
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -1,166 +1,305 @@
|
|
|
1
|
-
// src/client/
|
|
2
|
-
import axios
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// src/client/createAxiosClient.ts
|
|
2
|
+
import axios, {
|
|
3
|
+
AxiosHeaders
|
|
4
|
+
} from "axios";
|
|
5
|
+
|
|
6
|
+
// src/client/error.ts
|
|
7
|
+
var defaultShouldRefresh = (error) => {
|
|
8
|
+
return error.response?.status === 401;
|
|
9
|
+
};
|
|
10
|
+
function defaultParseRefreshResponse(data) {
|
|
11
|
+
const source = typeof data === "object" && data !== null ? data : {};
|
|
12
|
+
const accessToken = typeof source.result?.accessToken === "string" && source.result.accessToken || typeof source.accessToken === "string" && source.accessToken || typeof source.result?.token === "string" && source.result.token || typeof source.token === "string" && source.token || "";
|
|
13
|
+
const refreshToken = typeof source.result?.refreshToken === "string" && source.result.refreshToken || typeof source.refreshToken === "string" && source.refreshToken || void 0;
|
|
14
|
+
if (!accessToken) {
|
|
15
|
+
throw new Error("Refresh response does not contain accessToken");
|
|
16
|
+
}
|
|
6
17
|
return {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
accessToken,
|
|
19
|
+
refreshToken
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function getDefaultErrorMessage(error) {
|
|
23
|
+
const axiosError = error;
|
|
24
|
+
const status = axiosError.response?.status;
|
|
25
|
+
const serverMessage = axiosError.response?.data?.error;
|
|
26
|
+
if (serverMessage && serverMessage.trim().length > 0) {
|
|
27
|
+
return serverMessage;
|
|
28
|
+
}
|
|
29
|
+
switch (status) {
|
|
30
|
+
case 0:
|
|
31
|
+
return "REST API \uC11C\uBC84\uC5D0 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uC11C\uBC84 \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.";
|
|
32
|
+
case 400:
|
|
33
|
+
return "\uC798\uBABB\uB41C \uC694\uCCAD\uC785\uB2C8\uB2E4.";
|
|
34
|
+
case 401:
|
|
35
|
+
return "\uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
36
|
+
case 403:
|
|
37
|
+
return "\uC811\uADFC \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
38
|
+
case 404:
|
|
39
|
+
return "[404] REST API \uC694\uCCAD\uC5D0 \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4.";
|
|
40
|
+
case 500:
|
|
41
|
+
return "\uC11C\uBC84\uC5D0\uC11C \uCC98\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD558\uC600\uC2B5\uB2C8\uB2E4.";
|
|
42
|
+
default:
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/client/refreshManager.ts
|
|
48
|
+
var RefreshManager = class {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.isRefreshing = false;
|
|
51
|
+
this.subscribers = [];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 현재 refresh 진행 중인지 반환합니다.
|
|
55
|
+
*/
|
|
56
|
+
get refreshing() {
|
|
57
|
+
return this.isRefreshing;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* refresh 시작 상태로 전환합니다.
|
|
61
|
+
*/
|
|
62
|
+
begin() {
|
|
63
|
+
this.isRefreshing = true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* refresh 종료 상태로 전환합니다.
|
|
67
|
+
*/
|
|
68
|
+
end() {
|
|
69
|
+
this.isRefreshing = false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* refresh 완료를 기다리는 subscriber를 등록합니다.
|
|
73
|
+
*
|
|
74
|
+
* @param callback refresh 완료 후 호출될 함수
|
|
75
|
+
*/
|
|
76
|
+
subscribe(callback) {
|
|
77
|
+
this.subscribers.push(callback);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 대기 중인 subscriber들에게 결과를 알리고 큐를 초기화합니다.
|
|
81
|
+
*
|
|
82
|
+
* @param token 새 access token
|
|
83
|
+
*/
|
|
84
|
+
notify(token) {
|
|
85
|
+
this.subscribers.forEach((callback) => callback(token));
|
|
86
|
+
this.subscribers = [];
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
async function requestRefresh(params) {
|
|
90
|
+
const {
|
|
91
|
+
refreshUrl,
|
|
92
|
+
withCredentials,
|
|
93
|
+
refreshStrategy,
|
|
94
|
+
refreshToken,
|
|
95
|
+
parseRefreshResponse
|
|
96
|
+
} = params;
|
|
97
|
+
const requestInit = {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
"Content-Type": "application/json"
|
|
12
101
|
},
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
102
|
+
credentials: withCredentials ? "include" : "same-origin"
|
|
103
|
+
};
|
|
104
|
+
if (refreshStrategy === "body") {
|
|
105
|
+
if (!refreshToken) {
|
|
106
|
+
throw new Error("Refresh token not found");
|
|
107
|
+
}
|
|
108
|
+
requestInit.body = JSON.stringify({ refreshToken });
|
|
109
|
+
}
|
|
110
|
+
const response = await fetch(refreshUrl, requestInit);
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new Error("Token refresh failed");
|
|
113
|
+
}
|
|
114
|
+
const json = await response.json();
|
|
115
|
+
return parseRefreshResponse(json);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/client/tokenStorage.ts
|
|
119
|
+
function createAccessTokenAccess(mode, key = "accessToken") {
|
|
120
|
+
let memoryToken = null;
|
|
121
|
+
if (mode === "localStorage") {
|
|
122
|
+
return {
|
|
123
|
+
get: () => window.localStorage.getItem(key),
|
|
124
|
+
set: (token) => {
|
|
125
|
+
if (!token) {
|
|
126
|
+
window.localStorage.removeItem(key);
|
|
127
|
+
} else {
|
|
128
|
+
window.localStorage.setItem(key, token);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
clear: () => {
|
|
132
|
+
window.localStorage.removeItem(key);
|
|
16
133
|
}
|
|
17
|
-
|
|
18
|
-
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (mode === "memory") {
|
|
137
|
+
return {
|
|
138
|
+
get: () => memoryToken,
|
|
139
|
+
set: (token) => {
|
|
140
|
+
memoryToken = token;
|
|
141
|
+
},
|
|
142
|
+
clear: () => {
|
|
143
|
+
memoryToken = null;
|
|
19
144
|
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
get: () => null,
|
|
149
|
+
set: () => {
|
|
20
150
|
},
|
|
21
|
-
clear() {
|
|
22
|
-
window.localStorage.removeItem(accessTokenKey);
|
|
23
|
-
window.localStorage.removeItem(refreshTokenKey);
|
|
151
|
+
clear: () => {
|
|
24
152
|
}
|
|
25
153
|
};
|
|
26
154
|
}
|
|
27
|
-
function
|
|
28
|
-
const refreshTokenKey = keys?.refreshTokenKey ?? "refreshToken";
|
|
155
|
+
function createRefreshTokenAccess(key = "refreshToken") {
|
|
29
156
|
return {
|
|
30
|
-
get()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
set(data) {
|
|
38
|
-
if (data.refreshToken !== void 0) {
|
|
39
|
-
window.localStorage.setItem(refreshTokenKey, data.refreshToken ?? "");
|
|
157
|
+
get: () => window.localStorage.getItem(key),
|
|
158
|
+
set: (token) => {
|
|
159
|
+
if (!token) {
|
|
160
|
+
window.localStorage.removeItem(key);
|
|
161
|
+
} else {
|
|
162
|
+
window.localStorage.setItem(key, token);
|
|
40
163
|
}
|
|
41
164
|
},
|
|
42
|
-
clear() {
|
|
43
|
-
window.localStorage.removeItem(
|
|
165
|
+
clear: () => {
|
|
166
|
+
window.localStorage.removeItem(key);
|
|
44
167
|
}
|
|
45
168
|
};
|
|
46
169
|
}
|
|
170
|
+
|
|
171
|
+
// src/client/createAxiosClient.ts
|
|
172
|
+
function setAuthorizationHeader(config, accessToken) {
|
|
173
|
+
const headers = new AxiosHeaders();
|
|
174
|
+
if (config.headers) {
|
|
175
|
+
const current = config.headers instanceof AxiosHeaders ? config.headers.toJSON() : config.headers;
|
|
176
|
+
for (const [key, value] of Object.entries(current)) {
|
|
177
|
+
if (value !== void 0) {
|
|
178
|
+
headers.set(key, String(value));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
183
|
+
config.headers = headers;
|
|
184
|
+
return config;
|
|
185
|
+
}
|
|
47
186
|
function createAxiosClient(options) {
|
|
48
187
|
const {
|
|
49
188
|
baseURL,
|
|
50
189
|
timeout = 1e4,
|
|
51
190
|
withCredentials = true,
|
|
52
191
|
refreshUrl,
|
|
53
|
-
|
|
192
|
+
accessTokenStorage = "localStorage",
|
|
193
|
+
refreshStrategy = "body",
|
|
54
194
|
storageKeys,
|
|
55
195
|
onAuthStateChange,
|
|
56
196
|
onError,
|
|
57
|
-
getErrorMessage
|
|
197
|
+
getErrorMessage,
|
|
198
|
+
shouldRefresh = defaultShouldRefresh,
|
|
199
|
+
parseRefreshResponse = defaultParseRefreshResponse
|
|
58
200
|
} = options;
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
201
|
+
const accessTokenKey = storageKeys?.accessTokenKey ?? "accessToken";
|
|
202
|
+
const refreshTokenKey = storageKeys?.refreshTokenKey ?? "refreshToken";
|
|
203
|
+
const instance = axios.create({
|
|
204
|
+
baseURL,
|
|
205
|
+
timeout,
|
|
206
|
+
withCredentials
|
|
207
|
+
});
|
|
208
|
+
const accessTokenAccess = createAccessTokenAccess(accessTokenStorage, accessTokenKey);
|
|
209
|
+
const refreshTokenAccess = createRefreshTokenAccess(refreshTokenKey);
|
|
210
|
+
const refreshManager = new RefreshManager();
|
|
67
211
|
const clearAuth = () => {
|
|
68
|
-
|
|
212
|
+
accessTokenAccess.clear();
|
|
213
|
+
if (refreshStrategy === "body") {
|
|
214
|
+
refreshTokenAccess.clear();
|
|
215
|
+
}
|
|
69
216
|
onAuthStateChange?.("unauthenticated");
|
|
70
217
|
};
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
218
|
+
const getToken = () => {
|
|
219
|
+
return {
|
|
220
|
+
accessToken: accessTokenAccess.get(),
|
|
221
|
+
refreshToken: refreshStrategy === "body" ? refreshTokenAccess.get() : null
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
const setToken = (data) => {
|
|
225
|
+
if (data.accessToken !== void 0) {
|
|
226
|
+
accessTokenAccess.set(data.accessToken ?? null);
|
|
227
|
+
}
|
|
228
|
+
if (refreshStrategy === "body" && data.refreshToken !== void 0) {
|
|
229
|
+
refreshTokenAccess.set(data.refreshToken ?? null);
|
|
80
230
|
}
|
|
81
|
-
return await res.json();
|
|
82
231
|
};
|
|
83
232
|
instance.interceptors.request.use(
|
|
84
233
|
(config) => {
|
|
85
|
-
const
|
|
234
|
+
const accessToken = accessTokenAccess.get();
|
|
86
235
|
if (accessToken) {
|
|
87
|
-
config
|
|
88
|
-
config.headers.Authorization = `Bearer ${accessToken}`;
|
|
236
|
+
setAuthorizationHeader(config, accessToken);
|
|
89
237
|
}
|
|
90
238
|
return config;
|
|
91
239
|
},
|
|
92
|
-
(
|
|
240
|
+
(error) => Promise.reject(error)
|
|
93
241
|
);
|
|
94
242
|
instance.interceptors.response.use(
|
|
95
|
-
(
|
|
243
|
+
(response) => response,
|
|
96
244
|
async (error) => {
|
|
97
|
-
const response = error.response;
|
|
98
|
-
const status = response?.status;
|
|
99
245
|
const originalRequest = error.config ?? {};
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
case 0:
|
|
105
|
-
message = "REST API \uC11C\uBC84\uC5D0 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4\n\uC11C\uBC84 \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694";
|
|
106
|
-
break;
|
|
107
|
-
case 400:
|
|
108
|
-
message = "\uC798\uBABB\uB41C \uC694\uCCAD\uC785\uB2C8\uB2E4.";
|
|
109
|
-
break;
|
|
110
|
-
case 404:
|
|
111
|
-
message = "[404] REST API \uC694\uCCAD\uC5D0 \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4";
|
|
112
|
-
break;
|
|
113
|
-
case 500:
|
|
114
|
-
message = "\uC11C\uBC84\uC5D0\uC11C \uCC98\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD558\uC600\uC2B5\uB2C8\uB2E4.";
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
246
|
+
if (!shouldRefresh(error)) {
|
|
247
|
+
const message = getErrorMessage?.(error) ?? getDefaultErrorMessage(error);
|
|
248
|
+
if (message) {
|
|
249
|
+
onError?.(message, error);
|
|
117
250
|
}
|
|
118
|
-
if (message) onError?.(message, error);
|
|
119
251
|
return Promise.reject(error);
|
|
120
252
|
}
|
|
121
|
-
|
|
253
|
+
const requestUrl = originalRequest.url ?? "";
|
|
254
|
+
if (typeof requestUrl === "string" && requestUrl.includes(refreshUrl)) {
|
|
122
255
|
clearAuth();
|
|
123
256
|
return Promise.reject(error);
|
|
124
257
|
}
|
|
125
|
-
originalRequest._retry
|
|
126
|
-
const { refreshToken } = tokenAccess.get();
|
|
127
|
-
if (!refreshToken) {
|
|
258
|
+
if (originalRequest._retry) {
|
|
128
259
|
clearAuth();
|
|
129
260
|
return Promise.reject(error);
|
|
130
261
|
}
|
|
131
|
-
|
|
262
|
+
originalRequest._retry = true;
|
|
263
|
+
if (refreshManager.refreshing) {
|
|
132
264
|
return new Promise((resolve, reject) => {
|
|
133
|
-
|
|
134
|
-
if (!
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
265
|
+
refreshManager.subscribe((newAccessToken) => {
|
|
266
|
+
if (!newAccessToken) {
|
|
267
|
+
reject(error);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const retryRequest = setAuthorizationHeader(originalRequest, newAccessToken);
|
|
271
|
+
resolve(instance(retryRequest));
|
|
138
272
|
});
|
|
139
273
|
});
|
|
140
274
|
}
|
|
141
|
-
|
|
275
|
+
refreshManager.begin();
|
|
142
276
|
try {
|
|
143
|
-
const newTokens = await requestRefresh(
|
|
144
|
-
|
|
277
|
+
const newTokens = await requestRefresh({
|
|
278
|
+
refreshUrl,
|
|
279
|
+
withCredentials,
|
|
280
|
+
refreshStrategy,
|
|
281
|
+
refreshToken: refreshStrategy === "body" ? refreshTokenAccess.get() : null,
|
|
282
|
+
parseRefreshResponse
|
|
283
|
+
});
|
|
284
|
+
setToken(newTokens);
|
|
145
285
|
onAuthStateChange?.("authenticated");
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
notifySubscribers(null);
|
|
286
|
+
refreshManager.end();
|
|
287
|
+
refreshManager.notify(newTokens.accessToken);
|
|
288
|
+
const retryRequest = setAuthorizationHeader(originalRequest, newTokens.accessToken);
|
|
289
|
+
return instance(retryRequest);
|
|
290
|
+
} catch (refreshError) {
|
|
291
|
+
refreshManager.end();
|
|
292
|
+
refreshManager.notify(null);
|
|
154
293
|
clearAuth();
|
|
155
|
-
return Promise.reject(
|
|
294
|
+
return Promise.reject(refreshError);
|
|
156
295
|
}
|
|
157
296
|
}
|
|
158
297
|
);
|
|
159
298
|
return {
|
|
160
299
|
axios: instance,
|
|
161
|
-
setToken
|
|
162
|
-
getToken
|
|
163
|
-
clearToken:
|
|
300
|
+
setToken,
|
|
301
|
+
getToken,
|
|
302
|
+
clearToken: clearAuth
|
|
164
303
|
};
|
|
165
304
|
}
|
|
166
305
|
export {
|