@vef-framework/core 2.0.9 → 2.0.10

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.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("@vef-framework/shared"),g=require("axios"),y=require("./errors.cjs"),m=/:(?<key>\w+)/g,$=/filename[^;=\n]*=(?<name>(?<quote>['"]).*?\2|[^;\n]*)/,f="X-Skip-Authentication",k="1";class R{#e;#s;#t=!1;#n=[];constructor(e){this.#s=e;const{baseUrl:s,timeout:n=1e3*30}=e;this.#e=g.create({baseURL:s,allowAbsoluteUrls:!0,timeout:n,headers:{"Content-Type":"application/json"},paramsSerializer:t=>u.encodeQueryString(t,{arrayFormat:"repeat",skipNulls:!0,charset:"utf-8"}),responseType:"json",responseEncoding:"utf-8",validateStatus(t){return t>=200&&t<300},withCredentials:!1,timeoutErrorMessage:"请求超时"}),this.initInterceptors()}initInterceptors(){this.#e.interceptors.request.use(this.handleRequest.bind(this),this.handleRequestError.bind(this)),this.#e.interceptors.response.use(this.handleResponse.bind(this),this.handleResponseError.bind(this))}isBelongsTo(e,s){return u.isArray(s)?s.includes(e):e===s}async handleRequest(e){const s=e.headers[f]===k;if(this.#t&&!s&&!await new Promise(t=>{this.#n.push(t)}))throw new Error("登录已过期, 请重新登录");return s?delete e.headers[f]:await this.setAccessToken(e),this.replacePathParams(e),e}handleRequestError(e){const{showErrorMessage:s}=this.#s,n=e instanceof Error?e.message:String(e);return s?s(`发起请求失败: ${n||"未知错误"}`):console.error(`[HttpClient] ❌ 发起请求失败: ${n||"未知错误"}`),Promise.reject(e)}handleResponse(e){const{showWarningMessage:s,okCode:n=0}=this.#s,{code:t,message:o,data:r}=e.data;if(this.isBelongsTo(t,n))return e;throw s?s(o):console.warn(`[HttpClient] ⚠️ [${e.config.method}: ${e.config.url}] 返回错误: ${o}`),new y.BusinessError(t,o,r)}async handleResponseError(e){const{showInfoMessage:s,showWarningMessage:n,showErrorMessage:t,tokenExpiredCode:o=[]}=this.#s;if(e instanceof g.CanceledError){if(e.response){const{method:a,url:c}=e.response.config;console.warn(`[HttpClient] ⚠️ [${a}: ${c}] 被取消`)}return}const{response:r}=e;if(r){const{status:a,config:c,data:l}=r,{code:d,message:i}=l,h=`[${c.method}: ${c.url}]`;switch(a){case 401:{if(this.isBelongsTo(d,o)){if(await this.tryRefreshToken())return this.retryRequest(c);const w="登录已过期, 请重新登录";s?s(w):console.info(`[HttpClient] ℹ️ ${w}`)}await this.#s.onUnauthenticated?.();break}case 403:{n?.(`${i}, 请联系管理员为您开通`),console.warn(`[HttpClient] ⚠️ ${h} 访问被拒绝: ${i}`),await this.#s.onAccessDenied?.();break}case 400:{n?n(i):console.warn(`[HttpClient] ⚠️ ${h} 参数错误: ${i}`);break}default:t?t(i):console.error(`[HttpClient] ❌ ${h} 返回错误: ${i}`)}}else{const a=`请求失败: ${e.message||"未知错误"}`;t?t(a):console.error(`[HttpClient] ❌ ${a}`)}throw e}async setAccessToken(e){const{getAuthTokens:s}=this.#s;if(s){const t=(await s())?.accessToken;t&&(e.headers.Authorization=`Bearer ${t}`)}}replacePathParams(e){const{url:s,params:n={}}=e;return s&&m.test(s)&&(e.url=s.replaceAll(m,(t,o)=>{if(!Object.hasOwn(n,o))return console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 未在查询参数中定义, 请检查`),"unknown";const r=n[o];return u.isNullish(r)?(console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 在查询参数中为空, 请检查`),"unknown"):r})),e}async tryRefreshToken(){const{getAuthTokens:e,refreshToken:s,setAuthTokens:n}=this.#s;if(!e||!s||!n)return!1;this.#t=!0;let t=!1;try{const o=await e();if(!o)return!1;const r=await s(o);return await n(Object.freeze(r)),t=!0}catch(o){return console.error(`[HttpClient] ❌ 刷新令牌失败: ${o}`),!1}finally{this.#t=!1;for(const o of this.#n)o(t);this.#n=[]}}async retryRequest(e){const s={...e};return await this.setAccessToken(s),this.#e(s)}async get(e,s){return(await this.#e.get(e,s)).data}async post(e,s){const{data:n,...t}=s??{};return(await this.#e.post(e,n,t)).data}async put(e,s){const{data:n,...t}=s??{};return(await this.#e.put(e,n,t)).data}async delete(e,s){return(await this.#e.delete(e,s)).data}async upload(e,s){const{data:n,onProgress:t,...o}=s??{};return(await this.#e.postForm(e,n,{...o,onUploadProgress:u.isFunction(t)?t:void 0})).data}async download(e,s){const{onProgress:n,filename:t,...o}=s??{},{data:r,headers:a}=await this.#e.get(e,{...o,responseType:"blob",responseEncoding:"binary",onDownloadProgress:u.isFunction(n)?n:void 0});try{const l=JSON.parse(await r.text());throw new Error(l.message)}catch{}const c=a["content-disposition"];if(u.isString(c)){const l=$.exec(c);if(l&&l.groups){const{name:d}=l.groups,i=decodeURIComponent(d),h=URL.createObjectURL(r);try{const p=document.createElement("a");p.href=h,p.download=u.isFunction(t)?t(i):t??i,p.click()}finally{URL.revokeObjectURL(h)}}}}}exports.HttpClient=R;exports.skipAuthenticationHeader=f;exports.skipAuthenticationValue=k;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("@vef-framework/shared"),g=require("axios"),$=require("./errors.cjs"),m=/:(?<key>\w+)/g,R=/filename[^;=\n]*=(?<name>(?<quote>['"]).*?\2|[^;\n]*)/,f="X-Skip-Authentication",k="1";class b{#e;#s;#t=!1;#n=[];constructor(e){this.#s=e;const{baseUrl:s,timeout:n=1e3*30}=e;this.#e=g.create({baseURL:s,allowAbsoluteUrls:!0,timeout:n,headers:{"Content-Type":"application/json"},paramsSerializer:t=>u.encodeQueryString(t,{arrayFormat:"repeat",skipNulls:!0,charset:"utf-8"}),responseType:"json",responseEncoding:"utf-8",validateStatus(t){return t>=200&&t<300},withCredentials:!1,timeoutErrorMessage:"请求超时"}),this.initInterceptors()}initInterceptors(){this.#e.interceptors.request.use(this.handleRequest.bind(this),this.handleRequestError.bind(this)),this.#e.interceptors.response.use(this.handleResponse.bind(this),this.handleResponseError.bind(this))}isBelongsTo(e,s){return u.isArray(s)?s.includes(e):e===s}async handleRequest(e){const s=e.headers[f]===k;if(this.#t&&!s&&!await new Promise(t=>{this.#n.push(t)}))throw new Error("登录已过期, 请重新登录");return s?delete e.headers[f]:await this.setAccessToken(e),this.replacePathParams(e),e}handleRequestError(e){const{showErrorMessage:s}=this.#s,n=e instanceof Error?e.message:String(e);return s?s(`发起请求失败: ${n||"未知错误"}`):console.error(`[HttpClient] ❌ 发起请求失败: ${n||"未知错误"}`),Promise.reject(e)}handleResponse(e){const{showWarningMessage:s,okCode:n=0}=this.#s,{code:t,message:o,data:r}=e.data;if(this.isBelongsTo(t,n))return e;throw s?s(o):console.warn(`[HttpClient] ⚠️ [${e.config.method}: ${e.config.url}] 返回错误: ${o}`),new $.BusinessError(t,o,r)}async handleResponseError(e){const{showInfoMessage:s,showWarningMessage:n,showErrorMessage:t,tokenExpiredCode:o=[]}=this.#s;if(e instanceof g.CanceledError){if(e.response){const{method:a,url:c}=e.response.config;console.warn(`[HttpClient] ⚠️ [${a}: ${c}] 被取消`)}return}const{response:r}=e;if(r){const{status:a,config:c,data:l}=r,{code:d,message:i}=l,p=`[${c.method}: ${c.url}]`;switch(a){case 401:{if(this.isBelongsTo(d,o)){let h;if(this.#t?h=await new Promise(y=>{this.#n.push(y)}):h=await this.tryRefreshToken(),h)return this.retryRequest(c);const w="登录已过期, 请重新登录";s?s(w):console.info(`[HttpClient] ℹ️ ${w}`)}await this.#s.onUnauthenticated?.();break}case 403:{n?.(`${i}, 请联系管理员为您开通`),console.warn(`[HttpClient] ⚠️ ${p} 访问被拒绝: ${i}`),await this.#s.onAccessDenied?.();break}case 400:{n?n(i):console.warn(`[HttpClient] ⚠️ ${p} 参数错误: ${i}`);break}default:t?t(i):console.error(`[HttpClient] ❌ ${p} 返回错误: ${i}`)}}else{const a=`请求失败: ${e.message||"未知错误"}`;t?t(a):console.error(`[HttpClient] ❌ ${a}`)}throw e}async setAccessToken(e){const{getAuthTokens:s}=this.#s;if(s){const t=(await s())?.accessToken;t&&(e.headers.Authorization=`Bearer ${t}`)}}replacePathParams(e){const{url:s,params:n={}}=e;return s&&m.test(s)&&(e.url=s.replaceAll(m,(t,o)=>{if(!Object.hasOwn(n,o))return console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 未在查询参数中定义, 请检查`),"unknown";const r=n[o];return u.isNullish(r)?(console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 在查询参数中为空, 请检查`),"unknown"):r})),e}async tryRefreshToken(){const{getAuthTokens:e,refreshToken:s,setAuthTokens:n}=this.#s;if(!e||!s||!n)return!1;this.#t=!0;let t=!1;try{const o=await e();if(!o)return!1;const r=await s(o);return await n(Object.freeze(r)),t=!0}catch(o){return console.error(`[HttpClient] ❌ 刷新令牌失败: ${o}`),!1}finally{this.#t=!1;for(const o of this.#n)o(t);this.#n=[]}}async retryRequest(e){const s={...e};return await this.setAccessToken(s),this.#e(s)}async get(e,s){return(await this.#e.get(e,s)).data}async post(e,s){const{data:n,...t}=s??{};return(await this.#e.post(e,n,t)).data}async put(e,s){const{data:n,...t}=s??{};return(await this.#e.put(e,n,t)).data}async delete(e,s){return(await this.#e.delete(e,s)).data}async upload(e,s){const{data:n,onProgress:t,...o}=s??{};return(await this.#e.postForm(e,n,{...o,onUploadProgress:u.isFunction(t)?t:void 0})).data}async download(e,s){const{onProgress:n,filename:t,...o}=s??{},{data:r,headers:a}=await this.#e.get(e,{...o,responseType:"blob",responseEncoding:"binary",onDownloadProgress:u.isFunction(n)?n:void 0});try{const l=JSON.parse(await r.text());throw new Error(l.message)}catch{}const c=a["content-disposition"];if(u.isString(c)){const l=R.exec(c);if(l&&l.groups){const{name:d}=l.groups,i=decodeURIComponent(d),p=URL.createObjectURL(r);try{const h=document.createElement("a");h.href=p,h.download=u.isFunction(t)?t(i):t??i,h.click()}finally{URL.revokeObjectURL(p)}}}}}exports.HttpClient=b;exports.skipAuthenticationHeader=f;exports.skipAuthenticationValue=k;
@@ -1,8 +1,8 @@
1
- import { encodeQueryString as m, isArray as k, isNullish as y, isFunction as d, isString as $ } from "@vef-framework/shared";
2
- import R, { CanceledError as C } from "axios";
3
- import { BusinessError as T } from "./errors.js";
4
- const w = /:(?<key>\w+)/g, b = /filename[^;=\n]*=(?<name>(?<quote>['"]).*?\2|[^;\n]*)/, g = "X-Skip-Authentication", E = "1";
5
- class O {
1
+ import { encodeQueryString as k, isArray as y, isNullish as $, isFunction as d, isString as R } from "@vef-framework/shared";
2
+ import C, { CanceledError as T } from "axios";
3
+ import { BusinessError as b } from "./errors.js";
4
+ const w = /:(?<key>\w+)/g, E = /filename[^;=\n]*=(?<name>(?<quote>['"]).*?\2|[^;\n]*)/, g = "X-Skip-Authentication", A = "1";
5
+ class q {
6
6
  /**
7
7
  * The axios instance.
8
8
  */
@@ -25,14 +25,14 @@ class O {
25
25
  baseUrl: s,
26
26
  timeout: n = 1e3 * 30
27
27
  } = e;
28
- this.#e = R.create({
28
+ this.#e = C.create({
29
29
  baseURL: s,
30
30
  allowAbsoluteUrls: !0,
31
31
  timeout: n,
32
32
  headers: {
33
33
  "Content-Type": "application/json"
34
34
  },
35
- paramsSerializer: (t) => m(
35
+ paramsSerializer: (t) => k(
36
36
  t,
37
37
  {
38
38
  arrayFormat: "repeat",
@@ -66,7 +66,7 @@ class O {
66
66
  * @returns The result.
67
67
  */
68
68
  isBelongsTo(e, s) {
69
- return k(s) ? s.includes(e) : e === s;
69
+ return y(s) ? s.includes(e) : e === s;
70
70
  }
71
71
  /**
72
72
  * Handle the request.
@@ -75,7 +75,7 @@ class O {
75
75
  * @returns The axios request config.
76
76
  */
77
77
  async handleRequest(e) {
78
- const s = e.headers[g] === E;
78
+ const s = e.headers[g] === A;
79
79
  if (this.#t && !s && !await new Promise((t) => {
80
80
  this.#n.push(t);
81
81
  }))
@@ -108,7 +108,7 @@ class O {
108
108
  } = e.data;
109
109
  if (this.isBelongsTo(t, n))
110
110
  return e;
111
- throw s ? s(o) : console.warn(`[HttpClient] ⚠️ [${e.config.method}: ${e.config.url}] 返回错误: ${o}`), new T(t, o, r);
111
+ throw s ? s(o) : console.warn(`[HttpClient] ⚠️ [${e.config.method}: ${e.config.url}] 返回错误: ${o}`), new b(t, o, r);
112
112
  }
113
113
  /**
114
114
  * Handle the response error.
@@ -122,7 +122,7 @@ class O {
122
122
  showErrorMessage: t,
123
123
  tokenExpiredCode: o = []
124
124
  } = this.#s;
125
- if (e instanceof C) {
125
+ if (e instanceof T) {
126
126
  if (e.response) {
127
127
  const { method: i, url: c } = e.response.config;
128
128
  console.warn(`[HttpClient] ⚠️ [${i}: ${c}] 被取消`);
@@ -135,11 +135,14 @@ class O {
135
135
  status: i,
136
136
  config: c,
137
137
  data: l
138
- } = r, { code: p, message: a } = l, h = `[${c.method}: ${c.url}]`;
138
+ } = r, { code: p, message: a } = l, u = `[${c.method}: ${c.url}]`;
139
139
  switch (i) {
140
140
  case 401: {
141
141
  if (this.isBelongsTo(p, o)) {
142
- if (await this.tryRefreshToken())
142
+ let h;
143
+ if (this.#t ? h = await new Promise((m) => {
144
+ this.#n.push(m);
145
+ }) : h = await this.tryRefreshToken(), h)
143
146
  return this.retryRequest(c);
144
147
  const f = "登录已过期, 请重新登录";
145
148
  s ? s(f) : console.info(`[HttpClient] ℹ️ ${f}`);
@@ -148,15 +151,15 @@ class O {
148
151
  break;
149
152
  }
150
153
  case 403: {
151
- n?.(`${a}, 请联系管理员为您开通`), console.warn(`[HttpClient] ⚠️ ${h} 访问被拒绝: ${a}`), await this.#s.onAccessDenied?.();
154
+ n?.(`${a}, 请联系管理员为您开通`), console.warn(`[HttpClient] ⚠️ ${u} 访问被拒绝: ${a}`), await this.#s.onAccessDenied?.();
152
155
  break;
153
156
  }
154
157
  case 400: {
155
- n ? n(a) : console.warn(`[HttpClient] ⚠️ ${h} 参数错误: ${a}`);
158
+ n ? n(a) : console.warn(`[HttpClient] ⚠️ ${u} 参数错误: ${a}`);
156
159
  break;
157
160
  }
158
161
  default:
159
- t ? t(a) : console.error(`[HttpClient] ❌ ${h} 返回错误: ${a}`);
162
+ t ? t(a) : console.error(`[HttpClient] ❌ ${u} 返回错误: ${a}`);
160
163
  }
161
164
  } else {
162
165
  const i = `请求失败: ${e.message || "未知错误"}`;
@@ -188,7 +191,7 @@ class O {
188
191
  if (!Object.hasOwn(n, o))
189
192
  return console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 未在查询参数中定义, 请检查`), "unknown";
190
193
  const r = n[o];
191
- return y(r) ? (console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 在查询参数中为空, 请检查`), "unknown") : r;
194
+ return $(r) ? (console.warn(`[HttpClient] ⚠️ 接口: ${s} 路径参数 ${o} 在查询参数中为空, 请检查`), "unknown") : r;
192
195
  })), e;
193
196
  }
194
197
  /**
@@ -336,22 +339,22 @@ class O {
336
339
  } catch {
337
340
  }
338
341
  const c = i["content-disposition"];
339
- if ($(c)) {
340
- const l = b.exec(c);
342
+ if (R(c)) {
343
+ const l = E.exec(c);
341
344
  if (l && l.groups) {
342
- const { name: p } = l.groups, a = decodeURIComponent(p), h = URL.createObjectURL(r);
345
+ const { name: p } = l.groups, a = decodeURIComponent(p), u = URL.createObjectURL(r);
343
346
  try {
344
- const u = document.createElement("a");
345
- u.href = h, u.download = d(t) ? t(a) : t ?? a, u.click();
347
+ const h = document.createElement("a");
348
+ h.href = u, h.download = d(t) ? t(a) : t ?? a, h.click();
346
349
  } finally {
347
- URL.revokeObjectURL(h);
350
+ URL.revokeObjectURL(u);
348
351
  }
349
352
  }
350
353
  }
351
354
  }
352
355
  }
353
356
  export {
354
- O as HttpClient,
357
+ q as HttpClient,
355
358
  g as skipAuthenticationHeader,
356
- E as skipAuthenticationValue
359
+ A as skipAuthenticationValue
357
360
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vef-framework/core",
3
3
  "type": "module",
4
- "version": "2.0.9",
4
+ "version": "2.0.10",
5
5
  "private": false,
6
6
  "description": "Core features for VEF framework",
7
7
  "author": {
@@ -64,7 +64,7 @@
64
64
  "use-immer": "^0.11.0",
65
65
  "xstate": "^5.24.0",
66
66
  "zustand": "^5.0.8",
67
- "@vef-framework/shared": "2.0.9"
67
+ "@vef-framework/shared": "2.0.10"
68
68
  },
69
69
  "devDependencies": {
70
70
  "react": "^19.2.0"