@zhin.js/schema 0.0.14
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 +21 -0
- package/README.md +78 -0
- package/lib/index.d.ts +119 -0
- package/lib/index.js +394 -0
- package/lib/test.js +78 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 凉菜
|
|
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/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @zhinjs/schema
|
|
2
|
+
zhin配置声明
|
|
3
|
+
## 声明number
|
|
4
|
+
```javascript
|
|
5
|
+
Schema.number()
|
|
6
|
+
```
|
|
7
|
+
## 声明string
|
|
8
|
+
```javascript
|
|
9
|
+
Schema.string()
|
|
10
|
+
```
|
|
11
|
+
## 声明boolean
|
|
12
|
+
```javascript
|
|
13
|
+
Schema.boolean()
|
|
14
|
+
```
|
|
15
|
+
## 声明date
|
|
16
|
+
```javascript
|
|
17
|
+
Schema.date()
|
|
18
|
+
```
|
|
19
|
+
## 声明regexp
|
|
20
|
+
```javascript
|
|
21
|
+
Schema.regexp()
|
|
22
|
+
```
|
|
23
|
+
## 声明object
|
|
24
|
+
1. Record<string,string>
|
|
25
|
+
```javascript
|
|
26
|
+
Schema.dict(
|
|
27
|
+
Schema.string()
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
2. Record<string,number>
|
|
31
|
+
```javascript
|
|
32
|
+
Schema.dict(
|
|
33
|
+
Schema.number()
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
3. Record<string,Date>
|
|
37
|
+
```javascript
|
|
38
|
+
Schema.dict(
|
|
39
|
+
Schema.date()
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
4. Record<string,number[]>
|
|
43
|
+
```javascript
|
|
44
|
+
Schema.dict(
|
|
45
|
+
Schema.list(
|
|
46
|
+
Schema.number()
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
5. Record<string,{foo:string,bar:number[]}[]>
|
|
51
|
+
```javascript
|
|
52
|
+
Schema.dict(
|
|
53
|
+
Schema.list(
|
|
54
|
+
Schema.object({
|
|
55
|
+
foo:Schema.string(),
|
|
56
|
+
bar:Schema.list(Schema.number())
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
## 声明array
|
|
62
|
+
1. string[]
|
|
63
|
+
```javascript
|
|
64
|
+
Schema.array(
|
|
65
|
+
Schema.string()
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
2. {foo:number,bar:string}[]
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
Schema.array(
|
|
72
|
+
Schema.object({
|
|
73
|
+
foo:Schema.number(),
|
|
74
|
+
bar:Schema.string()
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export 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
|
+
component(component: string): this;
|
|
20
|
+
/** 设置默认值 */
|
|
21
|
+
default(defaultValue: T extends {} ? Partial<T> : T | (() => T)): this;
|
|
22
|
+
/** 设置选项列表 */
|
|
23
|
+
option(list: Schema.Option<T>[]): this;
|
|
24
|
+
/** 设置是否允许多选 */
|
|
25
|
+
multiple(): this;
|
|
26
|
+
/** 设置数值最小值,类型是string时,标识最小长度 */
|
|
27
|
+
min(min: number): this;
|
|
28
|
+
/** 设置数值最大值,类型是string时,标识最大长度 */
|
|
29
|
+
max(max: number): this;
|
|
30
|
+
/** 设置百分比步长 */
|
|
31
|
+
step(step: number): this;
|
|
32
|
+
/** 声明一个数字类型 */
|
|
33
|
+
static number(key?: string): Schema<number>;
|
|
34
|
+
/** 声明一个百分比类型 */
|
|
35
|
+
static percent(key?: string): Schema<number>;
|
|
36
|
+
/** 声明一个字符串类型 */
|
|
37
|
+
static string(key?: string): Schema<string>;
|
|
38
|
+
/** 声明一个布尔类型 */
|
|
39
|
+
static boolean(key?: string): Schema<boolean>;
|
|
40
|
+
/** 声明一个正则类型 */
|
|
41
|
+
static regexp(key?: string): Schema<string | RegExp, RegExp>;
|
|
42
|
+
/** 声明一个日期类型 */
|
|
43
|
+
static date(key?: string): Schema<number | Date, Date>;
|
|
44
|
+
/** 声明一个字典类型 */
|
|
45
|
+
static dict<X extends Schema>(inner: X, key?: string): Schema<Schema.Dict<X>, Schema.Dict<X>>;
|
|
46
|
+
/** 声明一个列表类型 */
|
|
47
|
+
static list<X extends Schema>(inner: X, key?: string): Schema<Schema.Types<X>[], Schema.Types<X>[]>;
|
|
48
|
+
/**
|
|
49
|
+
* 声明一个数组类型
|
|
50
|
+
* @deprecated use list instead
|
|
51
|
+
* */
|
|
52
|
+
static array<X extends Schema>(inner: X, key?: string): Schema<Schema.Types<X>[], Schema.Types<X>[]>;
|
|
53
|
+
/** 声明一个对象类型 */
|
|
54
|
+
static object<X extends {}>(dict: X, key?: string): Schema<Schema.Object<X>, Schema.Object<X>>;
|
|
55
|
+
/** 声明一个元组类型 */
|
|
56
|
+
static tuple<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Tuple<X>>;
|
|
57
|
+
/** 声明一个联合类型 */
|
|
58
|
+
static union<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Union<X>, Schema.Union<X>>;
|
|
59
|
+
/** 声明一个交叉类型 */
|
|
60
|
+
static intersect<X extends readonly any[]>(list: X, key?: string): Schema<Schema.Intersect<X>, Schema.Intersect<X>>;
|
|
61
|
+
/** 声明一个常量 */
|
|
62
|
+
static const<X extends string | number | boolean>(value: X, key?: string): Schema<X, X>;
|
|
63
|
+
/** 声明一个任意类型 */
|
|
64
|
+
static any(key?: string): Schema<any, any>;
|
|
65
|
+
/** 声明一个空类型 */
|
|
66
|
+
static never(key?: string): Schema<never, never>;
|
|
67
|
+
static resolve<T extends string>(type: T): Schema.Formatter | undefined;
|
|
68
|
+
static extend<T extends string>(type: T, formatter: Schema.Formatter): void;
|
|
69
|
+
}
|
|
70
|
+
export interface Schema<S = any> {
|
|
71
|
+
(value?: S): S;
|
|
72
|
+
}
|
|
73
|
+
export namespace Schema {
|
|
74
|
+
const formatters: Map<string, Formatter>;
|
|
75
|
+
type Formatter<S = any, T = S> = (this: Schema, value: S) => T;
|
|
76
|
+
type JSON<S = any, T = S> = Meta<S, T> & {
|
|
77
|
+
dict?: Record<string, JSON>;
|
|
78
|
+
inner?: JSON;
|
|
79
|
+
list?: JSON[];
|
|
80
|
+
};
|
|
81
|
+
interface Meta<S = any, T = S> {
|
|
82
|
+
key?: string;
|
|
83
|
+
hidden?: boolean;
|
|
84
|
+
type?: string;
|
|
85
|
+
default?: T extends {} ? Partial<T> : T | (() => T);
|
|
86
|
+
required?: boolean;
|
|
87
|
+
options?: Option[];
|
|
88
|
+
multiple?: boolean;
|
|
89
|
+
description?: string;
|
|
90
|
+
component?: string;
|
|
91
|
+
min?: number;
|
|
92
|
+
max?: number;
|
|
93
|
+
step?: number;
|
|
94
|
+
}
|
|
95
|
+
interface Options {
|
|
96
|
+
dict?: Record<string, Schema>;
|
|
97
|
+
inner?: Schema;
|
|
98
|
+
list?: readonly Schema[];
|
|
99
|
+
}
|
|
100
|
+
type Types<T> = T extends Schema<infer S, infer T> ? T : never;
|
|
101
|
+
type Union<T extends readonly any[]> = T extends readonly [infer L, ...infer R] ? Types<L> | Union<R> : never;
|
|
102
|
+
type Intersect<T extends readonly any[]> = T extends readonly [infer L, ...infer R] ? Types<L> & Intersect<R> : unknown;
|
|
103
|
+
type Dict<T> = {
|
|
104
|
+
[key: string]: Partial<Types<T>>;
|
|
105
|
+
} & Record<string, any>;
|
|
106
|
+
type Object<X extends {}> = {
|
|
107
|
+
[K in keyof X]?: Types<X[K]>;
|
|
108
|
+
};
|
|
109
|
+
type Tuple<X extends readonly any[]> = X extends readonly [infer L, ...infer R] ? [Types<L>, ...Tuple<R>] : [];
|
|
110
|
+
function checkDefault<T>(schema: Schema, value: T, fallback?: T): T;
|
|
111
|
+
type Option<T = any> = T extends Array<infer R> ? R | {
|
|
112
|
+
label: string;
|
|
113
|
+
value: R;
|
|
114
|
+
} : T;
|
|
115
|
+
function formatOptionList<T>(list: Schema.Option<T>[]): (Option<T> | {
|
|
116
|
+
label: Option<T> & string;
|
|
117
|
+
value: Option<T> & string;
|
|
118
|
+
})[];
|
|
119
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Schema = void 0;
|
|
5
|
+
class Schema {
|
|
6
|
+
constructor(meta, options = {}) {
|
|
7
|
+
this.meta = meta;
|
|
8
|
+
this.options = options;
|
|
9
|
+
this[_a] = 'Schema';
|
|
10
|
+
const _this = this;
|
|
11
|
+
const schema = function (value) {
|
|
12
|
+
const formatter = Schema.resolve(_this.meta.type);
|
|
13
|
+
if (!formatter)
|
|
14
|
+
throw new Error(`type ${_this.meta.type} not found`);
|
|
15
|
+
return formatter.call(_this, value);
|
|
16
|
+
};
|
|
17
|
+
return new Proxy(schema, {
|
|
18
|
+
get(target, p, receiver) {
|
|
19
|
+
return Reflect.get(_this, p, receiver);
|
|
20
|
+
},
|
|
21
|
+
set(target, p, value, receiver) {
|
|
22
|
+
return Reflect.set(_this, p, value, receiver);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
static fromJSON(json) {
|
|
27
|
+
const { dict, inner, list, ...meta } = json;
|
|
28
|
+
const options = {};
|
|
29
|
+
if (dict)
|
|
30
|
+
options.dict = Object.fromEntries(Object.entries(dict).map(([key, value]) => [key, Schema.fromJSON(value)]));
|
|
31
|
+
if (inner)
|
|
32
|
+
options.inner = Schema.fromJSON(inner);
|
|
33
|
+
if (list)
|
|
34
|
+
options.list = list.map(item => Schema.fromJSON(item));
|
|
35
|
+
return new Schema(meta, options);
|
|
36
|
+
}
|
|
37
|
+
toJSON() {
|
|
38
|
+
var _b, _c;
|
|
39
|
+
return Object.fromEntries(Object.entries({
|
|
40
|
+
...this.meta,
|
|
41
|
+
default: typeof this.meta.default === 'function' ? this.meta.default() : this.meta.default,
|
|
42
|
+
inner: (_b = this.options.inner) === null || _b === void 0 ? void 0 : _b.toJSON(),
|
|
43
|
+
list: (_c = this.options.list) === null || _c === void 0 ? void 0 : _c.map((item) => item.toJSON()),
|
|
44
|
+
dict: this.options.dict ? Object.fromEntries(Object.entries(this.options.dict || {}).map(([key, value]) => [key, value.toJSON()])) : undefined,
|
|
45
|
+
}).filter(([key, value]) => typeof value !== 'undefined'));
|
|
46
|
+
}
|
|
47
|
+
[(_a = Symbol.toStringTag, Symbol.unscopables)]() {
|
|
48
|
+
return {
|
|
49
|
+
options: true,
|
|
50
|
+
meta: true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** 设置是否必填 */
|
|
54
|
+
required() {
|
|
55
|
+
this.meta.required = true;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
/** 是否隐藏 */
|
|
59
|
+
hidden() {
|
|
60
|
+
this.meta.hidden = true;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/** 设置描述 */
|
|
64
|
+
description(description) {
|
|
65
|
+
this.meta.description = description;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
/** 设置渲染组件 */
|
|
69
|
+
component(component) {
|
|
70
|
+
this.meta.component = component;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
/** 设置默认值 */
|
|
74
|
+
default(defaultValue) {
|
|
75
|
+
this.meta.default = defaultValue;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/** 设置选项列表 */
|
|
79
|
+
option(list) {
|
|
80
|
+
this.meta.options = Schema.formatOptionList(list);
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
/** 设置是否允许多选 */
|
|
84
|
+
multiple() {
|
|
85
|
+
if (this.meta.type !== 'list')
|
|
86
|
+
throw new Error('multiple only support list type');
|
|
87
|
+
this.meta.multiple = true;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
/** 设置数值最小值,类型是string时,标识最小长度 */
|
|
91
|
+
min(min) {
|
|
92
|
+
this.meta.min = min;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
/** 设置数值最大值,类型是string时,标识最大长度 */
|
|
96
|
+
max(max) {
|
|
97
|
+
this.meta.max = max;
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/** 设置百分比步长 */
|
|
101
|
+
step(step) {
|
|
102
|
+
this.meta.step = step;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
/** 声明一个数字类型 */
|
|
106
|
+
static number(key) {
|
|
107
|
+
return new Schema({ key, type: "number" });
|
|
108
|
+
}
|
|
109
|
+
/** 声明一个百分比类型 */
|
|
110
|
+
static percent(key) {
|
|
111
|
+
return new Schema({ key, type: "percent" })
|
|
112
|
+
.component("slider")
|
|
113
|
+
.min(0)
|
|
114
|
+
.max(1)
|
|
115
|
+
.step(0.01);
|
|
116
|
+
}
|
|
117
|
+
/** 声明一个字符串类型 */
|
|
118
|
+
static string(key) {
|
|
119
|
+
return new Schema({ key, type: "string" });
|
|
120
|
+
}
|
|
121
|
+
/** 声明一个布尔类型 */
|
|
122
|
+
static boolean(key) {
|
|
123
|
+
return new Schema({ key, type: "boolean" });
|
|
124
|
+
}
|
|
125
|
+
/** 声明一个正则类型 */
|
|
126
|
+
static regexp(key) {
|
|
127
|
+
return new Schema({ key, type: "regexp" });
|
|
128
|
+
}
|
|
129
|
+
/** 声明一个日期类型 */
|
|
130
|
+
static date(key) {
|
|
131
|
+
return new Schema({ key, type: "date" }).component("date-picker");
|
|
132
|
+
}
|
|
133
|
+
/** 声明一个字典类型 */
|
|
134
|
+
static dict(inner, key) {
|
|
135
|
+
return new Schema({ key: key, type: "dict" }, { inner });
|
|
136
|
+
}
|
|
137
|
+
/** 声明一个列表类型 */
|
|
138
|
+
static list(inner, key) {
|
|
139
|
+
return new Schema({ key: key, type: "list" }, { inner });
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 声明一个数组类型
|
|
143
|
+
* @deprecated use list instead
|
|
144
|
+
* */
|
|
145
|
+
static array(inner, key) {
|
|
146
|
+
return Schema.list(inner, key);
|
|
147
|
+
}
|
|
148
|
+
/** 声明一个对象类型 */
|
|
149
|
+
static object(dict, key) {
|
|
150
|
+
return new Schema({ key: key, type: "object" }, { dict });
|
|
151
|
+
}
|
|
152
|
+
/** 声明一个元组类型 */
|
|
153
|
+
static tuple(list, key) {
|
|
154
|
+
return new Schema({ key: key, type: "tuple" }, { list });
|
|
155
|
+
}
|
|
156
|
+
/** 声明一个联合类型 */
|
|
157
|
+
static union(list, key) {
|
|
158
|
+
return new Schema({ key: key, type: "union" }, { list });
|
|
159
|
+
}
|
|
160
|
+
/** 声明一个交叉类型 */
|
|
161
|
+
static intersect(list, key) {
|
|
162
|
+
return new Schema({ key: key, type: "intersect" }, { list });
|
|
163
|
+
}
|
|
164
|
+
/** 声明一个常量 */
|
|
165
|
+
static const(value, key) {
|
|
166
|
+
return new Schema({ key: key, type: "const", default: value });
|
|
167
|
+
}
|
|
168
|
+
/** 声明一个任意类型 */
|
|
169
|
+
static any(key) {
|
|
170
|
+
return new Schema({ key, type: 'any' });
|
|
171
|
+
}
|
|
172
|
+
/** 声明一个空类型 */
|
|
173
|
+
static never(key) {
|
|
174
|
+
return new Schema({ key, type: 'never' });
|
|
175
|
+
}
|
|
176
|
+
static resolve(type) {
|
|
177
|
+
return Schema.formatters.get(type);
|
|
178
|
+
}
|
|
179
|
+
static extend(type, formatter) {
|
|
180
|
+
Schema.formatters.set(type, formatter);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.Schema = Schema;
|
|
184
|
+
(function (Schema) {
|
|
185
|
+
Schema.formatters = new Map();
|
|
186
|
+
function checkDefault(schema, value, fallback = value) {
|
|
187
|
+
const isEmpty = (value) => {
|
|
188
|
+
if (typeof value === "undefined")
|
|
189
|
+
return true;
|
|
190
|
+
if (typeof value === "string" && value === "")
|
|
191
|
+
return true;
|
|
192
|
+
if (typeof value === "object" && value === null)
|
|
193
|
+
return true;
|
|
194
|
+
if (Array.isArray(value) && value.length === 0)
|
|
195
|
+
return true;
|
|
196
|
+
if (value instanceof Date && isNaN(value.getTime()))
|
|
197
|
+
return true;
|
|
198
|
+
return value && typeof value === 'object' && Reflect.ownKeys(value).length === 0;
|
|
199
|
+
};
|
|
200
|
+
if (isEmpty(value)) {
|
|
201
|
+
if (typeof schema.meta.default === "function") {
|
|
202
|
+
value = schema.meta.default();
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
value = schema.meta.default || fallback;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const validateType = (schema, value) => {
|
|
209
|
+
var _b, _c;
|
|
210
|
+
switch (schema.meta.type) {
|
|
211
|
+
case "string":
|
|
212
|
+
if (!['string', 'undefined'].includes(typeof value))
|
|
213
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a string`);
|
|
214
|
+
break;
|
|
215
|
+
case "number":
|
|
216
|
+
if (!['number', 'undefined'].includes(typeof value))
|
|
217
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a number`);
|
|
218
|
+
break;
|
|
219
|
+
case "boolean":
|
|
220
|
+
if (!['boolean', 'undefined'].includes(typeof value))
|
|
221
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a boolean`);
|
|
222
|
+
break;
|
|
223
|
+
case "regexp":
|
|
224
|
+
if (!['string', 'undefined'].includes(typeof value) && !(value instanceof RegExp))
|
|
225
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a RegExp|string`);
|
|
226
|
+
break;
|
|
227
|
+
case "date":
|
|
228
|
+
if (!['number', 'undefined'].includes(typeof value) && !(value instanceof Date))
|
|
229
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a Date|number`);
|
|
230
|
+
if (value instanceof Date && isNaN(value.getTime()))
|
|
231
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid Date`);
|
|
232
|
+
break;
|
|
233
|
+
case "object":
|
|
234
|
+
case "dict":
|
|
235
|
+
if (!['object', 'undefined', 'null'].includes(typeof value))
|
|
236
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a object`);
|
|
237
|
+
break;
|
|
238
|
+
case "array":
|
|
239
|
+
case 'list':
|
|
240
|
+
if (typeof value !== 'undefined' && !Array.isArray(value))
|
|
241
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a list`);
|
|
242
|
+
break;
|
|
243
|
+
case "tuple":
|
|
244
|
+
if (typeof value !== 'undefined' && !Array.isArray(value))
|
|
245
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid tuple`);
|
|
246
|
+
break;
|
|
247
|
+
case "union":
|
|
248
|
+
if (typeof value !== 'undefined' && !((_b = schema.options.list) === null || _b === void 0 ? void 0 : _b.some(item => {
|
|
249
|
+
try {
|
|
250
|
+
item(value);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
})))
|
|
257
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid union`);
|
|
258
|
+
break;
|
|
259
|
+
case "intersect":
|
|
260
|
+
if (typeof value !== 'undefined' && !((_c = schema.options.list) === null || _c === void 0 ? void 0 : _c.every(item => {
|
|
261
|
+
try {
|
|
262
|
+
item(value);
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
})))
|
|
269
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid intersect`);
|
|
270
|
+
break;
|
|
271
|
+
case "const":
|
|
272
|
+
if (typeof value !== 'undefined' && value !== schema.meta.default)
|
|
273
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not const`);
|
|
274
|
+
break;
|
|
275
|
+
case "percent":
|
|
276
|
+
if (typeof value !== 'undefined' && (typeof value !== 'number' || value < 0 || value > 1))
|
|
277
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid percent`);
|
|
278
|
+
break;
|
|
279
|
+
case "any":
|
|
280
|
+
break;
|
|
281
|
+
case "never":
|
|
282
|
+
throw new TypeError(`${schema.meta.key || 'value'} is never`);
|
|
283
|
+
default:
|
|
284
|
+
throw new TypeError(`${schema.meta.key || 'value'} is not a valid type`);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
if (schema.meta.required && typeof value === 'undefined')
|
|
288
|
+
throw new Error(`${schema.meta.key || 'value'} is required`);
|
|
289
|
+
validateType(schema, value);
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
Schema.checkDefault = checkDefault;
|
|
293
|
+
function formatOptionList(list) {
|
|
294
|
+
return list.map(item => {
|
|
295
|
+
if (typeof item === "string") {
|
|
296
|
+
return {
|
|
297
|
+
label: item,
|
|
298
|
+
value: item,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
return item;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
Schema.formatOptionList = formatOptionList;
|
|
305
|
+
})(Schema || (exports.Schema = Schema = {}));
|
|
306
|
+
Schema.extend("number", function (value) {
|
|
307
|
+
value = Schema.checkDefault(this, value);
|
|
308
|
+
if (this.meta.max && value > this.meta.max)
|
|
309
|
+
throw new Error(`${this.meta.key || 'value'} is too large`);
|
|
310
|
+
if (this.meta.min && value < this.meta.min)
|
|
311
|
+
throw new Error(`${this.meta.key || 'value'} is too small`);
|
|
312
|
+
return value;
|
|
313
|
+
});
|
|
314
|
+
Schema.extend("string", function (value) {
|
|
315
|
+
value = Schema.checkDefault(this, value);
|
|
316
|
+
if (this.meta.max && (value === null || value === void 0 ? void 0 : value.length) > this.meta.max)
|
|
317
|
+
throw new Error(`${this.meta.key || 'value'} is too long`);
|
|
318
|
+
if (this.meta.min && (value === null || value === void 0 ? void 0 : value.length) < this.meta.min)
|
|
319
|
+
throw new Error(`${this.meta.key || 'value'} is too short`);
|
|
320
|
+
return value;
|
|
321
|
+
});
|
|
322
|
+
Schema.extend("boolean", function (value) {
|
|
323
|
+
return Schema.checkDefault(this, value);
|
|
324
|
+
});
|
|
325
|
+
Schema.extend("dict", function (value) {
|
|
326
|
+
value = Schema.checkDefault(this, value, {});
|
|
327
|
+
return Object.fromEntries(Object.entries(value).map(([key, schema]) => {
|
|
328
|
+
return [key, this.options.inner(value[key])];
|
|
329
|
+
}));
|
|
330
|
+
});
|
|
331
|
+
Schema.extend("list", function (value) {
|
|
332
|
+
value = Schema.checkDefault(this, value, []);
|
|
333
|
+
return value.map((item) => this.options.inner(item));
|
|
334
|
+
});
|
|
335
|
+
Schema.extend("object", function (value) {
|
|
336
|
+
value = Schema.checkDefault(this, value, {});
|
|
337
|
+
return Object.fromEntries(Object.entries(this.options.dict).map(([key, schema]) => {
|
|
338
|
+
return [key, schema(value[key])];
|
|
339
|
+
}));
|
|
340
|
+
});
|
|
341
|
+
Schema.extend("tuple", function (value) {
|
|
342
|
+
value = Schema.checkDefault(this, value, []);
|
|
343
|
+
return value.map((item, index) => this.options.list[index](item));
|
|
344
|
+
});
|
|
345
|
+
Schema.extend("union", function (value) {
|
|
346
|
+
value = Schema.checkDefault(this, value, []);
|
|
347
|
+
for (const schema of this.options.list) {
|
|
348
|
+
try {
|
|
349
|
+
return schema(value);
|
|
350
|
+
}
|
|
351
|
+
catch (e) { }
|
|
352
|
+
}
|
|
353
|
+
throw new Error("union type not match");
|
|
354
|
+
});
|
|
355
|
+
Schema.extend("regexp", function (value) {
|
|
356
|
+
value = Schema.checkDefault(this, value);
|
|
357
|
+
if (typeof value === "string") {
|
|
358
|
+
return new RegExp(value);
|
|
359
|
+
}
|
|
360
|
+
return value;
|
|
361
|
+
});
|
|
362
|
+
Schema.extend("intersect", function (value) {
|
|
363
|
+
value = Schema.checkDefault(this, value, []);
|
|
364
|
+
for (const schema of this.options.list) {
|
|
365
|
+
try {
|
|
366
|
+
value = schema(value);
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
throw new Error("intersect type not match");
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return value;
|
|
373
|
+
});
|
|
374
|
+
Schema.extend('never', function (value) {
|
|
375
|
+
throw new Error('never type not match');
|
|
376
|
+
});
|
|
377
|
+
Schema.extend("any", function (value) {
|
|
378
|
+
return value;
|
|
379
|
+
});
|
|
380
|
+
Schema.extend("date", function (value) {
|
|
381
|
+
value = Schema.checkDefault(this, value);
|
|
382
|
+
return new Date(value);
|
|
383
|
+
});
|
|
384
|
+
Schema.extend("const", function (value) {
|
|
385
|
+
value = Schema.checkDefault(this, value);
|
|
386
|
+
if (value !== this.meta.default) {
|
|
387
|
+
throw new Error("const value not match");
|
|
388
|
+
}
|
|
389
|
+
return value;
|
|
390
|
+
});
|
|
391
|
+
Schema.extend("percent", function (value) {
|
|
392
|
+
value = Schema.checkDefault(this, value);
|
|
393
|
+
return value;
|
|
394
|
+
});
|
package/lib/test.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("./index");
|
|
4
|
+
const str = index_1.Schema.string().default('123')('456');
|
|
5
|
+
console.log(str);
|
|
6
|
+
const num = index_1.Schema.number().default(123)(456);
|
|
7
|
+
console.log(num);
|
|
8
|
+
const bool = index_1.Schema.boolean().default(false)(true);
|
|
9
|
+
console.log(bool);
|
|
10
|
+
const reg = index_1.Schema.regexp().default(/123/)();
|
|
11
|
+
console.log(reg);
|
|
12
|
+
const date = index_1.Schema.date().default(new Date())(new Date().getTime() + 1000 * 60 * 60 * 24);
|
|
13
|
+
console.log(date);
|
|
14
|
+
const percent = index_1.Schema.percent().default(0.5).step(0.1)(0.6);
|
|
15
|
+
console.log(percent);
|
|
16
|
+
const dict = index_1.Schema.dict(index_1.Schema.object({
|
|
17
|
+
name: index_1.Schema.string().default('123'),
|
|
18
|
+
age: index_1.Schema.number().default(123),
|
|
19
|
+
performance: index_1.Schema.array(index_1.Schema.string()).default(['123']).description('爱好')
|
|
20
|
+
}))({
|
|
21
|
+
zs: {}
|
|
22
|
+
});
|
|
23
|
+
console.log(dict);
|
|
24
|
+
const arr = index_1.Schema.list(index_1.Schema.object({
|
|
25
|
+
name: index_1.Schema.string().default('123'),
|
|
26
|
+
age: index_1.Schema.number().default(123)
|
|
27
|
+
}))();
|
|
28
|
+
console.log(arr);
|
|
29
|
+
const obj = index_1.Schema.object({
|
|
30
|
+
name: index_1.Schema.string().default('123'),
|
|
31
|
+
age: index_1.Schema.number().default(123)
|
|
32
|
+
})();
|
|
33
|
+
console.log(obj);
|
|
34
|
+
const tuple = index_1.Schema.tuple([
|
|
35
|
+
index_1.Schema.const('123'),
|
|
36
|
+
index_1.Schema.const(123)
|
|
37
|
+
])();
|
|
38
|
+
console.log(tuple);
|
|
39
|
+
const union = index_1.Schema.union([
|
|
40
|
+
index_1.Schema.string().default('123'),
|
|
41
|
+
index_1.Schema.number().default(123)
|
|
42
|
+
])();
|
|
43
|
+
console.log(union);
|
|
44
|
+
const intersect = index_1.Schema.intersect([
|
|
45
|
+
index_1.Schema.const('123'),
|
|
46
|
+
index_1.Schema.string(),
|
|
47
|
+
])('123');
|
|
48
|
+
console.log('intersect', intersect);
|
|
49
|
+
const formatter = index_1.Schema.dict(index_1.Schema.list(index_1.Schema.object({
|
|
50
|
+
foo: index_1.Schema.string().option(['1', '3']),
|
|
51
|
+
bar: index_1.Schema.list(index_1.Schema.number()).default([123]).option([
|
|
52
|
+
{
|
|
53
|
+
label: '123',
|
|
54
|
+
value: 123
|
|
55
|
+
}
|
|
56
|
+
])
|
|
57
|
+
})));
|
|
58
|
+
const params = formatter({
|
|
59
|
+
az: [
|
|
60
|
+
{ foo: '456' }
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
console.log(JSON.stringify(formatter, null, 2), params);
|
|
64
|
+
const json = {
|
|
65
|
+
type: 'object',
|
|
66
|
+
dict: {
|
|
67
|
+
foo: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
default: '123',
|
|
70
|
+
},
|
|
71
|
+
bar: {
|
|
72
|
+
type: 'number',
|
|
73
|
+
default: 123
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const schema = index_1.Schema.fromJSON(json);
|
|
78
|
+
console.log(schema({ foo: '456' }));
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhin.js/schema",
|
|
3
|
+
"version": "0.0.14",
|
|
4
|
+
"description": "zhin配置声明模块",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/zhinjs/zhin.git",
|
|
10
|
+
"directory": "basic/schema"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"schema",
|
|
14
|
+
"zhin",
|
|
15
|
+
"zhinjs"
|
|
16
|
+
],
|
|
17
|
+
"author": "凉菜",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/zhinjs/zhin/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/zhinjs/zhin/tree/main/basic/schema#readme",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "latest",
|
|
25
|
+
"dtsc": "^2.3.0",
|
|
26
|
+
"ts-node-dev": "latest",
|
|
27
|
+
"tsc-alias": "latest",
|
|
28
|
+
"tsconfig-paths": "latest",
|
|
29
|
+
"typescript": "latest"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"/lib/**/*.js",
|
|
33
|
+
"/lib/**/*.d.ts",
|
|
34
|
+
"/lib/**/LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@zhinjs/shared": "^0.0.7"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"start": "node .",
|
|
41
|
+
"compile": "tsc --project tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
42
|
+
"build": "npm run compile && dtsc --project tsconfig.dtsc.json",
|
|
43
|
+
"dev": "ts-node-dev -r tsconfig-paths/register ./src/test.ts",
|
|
44
|
+
"pub": "npm publish --access public",
|
|
45
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
46
|
+
}
|
|
47
|
+
}
|