@anjianshi/utils 3.0.0 → 3.0.2
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/env-browser/device.d.ts +24 -0
- package/env-browser/device.js +50 -0
- package/env-browser/global.d.ts +10 -0
- package/env-browser/global.js +15 -0
- package/env-browser/load-script.d.ts +5 -0
- package/env-browser/load-script.js +13 -0
- package/env-browser/logging.d.ts +18 -0
- package/env-browser/logging.js +49 -0
- package/env-browser/manage-vconsole.d.ts +16 -0
- package/env-browser/manage-vconsole.js +38 -0
- package/env-node/crypto-random.d.ts +13 -0
- package/env-node/crypto-random.js +28 -0
- package/env-node/fs.d.ts +19 -0
- package/env-node/fs.js +48 -0
- package/env-node/index.d.ts +5 -0
- package/env-node/index.js +5 -0
- package/env-node/logging/handlers.d.ts +58 -0
- package/env-node/logging/handlers.js +154 -0
- package/env-node/logging/index.d.ts +11 -0
- package/env-node/logging/index.js +14 -0
- package/{src/env-react/emotion-register-globals.ts → env-react/emotion-register-globals.d.ts} +2 -5
- package/env-react/emotion-register-globals.js +5 -0
- package/env-react/emotion.d.ts +20 -0
- package/env-react/emotion.jsx +34 -0
- package/env-react/hooks.d.ts +23 -0
- package/env-react/hooks.js +47 -0
- package/env-react/index.d.ts +1 -0
- package/env-react/index.js +1 -0
- package/env-react/react-register-globals.d.ts +21 -0
- package/env-react/react-register-globals.js +19 -0
- package/env-service/controllers.d.ts +30 -0
- package/env-service/controllers.js +41 -0
- package/env-service/env-reader.d.ts +55 -0
- package/env-service/env-reader.js +79 -0
- package/env-service/index.d.ts +6 -0
- package/env-service/index.js +6 -0
- package/env-service/prisma/adapt-logging.d.ts +21 -0
- package/env-service/prisma/adapt-logging.js +30 -0
- package/env-service/prisma/extensions/exist.d.ts +10 -0
- package/env-service/prisma/extensions/exist.js +16 -0
- package/env-service/prisma/extensions/find-and-count.d.ts +7 -0
- package/env-service/prisma/extensions/find-and-count.js +19 -0
- package/env-service/prisma/extensions/soft-delete.d.ts +52 -0
- package/env-service/prisma/extensions/soft-delete.js +123 -0
- package/env-service/prisma/extensions/with-transaction.d.ts +9 -0
- package/env-service/prisma/extensions/with-transaction.js +54 -0
- package/env-service/prisma/index.d.ts +6 -0
- package/env-service/prisma/index.js +6 -0
- package/env-service/prisma/transaction-contexted.d.ts +11 -0
- package/env-service/prisma/transaction-contexted.js +52 -0
- package/env-service/redis-cache.d.ts +39 -0
- package/env-service/redis-cache.js +116 -0
- package/env-service/tasks.d.ts +12 -0
- package/env-service/tasks.js +37 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/init-dayjs.d.ts +2 -0
- package/init-dayjs.js +7 -0
- package/lang/async.d.ts +19 -0
- package/lang/async.js +34 -0
- package/lang/color.d.ts +37 -0
- package/lang/color.js +111 -0
- package/lang/index.d.ts +8 -0
- package/lang/index.js +8 -0
- package/lang/object.d.ts +12 -0
- package/lang/object.js +41 -0
- package/lang/random.d.ts +13 -0
- package/lang/random.js +24 -0
- package/lang/result.d.ts +47 -0
- package/lang/result.js +45 -0
- package/lang/string.d.ts +29 -0
- package/lang/string.js +92 -0
- package/lang/time.d.ts +10 -0
- package/lang/time.js +18 -0
- package/{src/lang/types.ts → lang/types.d.ts} +23 -43
- package/lang/types.js +28 -0
- package/logging/adapt.d.ts +10 -0
- package/logging/adapt.js +43 -0
- package/logging/formatters.d.ts +10 -0
- package/logging/formatters.js +22 -0
- package/logging/index.d.ts +45 -0
- package/logging/index.js +90 -0
- package/md5.d.ts +30 -0
- package/md5.js +308 -0
- package/package.json +10 -19
- package/safe-request.d.ts +53 -0
- package/safe-request.js +140 -0
- package/url.d.ts +77 -0
- package/url.js +149 -0
- package/validators/array.d.ts +30 -0
- package/validators/array.js +47 -0
- package/validators/base.d.ts +82 -0
- package/validators/base.js +42 -0
- package/validators/boolean.d.ts +3 -0
- package/validators/boolean.js +22 -0
- package/validators/datetime.d.ts +12 -0
- package/validators/datetime.js +30 -0
- package/validators/factory.d.ts +70 -0
- package/validators/factory.js +121 -0
- package/validators/index.d.ts +9 -0
- package/validators/index.js +9 -0
- package/validators/number.d.ts +19 -0
- package/validators/number.js +26 -0
- package/validators/object.d.ts +28 -0
- package/validators/object.js +49 -0
- package/validators/one-of.d.ts +10 -0
- package/validators/one-of.js +15 -0
- package/validators/string.d.ts +22 -0
- package/validators/string.js +35 -0
- package/README.md +0 -10
- package/eslint.config.cjs +0 -33
- package/publish-prepare.cjs +0 -16
- package/src/env-browser/device.ts +0 -62
- package/src/env-browser/global.ts +0 -21
- package/src/env-browser/load-script.ts +0 -13
- package/src/env-browser/logging.ts +0 -58
- package/src/env-browser/manage-vconsole.ts +0 -54
- package/src/env-node/crypto-random.ts +0 -30
- package/src/env-node/fs.ts +0 -50
- package/src/env-node/index.ts +0 -5
- package/src/env-node/logging/handlers.ts +0 -190
- package/src/env-node/logging/index.ts +0 -16
- package/src/env-react/emotion.tsx +0 -42
- package/src/env-react/hooks.ts +0 -59
- package/src/env-react/index.ts +0 -1
- package/src/env-react/react-register-globals.ts +0 -53
- package/src/env-service/controllers.ts +0 -93
- package/src/env-service/env-reader.ts +0 -141
- package/src/env-service/index.ts +0 -6
- package/src/env-service/prisma/adapt-logging.ts +0 -39
- package/src/env-service/prisma/extensions/exist.ts +0 -21
- package/src/env-service/prisma/extensions/find-and-count.ts +0 -24
- package/src/env-service/prisma/extensions/soft-delete.ts +0 -162
- package/src/env-service/prisma/extensions/with-transaction.ts +0 -65
- package/src/env-service/prisma/index.ts +0 -6
- package/src/env-service/prisma/transaction-contexted.ts +0 -80
- package/src/env-service/redis-cache.ts +0 -142
- package/src/env-service/tasks.ts +0 -45
- package/src/index.ts +0 -4
- package/src/init-dayjs.ts +0 -8
- package/src/lang/async.ts +0 -47
- package/src/lang/color.ts +0 -119
- package/src/lang/index.ts +0 -8
- package/src/lang/object.ts +0 -39
- package/src/lang/random.ts +0 -25
- package/src/lang/result.ts +0 -78
- package/src/lang/string.ts +0 -95
- package/src/lang/time.ts +0 -19
- package/src/logging/adapt.ts +0 -49
- package/src/logging/formatters.ts +0 -23
- package/src/logging/index.ts +0 -106
- package/src/md5.ts +0 -318
- package/src/safe-request.ts +0 -193
- package/src/url.ts +0 -185
- package/src/validators/array.ts +0 -97
- package/src/validators/base.ts +0 -145
- package/src/validators/boolean.ts +0 -21
- package/src/validators/datetime.ts +0 -39
- package/src/validators/factory.ts +0 -244
- package/src/validators/index.ts +0 -9
- package/src/validators/number.ts +0 -54
- package/src/validators/object.ts +0 -101
- package/src/validators/one-of.ts +0 -33
- package/src/validators/string.ts +0 -72
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动计算最合适的 rem 大小,应用到页面样式上。
|
|
3
|
+
*
|
|
4
|
+
* rem 的意义是对于大屏幕,适当地同步放大界面元素,以提供更饱满的显示效果。
|
|
5
|
+
* 不过也不是所有地方都原封不动地大就好了,所以还要配合 media query 来实现最佳效果。
|
|
6
|
+
*/
|
|
7
|
+
export declare function autoRem(): void;
|
|
8
|
+
/**
|
|
9
|
+
* 当前设备是否是全面屏
|
|
10
|
+
* 页面刚加载时可能取不到 offsetHeight,因此通过一个函数而不是常量来提供此值
|
|
11
|
+
*/
|
|
12
|
+
export declare function isFullScreen(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 当前是否是 iOS 设备
|
|
15
|
+
*/
|
|
16
|
+
export declare const isIOS: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 当前是否是移动设备
|
|
19
|
+
*/
|
|
20
|
+
export declare const isMobile: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 当前是否是微信内部浏览器
|
|
23
|
+
*/
|
|
24
|
+
export declare const isWechat: boolean;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-deprecated */
|
|
2
|
+
/**
|
|
3
|
+
* 自动计算最合适的 rem 大小,应用到页面样式上。
|
|
4
|
+
*
|
|
5
|
+
* rem 的意义是对于大屏幕,适当地同步放大界面元素,以提供更饱满的显示效果。
|
|
6
|
+
* 不过也不是所有地方都原封不动地大就好了,所以还要配合 media query 来实现最佳效果。
|
|
7
|
+
*/
|
|
8
|
+
export function autoRem() {
|
|
9
|
+
const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
|
|
10
|
+
/**
|
|
11
|
+
* 计算规则:
|
|
12
|
+
* - 微信标准的 375px 宽度下,1rem 为 50px(这个比例的来由是这样的:移动端通常像素比 >= 2,这个 375px 的实际渲染像素是 750px,也就是实际渲染 750px 的情况下,1rem 为 100px)。
|
|
13
|
+
* - 在平板等特大屏幕下,限制 rem 的最大值为 75px,再大就夸张了。
|
|
14
|
+
* - 竖屏时取宽度来适配 rem,横屏时取高度来适配,以保证屏幕朝向不同时,界面元素的放大比例是一致的(这应该更符合人的认知:对于一个同样大小的屏幕,无论什么朝向,上面的文字应该是一样大的)。
|
|
15
|
+
* 注意:此计算方式仅适合“不能任意调整浏览器大小的”移动设备,对于浏览器窗口可能任意尺寸、比例的桌面端,还是应该始终按照宽度来适配。
|
|
16
|
+
*
|
|
17
|
+
* 从 px 到 rem 的换算:
|
|
18
|
+
* 原 px 单位的值换算成 rem,只要除以 50 即可。例如 12px 变成 0.24rem。
|
|
19
|
+
*/
|
|
20
|
+
function updateRem() {
|
|
21
|
+
const { clientWidth, clientHeight } = document.documentElement;
|
|
22
|
+
const refSize = Math.min(clientWidth, clientHeight);
|
|
23
|
+
const rem = Math.min((refSize / 750) * 100, 75);
|
|
24
|
+
document.documentElement.style.fontSize = `${rem}px`;
|
|
25
|
+
}
|
|
26
|
+
window.addEventListener(resizeEvt, updateRem, false);
|
|
27
|
+
document.addEventListener('DOMContentLoaded', updateRem, false);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 当前设备是否是全面屏
|
|
31
|
+
* 页面刚加载时可能取不到 offsetHeight,因此通过一个函数而不是常量来提供此值
|
|
32
|
+
*/
|
|
33
|
+
export function isFullScreen() {
|
|
34
|
+
const rate = document.documentElement.offsetHeight / document.documentElement.offsetWidth;
|
|
35
|
+
const limit = window.screen.height === window.screen.availHeight ? 1.8 : 1.65; // 临界判断值
|
|
36
|
+
return rate > limit;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 当前是否是 iOS 设备
|
|
40
|
+
*/
|
|
41
|
+
export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
42
|
+
/**
|
|
43
|
+
* 当前是否是移动设备
|
|
44
|
+
*/
|
|
45
|
+
export const isMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(navigator.userAgent || navigator.vendor) ||
|
|
46
|
+
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test((navigator.userAgent || navigator.vendor).substr(0, 4));
|
|
47
|
+
/**
|
|
48
|
+
* 当前是否是微信内部浏览器
|
|
49
|
+
*/
|
|
50
|
+
export const isWechat = /micromessenger/i.test(navigator.userAgent);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通过此模块注册全局变量以便于调试
|
|
3
|
+
* 变量会注册到 window.app = {} 下
|
|
4
|
+
*/
|
|
5
|
+
// web-worker 环境下不存在 window 变量,无法执行注册
|
|
6
|
+
let hasWindow = false;
|
|
7
|
+
try {
|
|
8
|
+
window.app = {};
|
|
9
|
+
hasWindow = true;
|
|
10
|
+
}
|
|
11
|
+
catch (e) { } // eslint-disable-line no-empty
|
|
12
|
+
export default function registerGlobal(key, content) {
|
|
13
|
+
if (hasWindow)
|
|
14
|
+
window.app[key] = content;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 加载脚本文件
|
|
3
|
+
* 返回 Promise,成功则 resolve,失败 reject
|
|
4
|
+
*/
|
|
5
|
+
export default async function loadScript(url) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const script = document.createElement('script');
|
|
8
|
+
script.src = url;
|
|
9
|
+
script.onload = () => resolve();
|
|
10
|
+
script.onerror = err => reject(err); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
|
|
11
|
+
window.document.head.appendChild(script);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 针对浏览器环境定制 logging
|
|
3
|
+
*/
|
|
4
|
+
import { type Logger, type LogInfo, LogHandler } from '../logging/index.js';
|
|
5
|
+
export * from '../logging/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* 实现向浏览器 console 输出日志
|
|
8
|
+
*/
|
|
9
|
+
export declare class ConsoleHandler extends LogHandler {
|
|
10
|
+
log(info: LogInfo): void;
|
|
11
|
+
private static readonly colors;
|
|
12
|
+
private static readonly colorMap;
|
|
13
|
+
static getColor(name: string): string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 预设的初始化行为
|
|
17
|
+
*/
|
|
18
|
+
export declare function initLogger(logger?: Logger): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 针对浏览器环境定制 logging
|
|
3
|
+
*/
|
|
4
|
+
import { logger as defaultLogger, LogLevel, LogHandler, formatters, } from '../logging/index.js';
|
|
5
|
+
export * from '../logging/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* 实现向浏览器 console 输出日志
|
|
8
|
+
*/
|
|
9
|
+
export class ConsoleHandler extends LogHandler {
|
|
10
|
+
log(info) {
|
|
11
|
+
const color = (value = 'black') => `color: ${value};`;
|
|
12
|
+
const prefix = '%c' + [formatters.time(info), info.logger].map((v) => (v ? `[${v}]` : '')).join('');
|
|
13
|
+
const prefixColor = info.logger ? color(ConsoleHandler.getColor(info.logger)) : color();
|
|
14
|
+
const values = [prefix, prefixColor, ...info.args];
|
|
15
|
+
if (info.level === LogLevel.Debug)
|
|
16
|
+
console.debug(...values);
|
|
17
|
+
else if (info.level === LogLevel.Info)
|
|
18
|
+
console.log(...values);
|
|
19
|
+
else if (info.level === LogLevel.Warning)
|
|
20
|
+
console.warn(...values);
|
|
21
|
+
else
|
|
22
|
+
console.error(...values);
|
|
23
|
+
}
|
|
24
|
+
// 按顺序给各主题分配颜色(取自 http://chriskempson.com/projects/base16/)
|
|
25
|
+
static colors = [
|
|
26
|
+
'#dc9656',
|
|
27
|
+
'#7cafc2',
|
|
28
|
+
'#ba8baf',
|
|
29
|
+
'#a16946',
|
|
30
|
+
'#ab4642',
|
|
31
|
+
'#86c1b9',
|
|
32
|
+
'#a1b56c',
|
|
33
|
+
'#f7ca88',
|
|
34
|
+
];
|
|
35
|
+
static colorMap = new Map();
|
|
36
|
+
static getColor(name) {
|
|
37
|
+
if (!ConsoleHandler.colorMap.has(name)) {
|
|
38
|
+
const nextIndex = ConsoleHandler.colorMap.size % ConsoleHandler.colors.length;
|
|
39
|
+
ConsoleHandler.colorMap.set(name, ConsoleHandler.colors[nextIndex]);
|
|
40
|
+
}
|
|
41
|
+
return ConsoleHandler.colorMap.get(name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 预设的初始化行为
|
|
46
|
+
*/
|
|
47
|
+
export function initLogger(logger = defaultLogger) {
|
|
48
|
+
logger.addHandler(new ConsoleHandler());
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type default as VConsole } from 'vconsole';
|
|
2
|
+
export declare function registerVConsoleLib(lib: new () => VConsole): void;
|
|
3
|
+
/**
|
|
4
|
+
* 管理 VConsole 实例
|
|
5
|
+
*/
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
VConsole: VConsole | null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export declare function isVConsoleEnabled(): boolean;
|
|
12
|
+
export declare function detectVConsole(): void;
|
|
13
|
+
export declare function enableVConsole(): void;
|
|
14
|
+
export declare function disableVConsole(): void;
|
|
15
|
+
export declare function runVConsole(): void;
|
|
16
|
+
export declare function destoryVConsole(): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 注册 VConsole 代码
|
|
3
|
+
* 此类库自己并不加载 VConsole,需使用者自行通过依赖或者 CDN 加载 VConsole 然后传给此类库
|
|
4
|
+
*/
|
|
5
|
+
let VConsoleLib;
|
|
6
|
+
export function registerVConsoleLib(lib) {
|
|
7
|
+
VConsoleLib = lib;
|
|
8
|
+
}
|
|
9
|
+
window.VConsole = null;
|
|
10
|
+
export function isVConsoleEnabled() {
|
|
11
|
+
return localStorage.getItem('vconsole') === '1';
|
|
12
|
+
}
|
|
13
|
+
export function detectVConsole() {
|
|
14
|
+
if (isVConsoleEnabled()) {
|
|
15
|
+
runVConsole();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function enableVConsole() {
|
|
19
|
+
localStorage.setItem('vconsole', '1');
|
|
20
|
+
runVConsole();
|
|
21
|
+
}
|
|
22
|
+
export function disableVConsole() {
|
|
23
|
+
localStorage.setItem('vconsole', '0');
|
|
24
|
+
destoryVConsole();
|
|
25
|
+
}
|
|
26
|
+
export function runVConsole() {
|
|
27
|
+
if (window.VConsole !== null)
|
|
28
|
+
return;
|
|
29
|
+
if (VConsoleLib === undefined)
|
|
30
|
+
return console.warn('尚未传入 VConsole 对象,无法启动');
|
|
31
|
+
window.VConsole = new VConsoleLib();
|
|
32
|
+
}
|
|
33
|
+
export function destoryVConsole() {
|
|
34
|
+
if (!window.VConsole)
|
|
35
|
+
return;
|
|
36
|
+
window.VConsole.destroy();
|
|
37
|
+
window.VConsole = null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 返回随机数,包含 min 和 max
|
|
3
|
+
*/
|
|
4
|
+
export declare function getCryptoRandomInt(min: number, max: number): number;
|
|
5
|
+
/**
|
|
6
|
+
* 返回随机字符串
|
|
7
|
+
*/
|
|
8
|
+
export declare function getCryptoRandomString(length?: number, seed?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* 从给定的选择中随机选中一项
|
|
11
|
+
* 如果数组为空,会返回 undefined
|
|
12
|
+
*/
|
|
13
|
+
export declare function cryptoChoiceRandom<T>(choices: T[]): T | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类似 lang/random.ts,但基于 Node.js 的 crypto 模块,提供密码级的随机数。
|
|
3
|
+
*/
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
/**
|
|
6
|
+
* 返回随机数,包含 min 和 max
|
|
7
|
+
*/
|
|
8
|
+
export function getCryptoRandomInt(min, max) {
|
|
9
|
+
// 如果传入的 max 小于 min,把它拉到和 min 一样。不然 crypto.randomInt 无法处理
|
|
10
|
+
const fixedMax = Math.max(min, max);
|
|
11
|
+
return crypto.randomInt(min, fixedMax + 1);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 返回随机字符串
|
|
15
|
+
*/
|
|
16
|
+
export function getCryptoRandomString(length = 6, seed = '0123456789abcdefghijklmnopqrstuvwxyz') {
|
|
17
|
+
let result = '';
|
|
18
|
+
while (result.length < length)
|
|
19
|
+
result += seed[getCryptoRandomInt(0, seed.length - 1)];
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 从给定的选择中随机选中一项
|
|
24
|
+
* 如果数组为空,会返回 undefined
|
|
25
|
+
*/
|
|
26
|
+
export function cryptoChoiceRandom(choices) {
|
|
27
|
+
return choices[getCryptoRandomInt(0, choices.length - 1)];
|
|
28
|
+
}
|
package/env-node/fs.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 确认一个路径是否存在且是文件
|
|
3
|
+
*/
|
|
4
|
+
export declare function isFileExists(filepath: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* 确认一个路径是否存在且是文件夹
|
|
7
|
+
*/
|
|
8
|
+
export declare function isDirectoryExists(dirpath: string): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* 返回当前文件的绝对路径
|
|
11
|
+
* 需要传入当前文件的 ImportMeta 对象(可通过 import.meta 取得)
|
|
12
|
+
*/
|
|
13
|
+
export declare function getFilePath(fileUrl: string | ImportMeta): string;
|
|
14
|
+
/**
|
|
15
|
+
* 返回文件所处目录的绝对路径
|
|
16
|
+
*/
|
|
17
|
+
export declare function getDirectoryPath(fileUrl: string | ImportMeta): string;
|
|
18
|
+
/** 确保目录存在,如果不存在就创建(会递归创建上级目录) */
|
|
19
|
+
export declare function mkdirp(dirpath: string, mode?: number | string): Promise<void>;
|
package/env-node/fs.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* 确认一个路径是否存在且是文件
|
|
6
|
+
*/
|
|
7
|
+
export async function isFileExists(filepath) {
|
|
8
|
+
try {
|
|
9
|
+
const res = await fs.promises.stat(filepath);
|
|
10
|
+
return res.isFile();
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 确认一个路径是否存在且是文件夹
|
|
18
|
+
*/
|
|
19
|
+
export async function isDirectoryExists(dirpath) {
|
|
20
|
+
try {
|
|
21
|
+
const res = await fs.promises.stat(dirpath);
|
|
22
|
+
return res.isDirectory();
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 返回当前文件的绝对路径
|
|
30
|
+
* 需要传入当前文件的 ImportMeta 对象(可通过 import.meta 取得)
|
|
31
|
+
*/
|
|
32
|
+
export function getFilePath(fileUrl) {
|
|
33
|
+
if (typeof fileUrl !== 'string')
|
|
34
|
+
fileUrl = fileUrl.url;
|
|
35
|
+
return fileURLToPath(new URL(fileUrl));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 返回文件所处目录的绝对路径
|
|
39
|
+
*/
|
|
40
|
+
export function getDirectoryPath(fileUrl) {
|
|
41
|
+
return path.dirname(getFilePath(fileUrl));
|
|
42
|
+
}
|
|
43
|
+
/** 确保目录存在,如果不存在就创建(会递归创建上级目录) */
|
|
44
|
+
export async function mkdirp(dirpath, mode) {
|
|
45
|
+
if (!(await isDirectoryExists(dirpath))) {
|
|
46
|
+
await fs.promises.mkdir(dirpath, { recursive: true, mode });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type LogInfo, LogHandler } from '../../logging/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* 向 console 输出日志
|
|
4
|
+
*/
|
|
5
|
+
export declare class ConsoleHandler extends LogHandler {
|
|
6
|
+
log(info: LogInfo): void;
|
|
7
|
+
static readonly consoleMethods: {
|
|
8
|
+
1: {
|
|
9
|
+
(...data: any[]): void;
|
|
10
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
11
|
+
};
|
|
12
|
+
2: {
|
|
13
|
+
(...data: any[]): void;
|
|
14
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
15
|
+
};
|
|
16
|
+
3: {
|
|
17
|
+
(...data: any[]): void;
|
|
18
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
19
|
+
};
|
|
20
|
+
4: {
|
|
21
|
+
(...data: any[]): void;
|
|
22
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
static readonly levelColors: {
|
|
26
|
+
1: import("chalk").ChalkInstance;
|
|
27
|
+
2: import("chalk").ChalkInstance;
|
|
28
|
+
3: import("chalk").ChalkInstance;
|
|
29
|
+
4: import("chalk").ChalkInstance;
|
|
30
|
+
};
|
|
31
|
+
private static readonly loggerColors;
|
|
32
|
+
private static readonly loggerColorMap;
|
|
33
|
+
static getLoggerColor(logger: string): "green" | "yellow" | "blue" | "cyan" | "magenta" | "greenBright" | "yellowBright" | "blueBright" | "cyanBright" | "magentaBright";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 写入文件日志
|
|
37
|
+
*/
|
|
38
|
+
export interface FileHandlerOptions {
|
|
39
|
+
dir: string;
|
|
40
|
+
filePrefix: string;
|
|
41
|
+
maxLength: number;
|
|
42
|
+
flushLength: number;
|
|
43
|
+
flushInterval: number;
|
|
44
|
+
}
|
|
45
|
+
export declare class FileHandler extends LogHandler {
|
|
46
|
+
readonly options: FileHandlerOptions;
|
|
47
|
+
constructor(options?: Partial<FileHandlerOptions>);
|
|
48
|
+
log(info: LogInfo): void;
|
|
49
|
+
protected stringifyDataItem(item: unknown): string;
|
|
50
|
+
private buffer;
|
|
51
|
+
private bufferSize;
|
|
52
|
+
private flushTimeoutId;
|
|
53
|
+
protected pushBuffer(...strings: string[]): void;
|
|
54
|
+
protected flush(sync?: boolean): void;
|
|
55
|
+
get filepath(): string;
|
|
56
|
+
protected initLogDir(): void;
|
|
57
|
+
protected write(content: string, sync?: boolean): void;
|
|
58
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import util from 'node:util';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import dayjs from 'dayjs';
|
|
7
|
+
import { LogLevel, LogHandler, formatters } from '../../logging/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* 向 console 输出日志
|
|
10
|
+
*/
|
|
11
|
+
export class ConsoleHandler extends LogHandler {
|
|
12
|
+
log(info) {
|
|
13
|
+
const { logger, level, args } = info;
|
|
14
|
+
const method = ConsoleHandler.consoleMethods[level];
|
|
15
|
+
const levelColor = ConsoleHandler.levelColors[level];
|
|
16
|
+
const levelName = formatters.level(info);
|
|
17
|
+
const loggerColor = chalk[ConsoleHandler.getLoggerColor(logger)];
|
|
18
|
+
const prefix = [
|
|
19
|
+
chalk.white(`[${formatters.time(info)}]`),
|
|
20
|
+
levelColor(`[${levelName}]`),
|
|
21
|
+
...(logger ? [loggerColor(`[${logger}]`)] : []),
|
|
22
|
+
].join('');
|
|
23
|
+
method(prefix, ...args);
|
|
24
|
+
}
|
|
25
|
+
static consoleMethods = {
|
|
26
|
+
[LogLevel.Debug]: console.debug.bind(console),
|
|
27
|
+
[LogLevel.Info]: console.info.bind(console),
|
|
28
|
+
[LogLevel.Warning]: console.warn.bind(console),
|
|
29
|
+
[LogLevel.Error]: console.error.bind(console),
|
|
30
|
+
};
|
|
31
|
+
static levelColors = {
|
|
32
|
+
[LogLevel.Debug]: chalk.whiteBright,
|
|
33
|
+
[LogLevel.Info]: chalk.white,
|
|
34
|
+
[LogLevel.Warning]: chalk.yellowBright,
|
|
35
|
+
[LogLevel.Error]: chalk.redBright,
|
|
36
|
+
};
|
|
37
|
+
// 可供 logger 选择的颜色
|
|
38
|
+
static loggerColors = [
|
|
39
|
+
'green',
|
|
40
|
+
'yellow',
|
|
41
|
+
'blue',
|
|
42
|
+
'magenta',
|
|
43
|
+
'cyan',
|
|
44
|
+
'greenBright',
|
|
45
|
+
'yellowBright',
|
|
46
|
+
'blueBright',
|
|
47
|
+
'magentaBright',
|
|
48
|
+
'cyanBright',
|
|
49
|
+
];
|
|
50
|
+
static loggerColorMap = new Map();
|
|
51
|
+
static getLoggerColor(logger) {
|
|
52
|
+
if (!ConsoleHandler.loggerColorMap.has(logger)) {
|
|
53
|
+
const color = ConsoleHandler.loggerColors[ConsoleHandler.loggerColorMap.size % ConsoleHandler.loggerColors.length];
|
|
54
|
+
ConsoleHandler.loggerColorMap.set(logger, color);
|
|
55
|
+
}
|
|
56
|
+
return ConsoleHandler.loggerColorMap.get(logger);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export class FileHandler extends LogHandler {
|
|
60
|
+
options;
|
|
61
|
+
constructor(options) {
|
|
62
|
+
super();
|
|
63
|
+
const dirname = path.dirname(fileURLToPath(new URL(import.meta.url)));
|
|
64
|
+
this.options = {
|
|
65
|
+
dir: path.resolve(dirname, 'logs'),
|
|
66
|
+
filePrefix: '',
|
|
67
|
+
maxLength: 10000,
|
|
68
|
+
flushLength: 100000,
|
|
69
|
+
flushInterval: 1000,
|
|
70
|
+
...(options ?? {}),
|
|
71
|
+
};
|
|
72
|
+
this.initLogDir();
|
|
73
|
+
// 进程退出前把尚未写入文件的日志强制写入
|
|
74
|
+
// 这里必须用同步的方式来写,不然会写入不进去(可能是因为异步的话是放到下一个事件循环,但进程在这个事件循环内就退出了)
|
|
75
|
+
process.on('exit', () => this.flush(true));
|
|
76
|
+
}
|
|
77
|
+
// Format log content
|
|
78
|
+
log(info) {
|
|
79
|
+
const { logger, args } = info;
|
|
80
|
+
const prefix = [
|
|
81
|
+
`[${formatters.datetime(info)}]`,
|
|
82
|
+
`[${formatters.level(info)}]`,
|
|
83
|
+
logger ? `[${logger}]` : '',
|
|
84
|
+
].join('') + ' ';
|
|
85
|
+
const itemStrings = [];
|
|
86
|
+
let totalLength = prefix.length;
|
|
87
|
+
for (const item of args) {
|
|
88
|
+
const itemString = this.stringifyDataItem(item);
|
|
89
|
+
// 截断过长的日志内容 Truncate overly long log messages
|
|
90
|
+
if (totalLength + itemString.length < this.options.maxLength) {
|
|
91
|
+
itemStrings.push((totalLength === prefix.length ? '' : ' ') + itemString);
|
|
92
|
+
totalLength += itemString.length;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
itemStrings.push(itemString.slice(0, this.options.maxLength - totalLength) + ' [too long, sliced]');
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
this.pushBuffer(prefix, ...itemStrings, '\n');
|
|
100
|
+
}
|
|
101
|
+
stringifyDataItem(item) {
|
|
102
|
+
// 去掉颜色控制字符
|
|
103
|
+
if (typeof item === 'string')
|
|
104
|
+
item = item.replace(/\x1b\[\d+m/g, '');
|
|
105
|
+
// 利用 util.format() 获得和 console.log() 相同的输出(因为 console.log() 底层也是用的 util.format())
|
|
106
|
+
return util.format(item);
|
|
107
|
+
}
|
|
108
|
+
// Handle buffer & flush
|
|
109
|
+
buffer = [];
|
|
110
|
+
bufferSize = 0;
|
|
111
|
+
flushTimeoutId = null;
|
|
112
|
+
pushBuffer(...strings) {
|
|
113
|
+
this.buffer.push(...strings);
|
|
114
|
+
this.bufferSize = strings.reduce((sum, v) => sum + v.length, this.bufferSize);
|
|
115
|
+
if (this.options.flushInterval === 0 || this.bufferSize >= this.options.flushLength) {
|
|
116
|
+
this.flush();
|
|
117
|
+
}
|
|
118
|
+
else if (!this.flushTimeoutId) {
|
|
119
|
+
this.flushTimeoutId = setTimeout(() => this.flush(), this.options.flushInterval);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
flush(sync) {
|
|
123
|
+
if (this.flushTimeoutId) {
|
|
124
|
+
clearTimeout(this.flushTimeoutId);
|
|
125
|
+
this.flushTimeoutId = null;
|
|
126
|
+
}
|
|
127
|
+
if (this.buffer.length) {
|
|
128
|
+
const content = this.buffer.join('');
|
|
129
|
+
this.buffer = [];
|
|
130
|
+
this.bufferSize = 0;
|
|
131
|
+
this.write(content, sync);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 文件系统交互 File system interaction
|
|
135
|
+
get filepath() {
|
|
136
|
+
const { dir, filePrefix } = this.options;
|
|
137
|
+
return path.join(dir, `${filePrefix ? `${filePrefix}-` : ''}${dayjs().format('YYYY-MM-DD')}.log`);
|
|
138
|
+
}
|
|
139
|
+
initLogDir() {
|
|
140
|
+
if (!fs.existsSync(this.options.dir))
|
|
141
|
+
fs.mkdirSync(this.options.dir);
|
|
142
|
+
}
|
|
143
|
+
write(content, sync = false) {
|
|
144
|
+
if (sync) {
|
|
145
|
+
fs.appendFileSync(this.filepath, content);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
fs.appendFile(this.filepath, content, error => {
|
|
149
|
+
if (error)
|
|
150
|
+
console.error('[logger] write failed: ', error);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 针对 Node.js 环境定制 logging
|
|
3
|
+
* 注意:使用此模块需要 chalk 依赖
|
|
4
|
+
*/
|
|
5
|
+
import { type Logger } from '../../logging/index.js';
|
|
6
|
+
export * from './handlers.js';
|
|
7
|
+
export * from '../../logging/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* 预设的初始化行为
|
|
10
|
+
*/
|
|
11
|
+
export declare function initLogger(logger?: Logger): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 针对 Node.js 环境定制 logging
|
|
3
|
+
* 注意:使用此模块需要 chalk 依赖
|
|
4
|
+
*/
|
|
5
|
+
import { logger as defaultLogger } from '../../logging/index.js';
|
|
6
|
+
import { ConsoleHandler } from './handlers.js';
|
|
7
|
+
export * from './handlers.js';
|
|
8
|
+
export * from '../../logging/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* 预设的初始化行为
|
|
11
|
+
*/
|
|
12
|
+
export function initLogger(logger = defaultLogger) {
|
|
13
|
+
logger.addHandler(new ConsoleHandler());
|
|
14
|
+
}
|
package/{src/env-react/emotion-register-globals.ts → env-react/emotion-register-globals.d.ts}
RENAMED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 把 emotion 的 css 函数注册为全局变量,就不用每次使用都手动引入了
|
|
3
3
|
*/
|
|
4
|
-
import { css as cssValue } from '@emotion/react'
|
|
5
|
-
|
|
4
|
+
import { css as cssValue } from '@emotion/react';
|
|
6
5
|
declare global {
|
|
7
|
-
|
|
6
|
+
var css: typeof cssValue;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
globalThis.css = cssValue
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通过 React Hook 把 emotion css 转换成 className
|
|
3
|
+
* (不再需要 <ClassName>)
|
|
4
|
+
*
|
|
5
|
+
* 使用前提:
|
|
6
|
+
* 1. 只支持浏览器渲染
|
|
7
|
+
* 2. 用 EmotionCacheProvider 包裹 App 根元素
|
|
8
|
+
*
|
|
9
|
+
* 来自:
|
|
10
|
+
* https://github.com/emotion-js/emotion/issues/1853#issuecomment-623349622
|
|
11
|
+
*/
|
|
12
|
+
import { type EmotionCache } from '@emotion/react';
|
|
13
|
+
import { type CSSInterpolation } from '@emotion/serialize';
|
|
14
|
+
export declare const useEmotionCache: () => EmotionCache | undefined;
|
|
15
|
+
export declare const EmotionCacheProvider: import("react").FC<{
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
} & import("react").RefAttributes<any>> | import("react").ForwardRefExoticComponent<{
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
} & import("react").RefAttributes<any>>;
|
|
20
|
+
export declare function useEmotionClassName(): (...args: CSSInterpolation[]) => string;
|