typebad 1.0.0-alpha.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/dist/Type.d.ts +53 -0
- package/dist/Type.js +214 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/package.json +20 -0
package/dist/Type.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type InferType<T> = T extends Type<infer U> ? U : never;
|
|
2
|
+
export type InferSchema<T extends TypeSchema> = Simplify<{
|
|
3
|
+
[K in keyof T as undefined extends InferType<T[K]> ? never : unknown extends InferType<T[K]> ? never : K]: InferType<T[K]>;
|
|
4
|
+
} & {
|
|
5
|
+
[K in keyof T as undefined extends InferType<T[K]> ? unknown extends InferType<T[K]> ? never : K : never]?: InferType<T[K]>;
|
|
6
|
+
}>;
|
|
7
|
+
type Simplify<T> = {
|
|
8
|
+
[K in keyof T]: T[K];
|
|
9
|
+
} & {};
|
|
10
|
+
export type TypeSchema = Record<string, Type<any>>;
|
|
11
|
+
type NormalizedValue<T> = T extends undefined ? Exclude<T, undefined> : T extends (infer U)[] ? NormalizedValue<U>[] : T extends Record<string, any> ? {
|
|
12
|
+
[K in keyof T]-?: NormalizedValue<T[K]>;
|
|
13
|
+
} : T;
|
|
14
|
+
export type NormalizedType<T extends Type<any>> = NormalizedValue<InferType<T>>;
|
|
15
|
+
export interface ValidateOptions {
|
|
16
|
+
allowUnknownProperties: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface NormalizeOptions {
|
|
19
|
+
allowUnknownProperties: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class Type<T> {
|
|
22
|
+
private matcher;
|
|
23
|
+
private validator;
|
|
24
|
+
private normalizer;
|
|
25
|
+
private onValidateCallback;
|
|
26
|
+
private onNormalizeCallback;
|
|
27
|
+
private allowMissingProperty;
|
|
28
|
+
onValidate(callback: (value: T) => void): Type<T>;
|
|
29
|
+
onNormalize(callback: (value: NormalizedValue<T>) => void): Type<T>;
|
|
30
|
+
private validate;
|
|
31
|
+
private normalize;
|
|
32
|
+
extend(matcher: (value: T) => boolean): Type<T>;
|
|
33
|
+
default(defaultValue: T | (() => T)): Type<T | undefined>;
|
|
34
|
+
optional(): Type<T | null | undefined>;
|
|
35
|
+
static match<T>(matcher: (value: T) => boolean): Type<T>;
|
|
36
|
+
static enum<const V extends readonly (string | number)[]>(values: V): Type<V[number]>;
|
|
37
|
+
static array<T extends Type<any>>(type: T): Type<InferType<T>[]>;
|
|
38
|
+
static schema<S extends TypeSchema>(schema: S): Type<InferSchema<S>>;
|
|
39
|
+
static lazy<T extends Type<any>>(callback: () => T): T;
|
|
40
|
+
static validate(type: Type<any>, value: any, options?: Partial<ValidateOptions>): void;
|
|
41
|
+
static normalize<T extends Type<any>>(type: T, value: any, options?: Partial<NormalizeOptions>): NormalizedType<T>;
|
|
42
|
+
private static getValueError;
|
|
43
|
+
private static getUnknownPropertyError;
|
|
44
|
+
private static getMissingPropertyError;
|
|
45
|
+
static readonly Null: Type<null>;
|
|
46
|
+
static readonly String: Type<string>;
|
|
47
|
+
static readonly Boolean: Type<boolean>;
|
|
48
|
+
static readonly Number: Type<number>;
|
|
49
|
+
static readonly Integer: Type<number>;
|
|
50
|
+
static readonly Float: Type<number>;
|
|
51
|
+
static readonly Date: Type<Date>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
package/dist/Type.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Type = void 0;
|
|
4
|
+
class Type {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.matcher = () => true;
|
|
7
|
+
this.validator = () => true;
|
|
8
|
+
this.normalizer = (value) => value;
|
|
9
|
+
this.onValidateCallback = null;
|
|
10
|
+
this.onNormalizeCallback = null;
|
|
11
|
+
this.allowMissingProperty = false;
|
|
12
|
+
}
|
|
13
|
+
onValidate(callback) {
|
|
14
|
+
this.onValidateCallback = callback;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
onNormalize(callback) {
|
|
18
|
+
this.onNormalizeCallback = callback;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
validate(value, propertyName, options) {
|
|
22
|
+
if (!this.matcher(value) || !this.validator(value, propertyName, options)) {
|
|
23
|
+
throw new Error(Type.getValueError(propertyName));
|
|
24
|
+
}
|
|
25
|
+
this.onValidateCallback?.(value);
|
|
26
|
+
}
|
|
27
|
+
normalize(value, propertyName, options) {
|
|
28
|
+
if (!this.matcher(value)) {
|
|
29
|
+
throw new Error(Type.getValueError(propertyName));
|
|
30
|
+
}
|
|
31
|
+
const normalizedValue = this.normalizer(value, propertyName, options) ?? null;
|
|
32
|
+
this.onNormalizeCallback?.(normalizedValue);
|
|
33
|
+
return normalizedValue;
|
|
34
|
+
}
|
|
35
|
+
extend(matcher) {
|
|
36
|
+
const type = new Type();
|
|
37
|
+
type.matcher = (value) => {
|
|
38
|
+
return this.matcher(value) && matcher(value);
|
|
39
|
+
};
|
|
40
|
+
type.validator = this.validator;
|
|
41
|
+
type.normalizer = this.normalizer;
|
|
42
|
+
type.allowMissingProperty = this.allowMissingProperty;
|
|
43
|
+
return type;
|
|
44
|
+
}
|
|
45
|
+
default(defaultValue) {
|
|
46
|
+
const type = new Type();
|
|
47
|
+
type.matcher = (value) => {
|
|
48
|
+
return value === undefined || this.matcher(value);
|
|
49
|
+
};
|
|
50
|
+
type.validator = (value, propertyName, options) => {
|
|
51
|
+
if (value === undefined) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return this.validator(value, propertyName, options);
|
|
55
|
+
};
|
|
56
|
+
type.normalizer = (value, propertyName, options) => {
|
|
57
|
+
if (value === undefined) {
|
|
58
|
+
const tmpValue = typeof defaultValue === "function"
|
|
59
|
+
? defaultValue()
|
|
60
|
+
: defaultValue;
|
|
61
|
+
return this.normalizer(tmpValue, propertyName, options);
|
|
62
|
+
}
|
|
63
|
+
return this.normalizer(value, propertyName, options);
|
|
64
|
+
};
|
|
65
|
+
type.allowMissingProperty = true;
|
|
66
|
+
return type;
|
|
67
|
+
}
|
|
68
|
+
optional() {
|
|
69
|
+
const type = new Type();
|
|
70
|
+
type.matcher = (value) => {
|
|
71
|
+
return value === null || value === undefined || this.matcher(value);
|
|
72
|
+
};
|
|
73
|
+
type.validator = (value, propertyName, options) => {
|
|
74
|
+
if (value === null || value === undefined) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return this.validator(value, propertyName, options);
|
|
78
|
+
};
|
|
79
|
+
type.normalizer = (value, propertyName, options) => {
|
|
80
|
+
if (value === null || value === undefined) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return this.normalizer(value, propertyName, options);
|
|
84
|
+
};
|
|
85
|
+
type.allowMissingProperty = true;
|
|
86
|
+
return type;
|
|
87
|
+
}
|
|
88
|
+
static match(matcher) {
|
|
89
|
+
const type = new Type();
|
|
90
|
+
type.matcher = matcher;
|
|
91
|
+
return type;
|
|
92
|
+
}
|
|
93
|
+
static enum(values) {
|
|
94
|
+
const type = new Type();
|
|
95
|
+
type.matcher = (value) => {
|
|
96
|
+
return values.includes(value);
|
|
97
|
+
};
|
|
98
|
+
return type;
|
|
99
|
+
}
|
|
100
|
+
static array(type) {
|
|
101
|
+
const arrayType = new Type();
|
|
102
|
+
arrayType.matcher = (value) => {
|
|
103
|
+
return Array.isArray(value);
|
|
104
|
+
};
|
|
105
|
+
arrayType.validator = (value, propertyName, options) => {
|
|
106
|
+
value.forEach((tmpValue, i) => {
|
|
107
|
+
const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
|
|
108
|
+
type.validate(tmpValue, tmpPropertyName, options);
|
|
109
|
+
});
|
|
110
|
+
return true;
|
|
111
|
+
};
|
|
112
|
+
arrayType.normalizer = (value, propertyName, options) => {
|
|
113
|
+
return value.map((tmpValue, i) => {
|
|
114
|
+
const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
|
|
115
|
+
return type.normalize(tmpValue, tmpPropertyName, options);
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
return arrayType;
|
|
119
|
+
}
|
|
120
|
+
static schema(schema) {
|
|
121
|
+
const type = new Type();
|
|
122
|
+
type.matcher = (value) => {
|
|
123
|
+
return (value !== null && !Array.isArray(value) && typeof value === "object");
|
|
124
|
+
};
|
|
125
|
+
type.validator = (value, propertyName, options) => {
|
|
126
|
+
if (!options.allowUnknownProperties) {
|
|
127
|
+
for (const key in value) {
|
|
128
|
+
const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
|
|
129
|
+
if (!(key in schema)) {
|
|
130
|
+
throw new Error(this.getUnknownPropertyError(tmpPropertyName));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
for (const key in schema) {
|
|
135
|
+
const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
|
|
136
|
+
const tmpType = schema[key];
|
|
137
|
+
const tmpValue = value[key];
|
|
138
|
+
if (!(key in value) && !tmpType.allowMissingProperty) {
|
|
139
|
+
throw new Error(this.getMissingPropertyError(tmpPropertyName));
|
|
140
|
+
}
|
|
141
|
+
tmpType.validate(tmpValue, tmpPropertyName, options);
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
};
|
|
145
|
+
type.normalizer = (value, propertyName, options) => {
|
|
146
|
+
const objectValue = {};
|
|
147
|
+
if (!options.allowUnknownProperties) {
|
|
148
|
+
for (const key in value) {
|
|
149
|
+
const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
|
|
150
|
+
if (!(key in schema)) {
|
|
151
|
+
throw new Error(this.getUnknownPropertyError(tmpPropertyName));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const key in schema) {
|
|
156
|
+
const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
|
|
157
|
+
const tmpType = schema[key];
|
|
158
|
+
const tmpValue = value[key];
|
|
159
|
+
if (!(key in value) && !tmpType.allowMissingProperty) {
|
|
160
|
+
throw new Error(this.getMissingPropertyError(tmpPropertyName));
|
|
161
|
+
}
|
|
162
|
+
objectValue[key] = tmpType.normalize(tmpValue, tmpPropertyName, options);
|
|
163
|
+
}
|
|
164
|
+
return objectValue;
|
|
165
|
+
};
|
|
166
|
+
return type;
|
|
167
|
+
}
|
|
168
|
+
static lazy(callback) {
|
|
169
|
+
const type = new Type();
|
|
170
|
+
type.matcher = (value) => {
|
|
171
|
+
const tmpType = callback();
|
|
172
|
+
return tmpType.matcher(value);
|
|
173
|
+
};
|
|
174
|
+
type.validator = (value, propertyName, options) => {
|
|
175
|
+
const tmpType = callback();
|
|
176
|
+
return tmpType.validator(value, propertyName, options);
|
|
177
|
+
};
|
|
178
|
+
type.normalizer = (value, propertyName, options) => {
|
|
179
|
+
const tmpType = callback();
|
|
180
|
+
return tmpType.normalizer(value, propertyName, options);
|
|
181
|
+
};
|
|
182
|
+
return type;
|
|
183
|
+
}
|
|
184
|
+
static validate(type, value, options = {}) {
|
|
185
|
+
const tmpOptions = {
|
|
186
|
+
allowUnknownProperties: options.allowUnknownProperties ?? false,
|
|
187
|
+
};
|
|
188
|
+
type.validate(value, null, tmpOptions);
|
|
189
|
+
}
|
|
190
|
+
static normalize(type, value, options = {}) {
|
|
191
|
+
const tmpOptions = {
|
|
192
|
+
allowUnknownProperties: options.allowUnknownProperties ?? false,
|
|
193
|
+
};
|
|
194
|
+
return type.normalize(value, null, tmpOptions);
|
|
195
|
+
}
|
|
196
|
+
static getValueError(propertyName) {
|
|
197
|
+
return ("Invalid value" +
|
|
198
|
+
(propertyName !== null ? ` for "${propertyName}".` : "."));
|
|
199
|
+
}
|
|
200
|
+
static getUnknownPropertyError(propertyName) {
|
|
201
|
+
return `Unknown property "${propertyName}".`;
|
|
202
|
+
}
|
|
203
|
+
static getMissingPropertyError(propertyName) {
|
|
204
|
+
return `Missing property "${propertyName}".`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.Type = Type;
|
|
208
|
+
Type.Null = Type.match((value) => value === null);
|
|
209
|
+
Type.String = Type.match((value) => typeof value === "string");
|
|
210
|
+
Type.Boolean = Type.match((value) => typeof value === "boolean");
|
|
211
|
+
Type.Number = Type.match((value) => typeof value === "number");
|
|
212
|
+
Type.Integer = Type.match((value) => typeof value === "number" && Number.isInteger(value));
|
|
213
|
+
Type.Float = Type.match((value) => typeof value === "number" && !Number.isInteger(value));
|
|
214
|
+
Type.Date = Type.match((value) => value instanceof Date && !isNaN(value.getTime()));
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Type";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Type"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "typebad",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "Efficient type validator for complex structures.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"clean": "rm -rf dist",
|
|
8
|
+
"build": "npm run clean && tsc",
|
|
9
|
+
"prepare": "npm run build"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"type",
|
|
13
|
+
"validation",
|
|
14
|
+
"json"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.9.3"
|
|
19
|
+
}
|
|
20
|
+
}
|