@zhin.js/schema 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 凉菜
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './schema.js';
2
+ export * from './utils.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './schema.js';
2
+ export * from './utils.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,104 @@
1
+ export declare class Schema<S = any, T = S> {
2
+ meta: Schema.Meta<S, T>;
3
+ options: Schema.Options;
4
+ [Symbol.toStringTag]: string;
5
+ constructor(meta: Schema.Meta<S, T>, options?: Schema.Options);
6
+ static fromJSON<S, T>(json: Schema.JSON<S, T>): Schema<S, T>;
7
+ toJSON(): Record<string, any>;
8
+ [Symbol.unscopables](): {
9
+ options: boolean;
10
+ meta: boolean;
11
+ };
12
+ /** 设置是否必填 */
13
+ required(): this;
14
+ /** 是否隐藏 */
15
+ hidden(): this;
16
+ /** 设置描述 */
17
+ description(description: string): this;
18
+ /** 设置默认值 */
19
+ default(defaultValue: T): this;
20
+ /** 设置选项列表 */
21
+ option(list: (T | Schema.Option<T>)[]): this;
22
+ /** 设置是否允许多选 */
23
+ multiple(): this;
24
+ /** 设置最小值 */
25
+ min(value: number): this;
26
+ /** 设置最大值 */
27
+ max(value: number): this;
28
+ /** 设置步进值 */
29
+ step(value: number): this;
30
+ /** 设置组件类型 */
31
+ component(type: string): this;
32
+ /** 声明一个数字类型 */
33
+ static number(key?: string): Schema<number>;
34
+ /** 声明一个字符串类型 */
35
+ static string(key?: string): Schema<string>;
36
+ /** 声明一个布尔类型 */
37
+ static boolean(key?: string): Schema<boolean>;
38
+ /** 声明一个正则类型 */
39
+ static regexp(key?: string): Schema<string | RegExp, RegExp>;
40
+ /** 声明一个日期类型 */
41
+ static date(key?: string): Schema<number | Date, Date>;
42
+ /** 声明一个字典类型 */
43
+ static dict<X extends Schema>(input: X, key?: string): Schema<Record<string, Schema.Types<X>>, Record<string, Schema.Types<X>>>;
44
+ static object<X extends Record<string, Schema>>(input: X, key?: string): Schema<Schema.RecordTypes<X>, Schema.RecordTypes<X>>;
45
+ /** 声明一个列表类型 */
46
+ static list<X extends Schema>(inner: X, key?: string): Schema<Schema.Types<X>[], Schema.Types<X>[]>;
47
+ /** 声明一个元组类型 */
48
+ static tuple<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Tuple<X>, Schema.Tuple<X>>;
49
+ /** 声明一个联合类型 */
50
+ static union<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Union<X>, Schema.Union<X>>;
51
+ /** 声明一个交叉类型 */
52
+ static intersect<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Intersect<X>, Schema.Intersect<X>>;
53
+ /** 声明一个常量 */
54
+ static const<X extends string | number | boolean>(value: X, key?: string): Schema<X, X>;
55
+ /** 声明任意类型 */
56
+ static any(key?: any): Schema<any, any>;
57
+ static resolve<T extends string>(type: T): Schema.Formatter;
58
+ static extend<T extends string>(type: T, formatter: Schema.Formatter): void;
59
+ }
60
+ export interface Schema<S = any> {
61
+ (value?: S, key?: string): S;
62
+ }
63
+ export declare namespace Schema {
64
+ const formatters: Map<string, Formatter>;
65
+ type Formatter<S = any, T = S> = (this: Schema, key: string, value: S) => T;
66
+ type JSON<S = any, T = S> = Meta<S, T> & {
67
+ object?: Record<string, JSON>;
68
+ inner?: JSON;
69
+ list?: JSON[];
70
+ };
71
+ interface Meta<S = any, T = S> {
72
+ hidden?: boolean;
73
+ type: string;
74
+ default?: T;
75
+ required?: boolean;
76
+ options?: Option<T>[];
77
+ multiple?: boolean;
78
+ key?: string;
79
+ description?: string;
80
+ component?: string;
81
+ min?: number;
82
+ max?: number;
83
+ step?: number;
84
+ }
85
+ interface Options {
86
+ object?: Record<string, Schema>;
87
+ inner?: Schema;
88
+ list?: readonly Schema[];
89
+ }
90
+ type Types<T> = T extends Schema<infer S> ? S : never;
91
+ type RecordTypes<T> = T extends Record<string, Schema> ? {
92
+ [K in keyof T]?: Types<T[K]>;
93
+ } : unknown;
94
+ type Union<T extends readonly any[]> = T extends readonly [infer L, ...infer R] ? Types<L> | Union<R> : never;
95
+ type Intersect<T extends readonly any[]> = T extends readonly [infer L, ...infer R] ? Types<L> & Intersect<R> : unknown;
96
+ type Tuple<T extends readonly any[]> = T extends readonly [infer L, ...infer R] ? [Types<L>, ...Tuple<R>] : [];
97
+ function checkDefault<T>(schema: Schema, key: string, value: T, fallback?: T): T;
98
+ type Option<T = any> = {
99
+ label: string;
100
+ value: T;
101
+ };
102
+ function formatOptionList<T extends (any | Schema.Option)[]>(list: T): Schema.Option[];
103
+ }
104
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/lib/schema.js ADDED
@@ -0,0 +1,342 @@
1
+ import { isEmpty } from './utils.js';
2
+ export class Schema {
3
+ meta;
4
+ options;
5
+ [Symbol.toStringTag] = 'Schema';
6
+ constructor(meta, options = {}) {
7
+ this.meta = meta;
8
+ this.options = options;
9
+ const _this = this;
10
+ const schema = function (value, key = 'value') {
11
+ const formatter = Schema.resolve(_this.meta.type);
12
+ if (!formatter)
13
+ throw new Error(`type ${_this.meta.type} not found`);
14
+ return formatter.call(_this, key, value);
15
+ };
16
+ return new Proxy(schema, {
17
+ get(target, p, receiver) {
18
+ return Reflect.get(_this, p, receiver);
19
+ },
20
+ set(target, p, value, receiver) {
21
+ return Reflect.set(_this, p, value, receiver);
22
+ },
23
+ });
24
+ }
25
+ static fromJSON(json) {
26
+ const { object, inner, list, ...meta } = json;
27
+ const options = {};
28
+ if (object)
29
+ options.object = Object.fromEntries(Object.entries(object).map(([key, value]) => [key, Schema.fromJSON(value)]));
30
+ if (inner)
31
+ options.inner = Schema.fromJSON(inner);
32
+ if (list)
33
+ options.list = list.map(item => Schema.fromJSON(item));
34
+ return new Schema(meta, options);
35
+ }
36
+ toJSON() {
37
+ return Object.fromEntries(Object.entries({
38
+ ...this.meta,
39
+ default: typeof this.meta.default === 'function' ? this.meta.default() : this.meta.default,
40
+ inner: this.options.inner?.toJSON(),
41
+ object: this.options.object
42
+ ? Object.fromEntries(Object.entries(this.options.object || {}).map(([key, value]) => [
43
+ key,
44
+ {
45
+ ...value.toJSON(),
46
+ key
47
+ }
48
+ ]))
49
+ : undefined,
50
+ list: this.options.list?.map(item => item.toJSON()),
51
+ }).filter(([key, value]) => typeof value !== 'undefined'));
52
+ }
53
+ [Symbol.unscopables]() {
54
+ return {
55
+ options: true,
56
+ meta: true,
57
+ };
58
+ }
59
+ /** 设置是否必填 */
60
+ required() {
61
+ this.meta.required = true;
62
+ return this;
63
+ }
64
+ /** 是否隐藏 */
65
+ hidden() {
66
+ this.meta.hidden = true;
67
+ return this;
68
+ }
69
+ /** 设置描述 */
70
+ description(description) {
71
+ this.meta.description = description;
72
+ return this;
73
+ }
74
+ /** 设置默认值 */
75
+ default(defaultValue) {
76
+ this.meta.default = defaultValue;
77
+ return this;
78
+ }
79
+ /** 设置选项列表 */
80
+ option(list) {
81
+ this.meta.options = Schema.formatOptionList(list);
82
+ return this;
83
+ }
84
+ /** 设置是否允许多选 */
85
+ multiple() {
86
+ if (this.meta.type !== 'list')
87
+ throw new Error('multiple only support list type');
88
+ this.meta.multiple = true;
89
+ return this;
90
+ }
91
+ /** 设置最小值 */
92
+ min(value) {
93
+ this.meta.min = value;
94
+ return this;
95
+ }
96
+ /** 设置最大值 */
97
+ max(value) {
98
+ this.meta.max = value;
99
+ return this;
100
+ }
101
+ /** 设置步进值 */
102
+ step(value) {
103
+ this.meta.step = value;
104
+ return this;
105
+ }
106
+ /** 设置组件类型 */
107
+ component(type) {
108
+ this.meta.component = type;
109
+ return this;
110
+ }
111
+ /** 声明一个数字类型 */
112
+ static number(key) {
113
+ return new Schema({ type: 'number', key });
114
+ }
115
+ /** 声明一个字符串类型 */
116
+ static string(key) {
117
+ return new Schema({ type: 'string', key });
118
+ }
119
+ /** 声明一个布尔类型 */
120
+ static boolean(key) {
121
+ return new Schema({ type: 'boolean', key });
122
+ }
123
+ /** 声明一个正则类型 */
124
+ static regexp(key) {
125
+ return new Schema({ type: 'regexp', key });
126
+ }
127
+ /** 声明一个日期类型 */
128
+ static date(key) {
129
+ return new Schema({ type: 'date', key });
130
+ }
131
+ /** 声明一个字典类型 */
132
+ static dict(input, key) {
133
+ return new Schema({ type: 'dict', key }, { inner: input });
134
+ }
135
+ static object(input, key = '') {
136
+ return new Schema({ type: 'object', key }, { object: input });
137
+ }
138
+ /** 声明一个列表类型 */
139
+ static list(inner, key) {
140
+ return new Schema({ type: 'list', key }, { inner });
141
+ }
142
+ /** 声明一个元组类型 */
143
+ static tuple(list, key) {
144
+ return new Schema({ type: 'tuple', key }, { list });
145
+ }
146
+ /** 声明一个联合类型 */
147
+ static union(list, key) {
148
+ return new Schema({ type: 'union', key }, { list });
149
+ }
150
+ /** 声明一个交叉类型 */
151
+ static intersect(list, key) {
152
+ return new Schema({ type: 'intersect', key }, { list });
153
+ }
154
+ /** 声明一个常量 */
155
+ static const(value, key) {
156
+ return new Schema({ type: 'const', default: value, key });
157
+ }
158
+ /** 声明任意类型 */
159
+ static any(key) {
160
+ return new Schema({ type: 'any', key });
161
+ }
162
+ static resolve(type) {
163
+ return Schema.formatters.get(type);
164
+ }
165
+ static extend(type, formatter) {
166
+ Schema.formatters.set(type, formatter);
167
+ }
168
+ }
169
+ (function (Schema) {
170
+ Schema.formatters = new Map();
171
+ function checkDefault(schema, key, value, fallback = value) {
172
+ if (isEmpty(value)) {
173
+ value = schema.meta.default || fallback;
174
+ }
175
+ const validateType = (schema, key, value) => {
176
+ switch (schema.meta.type) {
177
+ case 'string':
178
+ if (!['string', 'undefined'].includes(typeof value))
179
+ throw new TypeError(`${key} is not a string`);
180
+ break;
181
+ case 'number':
182
+ if (!['number', 'undefined'].includes(typeof value))
183
+ throw new TypeError(`${key} is not a number`);
184
+ break;
185
+ case 'boolean':
186
+ if (!['boolean', 'undefined'].includes(typeof value))
187
+ throw new TypeError(`${key} is not a boolean`);
188
+ break;
189
+ case 'regexp':
190
+ if (!['string', 'undefined'].includes(typeof value) && !(value instanceof RegExp))
191
+ throw new TypeError(`${key} is not a RegExp|string`);
192
+ break;
193
+ case 'date':
194
+ if (!['number', 'undefined'].includes(typeof value) && !(value instanceof Date))
195
+ throw new TypeError(`${key} is not a Date|number`);
196
+ if (value instanceof Date && isNaN(value.getTime()))
197
+ throw new TypeError(`${key} is not a valid Date`);
198
+ break;
199
+ case 'dict':
200
+ if (!['object', 'undefined', 'null'].includes(typeof value))
201
+ throw new TypeError(`${key} is not a object`);
202
+ break;
203
+ case 'object':
204
+ if (!['object', 'undefined', 'null'].includes(typeof value))
205
+ throw new TypeError(`${key} is not a object`);
206
+ break;
207
+ case 'list':
208
+ if (typeof value !== 'undefined' && !Array.isArray(value))
209
+ throw new TypeError(`${key} is not a list`);
210
+ break;
211
+ case 'tuple':
212
+ if (typeof value !== 'undefined' && !Array.isArray(value))
213
+ throw new TypeError(`${key} is not a tuple`);
214
+ break;
215
+ case 'union':
216
+ // union类型不在这里验证,在extend函数中验证
217
+ break;
218
+ case 'intersect':
219
+ // intersect类型不在这里验证,在extend函数中验证
220
+ break;
221
+ case 'const':
222
+ if (typeof value !== 'undefined' && value !== schema.meta.default)
223
+ throw new TypeError(`${key} is not const`);
224
+ break;
225
+ case 'any':
226
+ // any类型不验证
227
+ break;
228
+ default:
229
+ throw new TypeError(`${key} is not a valid type`);
230
+ }
231
+ };
232
+ if (schema.meta.required && typeof value === 'undefined')
233
+ throw new Error(`${key} is required`);
234
+ validateType(schema, key, value);
235
+ return value;
236
+ }
237
+ Schema.checkDefault = checkDefault;
238
+ function formatOptionList(list) {
239
+ return list.map(item => {
240
+ if (typeof item === 'string') {
241
+ return {
242
+ label: `${item}`,
243
+ value: item,
244
+ };
245
+ }
246
+ return item;
247
+ });
248
+ }
249
+ Schema.formatOptionList = formatOptionList;
250
+ })(Schema || (Schema = {}));
251
+ Schema.extend('number', function (key, value) {
252
+ value = Schema.checkDefault(this, key, value);
253
+ return value;
254
+ });
255
+ Schema.extend('string', function (key, value) {
256
+ value = Schema.checkDefault(this, key, value);
257
+ return value;
258
+ });
259
+ Schema.extend('boolean', function (key, value) {
260
+ return Schema.checkDefault(this, key, value);
261
+ });
262
+ Schema.extend('dict', function (key, value) {
263
+ value = Schema.checkDefault(this, key, value, {});
264
+ return Object.fromEntries(Object.entries(value).map(([k, schema]) => {
265
+ return [k, this.options.inner(schema, `${key}.${k}`)];
266
+ }));
267
+ });
268
+ Schema.extend('object', function (key, value) {
269
+ const getDefault = (schema) => {
270
+ const result = Object.create(null);
271
+ for (const key in schema.options.object) {
272
+ const propSchema = schema.options.object[key];
273
+ result[key] = propSchema.default !== undefined ? propSchema.default : undefined;
274
+ }
275
+ return result;
276
+ };
277
+ value = Schema.checkDefault(this, key, value, getDefault(this));
278
+ const result = Object.create(null);
279
+ for (const [k, val] of Object.entries(value)) {
280
+ const propSchema = this.options.object[k];
281
+ if (propSchema) {
282
+ // propSchema是一个可调用的Schema实例
283
+ result[k] = propSchema(val, `${key}.${k}`);
284
+ }
285
+ else {
286
+ result[k] = val;
287
+ }
288
+ }
289
+ return result;
290
+ });
291
+ Schema.extend('list', function (key, value) {
292
+ value = Schema.checkDefault(this, key, value, []);
293
+ return value.map((item, index) => this.options.inner(item, `${key}[${index}]`));
294
+ });
295
+ Schema.extend('regexp', function (key, value) {
296
+ value = Schema.checkDefault(this, key, value);
297
+ if (typeof value === 'string') {
298
+ return new RegExp(value);
299
+ }
300
+ return value;
301
+ });
302
+ Schema.extend('date', function (key, value) {
303
+ value = Schema.checkDefault(this, key, value);
304
+ return new Date(value);
305
+ });
306
+ Schema.extend('const', function (key, value) {
307
+ value = Schema.checkDefault(this, key, value);
308
+ if (value !== this.meta.default) {
309
+ throw new Error(`${key} const value not match`);
310
+ }
311
+ return value;
312
+ });
313
+ Schema.extend('tuple', function (key, value) {
314
+ value = Schema.checkDefault(this, key, value, []);
315
+ return value.map((item, index) => this.options.list[index](item, `${key}[${index}]`));
316
+ });
317
+ Schema.extend('union', function (key, value) {
318
+ value = Schema.checkDefault(this, key, value);
319
+ for (const schema of this.options.list) {
320
+ try {
321
+ return schema(value, key);
322
+ }
323
+ catch (e) { }
324
+ }
325
+ throw new Error(`${key} union type not match`);
326
+ });
327
+ Schema.extend('intersect', function (key, value) {
328
+ value = Schema.checkDefault(this, key, value);
329
+ for (const schema of this.options.list) {
330
+ try {
331
+ value = schema(value, key);
332
+ }
333
+ catch (e) {
334
+ throw new Error(`${key} intersect type not match`);
335
+ }
336
+ }
337
+ return value;
338
+ });
339
+ Schema.extend('any', function (key, value) {
340
+ return Schema.checkDefault(this, key, value);
341
+ });
342
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/lib/utils.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 检查值是否为空
3
+ */
4
+ export declare function isEmpty(value: any): boolean;
5
+ /**
6
+ * 深度合并对象
7
+ */
8
+ export declare function deepMerge<T>(target: T, source: Partial<T>): T;
9
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAE3C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAqB7D"}
package/lib/utils.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 检查值是否为空
3
+ */
4
+ export function isEmpty(value) {
5
+ return value === null || value === undefined || value === '';
6
+ }
7
+ /**
8
+ * 深度合并对象
9
+ */
10
+ export function deepMerge(target, source) {
11
+ if (!target || !source)
12
+ return target;
13
+ const result = { ...target };
14
+ for (const key in source) {
15
+ const sourceValue = source[key];
16
+ const targetValue = result[key];
17
+ if (sourceValue !== undefined) {
18
+ if (typeof sourceValue === 'object' && sourceValue !== null &&
19
+ typeof targetValue === 'object' && targetValue !== null &&
20
+ !Array.isArray(sourceValue) && !Array.isArray(targetValue)) {
21
+ result[key] = deepMerge(targetValue, sourceValue);
22
+ }
23
+ else {
24
+ result[key] = sourceValue;
25
+ }
26
+ }
27
+ }
28
+ return result;
29
+ }
30
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAU;IAChC,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAI,MAAS,EAAE,MAAkB;IACxD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAEtC,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI;gBACvD,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI;gBACvD,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC9D,MAAc,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACL,MAAc,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@zhin.js/schema",
3
+ "version": "1.0.0",
4
+ "description": "Zhin Schema 配置验证系统",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "import": "./lib/index.js"
12
+ }
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.3.0",
16
+ "@types/node": "^24.3.0"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "clean": "rm -rf lib"
21
+ }
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './schema.js';
2
+ export * from './utils.js';
package/src/schema.ts ADDED
@@ -0,0 +1,374 @@
1
+ import { isEmpty } from './utils.js';
2
+
3
+ export class Schema<S = any, T = S> {
4
+ public [Symbol.toStringTag] = 'Schema';
5
+ constructor(
6
+ public meta: Schema.Meta<S, T>,
7
+ public options: Schema.Options = {},
8
+ ) {
9
+ const _this = this;
10
+ const schema = function (value?: S, key: string = 'value') {
11
+ const formatter = Schema.resolve(_this.meta.type);
12
+ if (!formatter) throw new Error(`type ${_this.meta.type} not found`);
13
+ return formatter.call(_this, key, value);
14
+ } as Schema<S, T>;
15
+ return new Proxy(schema, {
16
+ get(target, p: string | symbol, receiver: any): any {
17
+ return Reflect.get(_this, p, receiver);
18
+ },
19
+ set(target, p: string | symbol, value: any, receiver: any): boolean {
20
+ return Reflect.set(_this, p, value, receiver);
21
+ },
22
+ });
23
+ }
24
+ static fromJSON<S, T>(json: Schema.JSON<S, T>) {
25
+ const { object, inner, list, ...meta } = json;
26
+ const options: Schema.Options = {};
27
+ if (object)
28
+ options.object = Object.fromEntries(Object.entries(object).map(([key, value]) => [key, Schema.fromJSON(value)]));
29
+ if (inner) options.inner = Schema.fromJSON(inner);
30
+ if (list) options.list = list.map(item => Schema.fromJSON(item));
31
+ return new Schema<S, T>(meta, options);
32
+ }
33
+ toJSON(): Record<string, any> {
34
+ return Object.fromEntries(
35
+ Object.entries({
36
+ ...this.meta,
37
+ default: typeof this.meta.default === 'function' ? this.meta.default() : this.meta.default,
38
+ inner: this.options.inner?.toJSON(),
39
+ object: this.options.object
40
+ ? Object.fromEntries(Object.entries(this.options.object || {}).map(([key, value]) => [
41
+ key,
42
+ {
43
+ ...value.toJSON(),
44
+ key
45
+ }
46
+ ]))
47
+ : undefined,
48
+ list: this.options.list?.map(item => item.toJSON()),
49
+ }).filter(([key, value]) => typeof value !== 'undefined'),
50
+ );
51
+ }
52
+ [Symbol.unscopables]() {
53
+ return {
54
+ options: true,
55
+ meta: true,
56
+ };
57
+ }
58
+ /** 设置是否必填 */
59
+ required(): this {
60
+ this.meta.required = true;
61
+ return this;
62
+ }
63
+ /** 是否隐藏 */
64
+ hidden(): this {
65
+ this.meta.hidden = true;
66
+ return this;
67
+ }
68
+ /** 设置描述 */
69
+ description(description: string): this {
70
+ this.meta.description = description;
71
+ return this;
72
+ }
73
+ /** 设置默认值 */
74
+ default(defaultValue: T): this {
75
+ this.meta.default = defaultValue;
76
+ return this;
77
+ }
78
+ /** 设置选项列表 */
79
+ option(list: (T | Schema.Option<T>)[]): this {
80
+ this.meta.options = Schema.formatOptionList(list);
81
+ return this;
82
+ }
83
+ /** 设置是否允许多选 */
84
+ multiple(): this {
85
+ if (this.meta.type !== 'list') throw new Error('multiple only support list type');
86
+ this.meta.multiple = true;
87
+ return this;
88
+ }
89
+ /** 设置最小值 */
90
+ min(value: number): this {
91
+ this.meta.min = value;
92
+ return this;
93
+ }
94
+ /** 设置最大值 */
95
+ max(value: number): this {
96
+ this.meta.max = value;
97
+ return this;
98
+ }
99
+ /** 设置步进值 */
100
+ step(value: number): this {
101
+ this.meta.step = value;
102
+ return this;
103
+ }
104
+ /** 设置组件类型 */
105
+ component(type: string): this {
106
+ this.meta.component = type;
107
+ return this;
108
+ }
109
+ /** 声明一个数字类型 */
110
+ static number(key?: string): Schema<number> {
111
+ return new Schema<number>({ type: 'number', key });
112
+ }
113
+ /** 声明一个字符串类型 */
114
+ static string(key?: string): Schema<string> {
115
+ return new Schema<string>({ type: 'string', key });
116
+ }
117
+ /** 声明一个布尔类型 */
118
+ static boolean(key?: string): Schema<boolean> {
119
+ return new Schema<boolean>({ type: 'boolean', key });
120
+ }
121
+ /** 声明一个正则类型 */
122
+ static regexp(key?: string) {
123
+ return new Schema<RegExp | string, RegExp>({ type: 'regexp', key });
124
+ }
125
+ /** 声明一个日期类型 */
126
+ static date(key?: string) {
127
+ return new Schema<Date | number, Date>({ type: 'date', key });
128
+ }
129
+ /** 声明一个字典类型 */
130
+ static dict<X extends Schema>(input: X, key?: string) {
131
+ return new Schema<Record<string, Schema.Types<X>>>({ type: 'dict', key }, { inner: input });
132
+ }
133
+ static object<X extends Record<string, Schema>>(input: X, key: string = '') {
134
+ return new Schema<Schema.RecordTypes<X>>({ type: 'object', key }, { object: input });
135
+ }
136
+ /** 声明一个列表类型 */
137
+ static list<X extends Schema>(inner: X, key?: string) {
138
+ return new Schema<Schema.Types<X>[]>({ type: 'list', key }, { inner });
139
+ }
140
+ /** 声明一个元组类型 */
141
+ static tuple<X extends readonly any[]>(list: X, key?: string) {
142
+ return new Schema<Schema.Tuple<X>>({ type: 'tuple', key }, { list });
143
+ }
144
+ /** 声明一个联合类型 */
145
+ static union<X extends readonly any[]>(list: X, key?: string) {
146
+ return new Schema<Schema.Union<X>>({ type: 'union', key }, { list });
147
+ }
148
+ /** 声明一个交叉类型 */
149
+ static intersect<X extends readonly any[]>(list: X, key?: string) {
150
+ return new Schema<Schema.Intersect<X>>({ type: 'intersect', key }, { list });
151
+ }
152
+ /** 声明一个常量 */
153
+ static const<X extends string | number | boolean>(value: X, key?: string) {
154
+ return new Schema<X>({ type: 'const', default: value as any, key });
155
+ }
156
+ /** 声明任意类型 */
157
+ static any(key?: any) {
158
+ return new Schema<any>({ type: 'any', key });
159
+ }
160
+ static resolve<T extends string>(type: T): Schema.Formatter {
161
+ return Schema.formatters.get(type)!;
162
+ }
163
+ static extend<T extends string>(type: T, formatter: Schema.Formatter) {
164
+ Schema.formatters.set(type, formatter);
165
+ }
166
+ }
167
+ export interface Schema<S = any> {
168
+ (value?: S, key?: string): S;
169
+ }
170
+ export namespace Schema {
171
+ export const formatters: Map<string, Formatter> = new Map<string, Formatter>();
172
+ export type Formatter<S = any, T = S> = (this: Schema, key: string, value: S) => T;
173
+ export type JSON<S = any, T = S> = Meta<S, T> & {
174
+ object?: Record<string, JSON>;
175
+ inner?: JSON;
176
+ list?: JSON[];
177
+ };
178
+ export interface Meta<S = any, T = S> {
179
+ hidden?: boolean;
180
+ type: string;
181
+ default?: T;
182
+ required?: boolean;
183
+ options?: Option<T>[];
184
+ multiple?: boolean;
185
+ key?: string;
186
+ description?: string;
187
+ component?: string;
188
+ min?: number;
189
+ max?: number;
190
+ step?: number;
191
+ }
192
+ export interface Options {
193
+ object?: Record<string, Schema>;
194
+ inner?: Schema;
195
+ list?: readonly Schema[];
196
+ }
197
+ export type Types<T> = T extends Schema<infer S> ? S : never;
198
+ export type RecordTypes<T> = T extends Record<string, Schema>
199
+ ? {
200
+ [K in keyof T]?: Types<T[K]>;
201
+ }
202
+ : unknown;
203
+ export type Union<T extends readonly any[]> = T extends readonly [infer L, ...infer R]
204
+ ? Types<L> | Union<R>
205
+ : never;
206
+ export type Intersect<T extends readonly any[]> = T extends readonly [infer L, ...infer R]
207
+ ? Types<L> & Intersect<R>
208
+ : unknown;
209
+ export type Tuple<T extends readonly any[]> = T extends readonly [infer L, ...infer R]
210
+ ? [Types<L>, ...Tuple<R>]
211
+ : [];
212
+ export function checkDefault<T>(schema: Schema, key: string, value: T, fallback: T = value) {
213
+ if (isEmpty(value)) {
214
+ value = schema.meta.default || fallback;
215
+ }
216
+ const validateType = (schema: Schema, key: string, value: any) => {
217
+ switch (schema.meta.type) {
218
+ case 'string':
219
+ if (!['string', 'undefined'].includes(typeof value)) throw new TypeError(`${key} is not a string`);
220
+ break;
221
+ case 'number':
222
+ if (!['number', 'undefined'].includes(typeof value)) throw new TypeError(`${key} is not a number`);
223
+ break;
224
+ case 'boolean':
225
+ if (!['boolean', 'undefined'].includes(typeof value)) throw new TypeError(`${key} is not a boolean`);
226
+ break;
227
+ case 'regexp':
228
+ if (!['string', 'undefined'].includes(typeof value) && !(value instanceof RegExp))
229
+ throw new TypeError(`${key} is not a RegExp|string`);
230
+ break;
231
+ case 'date':
232
+ if (!['number', 'undefined'].includes(typeof value) && !(value instanceof Date))
233
+ throw new TypeError(`${key} is not a Date|number`);
234
+ if (value instanceof Date && isNaN(value.getTime())) throw new TypeError(`${key} is not a valid Date`);
235
+ break;
236
+ case 'dict':
237
+ if (!['object', 'undefined', 'null'].includes(typeof value)) throw new TypeError(`${key} is not a object`);
238
+ break;
239
+ case 'object':
240
+ if (!['object', 'undefined', 'null'].includes(typeof value)) throw new TypeError(`${key} is not a object`);
241
+ break;
242
+ case 'list':
243
+ if (typeof value !== 'undefined' && !Array.isArray(value)) throw new TypeError(`${key} is not a list`);
244
+ break;
245
+ case 'tuple':
246
+ if (typeof value !== 'undefined' && !Array.isArray(value)) throw new TypeError(`${key} is not a tuple`);
247
+ break;
248
+ case 'union':
249
+ // union类型不在这里验证,在extend函数中验证
250
+ break;
251
+ case 'intersect':
252
+ // intersect类型不在这里验证,在extend函数中验证
253
+ break;
254
+ case 'const':
255
+ if (typeof value !== 'undefined' && value !== schema.meta.default) throw new TypeError(`${key} is not const`);
256
+ break;
257
+ case 'any':
258
+ // any类型不验证
259
+ break;
260
+ default:
261
+ throw new TypeError(`${key} is not a valid type`);
262
+ }
263
+ };
264
+ if (schema.meta.required && typeof value === 'undefined') throw new Error(`${key} is required`);
265
+ validateType(schema, key, value);
266
+ return value;
267
+ }
268
+ export type Option<T = any> = {
269
+ label: string;
270
+ value: T;
271
+ };
272
+ export function formatOptionList<T extends (any | Schema.Option)[]>(list: T): Schema.Option[] {
273
+ return list.map(item => {
274
+ if (typeof item === 'string') {
275
+ return {
276
+ label: `${item}`,
277
+ value: item,
278
+ } as Schema.Option;
279
+ }
280
+ return item as unknown as Schema.Option;
281
+ });
282
+ }
283
+ }
284
+ Schema.extend('number', function (this: Schema, key: string, value: any) {
285
+ value = Schema.checkDefault(this, key, value);
286
+ return value;
287
+ });
288
+ Schema.extend('string', function (this: Schema, key: string, value: any) {
289
+ value = Schema.checkDefault(this, key, value);
290
+ return value;
291
+ });
292
+ Schema.extend('boolean', function (this: Schema, key: string, value: any) {
293
+ return Schema.checkDefault(this, key, value);
294
+ });
295
+ Schema.extend('dict', function (this: Schema, key: string, value: any) {
296
+ value = Schema.checkDefault(this, key, value, {});
297
+ return Object.fromEntries(
298
+ Object.entries(value).map(([k, schema]) => {
299
+ return [k, this.options.inner!(schema, `${key}.${k}`)];
300
+ }),
301
+ );
302
+ });
303
+
304
+ Schema.extend('object', function (this: Schema, key: string, value: any) {
305
+ const getDefault = (schema: Schema) => {
306
+ const result = Object.create(null);
307
+ for (const key in schema.options.object) {
308
+ const propSchema = schema.options.object[key];
309
+ result[key] = propSchema.default !== undefined ? propSchema.default : undefined;
310
+ }
311
+ return result;
312
+ };
313
+ value = Schema.checkDefault(this, key, value, getDefault(this));
314
+ const result = Object.create(null);
315
+ for (const [k, val] of Object.entries(value)) {
316
+ const propSchema = this.options.object![k];
317
+ if (propSchema) {
318
+ // propSchema是一个可调用的Schema实例
319
+ result[k] = (propSchema as any)(val, `${key}.${k}`);
320
+ } else {
321
+ result[k] = val;
322
+ }
323
+ }
324
+ return result;
325
+ });
326
+ Schema.extend('list', function (this: Schema, key: string, value: any) {
327
+ value = Schema.checkDefault(this, key, value, []);
328
+ return value.map((item: any, index: number) => this.options.inner!(item, `${key}[${index}]`));
329
+ });
330
+ Schema.extend('regexp', function (this: Schema, key: string, value: any) {
331
+ value = Schema.checkDefault(this, key, value);
332
+ if (typeof value === 'string') {
333
+ return new RegExp(value);
334
+ }
335
+ return value;
336
+ });
337
+ Schema.extend('date', function (this: Schema, key: string, value: any) {
338
+ value = Schema.checkDefault(this, key, value);
339
+ return new Date(value);
340
+ });
341
+ Schema.extend('const', function (this: Schema, key: string, value: any) {
342
+ value = Schema.checkDefault(this, key, value);
343
+ if (value !== this.meta.default) {
344
+ throw new Error(`${key} const value not match`);
345
+ }
346
+ return value;
347
+ });
348
+ Schema.extend('tuple', function (this: Schema, key: string, value: any) {
349
+ value = Schema.checkDefault(this, key, value, []);
350
+ return value.map((item: any, index: number) => this.options.list![index](item, `${key}[${index}]`));
351
+ });
352
+ Schema.extend('union', function (this: Schema, key: string, value: any) {
353
+ value = Schema.checkDefault(this, key, value);
354
+ for (const schema of this.options.list!) {
355
+ try {
356
+ return (schema as any)(value, key);
357
+ } catch (e) {}
358
+ }
359
+ throw new Error(`${key} union type not match`);
360
+ });
361
+ Schema.extend('intersect', function (this: Schema, key: string, value: any) {
362
+ value = Schema.checkDefault(this, key, value);
363
+ for (const schema of this.options.list!) {
364
+ try {
365
+ value = (schema as any)(value, key);
366
+ } catch (e) {
367
+ throw new Error(`${key} intersect type not match`);
368
+ }
369
+ }
370
+ return value;
371
+ });
372
+ Schema.extend('any', function (this: Schema, key: string, value: any) {
373
+ return Schema.checkDefault(this, key, value);
374
+ });
package/src/utils.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 检查值是否为空
3
+ */
4
+ export function isEmpty(value: any): boolean {
5
+ return value === null || value === undefined || value === '';
6
+ }
7
+
8
+ /**
9
+ * 深度合并对象
10
+ */
11
+ export function deepMerge<T>(target: T, source: Partial<T>): T {
12
+ if (!target || !source) return target;
13
+
14
+ const result = { ...target };
15
+
16
+ for (const key in source) {
17
+ const sourceValue = source[key];
18
+ const targetValue = result[key];
19
+
20
+ if (sourceValue !== undefined) {
21
+ if (typeof sourceValue === 'object' && sourceValue !== null &&
22
+ typeof targetValue === 'object' && targetValue !== null &&
23
+ !Array.isArray(sourceValue) && !Array.isArray(targetValue)) {
24
+ (result as any)[key] = deepMerge(targetValue, sourceValue);
25
+ } else {
26
+ (result as any)[key] = sourceValue;
27
+ }
28
+ }
29
+ }
30
+
31
+ return result;
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "allowImportingTsExtensions": false,
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "verbatimModuleSyntax": false,
14
+ "allowSyntheticDefaultImports": true,
15
+ "declaration": true,
16
+ "declarationMap": true,
17
+ "sourceMap": true,
18
+ "outDir": "./lib",
19
+ "rootDir": "./src"
20
+ },
21
+ "include": [
22
+ "src/**/*"
23
+ ]
24
+ }