@anjianshi/utils 3.0.0 → 3.0.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 (164) 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 +5 -0
  16. package/env-node/index.js +5 -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/{src/env-react/emotion-register-globals.ts → env-react/emotion-register-globals.d.ts} +2 -5
  22. package/env-react/emotion-register-globals.js +5 -0
  23. package/env-react/emotion.d.ts +20 -0
  24. package/env-react/emotion.jsx +34 -0
  25. package/env-react/hooks.d.ts +23 -0
  26. package/env-react/hooks.js +47 -0
  27. package/env-react/index.d.ts +1 -0
  28. package/env-react/index.js +1 -0
  29. package/env-react/react-register-globals.d.ts +21 -0
  30. package/env-react/react-register-globals.js +19 -0
  31. package/env-service/controllers.d.ts +30 -0
  32. package/env-service/controllers.js +41 -0
  33. package/env-service/env-reader.d.ts +55 -0
  34. package/env-service/env-reader.js +79 -0
  35. package/env-service/index.d.ts +6 -0
  36. package/env-service/index.js +6 -0
  37. package/env-service/prisma/adapt-logging.d.ts +21 -0
  38. package/env-service/prisma/adapt-logging.js +30 -0
  39. package/env-service/prisma/extensions/exist.d.ts +10 -0
  40. package/env-service/prisma/extensions/exist.js +16 -0
  41. package/env-service/prisma/extensions/find-and-count.d.ts +7 -0
  42. package/env-service/prisma/extensions/find-and-count.js +19 -0
  43. package/env-service/prisma/extensions/soft-delete.d.ts +52 -0
  44. package/env-service/prisma/extensions/soft-delete.js +123 -0
  45. package/env-service/prisma/extensions/with-transaction.d.ts +9 -0
  46. package/env-service/prisma/extensions/with-transaction.js +54 -0
  47. package/env-service/prisma/index.d.ts +6 -0
  48. package/env-service/prisma/index.js +6 -0
  49. package/env-service/prisma/transaction-contexted.d.ts +11 -0
  50. package/env-service/prisma/transaction-contexted.js +52 -0
  51. package/env-service/redis-cache.d.ts +39 -0
  52. package/env-service/redis-cache.js +116 -0
  53. package/env-service/tasks.d.ts +12 -0
  54. package/env-service/tasks.js +37 -0
  55. package/index.d.ts +4 -0
  56. package/index.js +4 -0
  57. package/init-dayjs.d.ts +2 -0
  58. package/init-dayjs.js +7 -0
  59. package/lang/async.d.ts +19 -0
  60. package/lang/async.js +34 -0
  61. package/lang/color.d.ts +37 -0
  62. package/lang/color.js +111 -0
  63. package/lang/index.d.ts +8 -0
  64. package/lang/index.js +8 -0
  65. package/lang/object.d.ts +12 -0
  66. package/lang/object.js +41 -0
  67. package/lang/random.d.ts +13 -0
  68. package/lang/random.js +24 -0
  69. package/lang/result.d.ts +47 -0
  70. package/lang/result.js +45 -0
  71. package/lang/string.d.ts +29 -0
  72. package/lang/string.js +92 -0
  73. package/lang/time.d.ts +10 -0
  74. package/lang/time.js +18 -0
  75. package/{src/lang/types.ts → lang/types.d.ts} +23 -43
  76. package/lang/types.js +28 -0
  77. package/logging/adapt.d.ts +10 -0
  78. package/logging/adapt.js +43 -0
  79. package/logging/formatters.d.ts +10 -0
  80. package/logging/formatters.js +22 -0
  81. package/logging/index.d.ts +45 -0
  82. package/logging/index.js +90 -0
  83. package/md5.d.ts +30 -0
  84. package/md5.js +308 -0
  85. package/package.json +10 -19
  86. package/safe-request.d.ts +53 -0
  87. package/safe-request.js +140 -0
  88. package/url.d.ts +77 -0
  89. package/url.js +149 -0
  90. package/validators/array.d.ts +30 -0
  91. package/validators/array.js +47 -0
  92. package/validators/base.d.ts +82 -0
  93. package/validators/base.js +42 -0
  94. package/validators/boolean.d.ts +3 -0
  95. package/validators/boolean.js +22 -0
  96. package/validators/datetime.d.ts +12 -0
  97. package/validators/datetime.js +30 -0
  98. package/validators/factory.d.ts +70 -0
  99. package/validators/factory.js +121 -0
  100. package/validators/index.d.ts +9 -0
  101. package/validators/index.js +9 -0
  102. package/validators/number.d.ts +19 -0
  103. package/validators/number.js +26 -0
  104. package/validators/object.d.ts +28 -0
  105. package/validators/object.js +49 -0
  106. package/validators/one-of.d.ts +10 -0
  107. package/validators/one-of.js +15 -0
  108. package/validators/string.d.ts +22 -0
  109. package/validators/string.js +35 -0
  110. package/README.md +0 -10
  111. package/eslint.config.cjs +0 -33
  112. package/publish-prepare.cjs +0 -16
  113. package/src/env-browser/device.ts +0 -62
  114. package/src/env-browser/global.ts +0 -21
  115. package/src/env-browser/load-script.ts +0 -13
  116. package/src/env-browser/logging.ts +0 -58
  117. package/src/env-browser/manage-vconsole.ts +0 -54
  118. package/src/env-node/crypto-random.ts +0 -30
  119. package/src/env-node/fs.ts +0 -50
  120. package/src/env-node/index.ts +0 -5
  121. package/src/env-node/logging/handlers.ts +0 -190
  122. package/src/env-node/logging/index.ts +0 -16
  123. package/src/env-react/emotion.tsx +0 -42
  124. package/src/env-react/hooks.ts +0 -59
  125. package/src/env-react/index.ts +0 -1
  126. package/src/env-react/react-register-globals.ts +0 -53
  127. package/src/env-service/controllers.ts +0 -93
  128. package/src/env-service/env-reader.ts +0 -141
  129. package/src/env-service/index.ts +0 -6
  130. package/src/env-service/prisma/adapt-logging.ts +0 -39
  131. package/src/env-service/prisma/extensions/exist.ts +0 -21
  132. package/src/env-service/prisma/extensions/find-and-count.ts +0 -24
  133. package/src/env-service/prisma/extensions/soft-delete.ts +0 -162
  134. package/src/env-service/prisma/extensions/with-transaction.ts +0 -65
  135. package/src/env-service/prisma/index.ts +0 -6
  136. package/src/env-service/prisma/transaction-contexted.ts +0 -80
  137. package/src/env-service/redis-cache.ts +0 -142
  138. package/src/env-service/tasks.ts +0 -45
  139. package/src/index.ts +0 -4
  140. package/src/init-dayjs.ts +0 -8
  141. package/src/lang/async.ts +0 -47
  142. package/src/lang/color.ts +0 -119
  143. package/src/lang/index.ts +0 -8
  144. package/src/lang/object.ts +0 -39
  145. package/src/lang/random.ts +0 -25
  146. package/src/lang/result.ts +0 -78
  147. package/src/lang/string.ts +0 -95
  148. package/src/lang/time.ts +0 -19
  149. package/src/logging/adapt.ts +0 -49
  150. package/src/logging/formatters.ts +0 -23
  151. package/src/logging/index.ts +0 -106
  152. package/src/md5.ts +0 -318
  153. package/src/safe-request.ts +0 -193
  154. package/src/url.ts +0 -185
  155. package/src/validators/array.ts +0 -97
  156. package/src/validators/base.ts +0 -145
  157. package/src/validators/boolean.ts +0 -21
  158. package/src/validators/datetime.ts +0 -39
  159. package/src/validators/factory.ts +0 -244
  160. package/src/validators/index.ts +0 -9
  161. package/src/validators/number.ts +0 -54
  162. package/src/validators/object.ts +0 -101
  163. package/src/validators/one-of.ts +0 -33
  164. package/src/validators/string.ts +0 -72
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 实现创建 validator 的快捷方式
3
+ */
4
+ import { type ArrayOptions, type TupleOptions } from './array.js';
5
+ import { type Validator, type CommonOptions, type Validated } from './base.js';
6
+ import { type BooleanOptions } from './boolean.js';
7
+ import { type DatetimeOptions, type DatetimeValue } from './datetime.js';
8
+ import { type NumberOptions, type NumberValueWithChoices } from './number.js';
9
+ import { type RecordOptions, type StructOptions } from './object.js';
10
+ import { type OneOfOptions } from './one-of.js';
11
+ import { type StringOptions, type StringValueWithChoices } from './string.js';
12
+ export interface AnyDefinition extends CommonOptions {
13
+ type: 'any';
14
+ }
15
+ export interface BooleanDefinition extends BooleanOptions {
16
+ type: 'boolean';
17
+ }
18
+ export interface NumberDefinition extends NumberOptions {
19
+ type: 'number';
20
+ }
21
+ export interface StringDefinition extends StringOptions {
22
+ type: 'string';
23
+ }
24
+ export interface DatetimeDefinition extends DatetimeOptions {
25
+ type: 'datetime';
26
+ }
27
+ export interface ArrayDefinition extends Omit<ArrayOptions, 'item'> {
28
+ type: 'array';
29
+ item: Definition;
30
+ }
31
+ export interface TupleDefinition extends Omit<TupleOptions, 'tuple'> {
32
+ type: 'tuple';
33
+ tuple: Definition[];
34
+ }
35
+ export interface StructDefinition extends Omit<StructOptions, 'struct'> {
36
+ type: 'struct';
37
+ struct: Record<string, Definition>;
38
+ }
39
+ export interface RecordDefinition extends Omit<RecordOptions, 'record'> {
40
+ type: 'record';
41
+ record: Definition;
42
+ }
43
+ export interface OneOfDefinition extends Omit<OneOfOptions, 'validators'> {
44
+ type: 'oneOf';
45
+ validators: Definition[];
46
+ }
47
+ export type Definition = AnyDefinition | BooleanDefinition | NumberDefinition | StringDefinition | DatetimeDefinition | ArrayDefinition | TupleDefinition | StructDefinition | RecordDefinition | OneOfDefinition;
48
+ export type ValueOfDefinition<Def extends Definition> = Def extends AnyDefinition ? unknown : Def extends BooleanDefinition ? boolean : Def extends NumberDefinition ? NumberValueWithChoices<Def> : Def extends StringDefinition ? StringValueWithChoices<Def> : Def extends DatetimeDefinition ? DatetimeValue<Def> : Def extends ArrayDefinition ? Validated<ValueOfDefinition<Def['item']>, Def['item']>[] : Def extends TupleDefinition ? {
49
+ [Key in keyof Def['tuple']]: Def['tuple'][Key] extends Definition ? Validated<ValueOfDefinition<Def['tuple'][Key]>, Def['tuple'][Key]> : Def['tuple'][Key];
50
+ } : Def extends StructDefinition ? {
51
+ [Key in keyof Def['struct']]: Validated<ValueOfDefinition<Def['struct'][Key]>, Def['struct'][Key]>;
52
+ } : Def extends RecordDefinition ? Record<string, Validated<ValueOfDefinition<Def['record']>, Def['record']>> : Def extends OneOfDefinition ? {
53
+ [Key in keyof Def['validators']]: Def['validators'][Key] extends Definition ? Validated<ValueOfDefinition<Def['validators'][Key]>, Def['validators'][Key]> : Def['validators'][Key];
54
+ }[number] : never;
55
+ export type OptionsFromDefinition<Def extends Definition> = Def extends ArrayDefinition ? Omit<Def, 'item'> & {
56
+ item: ValidatorForDefinition<Def['item']>;
57
+ } : Def extends TupleDefinition ? Omit<Def, 'tuple'> & {
58
+ tuple: {
59
+ [Key in keyof Def['tuple']]: Def['tuple'][Key] extends Definition ? ValidatorForDefinition<Def['tuple'][Key]> : never;
60
+ };
61
+ } : Def extends StructDefinition ? Omit<Def, 'struct'> & {
62
+ struct: {
63
+ [Key in keyof Def['struct']]: ValidatorForDefinition<Def['struct'][Key]>;
64
+ };
65
+ } : Def extends RecordDefinition ? Omit<Def, 'record'> & {
66
+ record: ValidatorForDefinition<Def['record']>;
67
+ } : Def;
68
+ export type ValidatorForDefinition<Def extends Definition> = Validator<ValueOfDefinition<Def>, OptionsFromDefinition<Def>>;
69
+ export type ResultForDefinition<Def extends Definition> = ReturnType<ValidatorForDefinition<Def>>;
70
+ export declare function getValidator<const InputDefinition extends Definition>(definition: InputDefinition): ValidatorForDefinition<InputDefinition>;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * 实现创建 validator 的快捷方式
3
+ */
4
+ import { getArrayValidator, getTupleValidator, } from './array.js';
5
+ import { getAnyValidator } from './base.js';
6
+ import { getBooleanValidator } from './boolean.js';
7
+ import { getDatetimeValidator } from './datetime.js';
8
+ import { getNumberValidator } from './number.js';
9
+ import { getRecordValidator, getStructValidator, } from './object.js';
10
+ import { getOneOfValidator } from './one-of.js';
11
+ import { getStringValidator } from './string.js';
12
+ export function getValidator(definition) {
13
+ switch (definition.type) {
14
+ case 'any':
15
+ return getAnyValidator(definition);
16
+ case 'boolean':
17
+ return getBooleanValidator(definition);
18
+ case 'number':
19
+ return getNumberValidator(definition);
20
+ case 'string':
21
+ return getStringValidator(definition);
22
+ case 'datetime':
23
+ return getDatetimeValidator(definition);
24
+ case 'array':
25
+ // @ts-ignore 允许递归类型推断
26
+ return getArrayValidator({
27
+ // @ts-ignore 允许递归类型推断
28
+ ...definition,
29
+ item: getValidator(definition.item),
30
+ });
31
+ case 'tuple':
32
+ return getTupleValidator({
33
+ ...definition,
34
+ tuple: definition.tuple.map(def => getValidator(def)),
35
+ });
36
+ case 'struct': {
37
+ const struct = {};
38
+ for (const [key, def] of Object.entries(definition.struct))
39
+ struct[key] = getValidator(def);
40
+ return getStructValidator({ ...definition, struct });
41
+ }
42
+ case 'record':
43
+ return getRecordValidator({
44
+ ...definition,
45
+ record: getValidator(definition.record),
46
+ });
47
+ case 'oneOf':
48
+ return getOneOfValidator({
49
+ ...definition,
50
+ validators: definition.validators.map(def => getValidator(def)),
51
+ });
52
+ }
53
+ }
54
+ // ---------------- 测试用例 -----------------
55
+ // const v1 = getValidator({ type: 'string' })(1)
56
+ // const v2 = getValidator({ type: 'string', null: true })(1)
57
+ // const v3 = getValidator({ type: 'string', null: true, required: false })(1)
58
+ // const v4 = getValidator({ type: 'string', null: true, required: false, choices: ['a', 'b', 'c'] })(
59
+ // 1,
60
+ // )
61
+ // const v5 = getValidator({
62
+ // type: 'string',
63
+ // null: true,
64
+ // required: false,
65
+ // choices: ['a', 'b', 'c'],
66
+ // defaults: 'd',
67
+ // })(1)
68
+ // const v6 = getValidator({
69
+ // type: 'array',
70
+ // null: true,
71
+ // required: false,
72
+ // item: { type: 'string', null: true, choices: ['a', 'b', 'c'] },
73
+ // })(1)
74
+ // const v7 = getValidator({
75
+ // type: 'tuple',
76
+ // tuple: [
77
+ // { type: 'string', choices: ['a', 'b', 'c'] },
78
+ // { type: 'string', null: true },
79
+ // ],
80
+ // })(1)
81
+ // if (v7.success) {
82
+ // const [a, b] = v7.data
83
+ // }
84
+ // const v8 = getValidator({
85
+ // type: 'struct',
86
+ // struct: {
87
+ // x: { type: 'string', choices: ['a', 'b', 'c'] },
88
+ // y: { type: 'string', null: true },
89
+ // },
90
+ // })(1)
91
+ // if (v8.success) {
92
+ // const { x, y } = v8.data
93
+ // }
94
+ // const v9 = getValidator({
95
+ // type: 'record',
96
+ // record: { type: 'string', null: true, choices: ['a', 'b', 'c'] },
97
+ // })(1)
98
+ // const v10 = getValidator({
99
+ // type: 'oneOf',
100
+ // validators: [
101
+ // { type: 'string', choices: ['a', 'b', 'c'] },
102
+ // { type: 'number', null: true },
103
+ // ],
104
+ // defaults: true,
105
+ // })(1)
106
+ // const v11 = getValidator({
107
+ // type: 'string',
108
+ // custom(value) {
109
+ // return { success: true, data: value }
110
+ // },
111
+ // defaults: 'some text',
112
+ // })(1)
113
+ // const v12 = getValidator({
114
+ // type: 'datetime',
115
+ // null: true,
116
+ // required: false,
117
+ // })(1)
118
+ // const v13 = getValidator({
119
+ // type: 'datetime',
120
+ // raw: true,
121
+ // })(1)
@@ -0,0 +1,9 @@
1
+ export * from './base.js';
2
+ export * from './boolean.js';
3
+ export * from './number.js';
4
+ export * from './string.js';
5
+ export * from './datetime.js';
6
+ export * from './array.js';
7
+ export * from './object.js';
8
+ export * from './one-of.js';
9
+ export * from './factory.js';
@@ -0,0 +1,9 @@
1
+ export * from './base.js';
2
+ export * from './boolean.js';
3
+ export * from './number.js';
4
+ export * from './string.js';
5
+ export * from './datetime.js';
6
+ export * from './array.js';
7
+ export * from './object.js';
8
+ export * from './one-of.js';
9
+ export * from './factory.js';
@@ -0,0 +1,19 @@
1
+ import { type CommonOptions } from './base.js';
2
+ export interface NumberOptions extends CommonOptions<number> {
3
+ /** 数值最小值 */
4
+ min?: number;
5
+ /** 数值最大值 */
6
+ max?: number;
7
+ /** 是否允许小数 @default false */
8
+ float?: boolean;
9
+ /**
10
+ * 指定可选值
11
+ * 若指定,前几个选项将不再生效 */
12
+ choices?: number[] | Record<number, string>;
13
+ }
14
+ export type NumberValueWithChoices<Options extends NumberOptions> = Options extends {
15
+ choices: (infer T)[];
16
+ } ? T : Options extends {
17
+ choices: Record<number, string>;
18
+ } ? Options['choices'][keyof Options['choices']] : number;
19
+ export declare function getNumberValidator<const Options extends NumberOptions>(options?: Options): import("./base.js").Validator<NumberValueWithChoices<Options>, Options>;
@@ -0,0 +1,26 @@
1
+ import { truthy, success, failed } from '../lang/index.js';
2
+ import { getValidatorGenerator } from './base.js';
3
+ export function getNumberValidator(options = {}) {
4
+ return getValidatorGenerator(function validate(field, value) {
5
+ if (typeof value === 'string')
6
+ value = parseFloat(value);
7
+ if (typeof value !== 'number' || !isFinite(value))
8
+ return failed(`${field} must be a valid number`);
9
+ if ('choices' in options && options.choices) {
10
+ const choices = Array.isArray(options.choices)
11
+ ? options.choices
12
+ : Object.values(options.choices).map(v => parseInt(v, 10));
13
+ if (!choices.includes(value))
14
+ return failed(`${field} can only be one of ${choices.join(', ')}.`);
15
+ }
16
+ else {
17
+ if (!truthy(options.float) && value % 1 !== 0)
18
+ return failed(`${field} must be a integer`);
19
+ if (typeof options.min === 'number' && value < options.min)
20
+ return failed(`${field} must >= ${options.min}`);
21
+ if (typeof options.max === 'number' && value > options.max)
22
+ return failed(`${field} must <= ${options.max}`);
23
+ }
24
+ return success(value);
25
+ })(options);
26
+ }
@@ -0,0 +1,28 @@
1
+ import { type CommonOptions, type Validator, type Validated } from './base.js';
2
+ /** 验证有明确键值对结构的对象 */
3
+ export interface StructOptions extends CommonOptions {
4
+ /** 定义对象结构,及各个值的验证规则 */
5
+ struct: Record<string, Validator<unknown, CommonOptions>>;
6
+ }
7
+ type StructValues<Options extends StructOptions> = {
8
+ [Key in keyof Options['struct']]: Options['struct'][Key] extends Validator<infer Value, infer Options> ? Validated<Value, Options> : never;
9
+ };
10
+ export declare function getStructValidator<const Options extends StructOptions>(options: Options): Validator<StructValues<Options>, Options>;
11
+ /**
12
+ * 验证有任意多个 key,但值的类型固定的对象
13
+ */
14
+ export interface RecordOptions extends CommonOptions {
15
+ /** 验证单个值 */
16
+ record: Validator<unknown, CommonOptions>;
17
+ /** 对象至少要有几项 */
18
+ min?: number;
19
+ /** 对象最多有几项 */
20
+ max?: number;
21
+ }
22
+ type RecordValues<Options extends RecordOptions> = Record<string, Validated<Options extends {
23
+ record: Validator<infer T, CommonOptions>;
24
+ } ? T : never, Options extends {
25
+ record: Validator<unknown, infer T>;
26
+ } ? T : never>>;
27
+ export declare function getRecordValidator<Options extends RecordOptions>(options: Options): Validator<RecordValues<Options>, Options>;
28
+ export {};
@@ -0,0 +1,49 @@
1
+ import isPlainObject from 'lodash/isPlainObject.js';
2
+ import { success, failed } from '../lang/index.js';
3
+ import { getValidatorGenerator, } from './base.js';
4
+ export function getStructValidator(options) {
5
+ return getValidatorGenerator(function validate(field, value, options) {
6
+ if (!isPlainObject(value))
7
+ return failed(`${field} should be a plain object`);
8
+ const formatted = {};
9
+ for (const [key, itemValidator] of Object.entries(options.struct)) {
10
+ const itemResult = itemValidator(`${field}["${key}"]`, value[key]);
11
+ if (itemResult.success) {
12
+ if (itemResult.data !== undefined)
13
+ formatted[key] = itemResult.data;
14
+ }
15
+ else {
16
+ return itemResult;
17
+ }
18
+ }
19
+ return success(formatted);
20
+ })(options);
21
+ }
22
+ export function getRecordValidator(options) {
23
+ return getValidatorGenerator(function validate(field, value, options) {
24
+ if (!isPlainObject(value))
25
+ return failed(`${field} should be a plain object`);
26
+ const formatted = {};
27
+ for (const [key, itemValue] of Object.entries(value)) {
28
+ // record 场景下,值为 undefined 的项目视为不存在,不保留在验证结果里,
29
+ // 不然一些因为不想赋值而填充了 undefined 值的项目可能意外触发验证失败,或意外得到了默认值。
30
+ // (因此 validator 的 required 选项和 defaults 选项也没有意义了)
31
+ if (itemValue === undefined)
32
+ continue;
33
+ const itemResult = options.record(`${field}["${key}"]`, itemValue);
34
+ if (itemResult.success) {
35
+ if (itemResult.data !== undefined)
36
+ formatted[key] = itemResult.data;
37
+ }
38
+ else {
39
+ return itemResult;
40
+ }
41
+ }
42
+ const length = Object.keys(formatted).length;
43
+ if (typeof options.min === 'number' && length < options.min)
44
+ return failed(`size of ${field} should >= ${options.min}`);
45
+ if (typeof options.max === 'number' && length > options.max)
46
+ return failed(`size of ${field} should <= ${options.max}`);
47
+ return success(formatted);
48
+ })(options);
49
+ }
@@ -0,0 +1,10 @@
1
+ import { type CommonOptions, type Validator } from './base.js';
2
+ /** 要求返回值通过其中一个验证器的检查 */
3
+ export interface OneOfOptions extends CommonOptions {
4
+ /** 验证数组各元素 */
5
+ validators: Validator<unknown, CommonOptions>[];
6
+ }
7
+ export type OneOfValue<Options extends OneOfOptions> = Options extends {
8
+ validators: Validator<infer T, CommonOptions>[];
9
+ } ? T : never;
10
+ export declare function getOneOfValidator<const Options extends OneOfOptions>(options?: Options): Validator<OneOfValue<Options>, Options>;
@@ -0,0 +1,15 @@
1
+ import { failed } from '../lang/index.js';
2
+ import { getValidatorGenerator, } from './base.js';
3
+ export function getOneOfValidator(options = {}) {
4
+ return getValidatorGenerator(function validate(field, value) {
5
+ const errors = [];
6
+ for (const validator of options.validators) {
7
+ const result = validator(field, value);
8
+ if (result.success)
9
+ return result;
10
+ else
11
+ errors.push(result.message);
12
+ }
13
+ return failed(`${field} do not match any valid format:\n- ` + errors.join('\n- '));
14
+ })(options);
15
+ }
@@ -0,0 +1,22 @@
1
+ import { type CommonOptions } from './base.js';
2
+ export interface StringOptions extends CommonOptions<string> {
3
+ /** 字符串最小长度。defaults='' 时默认为 0,否则默认为 1 */
4
+ min?: number;
5
+ /** 字符串最大长度。 */
6
+ max?: number;
7
+ /** 字符串需匹配此正则,也可传入关键词使用预置的正则。正则通常应以 ^ 和 $ 开头结尾。 */
8
+ pattern?: RegExp | 'uuid' | 'mobile';
9
+ /**
10
+ * 指定一个数组或 TypeScript enum,字段值必须在此 enum 之中。
11
+ * 若指定,前几个选项将不再生效。
12
+ */
13
+ choices?: string[] | Record<string, string>;
14
+ /** 验证之前,是否先清除两侧空白字符 @default true */
15
+ trim?: boolean;
16
+ }
17
+ export type StringValueWithChoices<Options extends StringOptions> = Options extends {
18
+ choices: (infer T)[];
19
+ } ? T : Options extends {
20
+ choices: Record<string, infer T>;
21
+ } ? T : string;
22
+ export declare function getStringValidator<const Options extends StringOptions>(options?: Options): import("./base.js").Validator<StringValueWithChoices<Options>, Options>;
@@ -0,0 +1,35 @@
1
+ import { success, failed } from '../lang/index.js';
2
+ import { getValidatorGenerator } from './base.js';
3
+ export function getStringValidator(options = {}) {
4
+ return getValidatorGenerator(function validate(field, value) {
5
+ if (typeof value !== 'string')
6
+ return failed(`${field} must be a string`);
7
+ const trim = options.trim ?? true;
8
+ const formatted = trim ? value.trim() : value;
9
+ if ('choices' in options && options.choices) {
10
+ const validValues = Array.isArray(options.choices)
11
+ ? options.choices
12
+ : Object.values(options.choices);
13
+ if (!validValues.includes(formatted))
14
+ return failed(`${field} can only be one of ${validValues.join(', ')}.`);
15
+ }
16
+ else {
17
+ const { min = options.defaults === '' ? 0 : 1, max, pattern } = options;
18
+ if (typeof min === 'number' && formatted.length < min)
19
+ return failed(`${field}'s length must >= ${min}`);
20
+ if (typeof max === 'number' && formatted.length > max)
21
+ return failed(`${field}'s length must <= ${max}`);
22
+ if (pattern !== undefined) {
23
+ if (pattern instanceof RegExp && !pattern.exec(formatted))
24
+ return failed(`${field} does not match the pattern`);
25
+ if (pattern === 'mobile' && !/^1\d{10}$/.test(formatted))
26
+ return failed(`${field} is not a valid mobile number`);
27
+ if (pattern === 'uuid') {
28
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(formatted))
29
+ return failed(`${field} is not a valid uuid`);
30
+ }
31
+ }
32
+ }
33
+ return success(formatted);
34
+ })(options);
35
+ }
package/README.md DELETED
@@ -1,10 +0,0 @@
1
- # Common JavaScript Utils
2
-
3
- ## 依赖
4
-
5
- - `init-dayjs` 依赖 `dayjs`
6
- - `/logging` 依赖 `dayjs`,且会自动引用 `init-dayjs`
7
- - `/env-node/logging` 依赖 `chalk`
8
- - `/env-react/emotion` 依赖 `@emotion/*` 系列类库和 `react`
9
- - `/env-service/prisma` 依赖 `@prisma/client`
10
- - `/env-service/env-reader` 依赖 `dotenv`
package/eslint.config.cjs DELETED
@@ -1,33 +0,0 @@
1
- const globals = require('@anjianshi/presets-eslint-base/globals.cjs')
2
- const { limitFiles } = require('@anjianshi/presets-eslint-base/utils.cjs')
3
-
4
- const configs = [
5
- ...require('@anjianshi/presets-eslint-base'),
6
- ...require('@anjianshi/presets-eslint-typescript'),
7
- ...limitFiles(
8
- ['*.cjs', 'src/{env-node, env-service}/'],
9
- require('@anjianshi/presets-eslint-node/exclusive.cjs'),
10
- ),
11
- ...limitFiles('src/env-browser/', {
12
- languageOptions: {
13
- globals: { ...globals.browser },
14
- },
15
- }),
16
- ...limitFiles('src/env-react/', require('@anjianshi/presets-eslint-react/exclusive.cjs')),
17
- ...limitFiles('src/env-service/prisma/', {
18
- rules: {
19
- // prisma 相关方法需要保证返回的是 PrismaPromise 而不能是经过 async function 转过的 Promise,所以不能把函数标记为 async function
20
- '@typescript-eslint/promise-function-async': 'off',
21
- },
22
- }),
23
- ...limitFiles('src/env-service/prisma/extensions/', {
24
- rules: {
25
- // prisma extension 需要用到 any 类型
26
- '@typescript-eslint/no-explicit-any': 'off',
27
- '@typescript-eslint/no-unsafe-call': 'off',
28
- '@typescript-eslint/no-unsafe-member-access': 'off',
29
- '@typescript-eslint/no-unsafe-assignment': 'off',
30
- },
31
- }),
32
- ]
33
- module.exports = configs
@@ -1,16 +0,0 @@
1
- const fs = require('node:fs')
2
- const path = require('node:path')
3
-
4
- const base = path.resolve(__dirname, './')
5
- const dist = path.join(base, 'dist')
6
-
7
- const packageJSON = require(path.join(base, 'package.json'))
8
- delete packageJSON.publishConfig.directory
9
- delete packageJSON.scripts
10
- for (const [key, value] of Object.entries(packageJSON.dependencies)) {
11
- packageJSON.dependencies[key] = value.replace('workspace:', 'workspace:../')
12
- }
13
- for (const [key, value] of Object.entries(packageJSON.devDependencies)) {
14
- packageJSON.devDependencies[key] = value.replace('workspace:', 'workspace:../')
15
- }
16
- fs.writeFileSync(path.join(dist, 'package.json'), JSON.stringify(packageJSON, null, 2))
@@ -1,62 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-deprecated */
2
-
3
- /**
4
- * 自动计算最合适的 rem 大小,应用到页面样式上。
5
- *
6
- * rem 的意义是对于大屏幕,适当地同步放大界面元素,以提供更饱满的显示效果。
7
- * 不过也不是所有地方都原封不动地大就好了,所以还要配合 media query 来实现最佳效果。
8
- */
9
- export function autoRem() {
10
- const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'
11
-
12
- /**
13
- * 计算规则:
14
- * - 微信标准的 375px 宽度下,1rem 为 50px(这个比例的来由是这样的:移动端通常像素比 >= 2,这个 375px 的实际渲染像素是 750px,也就是实际渲染 750px 的情况下,1rem 为 100px)。
15
- * - 在平板等特大屏幕下,限制 rem 的最大值为 75px,再大就夸张了。
16
- * - 竖屏时取宽度来适配 rem,横屏时取高度来适配,以保证屏幕朝向不同时,界面元素的放大比例是一致的(这应该更符合人的认知:对于一个同样大小的屏幕,无论什么朝向,上面的文字应该是一样大的)。
17
- * 注意:此计算方式仅适合“不能任意调整浏览器大小的”移动设备,对于浏览器窗口可能任意尺寸、比例的桌面端,还是应该始终按照宽度来适配。
18
- *
19
- * 从 px 到 rem 的换算:
20
- * 原 px 单位的值换算成 rem,只要除以 50 即可。例如 12px 变成 0.24rem。
21
- */
22
- function updateRem() {
23
- const { clientWidth, clientHeight } = document.documentElement
24
- const refSize = Math.min(clientWidth, clientHeight)
25
- const rem = Math.min((refSize / 750) * 100, 75)
26
- document.documentElement.style.fontSize = `${rem}px`
27
- }
28
-
29
- window.addEventListener(resizeEvt, updateRem, false)
30
- document.addEventListener('DOMContentLoaded', updateRem, false)
31
- }
32
-
33
- /**
34
- * 当前设备是否是全面屏
35
- * 页面刚加载时可能取不到 offsetHeight,因此通过一个函数而不是常量来提供此值
36
- */
37
- export function isFullScreen() {
38
- const rate = document.documentElement.offsetHeight / document.documentElement.offsetWidth
39
- const limit = window.screen.height === window.screen.availHeight ? 1.8 : 1.65 // 临界判断值
40
- return rate > limit
41
- }
42
-
43
- /**
44
- * 当前是否是 iOS 设备
45
- */
46
- export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
47
-
48
- /**
49
- * 当前是否是移动设备
50
- */
51
- export const isMobile =
52
- /(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(
53
- navigator.userAgent || navigator.vendor,
54
- ) ||
55
- /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(
56
- (navigator.userAgent || navigator.vendor).substr(0, 4),
57
- )
58
-
59
- /**
60
- * 当前是否是微信内部浏览器
61
- */
62
- export const isWechat = /micromessenger/i.test(navigator.userAgent)
@@ -1,21 +0,0 @@
1
- /**
2
- * 通过此模块注册全局变量以便于调试
3
- * 变量会注册到 window.app = {} 下
4
- */
5
-
6
- declare global {
7
- interface Window {
8
- app: Record<string, unknown>
9
- }
10
- }
11
-
12
- // web-worker 环境下不存在 window 变量,无法执行注册
13
- let hasWindow = false
14
- try {
15
- window.app = {}
16
- hasWindow = true
17
- } catch (e) {} // eslint-disable-line no-empty
18
-
19
- export default function registerGlobal(key: string, content: unknown) {
20
- if (hasWindow) window.app[key] = content
21
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * 加载脚本文件
3
- * 返回 Promise,成功则 resolve,失败 reject
4
- */
5
- export default async function loadScript(url: string): Promise<void> {
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
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * 针对浏览器环境定制 logging
3
- */
4
- import {
5
- logger as defaultLogger,
6
- type Logger,
7
- type LogInfo,
8
- LogLevel,
9
- LogHandler,
10
- formatters,
11
- } from '../logging/index.js'
12
-
13
- export * from '../logging/index.js'
14
-
15
- /**
16
- * 实现向浏览器 console 输出日志
17
- */
18
- export class ConsoleHandler extends LogHandler {
19
- log(info: LogInfo) {
20
- const color = (value = 'black') => `color: ${value};`
21
- const prefix =
22
- '%c' + [formatters.time(info), info.logger].map((v: string) => (v ? `[${v}]` : '')).join('')
23
- const prefixColor = info.logger ? color(ConsoleHandler.getColor(info.logger)) : color()
24
-
25
- const values = [prefix, prefixColor, ...info.args]
26
- if (info.level === LogLevel.Debug) console.debug(...values)
27
- else if (info.level === LogLevel.Info) console.log(...values)
28
- else if (info.level === LogLevel.Warning) console.warn(...values)
29
- else console.error(...values)
30
- }
31
-
32
- // 按顺序给各主题分配颜色(取自 http://chriskempson.com/projects/base16/)
33
- private static readonly colors = [
34
- '#dc9656',
35
- '#7cafc2',
36
- '#ba8baf',
37
- '#a16946',
38
- '#ab4642',
39
- '#86c1b9',
40
- '#a1b56c',
41
- '#f7ca88',
42
- ]
43
- private static readonly colorMap = new Map<string, string>()
44
- static getColor(name: string) {
45
- if (!ConsoleHandler.colorMap.has(name)) {
46
- const nextIndex = ConsoleHandler.colorMap.size % ConsoleHandler.colors.length
47
- ConsoleHandler.colorMap.set(name, ConsoleHandler.colors[nextIndex]!)
48
- }
49
- return ConsoleHandler.colorMap.get(name)!
50
- }
51
- }
52
-
53
- /**
54
- * 预设的初始化行为
55
- */
56
- export function initLogger(logger: Logger = defaultLogger) {
57
- logger.addHandler(new ConsoleHandler())
58
- }