@anjianshi/utils 2.7.0 → 2.8.1

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.
Files changed (153) hide show
  1. package/env-browser/device.d.ts +24 -0
  2. package/env-browser/device.js +50 -0
  3. package/env-browser/global.d.ts +10 -0
  4. package/env-browser/global.js +15 -0
  5. package/env-browser/load-script.d.ts +5 -0
  6. package/env-browser/load-script.js +13 -0
  7. package/env-browser/logging.d.ts +18 -0
  8. package/env-browser/logging.js +49 -0
  9. package/env-browser/manage-vconsole.d.ts +16 -0
  10. package/env-browser/manage-vconsole.js +38 -0
  11. package/env-node/crypto-random.d.ts +13 -0
  12. package/env-node/crypto-random.js +28 -0
  13. package/env-node/fs.d.ts +19 -0
  14. package/env-node/fs.js +48 -0
  15. package/env-node/index.d.ts +6 -0
  16. package/env-node/index.js +6 -0
  17. package/env-node/logging/handlers.d.ts +58 -0
  18. package/env-node/logging/handlers.js +154 -0
  19. package/env-node/logging/index.d.ts +11 -0
  20. package/env-node/logging/index.js +14 -0
  21. package/env-node/safe-request.d.ts +26 -0
  22. package/env-node/safe-request.js +40 -0
  23. package/env-react/emotion.d.ts +20 -0
  24. package/env-react/emotion.jsx +34 -0
  25. package/env-service/controllers.d.ts +30 -0
  26. package/env-service/controllers.js +41 -0
  27. package/env-service/env-reader.d.ts +55 -0
  28. package/env-service/env-reader.js +79 -0
  29. package/env-service/index.d.ts +6 -0
  30. package/env-service/index.js +6 -0
  31. package/env-service/prisma/adapt-logging.d.ts +21 -0
  32. package/env-service/prisma/adapt-logging.js +30 -0
  33. package/env-service/prisma/extensions/exist.d.ts +10 -0
  34. package/env-service/prisma/extensions/exist.js +16 -0
  35. package/env-service/prisma/extensions/find-and-count.d.ts +7 -0
  36. package/env-service/prisma/extensions/find-and-count.js +19 -0
  37. package/env-service/prisma/extensions/soft-delete.d.ts +52 -0
  38. package/env-service/prisma/extensions/soft-delete.js +123 -0
  39. package/env-service/prisma/extensions/with-transaction.d.ts +9 -0
  40. package/env-service/prisma/extensions/with-transaction.js +54 -0
  41. package/env-service/prisma/index.d.ts +6 -0
  42. package/env-service/prisma/index.js +6 -0
  43. package/env-service/prisma/transaction-contexted.d.ts +11 -0
  44. package/env-service/prisma/transaction-contexted.js +52 -0
  45. package/env-service/redis-cache.d.ts +39 -0
  46. package/env-service/redis-cache.js +116 -0
  47. package/env-service/tasks.d.ts +12 -0
  48. package/env-service/tasks.js +37 -0
  49. package/index.d.ts +3 -0
  50. package/index.js +3 -0
  51. package/init-dayjs.d.ts +2 -0
  52. package/init-dayjs.js +7 -0
  53. package/lang/async.d.ts +19 -0
  54. package/lang/async.js +34 -0
  55. package/lang/color.d.ts +37 -0
  56. package/lang/color.js +111 -0
  57. package/lang/index.d.ts +8 -0
  58. package/lang/index.js +8 -0
  59. package/lang/may-success.d.ts +40 -0
  60. package/lang/may-success.js +27 -0
  61. package/lang/object.d.ts +12 -0
  62. package/lang/object.js +41 -0
  63. package/lang/random.d.ts +13 -0
  64. package/lang/random.js +24 -0
  65. package/lang/string.d.ts +29 -0
  66. package/lang/string.js +92 -0
  67. package/lang/time.d.ts +10 -0
  68. package/lang/time.js +18 -0
  69. package/{src/lang/types.ts → lang/types.d.ts} +23 -43
  70. package/lang/types.js +28 -0
  71. package/logging/adapt.d.ts +10 -0
  72. package/logging/adapt.js +43 -0
  73. package/logging/formatters.d.ts +10 -0
  74. package/logging/formatters.js +22 -0
  75. package/logging/index.d.ts +45 -0
  76. package/logging/index.js +90 -0
  77. package/md5.d.ts +30 -0
  78. package/md5.js +308 -0
  79. package/package.json +14 -20
  80. package/url.d.ts +77 -0
  81. package/url.js +149 -0
  82. package/validators/array.d.ts +30 -0
  83. package/validators/array.js +47 -0
  84. package/validators/base.d.ts +82 -0
  85. package/validators/base.js +42 -0
  86. package/validators/boolean.d.ts +3 -0
  87. package/validators/boolean.js +22 -0
  88. package/validators/datetime.d.ts +12 -0
  89. package/validators/datetime.js +30 -0
  90. package/validators/factory.d.ts +70 -0
  91. package/validators/factory.js +121 -0
  92. package/validators/index.d.ts +9 -0
  93. package/validators/index.js +9 -0
  94. package/validators/number.d.ts +19 -0
  95. package/validators/number.js +26 -0
  96. package/validators/object.d.ts +28 -0
  97. package/validators/object.js +49 -0
  98. package/validators/one-of.d.ts +10 -0
  99. package/validators/one-of.js +15 -0
  100. package/validators/string.d.ts +22 -0
  101. package/validators/string.js +35 -0
  102. package/README.md +0 -10
  103. package/eslint.config.cjs +0 -33
  104. package/publish-prepare.cjs +0 -16
  105. package/src/env-browser/device.ts +0 -62
  106. package/src/env-browser/global.ts +0 -21
  107. package/src/env-browser/load-script.ts +0 -13
  108. package/src/env-browser/logging.ts +0 -58
  109. package/src/env-browser/manage-vconsole.ts +0 -54
  110. package/src/env-node/crypto-random.ts +0 -30
  111. package/src/env-node/fs.ts +0 -50
  112. package/src/env-node/index.ts +0 -6
  113. package/src/env-node/logging/handlers.ts +0 -190
  114. package/src/env-node/logging/index.ts +0 -16
  115. package/src/env-node/safe-request.ts +0 -66
  116. package/src/env-react/emotion.tsx +0 -42
  117. package/src/env-service/controllers.ts +0 -93
  118. package/src/env-service/env-reader.ts +0 -141
  119. package/src/env-service/index.ts +0 -6
  120. package/src/env-service/prisma/adapt-logging.ts +0 -39
  121. package/src/env-service/prisma/extensions/exist.ts +0 -21
  122. package/src/env-service/prisma/extensions/find-and-count.ts +0 -24
  123. package/src/env-service/prisma/extensions/soft-delete.ts +0 -162
  124. package/src/env-service/prisma/extensions/with-transaction.ts +0 -65
  125. package/src/env-service/prisma/index.ts +0 -6
  126. package/src/env-service/prisma/transaction-contexted.ts +0 -80
  127. package/src/env-service/redis-cache.ts +0 -142
  128. package/src/env-service/tasks.ts +0 -45
  129. package/src/index.ts +0 -3
  130. package/src/init-dayjs.ts +0 -8
  131. package/src/lang/async.ts +0 -47
  132. package/src/lang/color.ts +0 -119
  133. package/src/lang/index.ts +0 -7
  134. package/src/lang/may-success.ts +0 -57
  135. package/src/lang/object.ts +0 -39
  136. package/src/lang/random.ts +0 -25
  137. package/src/lang/string.ts +0 -95
  138. package/src/lang/time.ts +0 -19
  139. package/src/logging/adapt.ts +0 -49
  140. package/src/logging/formatters.ts +0 -23
  141. package/src/logging/index.ts +0 -106
  142. package/src/md5.ts +0 -318
  143. package/src/url.ts +0 -185
  144. package/src/validators/array.ts +0 -97
  145. package/src/validators/base.ts +0 -145
  146. package/src/validators/boolean.ts +0 -21
  147. package/src/validators/datetime.ts +0 -39
  148. package/src/validators/factory.ts +0 -244
  149. package/src/validators/index.ts +0 -9
  150. package/src/validators/number.ts +0 -54
  151. package/src/validators/object.ts +0 -101
  152. package/src/validators/one-of.ts +0 -33
  153. package/src/validators/string.ts +0 -72
