jinbi-utils 1.0.21 → 1.0.23
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/chunk-optimizer.cjs +703 -0
- package/dist/index.esm.js +3085 -0
- package/dist/index.esm.min.js +15 -0
- package/dist/index.umd.js +3198 -0
- package/dist/index.umd.min.js +16 -0
- package/package.json +36 -3
- package/.babelrc +0 -19
- package/.cz-config.js +0 -55
- package/.dockerignore +0 -3
- package/.editorconfig +0 -12
- package/.eslintignore +0 -8
- package/.eslintrc.js +0 -54
- package/.versionrc.json +0 -9
- package/CHUNK_OPTIMIZER_USAGE.md +0 -132
- package/Dockerfile +0 -3
- package/QUICK_RELEASE.md +0 -85
- package/RELEASE_GUIDE.md +0 -243
- package/api-extractor.json +0 -15
- package/commitlint.config.js +0 -3
- package/jest.config.js +0 -15
- package/rollup.config.chunk-optimizer.js +0 -32
- package/rollup.config.js +0 -73
- package/src/array/index.ts +0 -85
- package/src/build/chunk-optimizer/ARCHITECTURE.md +0 -347
- package/src/build/chunk-optimizer/QUICK_START.md +0 -370
- package/src/build/chunk-optimizer/README.md +0 -240
- package/src/build/chunk-optimizer/core/chunk-generator.ts +0 -166
- package/src/build/chunk-optimizer/core/classifier.ts +0 -148
- package/src/build/chunk-optimizer/core/dependency-reader.ts +0 -138
- package/src/build/chunk-optimizer/examples/basic-usage.ts +0 -234
- package/src/build/chunk-optimizer/index.ts +0 -166
- package/src/build/chunk-optimizer/rules/common-rules.ts +0 -131
- package/src/build/chunk-optimizer/rules/framework-rules.ts +0 -93
- package/src/build/chunk-optimizer/rules/index.ts +0 -27
- package/src/build/chunk-optimizer/test.ts +0 -94
- package/src/build/chunk-optimizer/types.ts +0 -128
- package/src/color/index.ts +0 -58
- package/src/common/index.ts +0 -353
- package/src/constant/common.constant.ts +0 -13
- package/src/date/index.ts +0 -143
- package/src/dom/index.ts +0 -198
- package/src/file/index.ts +0 -319
- package/src/http/apiBuilder/README.md +0 -648
- package/src/http/apiBuilder/api-builder.ts +0 -502
- package/src/http/apiBuilder/example.ts +0 -243
- package/src/http/apiBuilder/index.ts +0 -1
- package/src/http/apiBuilder//345/277/253/351/200/237/345/217/202/350/200/203.md +0 -199
- package/src/http/http.ts +0 -79
- package/src/http/httpEnums.ts +0 -61
- package/src/iam/index.ts +0 -46
- package/src/index.ts +0 -20
- package/src/middleware/requestLogger.middware.ts +0 -371
- package/src/middleware/requestLoggerUnified.ts +0 -371
- package/src/number/index.ts +0 -362
- package/src/object/index.ts +0 -54
- package/src/print/index.ts +0 -102
- package/src/string/index.ts +0 -189
- package/src/utils/curl.ts +0 -108
- package/src/validate/index.ts +0 -100
- package/src/websocket/emitter.ts +0 -39
- package/src/websocket/index.ts +0 -6
- package/src/websocket/manager.ts +0 -151
- package/src/websocket/pinia-store.ts +0 -91
- package/src/websocket/service.ts +0 -34
- package/src/websocket/types.ts +0 -45
- package/test/common/index.test.ts +0 -19
- package/test/date/index.test.ts +0 -107
- package/test/file/index.test.ts +0 -104
- package/test/number/index.test.ts +0 -108
- package/test/object/index.test.ts +0 -20
- package/test/string/index.test.ts +0 -82
- package/tsconfig.json +0 -39
- package/typedoc.json +0 -12
package/src/utils/curl.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'child_process'
|
|
2
|
-
|
|
3
|
-
export type CurlRequestOptions = {
|
|
4
|
-
method?: string
|
|
5
|
-
url: string
|
|
6
|
-
headers?: Record<string, string>
|
|
7
|
-
data?: any
|
|
8
|
-
timeoutMs?: number
|
|
9
|
-
followRedirects?: boolean
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type CurlResult = {
|
|
13
|
-
status: number
|
|
14
|
-
headers: Record<string, string>
|
|
15
|
-
body: string
|
|
16
|
-
json?: any
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function buildArgs(opts: CurlRequestOptions): string[] {
|
|
20
|
-
const args: string[] = ['-sS', '-i']
|
|
21
|
-
const method = (opts.method || 'GET').toUpperCase()
|
|
22
|
-
if (method !== 'GET') args.push('-X', method)
|
|
23
|
-
|
|
24
|
-
const headers = { ...(opts.headers || {}) }
|
|
25
|
-
const hasContentType = Object.keys(headers).some((k) => k.toLowerCase() === 'content-type')
|
|
26
|
-
const hasBody = opts.data !== undefined && opts.data !== null
|
|
27
|
-
if (hasBody && !hasContentType) headers['Content-Type'] = 'application/json'
|
|
28
|
-
for (const k of Object.keys(headers)) {
|
|
29
|
-
args.push('-H', `${k}: ${headers[k]}`)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (hasBody) {
|
|
33
|
-
let payload: string
|
|
34
|
-
if (Buffer.isBuffer(opts.data)) payload = opts.data.toString()
|
|
35
|
-
else if (typeof opts.data === 'string') payload = opts.data
|
|
36
|
-
else payload = JSON.stringify(opts.data)
|
|
37
|
-
args.push('--data', payload)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const follow = opts.followRedirects !== false
|
|
41
|
-
if (follow) args.push('-L')
|
|
42
|
-
|
|
43
|
-
const timeoutMs = typeof opts.timeoutMs === 'number' && opts.timeoutMs > 0 ? opts.timeoutMs : 15000
|
|
44
|
-
args.push('--max-time', String(Math.ceil(timeoutMs / 1000)))
|
|
45
|
-
|
|
46
|
-
args.push(opts.url)
|
|
47
|
-
return args
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function parseHeaders(headerText: string): Record<string, string> {
|
|
51
|
-
const headers: Record<string, string> = {}
|
|
52
|
-
const lines = headerText.split(/\r?\n/)
|
|
53
|
-
for (const line of lines) {
|
|
54
|
-
const idx = line.indexOf(':')
|
|
55
|
-
if (idx > 0) {
|
|
56
|
-
const k = line.substring(0, idx).trim()
|
|
57
|
-
const v = line.substring(idx + 1).trim()
|
|
58
|
-
if (k) headers[k.toLowerCase()] = v
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return headers
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function parseResponse(raw: string): CurlResult {
|
|
65
|
-
const sepIndex = raw.indexOf('\r\n\r\n') >= 0 ? raw.indexOf('\r\n\r\n') : raw.indexOf('\n\n')
|
|
66
|
-
const headerText = sepIndex >= 0 ? raw.substring(0, sepIndex) : ''
|
|
67
|
-
const body = sepIndex >= 0 ? raw.substring(sepIndex + (raw.substr(sepIndex, 4) === '\r\n\r\n' ? 4 : 2)) : raw
|
|
68
|
-
const headers = parseHeaders(headerText)
|
|
69
|
-
let status = 200
|
|
70
|
-
const statusLine = headerText.split(/\r?\n/)[0] || ''
|
|
71
|
-
const m = statusLine.match(/HTTP\/[0-9.]+\s+(\d{3})/)
|
|
72
|
-
if (m) status = parseInt(m[1], 10)
|
|
73
|
-
let json: any
|
|
74
|
-
const ct = headers['content-type'] || ''
|
|
75
|
-
if (ct.includes('application/json')) {
|
|
76
|
-
try { json = JSON.parse(body) } catch {}
|
|
77
|
-
}
|
|
78
|
-
return { status, headers, body, json }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export class CurlClient {
|
|
82
|
-
async request(opts: CurlRequestOptions): Promise<CurlResult> {
|
|
83
|
-
const args = buildArgs(opts)
|
|
84
|
-
const cmd = process.platform === 'win32' ? 'curl.exe' : 'curl'
|
|
85
|
-
return await new Promise<CurlResult>((resolve, reject) => {
|
|
86
|
-
const p = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
87
|
-
let out = ''
|
|
88
|
-
let err = ''
|
|
89
|
-
p.stdout.on('data', (d) => { out += d.toString() })
|
|
90
|
-
p.stderr.on('data', (d) => { err += d.toString() })
|
|
91
|
-
p.on('error', (e) => reject(e))
|
|
92
|
-
p.on('close', (code) => {
|
|
93
|
-
if (code !== 0 && !out) return reject(new Error(err || `curl exit ${code}`))
|
|
94
|
-
try { resolve(parseResponse(out)) } catch (e) { reject(e as Error) }
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async get(url: string, opts?: Omit<CurlRequestOptions, 'url' | 'method'>): Promise<CurlResult> {
|
|
100
|
-
return this.request({ ...(opts || {}), url, method: 'GET' })
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async post(url: string, data?: any, opts?: Omit<CurlRequestOptions, 'url' | 'method' | 'data'>): Promise<CurlResult> {
|
|
104
|
-
return this.request({ ...(opts || {}), url, method: 'POST', data })
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export const curl = new CurlClient()
|
package/src/validate/index.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* validate处理相关
|
|
3
|
-
* @packageDocumentation
|
|
4
|
-
* @module Validate
|
|
5
|
-
* @preferred
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** 是否是外部网址
|
|
9
|
-
* @param {string} path
|
|
10
|
-
* @returns {Boolean}
|
|
11
|
-
*/
|
|
12
|
-
export function isExternal(path: string) {
|
|
13
|
-
return /^(https?:|mailto:|tel:)/.test(path);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 去掉前后空格
|
|
18
|
-
* @param {*} str
|
|
19
|
-
*/
|
|
20
|
-
export function trimVal(str: string) {
|
|
21
|
-
if (!str) {
|
|
22
|
-
return str;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const reg = /^\s+|\s+$/g;
|
|
26
|
-
return str.replace(reg, '');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 校验 手机
|
|
31
|
-
* @param {*} val
|
|
32
|
-
*/
|
|
33
|
-
export function isMobile(val: string | number) {
|
|
34
|
-
const reg = /^0?(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$/;
|
|
35
|
-
return reg.test(val.toString());
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 校验 手机
|
|
40
|
-
* 规则: 以1为开头,总共11位数
|
|
41
|
-
* @export
|
|
42
|
-
* @param {*} val
|
|
43
|
-
* @returns
|
|
44
|
-
*/
|
|
45
|
-
export function isMobileSimple(val: string | number) {
|
|
46
|
-
const reg = /^1/;
|
|
47
|
-
val = val.toString();
|
|
48
|
-
return reg.test(val) && val.length === 11;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 校验 固定电话
|
|
53
|
-
* 规则: 必须带区号
|
|
54
|
-
* @param {*} val
|
|
55
|
-
*/
|
|
56
|
-
export function isTelephone(val: string | number) {
|
|
57
|
-
const reg = /^0[1-9][0-9]{1,2}-[2-8][0-9]{6,7}$/;
|
|
58
|
-
return reg.test(String(val));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 校验 邮箱
|
|
63
|
-
* @param {*} val
|
|
64
|
-
*/
|
|
65
|
-
export function isEmail(val: string) {
|
|
66
|
-
/* eslint-disable no-useless-escape */
|
|
67
|
-
const reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
|
|
68
|
-
return reg.test(val);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 校验 QQ
|
|
73
|
-
* @param {*} val
|
|
74
|
-
*/
|
|
75
|
-
export function isQQ(val: string | number) {
|
|
76
|
-
const reg = /^[1-9][0-9]{4,9}$/gim;
|
|
77
|
-
return reg.test(String(val));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 校验是否为有效的中国大陆手机号(精确版)
|
|
82
|
-
* @param value - 待验证的字符串
|
|
83
|
-
* @returns 是否为有效手机号
|
|
84
|
-
*/
|
|
85
|
-
export function isPhone(value: string): boolean {
|
|
86
|
-
return /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/.test(
|
|
87
|
-
value
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 验证是否为有效的 URL
|
|
93
|
-
* @param value - 待验证的字符串
|
|
94
|
-
* @returns 是否为有效 URL
|
|
95
|
-
*/
|
|
96
|
-
export function isUrl(value: string): boolean {
|
|
97
|
-
const pattern =
|
|
98
|
-
'^(?:(https?|ftp|rtsp|mms|ws|wss):\\/\\/)?(?:\\S+(?::\\S*)?@)?(?:(?:localhost)|(?:[1-9]\\d{0,2}(?:\\.\\d{1,3}){3})|(?:$[0-9a-fA-F:]+$)|(?:(?:[a-zA-Z0-9-_]+\\.)+[a-zA-Z]{2,63}))(?::\\d{1,5})?(?:[/?#]\\S*)?$';
|
|
99
|
-
return new RegExp(pattern, 'i').test(value);
|
|
100
|
-
}
|
package/src/websocket/emitter.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { WebSocketMessageType } from './types';
|
|
2
|
-
|
|
3
|
-
export type WebSocketEmitterEvents = {
|
|
4
|
-
'websocket-connected': boolean;
|
|
5
|
-
'websocket-message': WebSocketMessageType;
|
|
6
|
-
'websocket-error': { message: string };
|
|
7
|
-
'websocket-closed': void;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export class Emitter<E extends Record<string, any>> {
|
|
11
|
-
private listeners = new Map<keyof E, Set<(payload: any) => void>>();
|
|
12
|
-
|
|
13
|
-
on<K extends keyof E>(event: K, handler: (payload: E[K]) => void) {
|
|
14
|
-
if (!this.listeners.has(event)) {
|
|
15
|
-
this.listeners.set(event, new Set());
|
|
16
|
-
}
|
|
17
|
-
const set = this.listeners.get(event)!;
|
|
18
|
-
set.add(handler as any);
|
|
19
|
-
return () => set.delete(handler as any);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
off<K extends keyof E>(event: K, handler: (payload: E[K]) => void) {
|
|
23
|
-
const set = this.listeners.get(event);
|
|
24
|
-
if (!set) return;
|
|
25
|
-
set.delete(handler as any);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
emit<K extends keyof E>(event: K, payload: E[K]) {
|
|
29
|
-
const set = this.listeners.get(event);
|
|
30
|
-
if (!set) return;
|
|
31
|
-
set.forEach(fn => {
|
|
32
|
-
try {
|
|
33
|
-
fn(payload);
|
|
34
|
-
} catch {}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const emitter = new Emitter<WebSocketEmitterEvents>();
|
package/src/websocket/index.ts
DELETED
package/src/websocket/manager.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import type { WebSocketMessageType } from './types';
|
|
2
|
-
|
|
3
|
-
type EventMap = {
|
|
4
|
-
connected: boolean;
|
|
5
|
-
message: WebSocketMessageType;
|
|
6
|
-
error: { message: string };
|
|
7
|
-
closed: void;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export interface WebSocketOptions {
|
|
11
|
-
url: string;
|
|
12
|
-
reconnectAttemptsLimit?: number;
|
|
13
|
-
reconnectInterval?: number;
|
|
14
|
-
heartbeatInterval?: number;
|
|
15
|
-
registerMessage?: unknown;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class WebsocketManager {
|
|
19
|
-
private ws: WebSocket | null = null;
|
|
20
|
-
private reconnectAttempts = 0;
|
|
21
|
-
private heartbeatTimer: number | null = null;
|
|
22
|
-
private listeners = new Map<keyof EventMap, Set<(payload: any) => void>>();
|
|
23
|
-
|
|
24
|
-
private readonly opts: {
|
|
25
|
-
url: string;
|
|
26
|
-
reconnectAttemptsLimit: number;
|
|
27
|
-
reconnectInterval: number;
|
|
28
|
-
heartbeatInterval: number;
|
|
29
|
-
registerMessage?: unknown;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
constructor(options: WebSocketOptions) {
|
|
33
|
-
this.opts = {
|
|
34
|
-
url: options.url,
|
|
35
|
-
reconnectAttemptsLimit: options.reconnectAttemptsLimit || 5,
|
|
36
|
-
reconnectInterval: options.reconnectInterval || 5000,
|
|
37
|
-
heartbeatInterval: options.heartbeatInterval || 30000,
|
|
38
|
-
registerMessage: options.registerMessage
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
get url(): string {
|
|
43
|
-
return this.opts.url;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
on<E extends keyof EventMap>(event: E, handler: (payload: EventMap[E]) => void) {
|
|
47
|
-
if (!this.listeners.has(event)) {
|
|
48
|
-
this.listeners.set(event, new Set());
|
|
49
|
-
}
|
|
50
|
-
const set = this.listeners.get(event)!;
|
|
51
|
-
set.add(handler as any);
|
|
52
|
-
return () => set.delete(handler as any);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private emit<E extends keyof EventMap>(event: E, payload: EventMap[E]) {
|
|
56
|
-
const set = this.listeners.get(event);
|
|
57
|
-
if (!set) return;
|
|
58
|
-
set.forEach(fn => {
|
|
59
|
-
try {
|
|
60
|
-
fn(payload);
|
|
61
|
-
} catch {}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
connect() {
|
|
66
|
-
try {
|
|
67
|
-
this.ws = new WebSocket(this.opts.url);
|
|
68
|
-
this.setupEventListeners();
|
|
69
|
-
} catch (err) {
|
|
70
|
-
this.emit('error', { message: 'WebSocket create failed' });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
private setupEventListeners() {
|
|
75
|
-
if (!this.ws) return;
|
|
76
|
-
|
|
77
|
-
this.ws.onopen = () => {
|
|
78
|
-
this.reconnectAttempts = 0;
|
|
79
|
-
if (this.opts.registerMessage) {
|
|
80
|
-
this.send(this.opts.registerMessage);
|
|
81
|
-
}
|
|
82
|
-
this.startHeartbeat();
|
|
83
|
-
this.emit('connected', true);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
this.ws.onmessage = event => {
|
|
87
|
-
try {
|
|
88
|
-
const data: WebSocketMessageType = JSON.parse(event.data);
|
|
89
|
-
this.emit('message', data);
|
|
90
|
-
} catch {
|
|
91
|
-
this.emit('error', { message: 'Message parse failed' });
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
this.ws.onclose = () => {
|
|
96
|
-
this.stopHeartbeat();
|
|
97
|
-
this.emit('connected', false);
|
|
98
|
-
this.emit('closed', undefined);
|
|
99
|
-
this.reconnect();
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
this.ws.onerror = () => {
|
|
103
|
-
this.emit('error', { message: 'WebSocket error' });
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
send(data: unknown) {
|
|
108
|
-
if (this.ws && this.ws.readyState === 1) {
|
|
109
|
-
try {
|
|
110
|
-
this.ws.send(JSON.stringify(data));
|
|
111
|
-
} catch {}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private startHeartbeat() {
|
|
116
|
-
this.stopHeartbeat();
|
|
117
|
-
this.heartbeatTimer = (setInterval(() => {
|
|
118
|
-
this.send({ type: 'ping' });
|
|
119
|
-
}, this.opts.heartbeatInterval) as unknown) as number;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private stopHeartbeat() {
|
|
123
|
-
if (this.heartbeatTimer != null) {
|
|
124
|
-
clearInterval(this.heartbeatTimer);
|
|
125
|
-
this.heartbeatTimer = null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private reconnect() {
|
|
130
|
-
if (this.reconnectAttempts < this.opts.reconnectAttemptsLimit) {
|
|
131
|
-
this.reconnectAttempts++;
|
|
132
|
-
setTimeout(() => {
|
|
133
|
-
this.connect();
|
|
134
|
-
}, this.opts.reconnectInterval);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
disconnect() {
|
|
139
|
-
this.stopHeartbeat();
|
|
140
|
-
if (this.ws) {
|
|
141
|
-
try {
|
|
142
|
-
this.ws.close();
|
|
143
|
-
} catch {}
|
|
144
|
-
}
|
|
145
|
-
this.ws = null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
get isConnected(): boolean {
|
|
149
|
-
return !!this.ws && this.ws.readyState === 1;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { defineStore } from 'pinia';
|
|
2
|
-
import { WebsocketManager } from './manager';
|
|
3
|
-
import type { WebSocketMessageType } from './types';
|
|
4
|
-
import { emitter } from './emitter';
|
|
5
|
-
import type { WebSocketOptions } from './manager';
|
|
6
|
-
|
|
7
|
-
type MessageWithMeta = WebSocketMessageType & {
|
|
8
|
-
timestamp?: number;
|
|
9
|
-
id?: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const createWebSocketStore = (storeId = 'websocket') =>
|
|
13
|
-
defineStore(storeId, {
|
|
14
|
-
state: () => ({
|
|
15
|
-
isConnected: false as boolean,
|
|
16
|
-
wsManager: null as WebsocketManager | null,
|
|
17
|
-
messages: [] as MessageWithMeta[],
|
|
18
|
-
connectionError: null as string | null
|
|
19
|
-
}),
|
|
20
|
-
|
|
21
|
-
getters: {
|
|
22
|
-
connectionStatus: state => (state.isConnected ? '已连接' : '未连接'),
|
|
23
|
-
lastMessage: state => state.messages[state.messages.length - 1] || null
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
actions: {
|
|
27
|
-
async connect(options: WebSocketOptions) {
|
|
28
|
-
if (this.wsManager?.isConnected) return;
|
|
29
|
-
this.wsManager = new WebsocketManager(options);
|
|
30
|
-
this.setupEventListeners();
|
|
31
|
-
this.wsManager.connect();
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
setupEventListeners() {
|
|
35
|
-
if (!this.wsManager) return;
|
|
36
|
-
|
|
37
|
-
this.wsManager.on('connected', (status: boolean) => {
|
|
38
|
-
this.isConnected = status;
|
|
39
|
-
if (status) this.connectionError = null;
|
|
40
|
-
emitter.emit('websocket-connected', status);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
this.wsManager.on('message', (data: WebSocketMessageType) => {
|
|
44
|
-
this.addMessage(data);
|
|
45
|
-
emitter.emit('websocket-message', data);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
this.wsManager.on('error', (error: { message: string }) => {
|
|
49
|
-
this.connectionError = error?.message || 'WebSocket error';
|
|
50
|
-
emitter.emit('websocket-error', { message: this.connectionError! });
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
this.wsManager.on('closed', () => {
|
|
54
|
-
this.isConnected = false;
|
|
55
|
-
emitter.emit('websocket-closed', undefined);
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
sendMessage(data: WebSocketMessageType) {
|
|
60
|
-
this.wsManager?.send(data);
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
addMessage(message: WebSocketMessageType) {
|
|
64
|
-
const msg: MessageWithMeta = {
|
|
65
|
-
...message,
|
|
66
|
-
timestamp: Date.now(),
|
|
67
|
-
id: Math.random().toString(36).substring(2, 11)
|
|
68
|
-
};
|
|
69
|
-
this.messages.push(msg);
|
|
70
|
-
if (this.messages.length > 100) {
|
|
71
|
-
this.messages.shift();
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
clearMessages() {
|
|
76
|
-
this.messages = [];
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
disconnect() {
|
|
80
|
-
this.wsManager?.disconnect();
|
|
81
|
-
this.wsManager = null;
|
|
82
|
-
this.isConnected = false;
|
|
83
|
-
this.connectionError = null;
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
reset() {
|
|
87
|
-
this.disconnect();
|
|
88
|
-
this.clearMessages();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
package/src/websocket/service.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { WebsocketManager } from './manager';
|
|
2
|
-
import type { WebSocketOptions } from './manager';
|
|
3
|
-
|
|
4
|
-
class WebSocketService {
|
|
5
|
-
private static instance: WebSocketService;
|
|
6
|
-
private initialized = false;
|
|
7
|
-
private manager: WebsocketManager | null = null;
|
|
8
|
-
|
|
9
|
-
static getInstance(): WebSocketService {
|
|
10
|
-
if (!WebSocketService.instance) {
|
|
11
|
-
WebSocketService.instance = new WebSocketService();
|
|
12
|
-
}
|
|
13
|
-
return WebSocketService.instance;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async init(options: WebSocketOptions) {
|
|
17
|
-
if (this.initialized) return;
|
|
18
|
-
this.manager = new WebsocketManager(options);
|
|
19
|
-
this.manager.connect();
|
|
20
|
-
this.initialized = true;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
destroy() {
|
|
24
|
-
this.manager && this.manager.disconnect();
|
|
25
|
-
this.manager = null;
|
|
26
|
-
this.initialized = false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getManager() {
|
|
30
|
-
return this.manager;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const websocketService = WebSocketService.getInstance();
|
package/src/websocket/types.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export interface WebSocketMessage<T = unknown> {
|
|
2
|
-
type: string;
|
|
3
|
-
data?: T;
|
|
4
|
-
timestamp?: number;
|
|
5
|
-
messageId?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface WebSocketHeartbeat {
|
|
9
|
-
type: 'ping' | 'pong';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface WebSocketNotification {
|
|
13
|
-
type: 'notification';
|
|
14
|
-
data: {
|
|
15
|
-
title: string;
|
|
16
|
-
message: string;
|
|
17
|
-
level?: 'info' | 'success' | 'warning' | 'error';
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface WebSocketDataUpdate<T = unknown> {
|
|
22
|
-
type: 'data-update';
|
|
23
|
-
data: {
|
|
24
|
-
entity: string;
|
|
25
|
-
action: 'create' | 'update' | 'delete';
|
|
26
|
-
payload: T;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface WebSocketSystemMessage {
|
|
31
|
-
type: 'system';
|
|
32
|
-
data: {
|
|
33
|
-
code: string;
|
|
34
|
-
message: string;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type WebSocketMessageType =
|
|
39
|
-
| WebSocketHeartbeat
|
|
40
|
-
| WebSocketNotification
|
|
41
|
-
| WebSocketDataUpdate
|
|
42
|
-
| WebSocketSystemMessage
|
|
43
|
-
| WebSocketMessage;
|
|
44
|
-
|
|
45
|
-
export type WebSocketStatus = 'connected' | 'disconnected' | 'connecting' | 'error';
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { isEmpty } from '../../src';
|
|
2
|
-
|
|
3
|
-
describe('isEmpty', () => {
|
|
4
|
-
test(`isEmpty('') 返回 true`, () => {
|
|
5
|
-
expect(isEmpty('')).toBe(true);
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
test(`isEmpty(null) 返回 true`, () => {
|
|
9
|
-
expect(isEmpty(null)).toBe(true);
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
test(`isEmpty(undefined) 返回 true`, () => {
|
|
13
|
-
expect(isEmpty(undefined)).toBe(true);
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test(`isEmpty(12323) 返回 false`, () => {
|
|
17
|
-
expect(isEmpty(12323)).toBe(false);
|
|
18
|
-
})
|
|
19
|
-
});
|