@leon740727/type-schema 0.0.3
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/dst/src/check.d.ts +3 -0
- package/dst/src/check.js +133 -0
- package/dst/src/index.d.ts +18 -0
- package/dst/src/index.js +55 -0
- package/dst/src/transform.d.ts +3 -0
- package/dst/src/transform.js +73 -0
- package/dst/src/type.d.ts +122 -0
- package/dst/src/type.js +150 -0
- package/dst/src/util.d.ts +19 -0
- package/dst/src/util.js +16 -0
- package/dst/test/enums.d.ts +1 -0
- package/dst/test/enums.js +22 -0
- package/dst/test/nullable.d.ts +1 -0
- package/dst/test/nullable.js +69 -0
- package/dst/test/tuple.d.ts +1 -0
- package/dst/test/tuple.js +49 -0
- package/dst/test/union.d.ts +1 -0
- package/dst/test/union.js +89 -0
- package/package.json +21 -0
- package/src/check.ts +147 -0
- package/src/index.ts +71 -0
- package/src/transform.ts +96 -0
- package/src/type.ts +367 -0
- package/src/util.ts +35 -0
- package/test/enums.ts +32 -0
- package/test/nullable.ts +84 -0
- package/test/tuple.ts +63 -0
- package/test/union.ts +112 -0
- package/tsconfig.json +17 -0
package/dst/src/check.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.check = void 0;
|
|
4
|
+
const ramda_1 = require("ramda");
|
|
5
|
+
const types_1 = require("types");
|
|
6
|
+
const type_1 = require("./type");
|
|
7
|
+
const util_1 = require("./util");
|
|
8
|
+
function check(schema, value) {
|
|
9
|
+
return _check(schema, value).map(error2string);
|
|
10
|
+
}
|
|
11
|
+
exports.check = check;
|
|
12
|
+
function error2string(error) {
|
|
13
|
+
if (error.paths.length > 0) {
|
|
14
|
+
return `obj.${error.paths.join(".")} ${error.msg}`;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return error.msg;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function _check(schema, value) {
|
|
21
|
+
if (value === null && schema.isNullable) {
|
|
22
|
+
return types_1.Optional.empty();
|
|
23
|
+
}
|
|
24
|
+
if (value === undefined && schema.isOptional) {
|
|
25
|
+
return types_1.Optional.empty();
|
|
26
|
+
}
|
|
27
|
+
if (schema.type === type_1.SchemaType.object) {
|
|
28
|
+
if (typeof value === "object" && value !== null) {
|
|
29
|
+
// typeof null === 'object'
|
|
30
|
+
(0, util_1.assert)(schema.innerSchema, "object inner schema is null or undefined");
|
|
31
|
+
return checkObject(schema.innerSchema, value);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return types_1.Optional.of({
|
|
35
|
+
paths: [],
|
|
36
|
+
msg: "is not an object",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (schema.type === type_1.SchemaType.array) {
|
|
41
|
+
if (value instanceof Array) {
|
|
42
|
+
return checkArray(schema.itemSchema, value);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return types_1.Optional.of({
|
|
46
|
+
paths: [],
|
|
47
|
+
msg: "is not an Array",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (schema.type === type_1.SchemaType.tuple) {
|
|
52
|
+
if (value instanceof Array) {
|
|
53
|
+
(0, util_1.assert)(schema.innerSchema, "tuple inner schema is null or undefined");
|
|
54
|
+
return checkTuple(schema.innerSchema, value);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return types_1.Optional.of({
|
|
58
|
+
paths: [],
|
|
59
|
+
msg: "is not a tuple",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (schema.type === type_1.SchemaType.union) {
|
|
64
|
+
(0, util_1.assert)(schema.innerSchema, "union inner schema is null or undefined");
|
|
65
|
+
return checkUnion(schema.innerSchema, value);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return checkAtom(schema, value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function checkObject(schema, value) {
|
|
72
|
+
// schema 沒有規範到的欄位不檢查
|
|
73
|
+
const toChecks = (0, ramda_1.toPairs)(schema)
|
|
74
|
+
.filter(([field, schema]) => {
|
|
75
|
+
return (schema.isOptional === false ||
|
|
76
|
+
(schema.isOptional && value[field] !== undefined));
|
|
77
|
+
})
|
|
78
|
+
.map(([field, schema]) => [field, schema]);
|
|
79
|
+
const errors = toChecks.map(([field, schema]) => {
|
|
80
|
+
return _check(schema, value[field]).map((error) => ({
|
|
81
|
+
paths: [field].concat(error.paths),
|
|
82
|
+
msg: error.msg,
|
|
83
|
+
}));
|
|
84
|
+
});
|
|
85
|
+
return types_1.Optional.of(types_1.Optional.filter(errors)[0]);
|
|
86
|
+
}
|
|
87
|
+
function checkArray(schema, value) {
|
|
88
|
+
const errors = value.map((v, idx) => {
|
|
89
|
+
return _check(schema, v).map((error) => (0, util_1.pair)(idx, error));
|
|
90
|
+
});
|
|
91
|
+
return types_1.Optional.of(types_1.Optional.filter(errors)[0]).map(([idx, error]) => ({
|
|
92
|
+
paths: [],
|
|
93
|
+
msg: `array item ${idx} is wrong (${error2string(error)})`,
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
function checkTuple(schemas, values) {
|
|
97
|
+
if (schemas.length !== values.length) {
|
|
98
|
+
return types_1.Optional.of({ paths: [], msg: "tuple size error" });
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const errors = (0, ramda_1.zip)(schemas, values).map(([schema, value], idx) => _check(schema, value).map((error) => ({
|
|
102
|
+
paths: [],
|
|
103
|
+
msg: `tuple item ${idx} is wrong (${error2string(error)})`,
|
|
104
|
+
})));
|
|
105
|
+
return types_1.Optional.of(types_1.Optional.filter(errors)[0]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function checkUnion(schemas, value) {
|
|
109
|
+
const success = schemas.reduce((success, schema) => {
|
|
110
|
+
if (success) {
|
|
111
|
+
return success;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const error = _check(schema, value);
|
|
115
|
+
return (0, ramda_1.not)(error.present);
|
|
116
|
+
}
|
|
117
|
+
}, false);
|
|
118
|
+
if (success) {
|
|
119
|
+
return types_1.Optional.empty();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
return types_1.Optional.of({
|
|
123
|
+
paths: [],
|
|
124
|
+
msg: "not in union",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function checkAtom(schema, value) {
|
|
129
|
+
return types_1.Optional.of(schema.isa(value)).map((msg) => ({
|
|
130
|
+
paths: [],
|
|
131
|
+
msg,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Schema, build } from "./type";
|
|
2
|
+
export { Schema, SchemaType } from "./type";
|
|
3
|
+
export declare const value: typeof Schema.value;
|
|
4
|
+
export declare const object: typeof Schema.object;
|
|
5
|
+
export declare const array: typeof Schema.array;
|
|
6
|
+
export declare const tuple: typeof Schema.tuple;
|
|
7
|
+
export declare const union: typeof Schema.union;
|
|
8
|
+
export type buildType<T extends Schema, transformed extends boolean = true> = build<T, transformed>;
|
|
9
|
+
export declare function check(schema: Schema, value: any): string | null;
|
|
10
|
+
type Result<R> = [string, null] | [null, R];
|
|
11
|
+
export declare function transform<S extends Schema>(schema: S, value: any): Result<build<S, true>>;
|
|
12
|
+
export declare function any(): import("./type").AtomSchema<any, any>;
|
|
13
|
+
export declare function string(): import("./type").AtomSchema<string, string>;
|
|
14
|
+
export declare function number(): import("./type").AtomSchema<number, number>;
|
|
15
|
+
export declare function boolean(): import("./type").AtomSchema<boolean, boolean>;
|
|
16
|
+
export declare function bigint(): import("./type").AtomSchema<bigint, bigint>;
|
|
17
|
+
export declare function date(): import("./type").AtomSchema<Date, Date>;
|
|
18
|
+
export declare function enums<T extends number | string>(valids: T[]): import("./type").AtomSchema<T, T>;
|
package/dst/src/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enums = exports.date = exports.bigint = exports.boolean = exports.number = exports.string = exports.any = exports.transform = exports.check = exports.union = exports.tuple = exports.array = exports.object = exports.value = exports.SchemaType = exports.Schema = void 0;
|
|
4
|
+
const type_1 = require("./type");
|
|
5
|
+
const check_1 = require("./check");
|
|
6
|
+
const transform_1 = require("./transform");
|
|
7
|
+
var type_2 = require("./type");
|
|
8
|
+
Object.defineProperty(exports, "Schema", { enumerable: true, get: function () { return type_2.Schema; } });
|
|
9
|
+
Object.defineProperty(exports, "SchemaType", { enumerable: true, get: function () { return type_2.SchemaType; } });
|
|
10
|
+
exports.value = type_1.Schema.value;
|
|
11
|
+
exports.object = type_1.Schema.object;
|
|
12
|
+
exports.array = type_1.Schema.array;
|
|
13
|
+
exports.tuple = type_1.Schema.tuple;
|
|
14
|
+
exports.union = type_1.Schema.union;
|
|
15
|
+
function check(schema, value) {
|
|
16
|
+
return (0, check_1.check)(schema, value).orNull();
|
|
17
|
+
}
|
|
18
|
+
exports.check = check;
|
|
19
|
+
function transform(schema, value) {
|
|
20
|
+
return (0, transform_1.transform)(schema, value).either(
|
|
21
|
+
//@ts-ignore
|
|
22
|
+
(error) => [error, null], (value) => [null, value]);
|
|
23
|
+
}
|
|
24
|
+
exports.transform = transform;
|
|
25
|
+
// helper
|
|
26
|
+
function any() {
|
|
27
|
+
return (0, exports.value)((v) => null);
|
|
28
|
+
}
|
|
29
|
+
exports.any = any;
|
|
30
|
+
function string() {
|
|
31
|
+
return (0, exports.value)((v) => typeof v === "string" ? null : "is not a string");
|
|
32
|
+
}
|
|
33
|
+
exports.string = string;
|
|
34
|
+
function number() {
|
|
35
|
+
return (0, exports.value)((v) => typeof v === "number" ? null : "is not a number");
|
|
36
|
+
}
|
|
37
|
+
exports.number = number;
|
|
38
|
+
function boolean() {
|
|
39
|
+
return (0, exports.value)((v) => typeof v === "boolean" ? null : "is not a boolean");
|
|
40
|
+
}
|
|
41
|
+
exports.boolean = boolean;
|
|
42
|
+
function bigint() {
|
|
43
|
+
return (0, exports.value)((v) => typeof v === "bigint" ? null : "is not a bigint");
|
|
44
|
+
}
|
|
45
|
+
exports.bigint = bigint;
|
|
46
|
+
function date() {
|
|
47
|
+
return (0, exports.value)((v) => (v instanceof Date ? null : "is not a Date"));
|
|
48
|
+
}
|
|
49
|
+
exports.date = date;
|
|
50
|
+
function enums(valids) {
|
|
51
|
+
return type_1.Schema.value((v) => valids.includes(v)
|
|
52
|
+
? null
|
|
53
|
+
: `not a valid enum value, value should be one of [${valids}]`);
|
|
54
|
+
}
|
|
55
|
+
exports.enums = enums;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transform = void 0;
|
|
4
|
+
const ramda_1 = require("ramda");
|
|
5
|
+
const types_1 = require("types");
|
|
6
|
+
const type_1 = require("./type");
|
|
7
|
+
const check_1 = require("./check");
|
|
8
|
+
const util_1 = require("./util");
|
|
9
|
+
function transform(schema, value) {
|
|
10
|
+
return (0, check_1.check)(schema, value)
|
|
11
|
+
.map((error) => types_1.Result.fail(error))
|
|
12
|
+
.orExec(() => types_1.Result.ok(_transform(schema, value)));
|
|
13
|
+
}
|
|
14
|
+
exports.transform = transform;
|
|
15
|
+
function _transform(schema, value) {
|
|
16
|
+
if (value === null) {
|
|
17
|
+
// check() 已經確認過 null 是合法的
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (schema.type === type_1.SchemaType.atom) {
|
|
24
|
+
return transformAtom(schema, value);
|
|
25
|
+
}
|
|
26
|
+
else if (schema.type === type_1.SchemaType.array) {
|
|
27
|
+
return transformArray(schema, value);
|
|
28
|
+
}
|
|
29
|
+
else if (schema.type === type_1.SchemaType.tuple) {
|
|
30
|
+
return transformTuple(schema, value);
|
|
31
|
+
}
|
|
32
|
+
else if (schema.type === type_1.SchemaType.union) {
|
|
33
|
+
return transformUnion(schema, value);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return transformObject(schema, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function transformAtom(schema, value) {
|
|
40
|
+
return schema.transform(value);
|
|
41
|
+
}
|
|
42
|
+
function transformArray(schema, values) {
|
|
43
|
+
(0, util_1.assert)(schema.type === type_1.SchemaType.array, "");
|
|
44
|
+
//@ts-ignore
|
|
45
|
+
return values.map((value) => _transform(schema.itemSchema, value));
|
|
46
|
+
}
|
|
47
|
+
function transformTuple(schema, values) {
|
|
48
|
+
(0, util_1.assert)(schema.type === type_1.SchemaType.tuple, "");
|
|
49
|
+
(0, util_1.assert)(schema.innerSchema, "");
|
|
50
|
+
return (0, ramda_1.zip)(schema.innerSchema, values).map(([schema, value]) => _transform(schema, value));
|
|
51
|
+
}
|
|
52
|
+
function transformUnion(schema, value) {
|
|
53
|
+
(0, util_1.assert)(schema.type === type_1.SchemaType.union, "");
|
|
54
|
+
(0, util_1.assert)(schema.innerSchema, "");
|
|
55
|
+
// 這裡不能用 _transform
|
|
56
|
+
// 因為 union 裡面的 schema 可能是錯的,但 _transform 假設每一個 schema 都是對的
|
|
57
|
+
const results = schema.innerSchema.map((schema) => transform(schema, value));
|
|
58
|
+
const oks = results.filter((i) => i.ok).map((i) => i.orError());
|
|
59
|
+
(0, util_1.assert)(oks.length > 0, "");
|
|
60
|
+
return oks[0];
|
|
61
|
+
}
|
|
62
|
+
function transformObject(schema, value) {
|
|
63
|
+
const innerSchema = schema.innerSchema;
|
|
64
|
+
(0, util_1.assert)(innerSchema, "object inner schema is null or undefined");
|
|
65
|
+
const todos = (0, ramda_1.toPairs)(innerSchema)
|
|
66
|
+
.filter(([field, schema]) => {
|
|
67
|
+
return (schema.isOptional === false ||
|
|
68
|
+
(schema.isOptional && value[field] !== undefined));
|
|
69
|
+
})
|
|
70
|
+
.map(([field, schema]) => [field, schema]);
|
|
71
|
+
const v2 = (0, ramda_1.fromPairs)(todos.map(([field, schema]) => (0, util_1.pair)(field, _transform(schema, value[field]))));
|
|
72
|
+
return (0, ramda_1.mergeRight)(value, v2);
|
|
73
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { addQuestionMarks, flatten, resolveTuple } from "./util";
|
|
2
|
+
/**
|
|
3
|
+
* schema 有幾種類別
|
|
4
|
+
* 1. atom: 沒有 inner schema 描述的值。例如 number, string ...
|
|
5
|
+
* 注意,array 或 object 也可以是 atom 的,只要其值沒有另外的 schema 描述
|
|
6
|
+
* 2. compound: 裡面的值有另外的 inner schema 來描述。又分成二種 object, array
|
|
7
|
+
*/
|
|
8
|
+
export declare enum SchemaType {
|
|
9
|
+
atom = 0,
|
|
10
|
+
array = 1,
|
|
11
|
+
object = 2,
|
|
12
|
+
tuple = 3,
|
|
13
|
+
union = 4
|
|
14
|
+
}
|
|
15
|
+
type Attr = {
|
|
16
|
+
[field: string]: any;
|
|
17
|
+
};
|
|
18
|
+
export declare class AtomSchema<VT, VT2> {
|
|
19
|
+
readonly type: SchemaType.atom;
|
|
20
|
+
readonly value: VT;
|
|
21
|
+
readonly isNullable: boolean;
|
|
22
|
+
readonly isOptional: boolean;
|
|
23
|
+
readonly isa: (v: any) => string | null;
|
|
24
|
+
readonly transform: (v: VT) => VT2;
|
|
25
|
+
readonly attr: Attr;
|
|
26
|
+
constructor(type: SchemaType.atom, value: VT, isNullable: boolean, isOptional: boolean, isa: (v: any) => string | null, // 傳回錯誤訊息
|
|
27
|
+
transform: (v: VT) => VT2, attr: Attr);
|
|
28
|
+
nullable(): AtomSchema<VT | null, VT2>;
|
|
29
|
+
optional(): AtomSchema<VT | undefined, VT2>;
|
|
30
|
+
transformer<T2>(fn: (v: NonNullable<VT>) => T2): AtomSchema<VT, T2>;
|
|
31
|
+
set(attr: Attr): AtomSchema<VT, VT2>;
|
|
32
|
+
}
|
|
33
|
+
export declare class ArraySchema<InnerSchema extends InnerSchemaForArraySchema | null | undefined> {
|
|
34
|
+
readonly type: SchemaType.array;
|
|
35
|
+
readonly itemSchema: Schema;
|
|
36
|
+
readonly isNullable: boolean;
|
|
37
|
+
readonly isOptional: boolean;
|
|
38
|
+
readonly attr: Attr;
|
|
39
|
+
readonly innerSchema: InnerSchema;
|
|
40
|
+
constructor(type: SchemaType.array, itemSchema: Schema, isNullable: boolean, isOptional: boolean, attr: Attr);
|
|
41
|
+
nullable(): ArraySchema<InnerSchema | null>;
|
|
42
|
+
optional(): ArraySchema<InnerSchema | undefined>;
|
|
43
|
+
set(attr: Attr): ArraySchema<InnerSchema>;
|
|
44
|
+
}
|
|
45
|
+
export declare class ObjectSchema<InnerSchema extends InnerSchemaForObjectSchema | null | undefined> {
|
|
46
|
+
readonly type: SchemaType.object;
|
|
47
|
+
readonly innerSchema: InnerSchema;
|
|
48
|
+
readonly isNullable: boolean;
|
|
49
|
+
readonly isOptional: boolean;
|
|
50
|
+
readonly attr: Attr;
|
|
51
|
+
constructor(type: SchemaType.object, innerSchema: InnerSchema, isNullable: boolean, isOptional: boolean, attr: Attr);
|
|
52
|
+
nullable(): ObjectSchema<InnerSchema | null>;
|
|
53
|
+
optional(): ObjectSchema<InnerSchema | undefined>;
|
|
54
|
+
set(attr: Attr): ObjectSchema<InnerSchema>;
|
|
55
|
+
}
|
|
56
|
+
declare class TupleSchema<InnerSchema extends [Schema, ...Schema[]] | null | undefined> {
|
|
57
|
+
readonly type: SchemaType.tuple;
|
|
58
|
+
readonly innerSchema: InnerSchema;
|
|
59
|
+
readonly isNullable: boolean;
|
|
60
|
+
readonly isOptional: boolean;
|
|
61
|
+
readonly attr: Attr;
|
|
62
|
+
constructor(type: SchemaType.tuple, innerSchema: InnerSchema, isNullable: boolean, isOptional: boolean, attr: Attr);
|
|
63
|
+
nullable(): TupleSchema<InnerSchema | null>;
|
|
64
|
+
optional(): TupleSchema<InnerSchema | undefined>;
|
|
65
|
+
set(attr: Attr): TupleSchema<InnerSchema>;
|
|
66
|
+
}
|
|
67
|
+
declare class UnionSchema<InnerSchema extends Schema[] | null | undefined> {
|
|
68
|
+
readonly type: SchemaType.union;
|
|
69
|
+
readonly innerSchema: InnerSchema;
|
|
70
|
+
readonly isNullable: boolean;
|
|
71
|
+
readonly isOptional: boolean;
|
|
72
|
+
readonly attr: Attr;
|
|
73
|
+
constructor(type: SchemaType.union, innerSchema: InnerSchema, isNullable: boolean, isOptional: boolean, attr: Attr);
|
|
74
|
+
nullable(): UnionSchema<InnerSchema | null>;
|
|
75
|
+
optional(): UnionSchema<InnerSchema | undefined>;
|
|
76
|
+
set(attr: Attr): UnionSchema<InnerSchema>;
|
|
77
|
+
}
|
|
78
|
+
export type Schema = AtomSchema<any, any> | ArraySchema<InnerSchemaForArraySchema | null | undefined> | TupleSchema<[Schema, ...Schema[]] | null | undefined> | UnionSchema<Schema[] | null | undefined> | ObjectSchema<InnerSchemaForObjectSchema | null | undefined>;
|
|
79
|
+
type InnerSchemaForArraySchema = Schema[];
|
|
80
|
+
export type InnerSchemaForObjectSchema = {
|
|
81
|
+
[field: string]: Schema;
|
|
82
|
+
};
|
|
83
|
+
export declare namespace Schema {
|
|
84
|
+
function value<T>(isa: (v: any) => string | null): AtomSchema<T, T>;
|
|
85
|
+
function array<S extends Schema>(itemSchema: S): ArraySchema<S[]>;
|
|
86
|
+
function object<S extends InnerSchemaForObjectSchema>(innerSchema: S): ObjectSchema<S>;
|
|
87
|
+
function tuple<tuple extends [Schema, ...Schema[]]>(schemas: tuple): TupleSchema<tuple>;
|
|
88
|
+
function union<union extends Schema[]>(schemas: union): UnionSchema<union>;
|
|
89
|
+
}
|
|
90
|
+
export type fetchAtom<T extends Schema> = T extends {
|
|
91
|
+
type: SchemaType.atom;
|
|
92
|
+
} ? T : never;
|
|
93
|
+
export type fetchArray<T extends Schema> = T extends {
|
|
94
|
+
type: SchemaType.array;
|
|
95
|
+
} ? T : never;
|
|
96
|
+
export type fetchObject<T extends Schema> = T extends {
|
|
97
|
+
type: SchemaType.object;
|
|
98
|
+
} ? T : never;
|
|
99
|
+
export type fetchTuple<T extends Schema> = Extract<T, TupleSchema<any>>;
|
|
100
|
+
export type fetchUnion<T extends Schema> = Extract<T, UnionSchema<any>>;
|
|
101
|
+
export type build<S extends Schema, transformed extends boolean> = S extends {
|
|
102
|
+
type: SchemaType.object;
|
|
103
|
+
} ? buildObj<fetchObject<S>["innerSchema"], transformed> : S extends {
|
|
104
|
+
type: SchemaType.array;
|
|
105
|
+
} ? buildArray<fetchArray<S>["innerSchema"], transformed> : S extends {
|
|
106
|
+
type: SchemaType.tuple;
|
|
107
|
+
} ? buildTuple<fetchTuple<S>["innerSchema"], transformed> : S extends {
|
|
108
|
+
type: SchemaType.union;
|
|
109
|
+
} ? buildUnion<fetchUnion<S>["innerSchema"], transformed> : S extends {
|
|
110
|
+
type: SchemaType.atom;
|
|
111
|
+
} ? buildAtom<fetchAtom<S>, transformed> : never;
|
|
112
|
+
type buildObj<OS extends InnerSchemaForObjectSchema | null | undefined, transformed extends boolean> = OS extends InnerSchemaForObjectSchema ? flatten<addQuestionMarks<{
|
|
113
|
+
[f in keyof OS]: build<OS[f], transformed>;
|
|
114
|
+
}>> : OS;
|
|
115
|
+
type buildArray<S extends InnerSchemaForArraySchema | null | undefined, transformed extends boolean> = S extends InnerSchemaForArraySchema ? build<S[number], transformed>[] : S;
|
|
116
|
+
type buildTuple<S extends any[] | null | undefined, transformed extends boolean> = S extends [Schema, ...Schema[]] ? [
|
|
117
|
+
build<resolveTuple<S>[0], transformed>,
|
|
118
|
+
...buildTuple<resolveTuple<S>[1], transformed>
|
|
119
|
+
] : S;
|
|
120
|
+
type buildUnion<S extends Schema[] | null | undefined, transformed extends boolean> = S extends Schema[] ? build<S[number], transformed> : S;
|
|
121
|
+
type buildAtom<S extends Schema | null | undefined, transformed extends boolean> = S extends Schema ? transformed extends true ? ReturnType<fetchAtom<S>["transform"]> | Extract<fetchAtom<S>["value"], null | undefined> : fetchAtom<S>["value"] : S;
|
|
122
|
+
export {};
|
package/dst/src/type.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Schema = exports.ObjectSchema = exports.ArraySchema = exports.AtomSchema = exports.SchemaType = void 0;
|
|
4
|
+
const ramda_1 = require("ramda");
|
|
5
|
+
/**
|
|
6
|
+
* schema 有幾種類別
|
|
7
|
+
* 1. atom: 沒有 inner schema 描述的值。例如 number, string ...
|
|
8
|
+
* 注意,array 或 object 也可以是 atom 的,只要其值沒有另外的 schema 描述
|
|
9
|
+
* 2. compound: 裡面的值有另外的 inner schema 來描述。又分成二種 object, array
|
|
10
|
+
*/
|
|
11
|
+
var SchemaType;
|
|
12
|
+
(function (SchemaType) {
|
|
13
|
+
SchemaType[SchemaType["atom"] = 0] = "atom";
|
|
14
|
+
SchemaType[SchemaType["array"] = 1] = "array";
|
|
15
|
+
SchemaType[SchemaType["object"] = 2] = "object";
|
|
16
|
+
SchemaType[SchemaType["tuple"] = 3] = "tuple";
|
|
17
|
+
SchemaType[SchemaType["union"] = 4] = "union";
|
|
18
|
+
})(SchemaType = exports.SchemaType || (exports.SchemaType = {}));
|
|
19
|
+
class AtomSchema {
|
|
20
|
+
constructor(type, value, isNullable, isOptional, isa, // 傳回錯誤訊息
|
|
21
|
+
transform, attr // 額外附加的屬性
|
|
22
|
+
) {
|
|
23
|
+
this.type = type;
|
|
24
|
+
this.value = value;
|
|
25
|
+
this.isNullable = isNullable;
|
|
26
|
+
this.isOptional = isOptional;
|
|
27
|
+
this.isa = isa;
|
|
28
|
+
this.transform = transform;
|
|
29
|
+
this.attr = attr;
|
|
30
|
+
}
|
|
31
|
+
nullable() {
|
|
32
|
+
return new AtomSchema(SchemaType.atom, this.value, true, this.isOptional, this.isa, this.transform, this.attr);
|
|
33
|
+
}
|
|
34
|
+
optional() {
|
|
35
|
+
return new AtomSchema(SchemaType.atom, this.value, this.isNullable, true, this.isa, this.transform, this.attr);
|
|
36
|
+
}
|
|
37
|
+
transformer(fn) {
|
|
38
|
+
return new AtomSchema(SchemaType.atom, this.value, this.isNullable, this.isOptional, this.isa, fn, this.attr);
|
|
39
|
+
}
|
|
40
|
+
set(attr) {
|
|
41
|
+
const newAttr = (0, ramda_1.mergeRight)(this.attr, attr);
|
|
42
|
+
return new AtomSchema(SchemaType.atom, this.value, this.isNullable, this.isOptional, this.isa, this.transform, newAttr);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.AtomSchema = AtomSchema;
|
|
46
|
+
class ArraySchema {
|
|
47
|
+
constructor(type, itemSchema, isNullable, isOptional, attr // 額外附加的屬性
|
|
48
|
+
) {
|
|
49
|
+
this.type = type;
|
|
50
|
+
this.itemSchema = itemSchema;
|
|
51
|
+
this.isNullable = isNullable;
|
|
52
|
+
this.isOptional = isOptional;
|
|
53
|
+
this.attr = attr;
|
|
54
|
+
}
|
|
55
|
+
nullable() {
|
|
56
|
+
return new ArraySchema(SchemaType.array, this.itemSchema, true, this.isOptional, this.attr);
|
|
57
|
+
}
|
|
58
|
+
optional() {
|
|
59
|
+
return new ArraySchema(SchemaType.array, this.itemSchema, this.isNullable, true, this.attr);
|
|
60
|
+
}
|
|
61
|
+
set(attr) {
|
|
62
|
+
const newAttr = (0, ramda_1.mergeRight)(this.attr, attr);
|
|
63
|
+
return new ArraySchema(SchemaType.array, this.itemSchema, this.isNullable, this.isOptional, newAttr);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.ArraySchema = ArraySchema;
|
|
67
|
+
class ObjectSchema {
|
|
68
|
+
constructor(type, innerSchema, isNullable, isOptional, attr // 額外附加的屬性
|
|
69
|
+
) {
|
|
70
|
+
this.type = type;
|
|
71
|
+
this.innerSchema = innerSchema;
|
|
72
|
+
this.isNullable = isNullable;
|
|
73
|
+
this.isOptional = isOptional;
|
|
74
|
+
this.attr = attr;
|
|
75
|
+
}
|
|
76
|
+
nullable() {
|
|
77
|
+
return new ObjectSchema(SchemaType.object, this.innerSchema, true, this.isOptional, this.attr);
|
|
78
|
+
}
|
|
79
|
+
optional() {
|
|
80
|
+
return new ObjectSchema(SchemaType.object, this.innerSchema, this.isNullable, true, this.attr);
|
|
81
|
+
}
|
|
82
|
+
set(attr) {
|
|
83
|
+
const newAttr = (0, ramda_1.mergeRight)(this.attr, attr);
|
|
84
|
+
return new ObjectSchema(SchemaType.object, this.innerSchema, this.isNullable, this.isOptional, newAttr);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.ObjectSchema = ObjectSchema;
|
|
88
|
+
class TupleSchema {
|
|
89
|
+
constructor(type, innerSchema, isNullable, isOptional, attr // 額外附加的屬性
|
|
90
|
+
) {
|
|
91
|
+
this.type = type;
|
|
92
|
+
this.innerSchema = innerSchema;
|
|
93
|
+
this.isNullable = isNullable;
|
|
94
|
+
this.isOptional = isOptional;
|
|
95
|
+
this.attr = attr;
|
|
96
|
+
}
|
|
97
|
+
nullable() {
|
|
98
|
+
return new TupleSchema(SchemaType.tuple, this.innerSchema, true, this.isOptional, this.attr);
|
|
99
|
+
}
|
|
100
|
+
optional() {
|
|
101
|
+
return new TupleSchema(SchemaType.tuple, this.innerSchema, this.isNullable, true, this.attr);
|
|
102
|
+
}
|
|
103
|
+
set(attr) {
|
|
104
|
+
const newAttr = (0, ramda_1.mergeRight)(this.attr, attr);
|
|
105
|
+
return new TupleSchema(SchemaType.tuple, this.innerSchema, this.isNullable, this.isOptional, newAttr);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class UnionSchema {
|
|
109
|
+
constructor(type, innerSchema, isNullable, isOptional, attr // 額外附加的屬性
|
|
110
|
+
) {
|
|
111
|
+
this.type = type;
|
|
112
|
+
this.innerSchema = innerSchema;
|
|
113
|
+
this.isNullable = isNullable;
|
|
114
|
+
this.isOptional = isOptional;
|
|
115
|
+
this.attr = attr;
|
|
116
|
+
}
|
|
117
|
+
nullable() {
|
|
118
|
+
return new UnionSchema(SchemaType.union, this.innerSchema, true, this.isOptional, this.attr);
|
|
119
|
+
}
|
|
120
|
+
optional() {
|
|
121
|
+
return new UnionSchema(SchemaType.union, this.innerSchema, this.isNullable, true, this.attr);
|
|
122
|
+
}
|
|
123
|
+
set(attr) {
|
|
124
|
+
const newAttr = (0, ramda_1.mergeRight)(this.attr, attr);
|
|
125
|
+
return new UnionSchema(SchemaType.union, this.innerSchema, this.isNullable, this.isOptional, newAttr);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
var Schema;
|
|
129
|
+
(function (Schema) {
|
|
130
|
+
function value(isa) {
|
|
131
|
+
return new AtomSchema(SchemaType.atom, null, false, false, isa, (v) => v, {});
|
|
132
|
+
}
|
|
133
|
+
Schema.value = value;
|
|
134
|
+
function array(itemSchema) {
|
|
135
|
+
return new ArraySchema(SchemaType.array, itemSchema, false, false, {});
|
|
136
|
+
}
|
|
137
|
+
Schema.array = array;
|
|
138
|
+
function object(innerSchema) {
|
|
139
|
+
return new ObjectSchema(SchemaType.object, innerSchema, false, false, {});
|
|
140
|
+
}
|
|
141
|
+
Schema.object = object;
|
|
142
|
+
function tuple(schemas) {
|
|
143
|
+
return new TupleSchema(SchemaType.tuple, schemas, false, false, {});
|
|
144
|
+
}
|
|
145
|
+
Schema.tuple = tuple;
|
|
146
|
+
function union(schemas) {
|
|
147
|
+
return new UnionSchema(SchemaType.union, schemas, false, false, {});
|
|
148
|
+
}
|
|
149
|
+
Schema.union = union;
|
|
150
|
+
})(Schema = exports.Schema || (exports.Schema = {}));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type flatten<T> = identity<{
|
|
2
|
+
[k in keyof T]: T[k];
|
|
3
|
+
}>;
|
|
4
|
+
export type addQuestionMarks<T extends object> = {
|
|
5
|
+
[K in requiredKeys<T>]: T[K];
|
|
6
|
+
} & {
|
|
7
|
+
[K in optionalKeys<T>]?: T[K];
|
|
8
|
+
};
|
|
9
|
+
export type resolveTuple<tuple> = tuple extends [infer h, ...infer r] ? [h, r] : never;
|
|
10
|
+
type identity<T> = T;
|
|
11
|
+
type optionalKeys<T extends object> = {
|
|
12
|
+
[k in keyof T]: undefined extends T[k] ? k : never;
|
|
13
|
+
}[keyof T];
|
|
14
|
+
type requiredKeys<T extends object> = {
|
|
15
|
+
[k in keyof T]: undefined extends T[k] ? never : k;
|
|
16
|
+
}[keyof T];
|
|
17
|
+
export declare function assert(condition: unknown, message: string): asserts condition;
|
|
18
|
+
export declare function pair<A, B>(a: A, b: B): [A, B];
|
|
19
|
+
export {};
|
package/dst/src/util.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ref: https://github.com/colinhacks/zod
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.pair = exports.assert = void 0;
|
|
5
|
+
class AssertError extends Error {
|
|
6
|
+
}
|
|
7
|
+
function assert(condition, message) {
|
|
8
|
+
if (!condition) {
|
|
9
|
+
throw new AssertError(message);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.assert = assert;
|
|
13
|
+
function pair(a, b) {
|
|
14
|
+
return [a, b];
|
|
15
|
+
}
|
|
16
|
+
exports.pair = pair;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const Schema = require("../src/index");
|
|
5
|
+
describe("enums", () => {
|
|
6
|
+
const schema = Schema.enums([1, 2, "yes", "no"]);
|
|
7
|
+
it("type", () => {
|
|
8
|
+
const t1 = 1;
|
|
9
|
+
const t2 = 2;
|
|
10
|
+
const t3 = "yes";
|
|
11
|
+
const t4 = "no";
|
|
12
|
+
//@ts-expect-error
|
|
13
|
+
const t5 = 3;
|
|
14
|
+
//@ts-expect-error
|
|
15
|
+
const t6 = "error";
|
|
16
|
+
});
|
|
17
|
+
it("transform", () => {
|
|
18
|
+
assert.strictEqual(Schema.transform(schema, 2)[0], null);
|
|
19
|
+
assert.strictEqual(Schema.transform(schema, 3)[0], "not a valid enum value, value should be one of [1,2,yes,no]");
|
|
20
|
+
assert.strictEqual(Schema.transform(schema, "e")[0], "not a valid enum value, value should be one of [1,2,yes,no]");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|