@jetlinks-web/core 2.2.18 → 2.3.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.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { AxiosError, AxiosResponse, InternalAxiosRequestConfig, AxiosInstance } from 'axios';
2
- import { AxiosResponseRewrite } from '@jetlinks-web/types';
3
2
  import { Observable } from 'rxjs';
4
3
 
4
+ interface AxiosResponseRewrite<T = any> extends AxiosResponse<T, any> {
5
+ result: T;
6
+ success: boolean;
7
+ }
5
8
  interface Options {
6
- tokenExpiration: (err?: AxiosError<any>, response?: AxiosResponse) => void;
9
+ tokenExpiration: (err?: AxiosError<any>, response?: AxiosResponseRewrite) => void;
7
10
  handleReconnect: () => Promise<any>;
8
11
  filter_url?: Array<string>;
9
12
  code?: number;
@@ -18,7 +21,7 @@ interface Options {
18
21
  * response处理函数
19
22
  * @param response AxiosResponse实例
20
23
  */
21
- handleResponse?: (response: AxiosResponse) => void;
24
+ handleResponse?: (response: AxiosResponseRewrite) => void;
22
25
  /**
23
26
  * 错误处理函数
24
27
  * @param msg 错误消息
@@ -48,65 +51,153 @@ interface RequestOptions {
48
51
  [key: string]: any;
49
52
  }
50
53
 
51
- declare let instance: AxiosInstance;
52
- declare const abortAllRequests: () => void;
53
- declare const crateAxios: (options: Options) => void;
54
- declare const post: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
55
- declare const get: <T = any>(url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
56
- declare const put: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
57
- declare const patch: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
58
- declare const remove: <T = any>(url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
59
- declare const getStream: (url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<any>>;
60
- declare const postStream: (url: string, data: any, ext?: any) => Promise<AxiosResponseRewrite<any>>;
61
- declare const request: {
62
- post: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
63
- get: <T = any>(url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
64
- put: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
65
- patch: <T = any>(url: string, data?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
66
- remove: <T = any>(url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<T>>;
67
- getStream: (url: string, params?: any, ext?: any) => Promise<AxiosResponseRewrite<any>>;
68
- postStream: (url: string, data: any, ext?: any) => Promise<AxiosResponseRewrite<any>>;
69
- };
54
+ /**
55
+ * 扩展的 Options 接口,添加重复请求控制
56
+ */
57
+ interface ExtendedOptions extends Options {
58
+ /**
59
+ * 是否取消重复请求,只保留最后一次
60
+ * @default false
61
+ */
62
+ cancelDuplicateRequests?: boolean;
63
+ }
64
+ /**
65
+ * AxiosService - 封装所有 axios 相关功能
66
+ */
67
+ declare class AxiosService {
68
+ private instance;
69
+ private options;
70
+ private failedQueue;
71
+ private isRefreshing;
72
+ private pendingRequests;
73
+ private isApp;
74
+ constructor(options?: Partial<ExtendedOptions>);
75
+ /**
76
+ * 初始化 axios 实例
77
+ */
78
+ initialize(options?: Partial<ExtendedOptions>): void;
79
+ /**
80
+ * 获取 axios 实例
81
+ */
82
+ getInstance(): AxiosInstance;
83
+ /**
84
+ * 生成请求的唯一标识
85
+ * 如果启用了 cancelDuplicateRequests,则基于 method + url + params/data
86
+ * 否则使用随机字符串
87
+ */
88
+ private generateRequestKey;
89
+ /**
90
+ * 记录请求 - 支持 AbortController
91
+ */
92
+ private requestRecords;
93
+ /**
94
+ * 请求拦截器处理
95
+ */
96
+ private handleRequest;
97
+ /**
98
+ * 响应拦截器处理
99
+ */
100
+ private handleResponse;
101
+ /**
102
+ * Token 刷新处理
103
+ */
104
+ private createTokenRefreshHandler;
105
+ /**
106
+ * 错误处理
107
+ */
108
+ private errorHandler;
109
+ /**
110
+ * 取消所有进行中的请求
111
+ */
112
+ abortAllRequests(): void;
113
+ /**
114
+ * 取消特定请求
115
+ */
116
+ abortRequest(requestKey: string): void;
117
+ /**
118
+ * 获取当前进行中的请求数量
119
+ */
120
+ getPendingRequestsCount(): number;
121
+ /**
122
+ * HTTP 方法封装 - POST
123
+ */
124
+ post<T = any>(url: string, data?: any, ext?: any): Promise<AxiosResponseRewrite<T>>;
125
+ /**
126
+ * HTTP 方法封装 - GET
127
+ */
128
+ get<T = any>(url: string, params?: any, ext?: any): Promise<AxiosResponseRewrite<T>>;
129
+ /**
130
+ * HTTP 方法封装 - PUT
131
+ */
132
+ put<T = any>(url: string, data?: any, ext?: any): Promise<AxiosResponseRewrite<T>>;
133
+ /**
134
+ * HTTP 方法封装 - PATCH
135
+ */
136
+ patch<T = any>(url: string, data?: any, ext?: any): Promise<AxiosResponseRewrite<T>>;
137
+ /**
138
+ * HTTP 方法封装 - DELETE
139
+ */
140
+ remove<T = any>(url: string, params?: any, ext?: any): Promise<AxiosResponseRewrite<T>>;
141
+ /**
142
+ * 获取流数据 - GET
143
+ */
144
+ getStream(url: string, params?: any, ext?: any): Promise<any>;
145
+ /**
146
+ * 获取流数据 - POST
147
+ */
148
+ postStream(url: string, data: any, ext?: any): Promise<any>;
149
+ }
150
+ declare const createAxiosService: (options?: Partial<ExtendedOptions>) => AxiosService;
151
+ /**
152
+ * Request 类 - 业务请求封装
153
+ * 默认使用全局共享的 axios 实例,特殊情况可传入自定义实例
154
+ */
70
155
  declare class Request {
71
156
  basePath: string;
72
- constructor(basePath: string);
157
+ private _instance?;
158
+ constructor(basePath: string, instance?: AxiosInstance);
159
+ /**
160
+ * 获取 axios 实例
161
+ * 延迟获取以确保全局实例已初始化
162
+ */
163
+ private get instance();
73
164
  private requestWrapper;
74
165
  /**
75
166
  * 分页查询
76
- * @param {object} data 查询参数
77
- * @param {object} options 请求配置
167
+ * @param data 查询参数
168
+ * @param options 请求配置
78
169
  */
79
170
  page<T = any>(data?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<PageResult<T>>>;
80
171
  /**
81
172
  * 不分页查询
82
- * @param {object} data 查询参数
83
- * @param {object} options 请求配置
173
+ * @param data 查询参数
174
+ * @param options 请求配置
84
175
  */
85
176
  noPage<T = any>(data?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<T[]>>;
86
177
  /**
87
178
  * 详情查询
88
- * @param {string} id 详情ID
89
- * @param {object} params 查询参数
90
- * @param {object} options 请求配置
179
+ * @param id 详情ID
180
+ * @param params 查询参数
181
+ * @param options 请求配置
91
182
  */
92
183
  detail<T = any>(id: string, params?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<T>>;
93
184
  /**
94
185
  * 保存
95
- * @param {object} data 保存参数
96
- * @param {object} options 请求配置
186
+ * @param data 保存参数
187
+ * @param options 请求配置
97
188
  */
98
189
  save<T = any>(data?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<T>>;
99
190
  /**
100
191
  * 更新
101
- * @param {object} data 更新参数
102
- * @param {object} options 请求配置
192
+ * @param data 更新参数
193
+ * @param options 请求配置
103
194
  */
104
195
  update<T extends UpdateResult>(data?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<T>>;
105
196
  /**
106
197
  * 删除
107
- * @param {string} id 删除ID
108
- * @param {object} params 请求参数
109
- * @param {object} options 请求配置
198
+ * @param id 删除ID
199
+ * @param params 请求参数
200
+ * @param options 请求配置
110
201
  * @example ${basePath}/${id}
111
202
  */
112
203
  delete<T = any>(id: string, params?: any, options?: RequestOptions): Promise<AxiosResponseRewrite<T>>;
@@ -122,23 +213,94 @@ declare class Request {
122
213
  put<T = any>(url: string, data?: any, options?: any): Promise<AxiosResponseRewrite<T>>;
123
214
  patch<T = any>(url: string, data?: any, options?: any): Promise<AxiosResponseRewrite<T>>;
124
215
  remove<T = any>(url: string, params?: any, options?: any): Promise<AxiosResponseRewrite<T>>;
125
- getStream(url: string, params?: any, options?: any): Promise<AxiosResponseRewrite<any>>;
126
- postStream(url: string, data?: any, options?: any): Promise<AxiosResponseRewrite<any>>;
216
+ getStream(url: string, params?: any, options?: any): Promise<any>;
217
+ postStream(url: string, data?: any, options?: any): Promise<any>;
127
218
  }
219
+ declare const request: {
220
+ post: any;
221
+ get: any;
222
+ put: any;
223
+ patch: any;
224
+ remove: any;
225
+ getStream: any;
226
+ postStream: any;
227
+ };
228
+ declare const post: any;
229
+ declare const get: any;
230
+ declare const put: any;
231
+ declare const patch: any;
232
+ declare const remove: any;
233
+ declare const getStream: any;
234
+ declare const postStream: any;
235
+ declare const abortAllRequests: () => void;
236
+ declare const getInstance: () => AxiosInstance;
237
+ declare let instance: AxiosInstance;
238
+ declare const crateAxios: (options: ExtendedOptions) => void;
128
239
 
240
+ /**
241
+ * NdJson 配置选项
242
+ */
243
+ interface NdJsonOptions {
244
+ /** 成功状态码 */
245
+ code?: number;
246
+ /** 状态码字段名 */
247
+ codeKey?: string;
248
+ /** 不需要 token 的 URL 列表 */
249
+ filter_url?: string[];
250
+ /** token 过期回调 */
251
+ tokenExpiration?: () => void;
252
+ /** 自定义请求配置 */
253
+ requestOptions?: (config: RequestInit) => Record<string, unknown>;
254
+ /** 自定义响应处理 */
255
+ handleResponse?: <T>(response: T) => T;
256
+ /** 基础 API 地址,默认使用 BASE_API 常量 */
257
+ baseURL?: string;
258
+ }
129
259
  declare class NdJson {
130
- options: any;
131
- isRead: boolean;
132
- controller: any;
133
- constructor();
134
- create(options: any): void;
135
- getUrl(url: any): any;
136
- get(url: any, data?: string, extra?: {}): Observable<unknown>;
137
- post(url: any, data?: BodyInit | any, extra?: {}): Observable<unknown>;
138
- handleRequest(url: any): RequestInit;
139
- handleResponse(response: any): any;
140
- cancel(): void;
260
+ private options;
261
+ private activeRequests;
262
+ constructor(options?: NdJsonOptions);
263
+ /**
264
+ * 初始化/更新配置
265
+ */
266
+ create(options: NdJsonOptions): void;
267
+ /**
268
+ * 获取完整 URL
269
+ */
270
+ private getUrl;
271
+ /**
272
+ * 处理 NDJSON 流的核心逻辑
273
+ */
274
+ private processStream;
275
+ /**
276
+ * 解析缓冲区中的完整行
277
+ */
278
+ private parseLines;
279
+ /**
280
+ * 刷新剩余缓冲区
281
+ */
282
+ private flushBuffer;
283
+ /**
284
+ * 创建请求的 Observable
285
+ */
286
+ private request;
287
+ get<T = unknown>(url: string, _data?: string, extra?: RequestInit): Observable<T>;
288
+ post<T = unknown>(url: string, data?: BodyInit | Record<string, unknown>, extra?: RequestInit): Observable<T>;
289
+ private handleRequest;
290
+ handleResponse<T>(response: T): T;
291
+ /**
292
+ * 取消所有活跃的请求
293
+ */
294
+ cancelAll(): void;
141
295
  }
296
+ /**
297
+ * 创建新的 NdJson 实例
298
+ */
299
+ declare const createNdJson: (options?: NdJsonOptions) => NdJson;
300
+ /**
301
+ * 初始化默认实例
302
+ */
303
+ declare const createNdJsonService: (options: NdJsonOptions) => void;
142
304
  declare const ndJson: NdJson;
143
305
 
144
306
  interface WebSocketMessage {
@@ -208,4 +370,4 @@ declare const installStores: (_s?: {}) => void;
208
370
  declare let locales: any;
209
371
  declare const installLocales: (l: any) => void;
210
372
 
211
- export { NdJson, Request, WebSocketClient, abortAllRequests, crateAxios, get, getStream, installLocales, installRouter, installStores, instance, locales, ndJson, patch, post, postStream, put, remove, request, router, stores, wsClient };
373
+ export { AxiosService, type ExtendedOptions, NdJson, type NdJsonOptions, Request, WebSocketClient, abortAllRequests, crateAxios, createAxiosService, createNdJson, createNdJsonService, get, getInstance, getStream, installLocales, installRouter, installStores, instance, locales, ndJson, patch, post, postStream, put, remove, request, router, stores, wsClient };
package/dist/index.mjs CHANGED
@@ -1,3 +1,2 @@
1
- import{TOKEN_KEY as S,BASE_API as U,LOCAL_BASE_API as D}from"@jetlinks-web/constants";import{getToken as P,randomString as B}from"@jetlinks-web/utils";import J from"axios";import{isFunction as A,isObject as G}from"lodash-es";var l=window.JetlinksCore?.instance||null,o={filter_url:[],code:200,codeKey:"status",timeout:1e3*15,handleRequest:void 0,handleResponse:void 0,handleError:void 0,langKey:"lang",requestOptions:s=>({}),tokenExpiration:()=>{},handleReconnect:()=>Promise.resolve(),isCreateTokenRefresh:!1},R=[],q=!1,me=window.__MICRO_APP_ENVIRONMENT__,g=new Map,Y=s=>{let e=s.__requestKey;g.has(e)&&g.get(e)?.abort();let t=new AbortController;s.signal=t.signal,s.__requestKey=B(32),g.set(e,t)},F=s=>{Y(s);let e=P(),t=localStorage.getItem(o.langKey),n=localStorage.getItem(D);if(t&&(s.headers[o.langKey]=t),n&&!s.baseURL){let r=s.url.startsWith("/")?s.url:`/${s.url}`;s.url=n+r}if(!e&&!o.filter_url?.some(r=>s.url?.includes(r)))return o.tokenExpiration?.(),s;if(s.headers[S]||(s.headers[S]=e),o.requestOptions&&A(o.requestOptions)){let r=o.requestOptions(s);if(r&&G(r))for(let a in r)s[a]=r[a]}return s},V=s=>{if(o.handleResponse&&A(o.handleResponse))return o.handleResponse(s);let e=s.config?.__requestKey;if(e&&g.delete(e),s.data instanceof ArrayBuffer)return s;let t=s.data[o.codeKey||"status"];return typeof s.data=="object"&&typeof s.data.success>"u"&&(s.data.success=t===o.code),s.data},z=async s=>{let e=s.config;if(q)return new Promise((t,n)=>{R.push({resolve:t,reject:n})}).then(t=>(e.headers[S]=t,l(e))).catch(t=>Promise.reject(t));e._retry=!0,q=!0;try{if(await o.handleReconnect?.()){let n=P();return e.headers[S]=n,R.forEach(r=>r.resolve(n)),l(e)}}catch(t){throw R.forEach(n=>n.reject(t)),t}finally{R=[],q=!1}},M=async s=>{let e=s.response?.message||"Error",t=0;if(s.response){let{data:n,status:r}=s.response;switch(t=r,r){case 400:case 403:case 500:e=`${n?.message}`.substring(0,90);break;case 401:if(e=s.response.data.result?.text||"\u7528\u6237\u672A\u767B\u5F55",o.tokenExpiration?.(s),o.isCreateTokenRefresh)return z(s);break;case 404:e=s?.response?.data?.message||`${n?.error} ${n?.path}`;break;default:break}}else s.response===void 0&&(e=s.message.includes("timeout")?"\u63A5\u53E3\u54CD\u5E94\u8D85\u65F6":s.message,t="timeout");return o.handleError&&A(o.handleError)&&o.handleError(e,t,s),Promise.reject(s)},ge=()=>{g.forEach(s=>s.abort()),g.clear()},we=s=>{s&&(o=Object.assign(o,s)),l=J.create({withCredentials:!1,timeout:o.timeout,baseURL:U}),l.interceptors.request.use(F,M),l.interceptors.response.use(V,M)},x=(s,e={},t)=>l({method:"POST",url:s,data:e,...t}),T=(s,e=void 0,t)=>l({method:"GET",url:s,params:e,...t}),$=(s,e={},t)=>l({method:"PUT",url:s,data:e,...t}),j=(s,e={},t)=>l({method:"patch",url:s,data:e,...t}),I=(s,e=void 0,t)=>l({method:"DELETE",url:s,params:e,...t}),X=(s,e,t)=>T(s,e,{responseType:"arraybuffer",...t}),Z=(s,e,t)=>x(s,e,{responseType:"arraybuffer",...t}),ee={post:x,get:T,put:$,patch:j,remove:I,getStream:X,postStream:Z},v=class{constructor(e){this.basePath=e;this.basePath=e.startsWith("/")?e:`/${e}`}requestWrapper(e,t,n={},r={}){let{url:a=e,method:y=t,...i}=r;return ee[y](`${this.basePath}${a}`,n,i)}page(e={},t={url:void 0,method:void 0}){return this.requestWrapper("/_query","post",e,t)}noPage(e={},t={url:void 0,method:void 0}){return this.requestWrapper("/_query/no-paging","post",{paging:!1,...e},t)}detail(e,t,n={url:void 0,method:void 0}){return this.requestWrapper(`/${e}/detail`,"get",t,n)}save(e={},t={url:void 0,method:void 0}){return this.requestWrapper("","post",e,t)}update(e={},t={url:void 0,method:void 0}){return this.requestWrapper("","patch",e,t)}delete(e,t,n={url:void 0,method:void 0}){return this.requestWrapper(`/${e}`,"post",t,n)}batch(e={},t,n){let r=`/_batch${t?"/"+t:""}`;return this.requestWrapper(r,"post",e,n)}post(e,t,n){return x(`${this.basePath}${e}`,t,n)}get(e,t,n){return T(`${this.basePath}${e}`,t,n)}put(e,t,n){return $(`${this.basePath}${e}`,t,n)}patch(e,t,n){return j(`${this.basePath}${e}`,t,n)}remove(e,t,n){return I(`${this.basePath}${e}`,t,n)}getStream(e,t,n){return T(`${this.basePath}${e}`,t,{responseType:"arraybuffer",...n})}postStream(e,t,n){return x(`${this.basePath}${e}`,t,{responseType:"arraybuffer",...n})}};import{getToken as te}from"@jetlinks-web/utils";import{BASE_API as se,TOKEN_KEY as N}from"@jetlinks-web/constants";import{isFunction as K,isObject as L}from"lodash-es";import{Observable as H}from"rxjs";var _=class{options={code:200,codeKey:"status"};isRead=!1;controller=null;constructor(){}create(e){this.options=Object.assign(this.options,e)}getUrl(e){return se+e}get(e,t="{}",n={}){let r=this.getUrl(e),a=this,y=this.controller=new AbortController;return new H(i=>{let h;return fetch(r,{method:"GET",signal:y.signal,keepalive:!0,...n,...this.handleRequest(r)}).then(m=>{h=m.body?.getReader();let k=new TextDecoder,p="";if(!h){i.error(new Error("No readable stream available"));return}let w=()=>{if(!a.isRead){h.cancel(),i.complete();return}h.read().then(({done:d,value:E})=>{if(d){if(p.trim().length>0)try{i.next(JSON.parse(p.trim()))}catch(u){i.error(u)}i.complete();return}let C=k.decode(E,{stream:!0});p+=C;let f=p.split(`
2
- `);for(let u=0;u<f.length-1;++u){let b=f[u].trim();if(b.length>0)try{i.next(JSON.parse(b.startsWith("data:")?b.slice(5):b))}catch(O){i.error(O);return}}p=f[f.length-1],w()}).catch(d=>{d.name!=="AbortError"&&i.error(d)})};a.isRead=!0,w()}).catch(m=>{i.error(m)}),()=>{a.cancel()}})}post(e,t={},n={}){let r=this.getUrl(e),a=this,y=this.controller=new AbortController;return new H(i=>{let h;return fetch(r,{method:"POST",signal:y.signal,keepalive:!0,body:L(t)?JSON.stringify(t):t,...n,...this.handleRequest(r)}).then(async m=>{h=m.body?.getReader();let k=new TextDecoder,p="";if(!h){i.error(new Error("No readable stream available"));return}let w=()=>{if(!a.isRead){h.cancel(),i.complete();return}h.read().then(({done:d,value:E})=>{if(d){if(p.trim().length>0)try{i.next(JSON.parse(p.trim()))}catch(u){i.error(u)}i.complete();return}let C=k.decode(E,{stream:!0});p+=C;let f=p.split(`
3
- `);for(let u=0;u<f.length-1;++u){let b=f[u].trim();if(b.length>0)try{i.next(JSON.parse(b.startsWith("data:")?b.slice(5):b))}catch(O){i.error(O);return}}p=f[f.length-1],w()}).catch(d=>{d.name!=="AbortError"&&i.error(d)})};a.isRead=!0,w()}).catch(m=>{i.error(m)}),()=>{a.cancel()}})}handleRequest(e){let t={headers:{"Content-Type":"application/x-ndjson"}},n=te();if(!n&&this.options.filter_url?.some(r=>r.includes(e)))return this.options.tokenExpiration?.(),t;if(t.headers[N]||(t.headers[N]=n),this.options.requestOptions&&K(this.options.requestOptions)){let r=this.options.requestOptions(t);if(r&&L(r))for(let a in r)t[a]=r[a]}return t}handleResponse(e){return this.options.handleResponse&&K(this.options.handleResponse)?this.options.handleResponse(e):e}cancel(){this.isRead&&(this.isRead=!1),this.controller&&this.controller.abort()}},Ee=new _;import{webSocket as ne}from"rxjs/webSocket";import{Observable as re,Subject as ie,timer as Q,EMPTY as oe}from"rxjs";import{retry as ae,catchError as ce}from"rxjs/operators";import{notification as pe}from"ant-design-vue";var c=window.__MICRO_APP_ENVIRONMENT__,W=class{ws=null;subscriptions=new Map;pendingSubscriptions=new Map;heartbeatSubscription=null;reconnectAttempts=0;maxReconnectAttempts=2;isConnected=!1;tempQueue=[];url="";options={};wsClient;constructor(e){this.setOptions(e),this.setupConnectionMonitor(),c&&window.microApp.addGlobalDataListener(t=>{this.wsClient=t.wsClient})}setOptions(e){this.options=e||{}}initWebSocket(e){this.url=e}setupConnectionMonitor(){c||(window.addEventListener("online",()=>{console.log("Network is online, attempting to reconnect..."),this.reconnect()}),window.addEventListener("offline",()=>{console.log("Network is offline, caching subscriptions..."),this.cacheSubscriptions()}),window.addEventListener("beforeunload",()=>{this.disconnect()}))}getReconnectDelay(){return this.reconnectAttempts<=10?5e3:this.reconnectAttempts<=20?15e3:6e4}setupWebSocket(){if(c&&this.wsClient){this.wsClient.setupWebSocket();return}this.ws||!this.url||(this.ws=ne({url:this.url,openObserver:{next:()=>{console.log("WebSocket connected"),this.isConnected=!0,this.reconnectAttempts=0,this.startHeartbeat(),this.restoreSubscriptions(),this.processTempQueue()}},closeObserver:{next:()=>{console.log("WebSocket disconnected"),this.isConnected=!1;let e=this.getReconnectDelay();setTimeout(()=>{this.reconnectAttempts+=1,!(this.reconnectAttempts>this.maxReconnectAttempts)&&(this.cacheSubscriptions(),this.stopHeartbeat(),this.reconnect())},e)}}}),this.ws.pipe(ce(e=>(console.error("WebSocket error:",e),oe)),ae({delay:(e,t)=>{if(this.reconnectAttempts=t,t>this.maxReconnectAttempts)throw new Error("Max reconnection attempts reached");return Q(this.getReconnectDelay())}})).subscribe(e=>this.handleMessage(e),e=>console.error("WebSocket error:",e)))}startHeartbeat(){if(c&&this.wsClient){this.wsClient.startHeartbeat();return}this.stopHeartbeat(),this.heartbeatSubscription=Q(0,2e3).subscribe(()=>{this.send({type:"ping"})})}stopHeartbeat(){if(c&&this.wsClient){this.wsClient.stopHeartbeat();return}this.heartbeatSubscription&&(this.heartbeatSubscription.unsubscribe(),this.heartbeatSubscription=null)}handleMessage(e){if(c&&this.wsClient){this.wsClient.handleMessage(e);return}if(e.type==="pong")return;if(e.type==="error"){this.options.onError?this.options.onError(e):pe.error({key:"error",message:e.message});return}let t=this.subscriptions.get(e.requestId||"");t&&(e.type==="complete"?(t.complete(),this.subscriptions.delete(e.requestId||"")):e.type==="result"&&t.next(e))}processTempQueue(){if(c&&this.wsClient){this.wsClient.processTempQueue();return}for(;this.tempQueue.length>0;){let e=this.tempQueue.shift();e&&this.send(e)}}cacheSubscriptions(){if(c&&this.wsClient){this.wsClient.cacheSubscriptions();return}this.pendingSubscriptions=new Map(this.subscriptions),this.subscriptions.clear()}restoreSubscriptions(){if(c&&this.wsClient){this.wsClient.restoreSubscriptions();return}this.pendingSubscriptions.forEach((e,t)=>{this.subscriptions.set(t,e)}),this.pendingSubscriptions.clear()}reconnect(){if(c&&this.wsClient){this.wsClient.reconnect();return}!this.isConnected&&navigator.onLine&&(this.ws=null,this.setupWebSocket())}connect(){if(c&&this.wsClient){this.wsClient.connect();return}this.setupWebSocket()}disconnect(){if(c&&this.wsClient){this.wsClient.disconnect();return}this.ws&&(this.ws.complete(),this.ws=null),this.stopHeartbeat(),this.subscriptions.clear(),this.pendingSubscriptions.clear(),this.tempQueue=[]}send(e){if(c&&this.wsClient){this.wsClient.send(e);return}this.ws&&this.isConnected?this.ws.next(e):this.tempQueue.push(e)}getWebSocket(e,t,n={}){if(console.log("getWebSocket",this.wsClient,e),c&&this.wsClient)return this.wsClient.getWebSocket(e,t,n);let r=new ie;this.subscriptions.set(e,r);let a={id:e,topic:t,parameter:n,type:"sub"};return this.send(a),new re(y=>{let i=r.subscribe(y);return()=>{i.unsubscribe(),this.send({id:e,type:"unsub"}),this.subscriptions.delete(e)}})}},Me=new W;var ue,Pe=s=>{ue=s};var le={},je=(s={})=>{le=s};var he,Ne=s=>{he=s};export{_ as NdJson,v as Request,W as WebSocketClient,ge as abortAllRequests,we as crateAxios,T as get,X as getStream,Ne as installLocales,Pe as installRouter,je as installStores,l as instance,he as locales,Ee as ndJson,j as patch,x as post,Z as postStream,$ as put,I as remove,ee as request,ue as router,le as stores,Me as wsClient};
1
+ import{TOKEN_KEY as l,BASE_API as q,LOCAL_BASE_API as E}from"@jetlinks-web/constants";import{getToken as x,randomString as O}from"@jetlinks-web/utils";import b from"axios";import{isFunction as g,isObject as k}from"lodash-es";var h=class{instance=null;options;failedQueue=[];isRefreshing=!1;pendingRequests=new Map;isApp=window.__MICRO_APP_ENVIRONMENT__;constructor(e){this.options={filter_url:[],code:200,codeKey:"status",timeout:1e3*15,handleRequest:void 0,handleResponse:void 0,handleError:void 0,langKey:"lang",requestOptions:t=>({}),tokenExpiration:()=>{},handleReconnect:()=>Promise.resolve(),isCreateTokenRefresh:!1,cancelDuplicateRequests:!1,...e},window.JetlinksCore?.instance&&(this.instance=window.JetlinksCore.instance)}initialize(e){e&&(this.options={...this.options,...e}),this.instance=b.create({withCredentials:!1,timeout:this.options.timeout,baseURL:q}),this.instance.interceptors.request.use(t=>this.handleRequest(t),t=>this.errorHandler(t)),this.instance.interceptors.response.use(t=>this.handleResponse(t),t=>this.errorHandler(t))}getInstance(){return this.instance||this.initialize(),this.instance}generateRequestKey(e){let t=e.method?.toUpperCase()||"GET",s=e.url||"",n=e.params||{},i=e.data||{},r=t==="GET"?n:i,a="";try{a=JSON.stringify(r,Object.keys(r).sort())}catch{a=O(16)}return`${t}:${s}:${a}`}requestRecords(e){if(!this.options.cancelDuplicateRequests)return;let t=this.generateRequestKey(e);this.pendingRequests.has(t)&&(this.pendingRequests.get(t)?.abort(),this.pendingRequests.delete(t));let s=new AbortController;e.signal=s.signal,e.__requestKey=t,this.pendingRequests.set(t,s)}handleRequest(e){this.requestRecords(e);let t=x(),s=localStorage.getItem(this.options.langKey),n=localStorage.getItem(E);if(s&&(e.headers[this.options.langKey]=s),n&&!e.baseURL){let i=e.url.startsWith("/")?e.url:`/${e.url}`;e.url=n+i}if(!t&&!this.options.filter_url?.some(i=>e.url?.includes(i)))return this.options.tokenExpiration?.(),e;if(e.headers[l]||(e.headers[l]=t),this.options.requestOptions&&g(this.options.requestOptions)){let i=this.options.requestOptions(e);if(i&&k(i))for(let r in i)e[r]=i[r]}return e}handleResponse(e){let t=e.config?.__requestKey;if(t&&this.pendingRequests.delete(t),this.options.handleResponse&&g(this.options.handleResponse))return this.options.handleResponse(e);if(e.data instanceof ArrayBuffer)return e;let s=e.data[this.options.codeKey||"status"];return typeof e.data=="object"&&typeof e.data.success>"u"&&(e.data.success=s===this.options.code),e.data}async createTokenRefreshHandler(e){let t=e.config;if(this.isRefreshing)return new Promise((s,n)=>{this.failedQueue.push({resolve:s,reject:n})}).then(s=>t.signal?.aborted?Promise.reject(new b.Cancel("Request aborted")):(t.headers[l]=s,this.instance(t))).catch(s=>Promise.reject(s));t._retry=!0,this.isRefreshing=!0;try{if(await this.options.handleReconnect?.()){let n=x();return t.headers[l]=n,this.failedQueue.forEach(i=>i.resolve(n)),this.instance(t)}}catch(s){throw this.failedQueue.forEach(n=>n.reject(s)),s}finally{this.failedQueue=[],this.isRefreshing=!1}}async errorHandler(e){let t=e.config?.__requestKey;if(t&&this.pendingRequests.delete(t),b.isCancel(e))return Promise.reject(e);let s=e.response?.message||"Error",n=0,i=e.response;if(i){let{data:r,status:a}=i;switch(n=a,a){case 400:case 403:case 500:s=`${r?.message}`.substring(0,90);break;case 401:if(s=r?.result?.text||"\u7528\u6237\u672A\u767B\u5F55",this.options.tokenExpiration?.(e),this.options.isCreateTokenRefresh)return this.createTokenRefreshHandler(e);break;case 404:s=r?.message||`${r?.error} ${r?.path}`;break;default:break}}else{let r=e;r.message&&(s=r.message.includes("timeout")?"\u63A5\u53E3\u54CD\u5E94\u8D85\u65F6":r.message,n="timeout")}return this.options.handleError&&g(this.options.handleError)&&this.options.handleError(s,n,e),Promise.reject(e)}abortAllRequests(){this.pendingRequests.forEach(e=>e.abort()),this.pendingRequests.clear()}abortRequest(e){let t=this.pendingRequests.get(e);t&&(t.abort(),this.pendingRequests.delete(e))}getPendingRequestsCount(){return this.pendingRequests.size}post(e,t={},s){return this.getInstance()({method:"POST",url:e,data:t,...s})}get(e,t=void 0,s){return this.getInstance()({method:"GET",url:e,params:t,...s})}put(e,t={},s){return this.getInstance()({method:"PUT",url:e,data:t,...s})}patch(e,t={},s){return this.getInstance()({method:"PATCH",url:e,data:t,...s})}remove(e,t=void 0,s){return this.getInstance()({method:"DELETE",url:e,params:t,...s})}getStream(e,t,s){return this.get(e,t,{responseType:"arraybuffer",...s})}postStream(e,t,s){return this.post(e,t,{responseType:"arraybuffer",...s})}},o=new h,Y=p=>{let e=new h(p);return e.initialize(),e},w=class{constructor(e,t){this.basePath=e;this.basePath=e.startsWith("/")?e:`/${e}`,this._instance=t}_instance;get instance(){return this._instance||o.getInstance()}requestWrapper(e,t,s={},n={}){let{url:i=e,method:r=t,...a}=n;return this[r].call(this,i,s,a)}page(e={},t={url:void 0,method:void 0}){return this.requestWrapper("/_query","post",e,t)}noPage(e={},t={url:void 0,method:void 0}){return this.requestWrapper("/_query/no-paging","post",{paging:!1,...e},t)}detail(e,t,s={url:void 0,method:void 0}){return this.requestWrapper(`/${e}/detail`,"get",t,s)}save(e={},t={url:void 0,method:void 0}){return this.requestWrapper("","post",e,t)}update(e={},t={url:void 0,method:void 0}){return this.requestWrapper("","patch",e,t)}delete(e,t,s={url:void 0,method:void 0}){return this.requestWrapper(`/${e}`,"remove",t,s)}batch(e={},t,s){let n=`/_batch${t?"/"+t:""}`;return this.requestWrapper(n,"post",e,s)}post(e,t,s){return this.instance({method:"POST",url:`${this.basePath}${e}`,data:t,...s})}get(e,t,s){return this.instance({method:"GET",url:`${this.basePath}${e}`,params:t,...s})}put(e,t,s){return this.instance({method:"PUT",url:`${this.basePath}${e}`,data:t,...s})}patch(e,t,s){return this.instance({method:"PATCH",url:`${this.basePath}${e}`,data:t,...s})}remove(e,t,s){return this.instance({method:"DELETE",url:`${this.basePath}${e}`,params:t,...s})}getStream(e,t,s){return this.get(`${e}`,t,{responseType:"arraybuffer",...s})}postStream(e,t,s){return this.post(`${e}`,t,{responseType:"arraybuffer",...s})}},F={post:o.post.bind(o),get:o.get.bind(o),put:o.put.bind(o),patch:o.patch.bind(o),remove:o.remove.bind(o),getStream:o.getStream.bind(o),postStream:o.postStream.bind(o)},V=o.post.bind(o),X=o.get.bind(o),Z=o.put.bind(o),ee=o.patch.bind(o),te=o.remove.bind(o),se=o.getStream.bind(o),ne=o.postStream.bind(o),ie=()=>o.abortAllRequests(),re=()=>o.getInstance(),C,oe=p=>{o.initialize(p),C=o.getInstance()};import{getToken as P}from"@jetlinks-web/utils";import{BASE_API as _,TOKEN_KEY as W}from"@jetlinks-web/constants";import{isFunction as T,isObject as S}from"lodash-es";import{Observable as I}from"rxjs";var d=class{options={code:200,codeKey:"status"};activeRequests=new Set;constructor(e){e&&(this.options={...this.options,...e})}create(e){this.options={...this.options,...e}}getUrl(e){return(this.options.baseURL??_)+e}processStream(e,t,s){let n=new TextDecoder,i="",r=()=>{if(!s.isActive){e.cancel(),t.complete();return}e.read().then(({done:a,value:u})=>{if(a){this.flushBuffer(i,t),t.complete();return}i+=n.decode(u,{stream:!0}),i=this.parseLines(i,t),r()}).catch(a=>{a.name!=="AbortError"&&t.error(a)})};r()}parseLines(e,t){let s=e.split(`
2
+ `);for(let n=0;n<s.length-1;n++){let i=s[n].trim();if(i.length>0)try{let r=i.startsWith("data:")?i.slice(5):i;t.next(JSON.parse(r))}catch(r){return t.error(r),""}}return s[s.length-1]}flushBuffer(e,t){let s=e.trim();if(s.length>0)try{t.next(JSON.parse(s))}catch(n){t.error(n)}}request(e,t,s,n={}){let i=this.getUrl(t);return new I(r=>{let a=new AbortController,u={controller:a,isActive:!0};this.activeRequests.add(u);let m={method:e,signal:a.signal,keepalive:!0,...n,...this.handleRequest(i,e)};return e==="POST"&&s!==void 0&&(m.body=S(s)?JSON.stringify(s):s),fetch(i,m).then(f=>{let y=f.body?.getReader();if(!y){r.error(new Error("No readable stream available"));return}u.isActive=!0,this.processStream(y,r,u)}).catch(f=>{r.error(f)}),()=>{u.isActive=!1,a.abort(),this.activeRequests.delete(u)}})}get(e,t="{}",s={}){return this.request("GET",e,void 0,s)}post(e,t={},s={}){return this.request("POST",e,t,s)}handleRequest(e,t){let s={};t==="POST"&&(s["Content-Type"]="application/x-ndjson");let n={headers:s},i=P();if(!i&&this.options.filter_url?.some(r=>e.includes(r)))return this.options.tokenExpiration?.(),n;if(i&&(s[W]=i),this.options.requestOptions&&T(this.options.requestOptions)){let r=this.options.requestOptions(n);r&&S(r)&&Object.assign(n,r)}return n}handleResponse(e){return this.options.handleResponse&&T(this.options.handleResponse)?this.options.handleResponse(e):e}cancelAll(){this.activeRequests.forEach(e=>{e.isActive=!1,e.controller.abort()}),this.activeRequests.clear()}},v=new d,de=p=>new d(p),fe=p=>{v.create(p)},be=v;import{webSocket as M}from"rxjs/webSocket";import{Observable as N,Subject as $,timer as A,EMPTY as j}from"rxjs";import{retry as H,catchError as L}from"rxjs/operators";import{notification as J}from"ant-design-vue";var c=window.__MICRO_APP_ENVIRONMENT__,R=class{ws=null;subscriptions=new Map;pendingSubscriptions=new Map;heartbeatSubscription=null;reconnectAttempts=0;maxReconnectAttempts=2;isConnected=!1;tempQueue=[];url="";options={};wsClient;constructor(e){this.setOptions(e),this.setupConnectionMonitor(),c&&window.microApp.addGlobalDataListener(t=>{this.wsClient=t.wsClient})}setOptions(e){this.options=e||{}}initWebSocket(e){this.url=e}setupConnectionMonitor(){c||(window.addEventListener("online",()=>{console.log("Network is online, attempting to reconnect..."),this.reconnect()}),window.addEventListener("offline",()=>{console.log("Network is offline, caching subscriptions..."),this.cacheSubscriptions()}),window.addEventListener("beforeunload",()=>{this.disconnect()}))}getReconnectDelay(){return this.reconnectAttempts<=10?5e3:this.reconnectAttempts<=20?15e3:6e4}setupWebSocket(){if(c&&this.wsClient){this.wsClient.setupWebSocket();return}this.ws||!this.url||(this.ws=M({url:this.url,openObserver:{next:()=>{console.log("WebSocket connected"),this.isConnected=!0,this.reconnectAttempts=0,this.startHeartbeat(),this.restoreSubscriptions(),this.processTempQueue()}},closeObserver:{next:()=>{console.log("WebSocket disconnected"),this.isConnected=!1;let e=this.getReconnectDelay();setTimeout(()=>{this.reconnectAttempts+=1,!(this.reconnectAttempts>this.maxReconnectAttempts)&&(this.cacheSubscriptions(),this.stopHeartbeat(),this.reconnect())},e)}}}),this.ws.pipe(L(e=>(console.error("WebSocket error:",e),j)),H({delay:(e,t)=>{if(this.reconnectAttempts=t,t>this.maxReconnectAttempts)throw new Error("Max reconnection attempts reached");return A(this.getReconnectDelay())}})).subscribe(e=>this.handleMessage(e),e=>console.error("WebSocket error:",e)))}startHeartbeat(){if(c&&this.wsClient){this.wsClient.startHeartbeat();return}this.stopHeartbeat(),this.heartbeatSubscription=A(0,2e3).subscribe(()=>{this.send({type:"ping"})})}stopHeartbeat(){if(c&&this.wsClient){this.wsClient.stopHeartbeat();return}this.heartbeatSubscription&&(this.heartbeatSubscription.unsubscribe(),this.heartbeatSubscription=null)}handleMessage(e){if(c&&this.wsClient){this.wsClient.handleMessage(e);return}if(e.type==="pong")return;if(e.type==="error"){this.options.onError?this.options.onError(e):J.error({key:"error",message:e.message});return}let t=this.subscriptions.get(e.requestId||"");t&&(e.type==="complete"?(t.complete(),this.subscriptions.delete(e.requestId||"")):e.type==="result"&&t.next(e))}processTempQueue(){if(c&&this.wsClient){this.wsClient.processTempQueue();return}for(;this.tempQueue.length>0;){let e=this.tempQueue.shift();e&&this.send(e)}}cacheSubscriptions(){if(c&&this.wsClient){this.wsClient.cacheSubscriptions();return}this.pendingSubscriptions=new Map(this.subscriptions),this.subscriptions.clear()}restoreSubscriptions(){if(c&&this.wsClient){this.wsClient.restoreSubscriptions();return}this.pendingSubscriptions.forEach((e,t)=>{this.subscriptions.set(t,e)}),this.pendingSubscriptions.clear()}reconnect(){if(c&&this.wsClient){this.wsClient.reconnect();return}!this.isConnected&&navigator.onLine&&(this.ws=null,this.setupWebSocket())}connect(){if(c&&this.wsClient){this.wsClient.connect();return}this.setupWebSocket()}disconnect(){if(c&&this.wsClient){this.wsClient.disconnect();return}this.ws&&(this.ws.complete(),this.ws=null),this.stopHeartbeat(),this.subscriptions.clear(),this.pendingSubscriptions.clear(),this.tempQueue=[]}send(e){if(c&&this.wsClient){this.wsClient.send(e);return}this.ws&&this.isConnected?this.ws.next(e):this.tempQueue.push(e)}getWebSocket(e,t,s={}){if(console.log("getWebSocket",this.wsClient,e),c&&this.wsClient)return this.wsClient.getWebSocket(e,t,s);let n=new $;this.subscriptions.set(e,n);let i={id:e,topic:t,parameter:s,type:"sub"};return this.send(i),new N(r=>{let a=n.subscribe(r);return()=>{a.unsubscribe(),this.send({id:e,type:"unsub"}),this.subscriptions.delete(e)}})}},Te=new R;var K,ve=p=>{K=p};var U={},qe=(p={})=>{U=p};var Q,Oe=p=>{Q=p};export{h as AxiosService,d as NdJson,w as Request,R as WebSocketClient,ie as abortAllRequests,oe as crateAxios,Y as createAxiosService,de as createNdJson,fe as createNdJsonService,X as get,re as getInstance,se as getStream,Oe as installLocales,ve as installRouter,qe as installStores,C as instance,Q as locales,be as ndJson,ee as patch,V as post,ne as postStream,Z as put,te as remove,F as request,K as router,U as stores,Te as wsClient};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetlinks-web/core",
3
- "version": "2.2.18",
3
+ "version": "2.3.0",
4
4
  "main": "dist/index.mjs",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "axios": "^1.7.4",
30
30
  "rxjs": "^7.8.1",
31
31
  "@jetlinks-web/constants": "^1.0.9",
32
- "@jetlinks-web/types": "^1.0.2",
33
- "@jetlinks-web/utils": "^1.2.12"
32
+ "@jetlinks-web/utils": "^1.2.12",
33
+ "@jetlinks-web/types": "^1.0.2"
34
34
  },
35
35
  "publishConfig": {
36
36
  "registry": "https://registry.npmjs.org/",