@0xchain/request 1.1.0-beta.7 → 1.1.0-beta.71
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/LICENSE +21 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -119
- package/dist/interceptors/commonHeader.d.ts +8 -0
- package/dist/interceptors/commonHeader.d.ts.map +1 -1
- package/dist/interceptors/refreshToken.d.ts +4 -0
- package/dist/interceptors/refreshToken.d.ts.map +1 -1
- package/dist/interceptors/response.d.ts +12 -2
- package/dist/interceptors/response.d.ts.map +1 -1
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/tokenRefresh.d.ts +11 -0
- package/dist/tokenRefresh.d.ts.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -8
- package/dist/__vite-browser-external-2Ng8QIWW.js +0 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present 0xchain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
CHANGED
|
@@ -11,5 +11,7 @@ export interface ApiError extends AxiosError {
|
|
|
11
11
|
}
|
|
12
12
|
declare const axiosInstance: import('axios').AxiosInstance;
|
|
13
13
|
export default axiosInstance;
|
|
14
|
-
export
|
|
14
|
+
export { setServerHeaderProvider, setClientLocales } from './interceptors/commonHeader';
|
|
15
|
+
export { refreshAccessToken, getCurrentAccessToken } from './tokenRefresh';
|
|
16
|
+
export type { ApiRequestConfig, ServerHeaderProvider, Pagination, Status, IResponse, IFilters, IPageRequest, } from './types';
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAQ/C,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,IAAI,EAAE,CAAC,CAAC;CACT;AAED,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD,QAAA,MAAM,aAAa,+BAYjB,CAAC;AASH,eAAe,aAAa,CAAC;AAE7B,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,SAAS,EACT,QAAQ,EACR,YAAY,GACb,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAQ/C,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,IAAI,EAAE,CAAC,CAAC;CACT;AAED,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD,QAAA,MAAM,aAAa,+BAYjB,CAAC;AASH,eAAe,aAAa,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAExF,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE3E,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,MAAM,EACN,SAAS,EACT,QAAQ,EACR,YAAY,GACb,MAAM,SAAS,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
import axios, { isAxiosError, AxiosError } from "axios";
|
|
2
2
|
import qs from "qs";
|
|
3
3
|
import jsCookie from "js-cookie";
|
|
4
|
+
import { envCookieName, getToken, removeAllToken, isTokenExpired } from "@0xchain/token";
|
|
4
5
|
import { logger } from "@0xchain/logger";
|
|
5
|
-
import TokenManager, { isTokenExpired, getToken, removeAllToken } from "@0xchain/token";
|
|
6
6
|
const isSimplifiedChinese = () => {
|
|
7
|
-
return /^(zh-hans|zh-cn|zh-sg|zh-my)$/.test(window
|
|
7
|
+
return /^(zh-hans|zh-cn|zh-sg|zh-my)$/.test(window?.navigator.language.toLowerCase());
|
|
8
8
|
};
|
|
9
9
|
const matchSystemLanguage = () => {
|
|
10
10
|
return isSimplifiedChinese() ? "zh-hans" : "";
|
|
11
11
|
};
|
|
12
|
+
function isServerRuntime() {
|
|
13
|
+
if (typeof window !== "undefined") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
12
18
|
let httpAgent;
|
|
13
19
|
let httpsAgent;
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
let serverHeaderProvider;
|
|
21
|
+
function setServerHeaderProvider(provider) {
|
|
22
|
+
serverHeaderProvider = provider;
|
|
23
|
+
}
|
|
24
|
+
let clientLocales;
|
|
25
|
+
function setClientLocales(locales) {
|
|
26
|
+
clientLocales = locales;
|
|
27
|
+
}
|
|
28
|
+
function getUrlLocale() {
|
|
29
|
+
if (typeof window === "undefined" || !clientLocales?.length) return void 0;
|
|
30
|
+
const seg = window.location.pathname.split("/")[1];
|
|
31
|
+
return seg && clientLocales.includes(seg) ? seg : void 0;
|
|
16
32
|
}
|
|
17
33
|
function isEdgeRuntime() {
|
|
18
34
|
return process.env.NEXT_RUNTIME === "edge" || typeof globalThis.EdgeRuntime !== "undefined";
|
|
@@ -22,51 +38,61 @@ function isNodeRuntime() {
|
|
|
22
38
|
}
|
|
23
39
|
function getHeader(config, key) {
|
|
24
40
|
const h = config.headers;
|
|
25
|
-
if (
|
|
26
|
-
return h
|
|
41
|
+
if (h?.get && typeof h.get === "function") return h.get(key);
|
|
42
|
+
return h?.[key];
|
|
27
43
|
}
|
|
28
44
|
function setHeader(config, key, value) {
|
|
29
45
|
const h = config.headers;
|
|
30
|
-
if (
|
|
46
|
+
if (h?.set && typeof h.set === "function") {
|
|
31
47
|
h.set(key, value);
|
|
32
48
|
} else {
|
|
33
49
|
h[key] = value;
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
async function commonHeader(config) {
|
|
37
|
-
var _a, _b;
|
|
38
53
|
let access_token = "";
|
|
39
54
|
let language = "";
|
|
40
55
|
if (isServerRuntime()) {
|
|
41
56
|
if (isNodeRuntime()) {
|
|
42
57
|
try {
|
|
43
58
|
const http = await import("http");
|
|
44
|
-
const https = await import("
|
|
45
|
-
const baseURL = config
|
|
59
|
+
const https = await import("https");
|
|
60
|
+
const baseURL = config?.baseURL;
|
|
46
61
|
const isHttps = typeof baseURL === "string" ? baseURL.startsWith("https://") : false;
|
|
47
|
-
if (!httpAgent &&
|
|
48
|
-
if (!httpsAgent &&
|
|
62
|
+
if (!httpAgent && http?.Agent) httpAgent = new http.Agent({ keepAlive: true });
|
|
63
|
+
if (!httpsAgent && https?.Agent) httpsAgent = new https.Agent({ keepAlive: true });
|
|
49
64
|
config.httpAgent = isHttps ? void 0 : httpAgent;
|
|
50
65
|
config.httpsAgent = isHttps ? httpsAgent : void 0;
|
|
51
66
|
} catch {
|
|
52
67
|
}
|
|
53
68
|
}
|
|
54
|
-
|
|
69
|
+
setHeader(config, "x-whistle-nohost-env", process.env.NO_HOST_ENV || "ichaingo");
|
|
70
|
+
if (serverHeaderProvider) {
|
|
55
71
|
const startLanguage = getHeader(config, "Accept-Language");
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
72
|
+
try {
|
|
73
|
+
access_token = await serverHeaderProvider.getAccessToken() || "";
|
|
74
|
+
language = startLanguage || await serverHeaderProvider.getLanguage() || "en";
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("Error accessing server headers via provider:", error);
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
try {
|
|
80
|
+
const startLanguage = getHeader(config, "Accept-Language");
|
|
81
|
+
const { cookies, headers } = await import("next/headers");
|
|
82
|
+
const cookieStore = await cookies();
|
|
83
|
+
access_token = cookieStore.get(envCookieName("access_token"))?.value || "";
|
|
84
|
+
const hList = await headers();
|
|
85
|
+
language = startLanguage || hList.get("x-next-intl-locale") || "en";
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error("Error accessing cookies in server component:", error);
|
|
88
|
+
}
|
|
64
89
|
}
|
|
65
90
|
} else {
|
|
66
|
-
access_token = jsCookie.get("access_token") || "";
|
|
67
|
-
|
|
91
|
+
access_token = jsCookie.get(envCookieName("access_token")) || "";
|
|
92
|
+
const preset = getHeader(config, "Accept-Language");
|
|
93
|
+
language = preset || getUrlLocale() || jsCookie.get("NEXT_LOCALE") || matchSystemLanguage() || "en";
|
|
68
94
|
}
|
|
69
|
-
const isRefreshToken =
|
|
95
|
+
const isRefreshToken = config.headers?.["isRefreshToken"];
|
|
70
96
|
setHeader(config, "Accept-Language", language);
|
|
71
97
|
if (access_token && !isRefreshToken) {
|
|
72
98
|
setHeader(config, "Authorization", `Bearer ${access_token}`);
|
|
@@ -78,13 +104,12 @@ function toUpperMethod(m) {
|
|
|
78
104
|
return m ? m.toUpperCase() : void 0;
|
|
79
105
|
}
|
|
80
106
|
function getTypeName(v) {
|
|
81
|
-
var _a;
|
|
82
107
|
if (v === null) return "null";
|
|
83
108
|
if (v === void 0) return "undefined";
|
|
84
109
|
const t = typeof v;
|
|
85
110
|
if (t !== "object") return t;
|
|
86
111
|
try {
|
|
87
|
-
return
|
|
112
|
+
return v?.constructor?.name || "object";
|
|
88
113
|
} catch {
|
|
89
114
|
return "object";
|
|
90
115
|
}
|
|
@@ -105,15 +130,14 @@ function n(v) {
|
|
|
105
130
|
return v === void 0 ? null : v;
|
|
106
131
|
}
|
|
107
132
|
function buildErrorMessage(err) {
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const serverMsg = (respData == null ? void 0 : respData.message) || (respData == null ? void 0 : respData.msg) || ((_j = respData == null ? void 0 : respData.status) == null ? void 0 : _j.msg);
|
|
133
|
+
const method = toUpperMethod(err.config?.method);
|
|
134
|
+
const baseURL = err.config?.baseURL || "";
|
|
135
|
+
const url = err.config?.url ? `${baseURL || ""}${err.config.url}` : "";
|
|
136
|
+
const status = err.response?.status;
|
|
137
|
+
const statusText = err.response?.statusText;
|
|
138
|
+
const respData = err.response?.data;
|
|
139
|
+
const requestId = err.response?.headers?.["x-request-id"] || respData?.status?.requestId;
|
|
140
|
+
const serverMsg = respData?.message || respData?.msg || respData?.status?.msg;
|
|
117
141
|
const summary = [
|
|
118
142
|
status ? `HTTP ${status}` : "HTTP ERROR",
|
|
119
143
|
method,
|
|
@@ -127,18 +151,17 @@ function buildErrorMessage(err) {
|
|
|
127
151
|
return [summary, shortMsg, ridMsg, codeMsg, localMsg].filter(Boolean).join(" | ");
|
|
128
152
|
}
|
|
129
153
|
function buildDetails(err) {
|
|
130
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
131
154
|
const cfg = err.config;
|
|
132
|
-
let body = cfg
|
|
155
|
+
let body = cfg?.data;
|
|
133
156
|
if (typeof body === "string") {
|
|
134
157
|
try {
|
|
135
158
|
body = JSON.parse(body);
|
|
136
159
|
} catch {
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
|
-
const baseURL = cfg
|
|
140
|
-
const url =
|
|
141
|
-
const requestId =
|
|
162
|
+
const baseURL = cfg?.baseURL;
|
|
163
|
+
const url = cfg?.url ? baseURL ? `${baseURL}${cfg.url}` : cfg.url : void 0;
|
|
164
|
+
const requestId = err.response?.headers?.["x-request-id"] || err.response?.data?.status?.requestId;
|
|
142
165
|
return {
|
|
143
166
|
// error 基础信息(保证不会被 stringify 成 {})
|
|
144
167
|
name: n(err.name),
|
|
@@ -146,29 +169,28 @@ function buildDetails(err) {
|
|
|
146
169
|
code: n(err.code),
|
|
147
170
|
stack: n(err.stack),
|
|
148
171
|
// request 信息
|
|
149
|
-
method: n(toUpperMethod(cfg
|
|
172
|
+
method: n(toUpperMethod(cfg?.method)),
|
|
150
173
|
baseURL: n(baseURL),
|
|
151
174
|
url: n(url),
|
|
152
|
-
timeout: n(cfg
|
|
153
|
-
withCredentials: n(cfg
|
|
154
|
-
params: n(cfg
|
|
175
|
+
timeout: n(cfg?.timeout),
|
|
176
|
+
withCredentials: n(cfg?.withCredentials),
|
|
177
|
+
params: n(cfg?.params),
|
|
155
178
|
data: n(body),
|
|
156
179
|
// response 信息
|
|
157
|
-
status: n(
|
|
158
|
-
statusText: n(
|
|
180
|
+
status: n(err.response?.status),
|
|
181
|
+
statusText: n(err.response?.statusText),
|
|
159
182
|
requestId: n(requestId),
|
|
160
|
-
responseDataType: n(getTypeName(
|
|
161
|
-
responseData: n(safeResponseData(
|
|
183
|
+
responseDataType: n(getTypeName(err.response?.data)),
|
|
184
|
+
responseData: n(safeResponseData(err.response?.data))
|
|
162
185
|
};
|
|
163
186
|
}
|
|
164
187
|
function errorInterceptor(error) {
|
|
165
|
-
var _a, _b, _c;
|
|
166
188
|
const err = isAxiosError(error) ? error : new AxiosError(String(error));
|
|
167
189
|
const isTimeout = err.code === "ECONNABORTED" || /timeout/i.test(err.message || "");
|
|
168
190
|
if (isTimeout) {
|
|
169
|
-
const method = toUpperMethod(
|
|
170
|
-
const baseURL =
|
|
171
|
-
const url =
|
|
191
|
+
const method = toUpperMethod(err.config?.method);
|
|
192
|
+
const baseURL = err.config?.baseURL || "";
|
|
193
|
+
const url = err.config?.url ? `${baseURL || ""}${err.config.url}` : "";
|
|
172
194
|
const summary = [`TIMEOUT`, method, url].filter(Boolean).join(" | ");
|
|
173
195
|
err.summary = summary;
|
|
174
196
|
err.details = buildDetails(err);
|
|
@@ -181,40 +203,94 @@ function errorInterceptor(error) {
|
|
|
181
203
|
logger.error({ message, details: err.details, errType: "server error" });
|
|
182
204
|
return Promise.reject(err);
|
|
183
205
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
206
|
+
const refreshBaseURL = typeof window !== "undefined" ? process.env.NEXT_PUBLIC_AUTH_URL || window.location.origin : process.env.AUTH_INNER_API_URL || process.env.NEXT_PUBLIC_AUTH_URL;
|
|
207
|
+
const refreshClient = axios.create({
|
|
208
|
+
baseURL: refreshBaseURL,
|
|
209
|
+
timeout: 1e4,
|
|
210
|
+
withCredentials: true,
|
|
211
|
+
paramsSerializer(params) {
|
|
212
|
+
return qs.stringify(params, { arrayFormat: "brackets" });
|
|
189
213
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
214
|
+
});
|
|
215
|
+
let refreshPromise = null;
|
|
216
|
+
const REFRESH_DEDUPE_MS = 2e3;
|
|
217
|
+
let lastRefreshAt = 0;
|
|
218
|
+
let lastRefreshResult = null;
|
|
219
|
+
function refreshAccessToken() {
|
|
220
|
+
if (refreshPromise) {
|
|
221
|
+
return refreshPromise;
|
|
222
|
+
}
|
|
223
|
+
if (Date.now() - lastRefreshAt < REFRESH_DEDUPE_MS) {
|
|
224
|
+
return Promise.resolve(lastRefreshResult);
|
|
225
|
+
}
|
|
226
|
+
const refresh_token = getToken("refresh_token");
|
|
227
|
+
if (!refresh_token) {
|
|
228
|
+
return Promise.resolve(null);
|
|
229
|
+
}
|
|
230
|
+
const pending = refreshClient.get("/api/refresh/token", {
|
|
231
|
+
params: {
|
|
232
|
+
grant_type: "refresh_token",
|
|
233
|
+
refresh_token
|
|
234
|
+
},
|
|
235
|
+
headers: {
|
|
236
|
+
isRefreshToken: true
|
|
193
237
|
}
|
|
238
|
+
}).then((res) => {
|
|
239
|
+
const data = res?.data?.data;
|
|
240
|
+
if (data?.access_token) {
|
|
241
|
+
if (typeof window !== "undefined") {
|
|
242
|
+
window.dispatchEvent(new CustomEvent("user-profile-updated"));
|
|
243
|
+
}
|
|
244
|
+
return data.access_token;
|
|
245
|
+
}
|
|
246
|
+
removeAllToken();
|
|
247
|
+
return null;
|
|
248
|
+
}).catch(() => null).then((result) => {
|
|
249
|
+
lastRefreshAt = Date.now();
|
|
250
|
+
lastRefreshResult = result;
|
|
251
|
+
return result;
|
|
252
|
+
}).finally(() => {
|
|
253
|
+
refreshPromise = null;
|
|
254
|
+
});
|
|
255
|
+
refreshPromise = pending;
|
|
256
|
+
return pending;
|
|
257
|
+
}
|
|
258
|
+
function getCurrentAccessToken() {
|
|
259
|
+
return jsCookie.get(envCookieName("access_token")) || getToken("access_token") || "";
|
|
260
|
+
}
|
|
261
|
+
function shouldSkipRefresh(config) {
|
|
262
|
+
const url = typeof config?.url === "string" ? config.url : "";
|
|
263
|
+
if (url.includes("/api/refresh/token")) return true;
|
|
264
|
+
if (url.includes("/v2/auth/")) return true;
|
|
265
|
+
if (config?.headers?.isRefreshToken) return true;
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
function handleBusinessResponse(response) {
|
|
269
|
+
const config = response.config;
|
|
270
|
+
if (config.returnFullResponse) {
|
|
194
271
|
return response;
|
|
195
272
|
}
|
|
196
273
|
if (response.status === 200) {
|
|
197
|
-
if (
|
|
274
|
+
if (response.headers["content-type"]?.includes("text/event-stream")) {
|
|
198
275
|
return response;
|
|
199
276
|
}
|
|
200
277
|
const data = response.data;
|
|
201
278
|
const businessStatus = data.status;
|
|
202
279
|
if (businessStatus && typeof businessStatus.code === "number" && ![0, 200].includes(businessStatus.code) && businessStatus.code !== 401) {
|
|
203
280
|
const err = new AxiosError(
|
|
204
|
-
|
|
281
|
+
businessStatus?.msg || "Business error",
|
|
205
282
|
String(businessStatus.code),
|
|
206
283
|
response.config,
|
|
207
284
|
response.request,
|
|
208
285
|
response
|
|
209
286
|
);
|
|
210
|
-
const summary = `BIZ ${businessStatus.code} | ${
|
|
287
|
+
const summary = `BIZ ${businessStatus.code} | ${response.config?.method?.toUpperCase()} | ${response.config?.baseURL || ""}${response.config?.url || ""} | server: ${businessStatus?.msg}`;
|
|
211
288
|
const details = {
|
|
212
|
-
method:
|
|
213
|
-
url:
|
|
214
|
-
params:
|
|
289
|
+
method: response.config?.method?.toUpperCase(),
|
|
290
|
+
url: response.config?.baseURL ? `${response.config.baseURL}${response.config.url || ""}` : response.config.url,
|
|
291
|
+
params: response.config?.params,
|
|
215
292
|
data: (() => {
|
|
216
|
-
|
|
217
|
-
let d = (_a2 = response.config) == null ? void 0 : _a2.data;
|
|
293
|
+
let d = response.config?.data;
|
|
218
294
|
if (typeof d === "string") {
|
|
219
295
|
try {
|
|
220
296
|
d = JSON.parse(d);
|
|
@@ -225,7 +301,7 @@ function ResponseInterceptor(response) {
|
|
|
225
301
|
})(),
|
|
226
302
|
status: response.status,
|
|
227
303
|
statusText: response.statusText,
|
|
228
|
-
requestId:
|
|
304
|
+
requestId: response.headers?.["x-request-id"] || businessStatus?.requestId,
|
|
229
305
|
responseData: data
|
|
230
306
|
};
|
|
231
307
|
if (process.env.NODE_ENV !== "development") {
|
|
@@ -237,26 +313,43 @@ function ResponseInterceptor(response) {
|
|
|
237
313
|
}
|
|
238
314
|
return response;
|
|
239
315
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
316
|
+
function createResponseInterceptor(instance) {
|
|
317
|
+
return async function ResponseInterceptor(response) {
|
|
318
|
+
if (response.status === 401) {
|
|
319
|
+
const config = response.config;
|
|
320
|
+
const canRetry = !isServerRuntime() && !shouldSkipRefresh(config) && !config.__isRetryAfterRefresh;
|
|
321
|
+
if (canRetry) {
|
|
322
|
+
const newAccessToken = await refreshAccessToken();
|
|
323
|
+
if (newAccessToken) {
|
|
324
|
+
config.__isRetryAfterRefresh = true;
|
|
325
|
+
return instance(config);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return response;
|
|
329
|
+
}
|
|
330
|
+
return handleBusinessResponse(response);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function setAuthHeader(config, accessToken) {
|
|
334
|
+
const value = `Bearer ${accessToken}`;
|
|
335
|
+
const h = config.headers;
|
|
336
|
+
if (h?.set && typeof h.set === "function") {
|
|
337
|
+
h.set("Authorization", value);
|
|
338
|
+
} else {
|
|
339
|
+
h.Authorization = value;
|
|
248
340
|
}
|
|
249
|
-
}
|
|
341
|
+
}
|
|
250
342
|
async function refreshToken(config) {
|
|
251
|
-
if (
|
|
343
|
+
if (isServerRuntime()) {
|
|
252
344
|
return config;
|
|
253
345
|
}
|
|
254
346
|
if (typeof config.url === "string" && config.url.includes("/api/refresh/token")) {
|
|
255
347
|
return config;
|
|
256
348
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
349
|
+
if (typeof config.url === "string" && config.url.includes("/v2/auth/")) {
|
|
350
|
+
return config;
|
|
351
|
+
}
|
|
352
|
+
if (config.headers?.isRefreshToken) {
|
|
260
353
|
return config;
|
|
261
354
|
}
|
|
262
355
|
let expired = false;
|
|
@@ -266,42 +359,11 @@ async function refreshToken(config) {
|
|
|
266
359
|
expired = false;
|
|
267
360
|
}
|
|
268
361
|
if (expired) {
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
362
|
+
const newAccessToken = await refreshAccessToken();
|
|
363
|
+
const accessToken = newAccessToken || getCurrentAccessToken();
|
|
364
|
+
if (accessToken) {
|
|
365
|
+
setAuthHeader(config, accessToken);
|
|
273
366
|
}
|
|
274
|
-
if (refreshPromise) {
|
|
275
|
-
await refreshPromise;
|
|
276
|
-
return config;
|
|
277
|
-
}
|
|
278
|
-
const refreshHeaders = { ...headers };
|
|
279
|
-
delete refreshHeaders.Authorization;
|
|
280
|
-
delete refreshHeaders.authorization;
|
|
281
|
-
refreshHeaders.isRefreshToken = true;
|
|
282
|
-
refreshPromise = axiosInstance$1.get(`/api/refresh/token`, {
|
|
283
|
-
params: {
|
|
284
|
-
grant_type: "refresh_token",
|
|
285
|
-
refresh_token
|
|
286
|
-
},
|
|
287
|
-
headers: {
|
|
288
|
-
...refreshHeaders
|
|
289
|
-
}
|
|
290
|
-
}).then((res) => {
|
|
291
|
-
var _a;
|
|
292
|
-
if ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.data) {
|
|
293
|
-
return res.data.data;
|
|
294
|
-
} else {
|
|
295
|
-
removeAllToken();
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
}).catch(() => {
|
|
299
|
-
removeAllToken();
|
|
300
|
-
return null;
|
|
301
|
-
}).finally(() => {
|
|
302
|
-
refreshPromise = null;
|
|
303
|
-
});
|
|
304
|
-
await refreshPromise;
|
|
305
367
|
}
|
|
306
368
|
return config;
|
|
307
369
|
}
|
|
@@ -319,7 +381,11 @@ const axiosInstance = axios.create({
|
|
|
319
381
|
});
|
|
320
382
|
axiosInstance.interceptors.request.use(refreshToken);
|
|
321
383
|
axiosInstance.interceptors.request.use(commonHeader, errorInterceptor);
|
|
322
|
-
axiosInstance.interceptors.response.use(
|
|
384
|
+
axiosInstance.interceptors.response.use(createResponseInterceptor(axiosInstance), errorInterceptor);
|
|
323
385
|
export {
|
|
324
|
-
axiosInstance as default
|
|
386
|
+
axiosInstance as default,
|
|
387
|
+
getCurrentAccessToken,
|
|
388
|
+
refreshAccessToken,
|
|
389
|
+
setClientLocales,
|
|
390
|
+
setServerHeaderProvider
|
|
325
391
|
};
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import { InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
import { ServerHeaderProvider } from '../types';
|
|
3
|
+
export declare function setServerHeaderProvider(provider: ServerHeaderProvider): void;
|
|
4
|
+
/**
|
|
5
|
+
* 注入应用支持的 locale 列表后,客户端 Accept-Language 以 URL 首段为准,
|
|
6
|
+
* 避免 iframe 分区(partitioned)或重复的 NEXT_LOCALE cookie 读到旧值。
|
|
7
|
+
* 未调用时行为不变(仍读 cookie)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function setClientLocales(locales: readonly string[]): void;
|
|
2
10
|
export declare function commonHeader(config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig<any>>;
|
|
3
11
|
//# sourceMappingURL=commonHeader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commonHeader.d.ts","sourceRoot":"","sources":["../../src/interceptors/commonHeader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"commonHeader.d.ts","sourceRoot":"","sources":["../../src/interceptors/commonHeader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,0BAA0B,EAAE,MAAM,OAAO,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAQrD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,oBAAoB,QAErE;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,QAE1D;AA+BD,wBAAsB,YAAY,CAChC,MAAM,EAAE,0BAA0B,4CA8DnC"}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* 请求前的「预测式刷新」:若本地判断 access_token 即将/已过期,提前刷新。
|
|
4
|
+
* 这是一层优化;真正的兜底由响应拦截器的 401 自动刷新+重放保证。
|
|
5
|
+
*/
|
|
2
6
|
export default function refreshToken(config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig<any>>;
|
|
3
7
|
//# sourceMappingURL=refreshToken.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refreshToken.d.ts","sourceRoot":"","sources":["../../src/interceptors/refreshToken.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"refreshToken.d.ts","sourceRoot":"","sources":["../../src/interceptors/refreshToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAerE;;;GAGG;AACH,wBAA8B,YAAY,CACxC,MAAM,EAAE,0BAA0B,4CAkCnC"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import { AxiosResponse } from 'axios';
|
|
2
|
-
|
|
1
|
+
import { AxiosInstance, AxiosResponse } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* 响应拦截器工厂。
|
|
4
|
+
*
|
|
5
|
+
* 业界标准 401 处理:业务接口返回 401 时,用 refresh_token 刷新一次并重放原请求;
|
|
6
|
+
* 仅当刷新失败时把 401 透传给上层(由显式 logout 决定是否清登录态)。
|
|
7
|
+
* - 仅在客户端重试(SSR 无 cookie jar)。
|
|
8
|
+
* - 每个请求最多重试一次,避免死循环。
|
|
9
|
+
* - /v2/auth/*、刷新接口本身不触发重试。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createResponseInterceptor(instance: AxiosInstance): (response: AxiosResponse) => Promise<any>;
|
|
12
|
+
export default createResponseInterceptor;
|
|
3
13
|
//# sourceMappingURL=response.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/interceptors/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/interceptors/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,aAAa,EAA8B,MAAM,OAAO,CAAC;AAqE7F;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,aAAa,IACrB,UAAU,aAAa,KAAG,OAAO,CAAC,GAAG,CAAC,CAoBjF;AAED,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断是否处于“服务端运行时”。
|
|
3
|
+
*
|
|
4
|
+
* 关键点:只要存在真实浏览器 window,就一律视为客户端,
|
|
5
|
+
* 避免业务方在客户端 bundle 中固化 IS_SERVER=true 时被误判为服务端,
|
|
6
|
+
* 从而走 next/headers 读 cookie 失败、请求不带 Authorization。
|
|
7
|
+
*/
|
|
8
|
+
export declare function isServerRuntime(): boolean;
|
|
9
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAKzC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 触发一次 token 刷新;并发/紧邻调用共享同一个结果。
|
|
3
|
+
*
|
|
4
|
+
* 关键设计:cookie 由服务端 `/api/refresh/token` 的 Set-Cookie 统一写入,
|
|
5
|
+
* 客户端这里「绝不」再用 js-cookie 写一遍,避免双写竞态把 cookie 写坏。
|
|
6
|
+
*
|
|
7
|
+
* @returns 新的 access_token;刷新失败返回 null。
|
|
8
|
+
*/
|
|
9
|
+
export declare function refreshAccessToken(): Promise<string | null>;
|
|
10
|
+
export declare function getCurrentAccessToken(): string;
|
|
11
|
+
//# sourceMappingURL=tokenRefresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../src/tokenRefresh.ts"],"names":[],"mappings":"AAqCA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwD3D;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
export interface ServerHeaderProvider {
|
|
3
|
+
getAccessToken(): Promise<string | undefined>;
|
|
4
|
+
getLanguage(): Promise<string | undefined>;
|
|
5
|
+
}
|
|
2
6
|
export interface ApiRequestConfig extends AxiosRequestConfig {
|
|
3
7
|
withCredentials?: boolean;
|
|
4
8
|
returnFullResponse?: boolean;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AAED;;EAEE;AACF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AACD;;GAEG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC;IACR,UAAU,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AAED;;EAEE;AACF,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AACD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IACnC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9C,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AAED;;EAEE;AACF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AACD;;GAEG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC;IACR,UAAU,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AAED;;EAEE;AACF,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AACD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xchain/request",
|
|
3
|
-
"version": "1.1.0-beta.
|
|
3
|
+
"version": "1.1.0-beta.71",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -17,17 +17,28 @@
|
|
|
17
17
|
"dist",
|
|
18
18
|
"!**/*.tsbuildinfo"
|
|
19
19
|
],
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"axios": "1.13.2",
|
|
22
|
+
"js-cookie": "3.0.5",
|
|
23
|
+
"next": "16.1.6",
|
|
24
|
+
"qs": "6.14.0"
|
|
25
|
+
},
|
|
20
26
|
"devDependencies": {
|
|
21
27
|
"@types/js-cookie": "3.0.6",
|
|
22
|
-
"@types/qs": "6.14.0"
|
|
23
|
-
},
|
|
24
|
-
"dependencies": {
|
|
28
|
+
"@types/qs": "6.14.0",
|
|
25
29
|
"axios": "1.13.2",
|
|
26
30
|
"js-cookie": "3.0.5",
|
|
27
|
-
"next": "
|
|
28
|
-
"qs": "6.14.0"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
"next": "16.1.6",
|
|
32
|
+
"qs": "6.14.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@0xchain/token": "1.1.0-beta.71",
|
|
36
|
+
"@0xchain/logger": "1.1.0-beta.71"
|
|
37
|
+
},
|
|
38
|
+
"nx": {
|
|
39
|
+
"tags": [
|
|
40
|
+
"type:util"
|
|
41
|
+
]
|
|
31
42
|
},
|
|
32
43
|
"publishConfig": {
|
|
33
44
|
"access": "public"
|