@@ -1,142 +0,0 @@
1
- import { type RedisClientType } from 'redis'
2
- import { type Logger, logger as rootLogger } from '../logging/index.js'
3
-
4
- export function initRedisLogging(redis: RedisClientType, logger?: Logger) {
5
- logger ??= rootLogger.getChild('redis')
6
- redis.on('connect', () => logger.info('connecting'))
7
- redis.on('ready', () => logger.info('connected'))
8
- redis.on('end', () => logger.info('connection closed'))
9
- redis.on('reconnecting', () => logger.info('reconnecting'))
10
- redis.on('error', error => logger.error(error))
11
- }
12
-
13
- export interface CacheOptions {
14
- logger: Logger
15
-
16
- /** 数据有效期,单位秒。默认为 10 分钟。小于等于 0 代表不设有效期 */
17
- expires: number
18
-
19
- /** 读取时是否自动刷新有效期,仅设置了 expire 时有效,默认为 true */
20
- refreshOnRead: boolean
21
-
22
- /** 若为 true,读取数据后会立即将其删除,默认为 false */
23
- oneTime: boolean
24
- }
25
-
26
- /**
27
- * 维护缓存数据
28
- * 1. 每个 Cache 实例只维护一个主题的数据,且需明确定义数据类型,这样设计可明确对每一项缓存的使用、避免混乱。
29
- * 2. 值在存储时会 JSON 化,读取时再进行 JSON 解析(支持 JSON 化 Date 对象)。
30
- */
31
- export class Cache<T> {
32
- readonly options: CacheOptions
33
-
34
- constructor(
35
- readonly redis: RedisClientType,
36
- readonly topic: string,
37
- options?: Partial<CacheOptions>,
38
- ) {
39
- this.options = {
40
- logger: rootLogger.getChild('cache'),
41
- expires: 600,
42
- refreshOnRead: true,
43
- oneTime: false,
44
- ...(options ?? {}),
45
- }
46
- }
47
-
48
- get logger() {
49
- return this.options.logger
50
- }
51
-
52
- // 经过定制的 JSON 序列化和解析方法
53
- protected jsonStringify(value: T) {
54
- // 参考:https://stackoverflow.com/a/54037861/2815178
55
- function replacer(this: T, key: string, value: unknown) {
56
- // value 是经过预处理过的值,对于 Date 对象,此时已经是 string,需要通过 this[key] 才能拿到 Date 值。
57
- const rawValue = this[key as keyof T] as unknown
58
- if (rawValue instanceof Date) return { __json_type: 'date', value: rawValue.toISOString() }
59
- return value
60
- }
61
- return JSON.stringify(value, replacer)
62
- }
63
- protected jsonParse(redisValue: string) {
64
- function reviver(key: string, value: unknown) {
65
- if (typeof value === 'object' && value !== null) {
66
- const obj = value as Record<string, unknown>
67
- if (obj.__json_type === 'date') return new Date(obj.value as string)
68
- }
69
- return value
70
- }
71
- return JSON.parse(redisValue, reviver) as T
72
- }
73
-
74
- protected getRedisKey(identity: string) {
75
- return `${this.topic}${identity ? ':' + identity : ''}`
76
- }
77
-
78
- // ----------------------------------------------
79
-
80
- /** 读取一项内容 */
81
- get(identity: string, defaults: T): Promise<T>
82
- get(identity?: string): Promise<T | undefined>
83
- async get(identity = '', defaults?: T) {
84
- const redisKey = this.getRedisKey(identity)
85
- const redisValue = await this.redis.get(redisKey)
86
- this.logger.debug('get', redisKey, redisValue)
87
- if (redisValue === null) return defaults
88
- if (this.options.refreshOnRead) void this.refresh(identity)
89
-
90
- try {
91
- const value = this.jsonParse(redisValue)
92
- if (this.options.oneTime) await this.delete(identity)
93
- return value
94
- } catch (error) {
95
- this.logger.error(`解析 cache 数据失败,key=${redisKey},value=${redisValue}`, error)
96
- void this.delete(identity)
97
- return defaults
98
- }
99
- }
100
-
101
- /** 写入/更新一项内容 */
102
- set(value: T): Promise<void>
103
- set(identity: string, value: T): Promise<void>
104
- async set(identity: string | T, value?: T) {
105
- if (value === undefined) {
106
- value = identity as T
107
- identity = ''
108
- } else {
109
- identity = identity as string
110
- }
111
-
112
- const redisKey = this.getRedisKey(identity)
113
- let redisValue
114
- try {
115
- redisValue = this.jsonStringify(value)
116
- } catch (error) {
117
- this.logger.error(`格式化 cache 数据失败,key=${redisKey}`, value, error)
118
- throw error
119
- }
120
- this.logger.debug('set', redisKey, redisValue)
121
- await this.redis.set(redisKey, redisValue, { EX: this.options.expires })
122
- }
123
-
124
- /** 移除一项内容 */
125
- async delete(identity: string | string[] = '') {
126
- const identities = Array.isArray(identity) ? identity : [identity]
127
- this.logger.debug('delete', identities)
128
- return this.redis.del(identities.map(identity => this.getRedisKey(identity)))
129
- }
130
-
131
- /** 刷新一项内容的过期时间 */
132
- async refresh(identity = '') {
133
- if (this.options.expires >= 0)
134
- return this.redis.expire(this.getRedisKey(identity), this.options.expires)
135
- else return false
136
- }
137
-
138
- /** 确认一项内容是否存在 */
139
- async exists(identity = '') {
140
- return (await this.redis.exists(this.getRedisKey(identity))) === 1
141
- }
142
- }
@@ -1,45 +0,0 @@
1
- import { sleep } from '../lang/async.js'
2
- import { type Logger, logger as rootLogger } from '../logging/index.js'
3
-
4
- /** 返回 false 可结束任务 */
5
- export type TaskExecutor<Context> = (
6
- context: Context,
7
- logger: Logger,
8
- ) => Promise<void> | Promise<undefined | boolean>
9
-
10
- /**
11
- * 执行定期任务
12
- */
13
- export abstract class TaskManager<Context> {
14
- constructor(protected baseLogger: Logger = rootLogger.getChild('task')) {}
15
-
16
- abstract getContext(taskName: string): Context
17
-
18
- async run(name: string, interval: number, executor: TaskExecutor<Context>) {
19
- await sleep(1000)
20
-
21
- const logger = this.baseLogger.getChild(name)
22
- let nextId = 1
23
- while (true) {
24
- const id = nextId++
25
- if (id >= Number.MAX_SAFE_INTEGER) nextId = 1
26
-
27
- const start = Date.now()
28
- logger.info(`#${id} 任务开始`)
29
- try {
30
- const context = this.getContext(name)
31
- const result = await executor(context, logger)
32
- const cost = (Date.now() - start) / 1000
33
- logger.info(`#${id} 任务完成,耗时 ${cost}s`)
34
- if (result === false) {
35
- logger.info('任务结束')
36
- break
37
- }
38
- } catch (err) {
39
- logger.error(`#${id} 任务失败`, err)
40
- }
41
-
42
- await sleep(interval)
43
- }
44
- }
45
- }
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './lang/index.js'
2
- export * from './url.js'
3
- export * from './logging/index.js'
package/src/init-dayjs.ts DELETED
@@ -1,8 +0,0 @@
1
- import dayjs from 'dayjs'
2
- import objectSupport from 'dayjs/plugin/objectSupport.js'
3
- import 'dayjs/locale/zh-cn.js'
4
-
5
- export function initDayJs() {
6
- dayjs.extend(objectSupport)
7
- dayjs.locale('zh-cn')
8
- }
package/src/lang/async.ts DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * 异步行为相关函数
3
- */
4
-
5
- /**
6
- * 返回一个指定毫秒后 resolve 的 Promise
7
- * 若指定了 resolveValue,则 Promise 最终会解析出此值
8
- */
9
- async function sleep(ms: number): Promise<void>
10
- async function sleep<T>(ms: number, resolveValue: T): Promise<T>
11
- async function sleep<T>(ms: number, resolveValue?: T) {
12
- return new Promise<T>(resolve => {
13
- setTimeout(() => resolve(resolveValue as T), ms)
14
- })
15
- }
16
- export { sleep }
17
-
18
- /**
19
- * 给 Promise 增加时限。
20
- * - 若 silent 为 false,超时时 reject 一个 TimeoutError
21
- * - 若 silent 为 true,则超时后,promise 永远不会 resolve 或 reject
22
- */
23
- export async function timeout<T>(
24
- promise: Promise<T>,
25
- timeoutMS: number,
26
- silent = false,
27
- ): Promise<T> {
28
- return new Promise((resolve, reject) => {
29
- let isTimeout = false
30
- setTimeout(() => {
31
- isTimeout = true
32
- if (!silent) reject(new TimeoutError('timeout'))
33
- }, timeoutMS)
34
-
35
- promise.then(
36
- result => {
37
- if (!isTimeout) resolve(result)
38
- },
39
- (error: unknown) => {
40
- if (!isTimeout) reject(error as Error)
41
- },
42
- )
43
- })
44
- }
45
- export class TimeoutError extends Error {
46
- isTimeout = true
47
- }
package/src/lang/color.ts DELETED
@@ -1,119 +0,0 @@
1
- /**
2
- * 计算颜色数值
3
- */
4
-
5
- /**
6
- * hex 颜色字符串转换为 rgba 数值
7
- * 例如 '#22334455' => [34,51,68,0.33]
8
- *
9
- * hex 字符串支持以下格式:
10
- * - 带/不带开头的 #
11
- * - #RGB
12
- * - #RGBA
13
- * - #RRGGBB
14
- * - #RRGGBBAA
15
- */
16
- export function hex2rgba(rawHex: string) {
17
- let hex = rawHex
18
- if (hex.startsWith('#')) hex = hex.slice(1) // 去掉开头的 #
19
- if (hex.length === 3) hex = `${hex[0]!}${hex[0]!}${hex[1]!}${hex[1]!}${hex[2]!}${hex[2]!}FF`
20
- if (hex.length === 4)
21
- hex = `${hex[0]!}${hex[0]!}${hex[1]!}${hex[1]!}${hex[2]!}${hex[2]!}${hex[3]!}${hex[3]!}`
22
- if (hex.length === 6) hex = `${hex}FF`
23
- if (hex.length !== 8) throw new Error(`不支持的颜色值:${rawHex}`)
24
- hex = hex.toUpperCase() // 转成全大写
25
-
26
- const [r, g, b, a255] = [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6), hex.slice(6)].map(
27
- part => parseInt(part, 16)
28
- ) as [number, number, number, number]
29
- const a = Math.round((a255 / 255) * 100) / 100
30
-
31
- return [r, g, b, a] as const
32
- }
33
-
34
- /**
35
- * 把 rgba 数组转换为 hex 颜色字符串
36
- * 如果 alpha 部分为 1,会将其省略
37
- * [34,51,68,0.33] => '#22334455'
38
- */
39
- export function rgba2hex(rgba: [number, number, number, number]) {
40
- const withAlpha = rgba[3] !== 1
41
- return (
42
- '#' +
43
- [
44
- rgba[0].toString(16),
45
- rgba[1].toString(16),
46
- rgba[2].toString(16),
47
- ...(withAlpha ? [Math.round(rgba[3] * 255).toString(16)] : []),
48
- ]
49
- .map(v => (v.length === 1 ? `0${v}` : v))
50
- .join('')
51
- )
52
- }
53
-
54
- /**
55
- * rgb 转成 hsl 值(以便计算亮度)
56
- * https://www.rapidtables.com/convert/color/rgb-to-hsl.html
57
- */
58
- export function rgb2hsl([r, g, b]: [number, number, number]) {
59
- const [r1, g1, b1] = [r, g, b].map(v => v / 255) as [number, number, number]
60
- const cMax = Math.max(r1, g1, b1)
61
- const cMin = Math.min(r1, g1, b1)
62
- const delta = cMax - cMin
63
-
64
- let h: number
65
- if (delta === 0) h = 0
66
- else if (cMax === r1) h = 60 * (((g1 - b1) / delta) % 6)
67
- else if (cMax === g1) h = 60 * ((b1 - r1) / delta + 2)
68
- else h = 60 * ((r1 - g1) / delta + 4)
69
- if (h < 0) h = 360 + h
70
- const roundH = Math.round(h)
71
-
72
- const l = (cMax + cMin) / 2
73
- const roundL = Math.round(l * 100) / 100
74
-
75
- const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))
76
- const roundS = Math.round(s * 100) / 100
77
-
78
- return [roundH, roundS, roundL] as const
79
- }
80
-
81
- /**
82
- * hsl 转成 rgb
83
- * https://www.rapidtables.com/convert/color/hsl-to-rgb.html
84
- */
85
- export function hsl2rgb([h, s, l]: [number, number, number]) {
86
- const c = (1 - Math.abs(2 * l - 1)) * s
87
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
88
- const m = l - c / 2
89
-
90
- const [r1, g1, b1] = (() => {
91
- if (h < 60) return [c, x, 0]
92
- if (h < 120) return [x, c, 0]
93
- if (h < 180) return [0, c, x]
94
- if (h < 240) return [0, x, c]
95
- if (h < 300) return [x, 0, c]
96
- return [c, 0, x]
97
- })()
98
-
99
- const [r, g, b] = [r1 + m, g1 + m, b1 + m].map(v => Math.round(v * 255)) as [
100
- number,
101
- number,
102
- number
103
- ]
104
-
105
- return [r, g, b] as const
106
- }
107
-
108
- /**
109
- * 调整颜色深浅
110
- * lightness('#111', 0.2) => lighten(20%)
111
- * lightness('#111', -0.5) => darken(50%)
112
- */
113
- export function lightness(hex: string, ratio: number) {
114
- const [r, g, b, a] = hex2rgba(hex)
115
- const [h, s, l] = rgb2hsl([r, g, b])
116
- const l1 = Math.max(0, Math.min(1, l + ratio))
117
- const [r1, g1, b1] = hsl2rgb([h, s, l1])
118
- return rgba2hex([r1, g1, b1, a])
119
- }
package/src/lang/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export * from './types.js'
2
- export * from './may-success.js'
3
- export * from './string.js'
4
- export * from './object.js'
5
- export * from './time.js'
6
- export * from './async.js'
7
- export * from './random.js'
@@ -1,57 +0,0 @@
1
- /**
2
- * MaySuccess:代表一种“可能失败”的操作结果,可以作为函数返回值,也可以作为接口响应值。
3
- * 它的灵感来自 Scala 的 Option 类型。
4
- *
5
- * 原本,在 JavaScript 里一个可能失败的操作有两种表示失败的方式:
6
- * 1. 返回空值,如 null、0、''
7
- * 2. 抛出异常
8
- * 返回空值的方式无法附带失败信息;而抛出异常会导致层层嵌套的 try catch 语句,且其实性能不好。
9
- *
10
- * MaySuccess 就是为了解决这两个痛点:
11
- * 1. 它的 Failed 类型可以携带失败信息。
12
- * 2. 无需 try catch,只需简单的 result.success 判断
13
- */
14
-
15
- /** 类型定义 */
16
- export interface Success<T = void> {
17
- success: true
18
- data: T
19
- }
20
- export interface Failed<T = void> {
21
- success: false
22
- message: string
23
- code?: string | number
24
- data: T
25
- }
26
- export type MaySuccess<T = void, FT = void> = Success<T> | Failed<FT>
27
-
28
- /** 生成 Success 数据 */
29
- function success(): Success
30
- function success<T>(data: T): Success<T>
31
- function success<T = void>(data?: T) {
32
- return { success: true, data }
33
- }
34
- export { success }
35
-
36
- /** 生成 Failed 数据 */
37
- function failed(message: string, code?: string | number): Failed
38
- function failed<T>(message: string, code: string | number | undefined, data: T): Failed<T>
39
- function failed<T>(message: string, code?: string | number, data?: T): Failed<T> {
40
- return { success: false, message, code, data: data as T }
41
- }
42
- export { failed }
43
-
44
- /**
45
- * 若传入值为 success,格式化其 data;否则原样返回错误
46
- * 支持传入会返回 MaySuccess 的 Promise
47
- */
48
- function formatSuccess<T1, T2, FT = void>(value: MaySuccess<T1, FT>, formatter: (value: T1) => T2): MaySuccess<T2, FT> // prettier-ignore
49
- function formatSuccess<T1, T2, FT = void>(value: Promise<MaySuccess<T1, FT>>, formatter: (value: T1) => T2): Promise<MaySuccess<T2, FT>> // prettier-ignore
50
- function formatSuccess<T1, T2, FT = void>(
51
- value: MaySuccess<T1, FT> | Promise<MaySuccess<T1, FT>>,
52
- formatter: (value: T1) => T2,
53
- ) {
54
- if ('then' in value) return value.then(finalValue => formatSuccess(finalValue, formatter))
55
- return value.success ? success(formatter(value.data)) : value
56
- }
57
- export { formatSuccess }
@@ -1,39 +0,0 @@
1
- /**
2
- * 对两个对象进行浅比较
3
- * 代码取自 react-addons-shallow-compare 包
4
- */
5
- export function shallowEqual(objA: unknown, objB: unknown) {
6
- if (is(objA, objB)) return true
7
- if (!isObject(objA) || !isObject(objB)) return false
8
-
9
- const keysA = Object.keys(objA)
10
- const keysB = Object.keys(objB)
11
- if (keysA.length !== keysB.length) return false
12
-
13
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
14
- for (let i = 0; i < keysA.length; i++) {
15
- const key = keysA[i]!
16
- // eslint-disable-next-line prefer-object-has-own
17
- if (!Object.prototype.hasOwnProperty.call(objB, key) || !is(objA[key], objB[key])) return false
18
- }
19
-
20
- return true
21
- }
22
- function is(x: unknown, y: unknown) {
23
- if (x === y) return x !== 0 || y !== 0 || 1 / x === 1 / y
24
- else return x !== x && y !== y // eslint-disable-line no-self-compare
25
- }
26
- function isObject(value: unknown): value is Record<string, unknown> {
27
- return typeof value === 'object' && value !== null
28
- }
29
-
30
- /**
31
- * 获取对象的方法,并将它的 this 锁定在原对象
32
- *
33
- * const method = getBoundMethod(object, 'someMethod')
34
- * method(1,2,3) // 不用担心 this 改变
35
- */
36
- export function getBoundMethod<T extends object, K extends keyof T>(object: T, key: K): T[K] {
37
- const property = object[key]
38
- return (typeof property === 'function' ? property.bind(object) : property) as T[K]
39
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * 返回随机数,包含 min 和 max
3
- */
4
- export function getRandomInt(min: number, max: number) {
5
- const minCeiled = Math.ceil(min)
6
- const maxFloored = Math.floor(max)
7
- return Math.floor(Math.random() * (maxFloored - minCeiled + 1) + minCeiled)
8
- }
9
-
10
- /**
11
- * 返回随机字符串
12
- */
13
- export function getRandomString(length = 6, seed = '0123456789abcdefghijklmnopqrstuvwxyz') {
14
- let result = ''
15
- while (result.length < length) result += seed[getRandomInt(0, seed.length - 1)]!
16
- return result
17
- }
18
-
19
- /**
20
- * 从给定的选择中随机选中一项
21
- * 如果数组为空,会返回 undefined
22
- */
23
- export function choiceRandom<T>(choices: T[]) {
24
- return choices[getRandomInt(0, choices.length - 1)]
25
- }
@@ -1,95 +0,0 @@
1
- /**
2
- * 字符串处理相关函数
3
- */
4
- import escapeRegExp from 'lodash/escapeRegExp.js'
5
- import padStart from 'lodash/padStart.js'
6
-
7
- /**
8
- * 将数字字符串化,并在左侧填充 0 直到达到 length 参数指定的长度
9
- * 若数字本身达到或超过此长度,则不填充
10
- */
11
- export function zfill(num: number, length = 2) {
12
- return padStart(String(num), length, '0')
13
- }
14
-
15
- /**
16
- * 执行关键词匹配
17
- * 成功返回 true;失败返回 false
18
- */
19
- const kwCache: Record<string, RegExp> = {} // 避免大量重复构建正则表达式影响性能
20
- export function keywordCompare(keyword: string, target: string) {
21
- if (!keyword) return true
22
- if (!(keyword in kwCache)) {
23
- const regStr = keyword.split('').map(escapeRegExp).join('.*')
24
- kwCache[keyword] = new RegExp(regStr, 'i')
25
- }
26
- return kwCache[keyword]!.test(target)
27
- }
28
-
29
- /**
30
- * 对两个字符串进行排序,支持处理逻辑上的数字。
31
- * 两个字符串都完全由数字组成,且都不以 0 开头时,以数字大小排序;否则以字符串形式排序。
32
- *
33
- * 以 0 开头是特例,例如 019 和 12 两个字符串,按直觉还是应该 019 在前面,毕竟本质还是字符串排序。
34
- *
35
- * "123" "456" 数字排序
36
- * "123你好" "133我好" 字符串排序
37
- * "019" "12" 字符串排序
38
- */
39
- const _pattern = /^[1-9][0-9]*$/
40
- export function numericCompare(a: string, b: string) {
41
- if (_pattern.exec(a) && _pattern.exec(b)) return parseInt(a, 10) - parseInt(b, 10)
42
- return a.localeCompare(b)
43
- }
44
-
45
- /**
46
- * 字符串解析成整数,补充安全措施:
47
- * 1. 默认设置 radix 为 10,无需再手动指定
48
- * 2. 支持指定 fallback,当解析出来的数字是 NaN 时返回这个值
49
- */
50
- export function safeParseInt(value: string | number, fallback?: number, redix = 10) {
51
- const raw = parseInt(String(value), redix)
52
- return isFinite(raw) ? raw : (fallback ?? raw)
53
- }
54
-
55
- /**
56
- * 字符串解析成浮点数;若解析结果是 NaN,返回 fallback
57
- */
58
- export function safeParseFloat(value: string | number, fallback: number) {
59
- const raw = parseFloat(String(value))
60
- return isFinite(raw) ? raw : fallback
61
- }
62
-
63
- /**
64
- * 返回人类可读的文件尺寸
65
- * 来自:https://stackoverflow.com/a/14919494
66
- *
67
- * si: true 则使用 1000 进制,否则 1024 进制(默认 false)
68
- * dp: 保留几位小数
69
- */
70
- export function readableSize(bytes: number, si = false, dp = 1) {
71
- const thresh = si ? 1000 : 1024
72
- if (Math.abs(bytes) < thresh) return bytes.toString() + ' B'
73
- const units = si
74
- ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
75
- : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
76
- let u = -1
77
- const r = 10 ** dp
78
- do {
79
- bytes /= thresh
80
- ++u
81
- } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1)
82
- return `${bytes.toFixed(dp)} ${units[u]!}`
83
- }
84
-
85
- /**
86
- * 解析 JSON,失败时不抛出异常,而是返回 undefined
87
- */
88
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
89
- export function safeParseJSON<T>(json: string): T | undefined {
90
- try {
91
- return JSON.parse(json) as T
92
- } catch (e) {
93
- return undefined
94
- }
95
- }
package/src/lang/time.ts DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * 时间处理相关函数
3
- */
4
-
5
- /**
6
- * 返回指定时间长度的 毫秒数
7
- */
8
- export function daysMS(n: number) {
9
- return hoursMS(n * 24)
10
- }
11
- export function hoursMS(n: number) {
12
- return minutesMS(n * 60)
13
- }
14
- export function minutesMS(n: number) {
15
- return secondsMS(n * 60)
16
- }
17
- export function secondsMS(n: number) {
18
- return n * 1000
19
- }
@@ -1,49 +0,0 @@
1
- import { getLogger, type Logger } from './index.js'
2
-
3
- interface Debug {
4
- enable: (namespaces: string) => void
5
- log: (...args: any[]) => any // eslint-disable-line @typescript-eslint/no-explicit-any
6
- }
7
-
8
- /**
9
- * 适配 debug package
10
- */
11
- export function adaptDebugLib(debugLib: Debug, enable = '', logger?: Logger) {
12
- // 不在 localStorage 里记录 debugLib enable 状态,
13
- // 以解决 web worker 里读不到 localStorage 而无法启用 debugLib 日志的问题
14
- const emulate = {
15
- storage: {
16
- data: {} as Record<string, string>,
17
- getItem(name: string) {
18
- return emulate.storage.data[name]
19
- },
20
- setItem(name: string, value: string) {
21
- emulate.storage.data[name] = value
22
- },
23
- removeItem(name: string) {
24
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
25
- delete emulate.storage.data[name]
26
- },
27
- },
28
- save(namespaces: string) {
29
- if (namespaces) emulate.storage.setItem('debug', namespaces)
30
- else emulate.storage.removeItem('debug')
31
- },
32
- load() {
33
- return emulate.storage.getItem('debug')
34
- },
35
- }
36
- Object.assign(debugLib, emulate)
37
-
38
- // 将 debugLib 日志转发给 logger
39
- if (!logger) logger = getLogger('3rd-library')
40
- debugLib.log = logger.debug.bind(logger)
41
-
42
- if (enable) {
43
- // 有些库(例如 prisma)重新实现了自己的 debug 库,且模仿 debug 也读取 DEBUG 环境变量。
44
- // 这里除了设置 debug 库,顺便也适配这些遵循 debug 库模式的自定义库。
45
- process.env.DEBUG = enable
46
-
47
- debugLib.enable(enable)
48
- }
49
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * 日志常用数据的格式化函数
3
- */
4
- import { type LogInfo, LogLevel } from './index.js'
5
-
6
- const formatters = {
7
- time(info: LogInfo) {
8
- return info.time.format('HH:mm:ss.SSS')
9
- },
10
- datetime(info: LogInfo) {
11
- return info.time.format('YY-MM-DD HH:mm:ss.SSS')
12
- },
13
- level(info: LogInfo) {
14
- const map = {
15
- [LogLevel.Debug]: 'debug',
16
- [LogLevel.Info]: 'info',
17
- [LogLevel.Warning]: 'warn',
18
- [LogLevel.Error]: 'error',
19
- }
20
- return map[info.level]
21
- },
22
- }
23
- export default formatters