@oscarpalmer/jhunal 0.3.0 → 0.5.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/README.md +2 -0
- package/dist/helpers.js +35 -16
- package/dist/index.js +2 -1
- package/dist/is.js +9 -0
- package/dist/jhunal.full.js +134 -0
- package/dist/model.js +0 -1
- package/dist/schematic.js +19 -9
- package/dist/validation/schema.validation.js +36 -0
- package/dist/validation/value.validation.js +39 -0
- package/package.json +15 -19
- package/src/helpers.ts +33 -5
- package/src/index.ts +2 -2
- package/src/is.ts +22 -0
- package/src/model.ts +121 -63
- package/src/schematic.ts +38 -16
- package/src/validation/schema.validation.ts +63 -0
- package/src/validation/value.validation.ts +69 -0
- package/types/helpers.d.ts +2 -2
- package/types/index.d.ts +2 -2
- package/types/is.d.ts +3 -0
- package/types/model.d.ts +49 -31
- package/types/schematic.d.ts +16 -3
- package/types/validation/schema.validation.d.ts +3 -0
- package/types/validation/value.validation.d.ts +3 -0
- package/dist/helpers.cjs +0 -24
- package/dist/index.cjs +0 -9
- package/dist/model.cjs +0 -2
- package/dist/schematic.cjs +0 -15
- package/dist/validation.cjs +0 -89
- package/dist/validation.js +0 -84
- package/src/validation.ts +0 -109
- package/types/helpers.d.cts +0 -18
- package/types/index.d.cts +0 -349
- package/types/model.d.cts +0 -350
- package/types/schematic.d.cts +0 -349
- package/types/validation.d.cts +0 -28
- package/types/validation.d.ts +0 -3
package/src/model.ts
CHANGED
|
@@ -1,90 +1,113 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
RequiredKeysOf,
|
|
4
|
-
Simplify,
|
|
5
|
-
UnionToTuple,
|
|
6
|
-
} from 'type-fest';
|
|
1
|
+
import type {Simplify} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import type {Schematic} from './schematic';
|
|
7
3
|
|
|
8
|
-
export type
|
|
9
|
-
[Key in keyof Values]: Value extends Values[Key] ? Key : never;
|
|
10
|
-
}[keyof Values];
|
|
11
|
-
|
|
12
|
-
export type GetTypes<Value extends unknown[]> = Value extends [infer Single]
|
|
13
|
-
? Single
|
|
14
|
-
: Value;
|
|
15
|
-
|
|
16
|
-
export type GetType<Value> = GetTypes<UnionToTuple<GetKey<Value>>>;
|
|
4
|
+
export type AutoInferExclude = 'date-like' | 'numerical';
|
|
17
5
|
|
|
18
6
|
export type InferProperty<Value> = Value extends Property
|
|
19
|
-
? Value['type']
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
? InferPropertyType<Value['type']>
|
|
8
|
+
: never;
|
|
9
|
+
|
|
10
|
+
type InferPropertyType<Value> = Value extends (infer Type)[]
|
|
11
|
+
? InferPropertyTypeValue<Type>[]
|
|
12
|
+
: InferPropertyTypeValue<Value>;
|
|
13
|
+
|
|
14
|
+
type InferPropertyTypeValue<Value> = Value extends PropertyType
|
|
15
|
+
? Value extends Schema
|
|
16
|
+
? Inferred<Value>
|
|
17
|
+
: Value extends Schematic<infer Model>
|
|
18
|
+
? Model
|
|
19
|
+
: Value extends ValueKey
|
|
20
|
+
? Values[Value]
|
|
21
|
+
: Value extends (infer NestedElementType)[]
|
|
22
|
+
? InferPropertyTypeValue<NestedElementType>[]
|
|
23
|
+
: Value
|
|
24
24
|
: never;
|
|
25
25
|
|
|
26
|
-
export type InferValue<Value> = Value extends
|
|
26
|
+
export type InferValue<Value> = Value extends ValueKey
|
|
27
27
|
? Values[Value]
|
|
28
|
-
: Value extends
|
|
28
|
+
: Value extends ValueKey[]
|
|
29
29
|
? Values[Value[number]]
|
|
30
30
|
: never;
|
|
31
31
|
|
|
32
|
-
export type Inferred<Model extends Schema> =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
export type Inferred<Model extends Schema> = Simplify<
|
|
33
|
+
{
|
|
34
|
+
[Key in InferredRequiredProperties<Model>]: Model[Key] extends Property
|
|
35
|
+
? InferProperty<Model[Key]>
|
|
36
|
+
: InferValue<Model[Key]>;
|
|
37
|
+
} & {
|
|
38
|
+
[Key in InferredOptionalProperties<Model>]?: Model[Key] extends Property
|
|
39
|
+
? InferProperty<Model[Key]>
|
|
40
|
+
: never;
|
|
41
|
+
}
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
export type InferredOptionalProperties<Model extends Schema> = keyof {
|
|
45
|
+
[Key in keyof Model as IsOptionalInSchema<Model[Key]> extends true ? Key : never]: never;
|
|
40
46
|
};
|
|
41
47
|
|
|
42
|
-
export type
|
|
43
|
-
[Key in keyof Model
|
|
44
|
-
|
|
45
|
-
? Key
|
|
46
|
-
: never
|
|
47
|
-
: never;
|
|
48
|
-
}[keyof Model];
|
|
48
|
+
export type InferredRequiredProperties<Model extends Schema> = keyof {
|
|
49
|
+
[Key in keyof Model as IsOptionalInSchema<Model[Key]> extends true ? never : Key]: never;
|
|
50
|
+
};
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
?
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
type IsNestedObject<Value> = Value extends object
|
|
53
|
+
? Value extends any[] | Date | Function
|
|
54
|
+
? false
|
|
55
|
+
: true
|
|
56
|
+
: false;
|
|
57
|
+
|
|
58
|
+
type IsOptionalInSchema<Prop> = Prop extends Property
|
|
59
|
+
? Prop['required'] extends false
|
|
60
|
+
? true
|
|
61
|
+
: false
|
|
62
|
+
: false;
|
|
63
|
+
|
|
64
|
+
export type OptionalKeys<Value> = {
|
|
65
|
+
[Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never;
|
|
66
|
+
}[keyof Value];
|
|
57
67
|
|
|
58
68
|
export type OptionalProperty<Type> = {
|
|
59
69
|
required: false;
|
|
60
|
-
type:
|
|
70
|
+
type: PropertyTypeFor<Type>;
|
|
61
71
|
};
|
|
62
72
|
|
|
63
73
|
export type Property = {
|
|
64
74
|
required?: boolean;
|
|
65
|
-
type:
|
|
75
|
+
type: PropertyType | PropertyType[];
|
|
66
76
|
};
|
|
67
77
|
|
|
78
|
+
type PropertyType = Schema | Schematic<unknown> | ValueKey;
|
|
79
|
+
|
|
80
|
+
type PropertyTypeFor<Value> =
|
|
81
|
+
IsNestedObject<Value> extends true
|
|
82
|
+
? Value extends Typed
|
|
83
|
+
? TypedSchema<Value>
|
|
84
|
+
: never
|
|
85
|
+
: ValueToType<Value>;
|
|
86
|
+
|
|
87
|
+
type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
|
|
88
|
+
|
|
68
89
|
export type RequiredProperty<Type> = {
|
|
69
90
|
required: true;
|
|
70
|
-
type:
|
|
91
|
+
type: PropertyTypeFor<Type>;
|
|
71
92
|
};
|
|
72
93
|
|
|
73
94
|
/**
|
|
74
95
|
* A schema for validating objects
|
|
75
96
|
*/
|
|
76
|
-
export type Schema =
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* A schematic for validating objects
|
|
80
|
-
*/
|
|
81
|
-
export type Schematic<Model> = {
|
|
82
|
-
/**
|
|
83
|
-
* Does the value match the schema?
|
|
84
|
-
*/
|
|
85
|
-
is(value: unknown): value is Model;
|
|
97
|
+
export type Schema = {
|
|
98
|
+
[key: string]: Property | ValueKey | ValueKey[];
|
|
86
99
|
};
|
|
87
100
|
|
|
101
|
+
export type TypeToValueKey<Value> = {
|
|
102
|
+
[Key in Exclude<ValueKey, AutoInferExclude>]: Value extends Values[Key] ? Key : never;
|
|
103
|
+
}[Exclude<ValueKey, AutoInferExclude>] extends infer SpecificKey
|
|
104
|
+
? SpecificKey extends never
|
|
105
|
+
? {
|
|
106
|
+
[Key in AutoInferExclude]: Value extends Values[Key] ? Key : never;
|
|
107
|
+
}[AutoInferExclude]
|
|
108
|
+
: SpecificKey
|
|
109
|
+
: never;
|
|
110
|
+
|
|
88
111
|
export type Typed = Record<string, unknown>;
|
|
89
112
|
|
|
90
113
|
/**
|
|
@@ -92,34 +115,69 @@ export type Typed = Record<string, unknown>;
|
|
|
92
115
|
*/
|
|
93
116
|
export type TypedSchema<Model extends Typed> = Simplify<
|
|
94
117
|
{
|
|
95
|
-
[Key in
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
[Key in RequiredKeys<Model>]: IsNestedObject<Model[Key]> extends true
|
|
119
|
+
? Model[Key] extends Typed
|
|
120
|
+
? TypedSchema<Model[Key]>
|
|
121
|
+
: never
|
|
122
|
+
: ValueToType<Model[Key]> | RequiredProperty<Model[Key]>;
|
|
98
123
|
} & {
|
|
99
|
-
[Key in
|
|
124
|
+
[Key in OptionalKeys<Model>]: IsNestedObject<Model[Key]> extends true
|
|
125
|
+
? Exclude<Model[Key], undefined> extends Typed
|
|
126
|
+
? TypedSchema<Exclude<Model[Key], undefined>> | OptionalProperty<Model[Key]>
|
|
127
|
+
: never
|
|
128
|
+
: OptionalProperty<Model[Key]>;
|
|
100
129
|
}
|
|
101
130
|
>;
|
|
102
131
|
|
|
132
|
+
type UnionToIntersection<Value> = (Value extends any ? (value: Value) => void : never) extends (
|
|
133
|
+
item: infer Item,
|
|
134
|
+
) => void
|
|
135
|
+
? Item
|
|
136
|
+
: never;
|
|
137
|
+
|
|
138
|
+
type LastOfUnion<Value> =
|
|
139
|
+
UnionToIntersection<Value extends any ? () => Value : never> extends () => infer Item
|
|
140
|
+
? Item
|
|
141
|
+
: never;
|
|
142
|
+
|
|
143
|
+
type UnionToTuple<Value, Values extends any[] = []> = [Value] extends [never]
|
|
144
|
+
? Values
|
|
145
|
+
: UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Values]>;
|
|
146
|
+
|
|
103
147
|
export type ValidatedProperty = {
|
|
104
148
|
required: boolean;
|
|
105
|
-
types:
|
|
149
|
+
types: ValidatedPropertyType[];
|
|
106
150
|
};
|
|
107
151
|
|
|
152
|
+
export type ValidatedPropertyType = Schematic<unknown> | ValidatedSchema | ValueKey;
|
|
153
|
+
|
|
108
154
|
export type ValidatedSchema = {
|
|
109
155
|
keys: string[];
|
|
110
156
|
length: number;
|
|
111
|
-
properties:
|
|
157
|
+
properties: ValidatedSchemaProperties;
|
|
112
158
|
};
|
|
113
159
|
|
|
160
|
+
type ValidatedSchemaProperties = {
|
|
161
|
+
[key: string]: ValidatedProperty;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export type ValueKey = keyof Values;
|
|
165
|
+
|
|
166
|
+
export type ValueToType<Value> = ValueToTypes<UnionToTuple<TypeToValueKey<Value>>>;
|
|
167
|
+
|
|
168
|
+
export type ValueToTypes<Value extends unknown[]> = Value extends [infer Type] ? Type : Value;
|
|
169
|
+
|
|
114
170
|
export type Values = {
|
|
115
171
|
array: unknown[];
|
|
116
172
|
bigint: bigint;
|
|
117
173
|
boolean: boolean;
|
|
118
174
|
date: Date;
|
|
175
|
+
'date-like': number | string | Date;
|
|
119
176
|
// biome-ignore lint/complexity/noBannedTypes: it's the most basic value type, so I think it's ok
|
|
120
177
|
function: Function;
|
|
121
178
|
null: null;
|
|
122
179
|
number: number;
|
|
180
|
+
numerical: bigint | number;
|
|
123
181
|
object: object;
|
|
124
182
|
string: string;
|
|
125
183
|
symbol: symbol;
|
package/src/schematic.ts
CHANGED
|
@@ -1,26 +1,48 @@
|
|
|
1
|
-
import type {Inferred, Schema,
|
|
2
|
-
import {
|
|
1
|
+
import type {Inferred, Schema, Typed, TypedSchema, ValidatedSchema} from './model';
|
|
2
|
+
import {validateSchema} from './validation/schema.validation';
|
|
3
|
+
import {validateValue} from './validation/value.validation';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* A schematic for validating objects
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export class Schematic<Model> {
|
|
9
|
+
declare private readonly $schematic: true;
|
|
10
|
+
|
|
11
|
+
#schema: ValidatedSchema;
|
|
12
|
+
#validatable: boolean;
|
|
13
|
+
|
|
14
|
+
get validatable(): boolean {
|
|
15
|
+
return this.#validatable;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(schema: Model) {
|
|
19
|
+
Object.defineProperty(this, '$schematic', {
|
|
20
|
+
value: true,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
this.#schema = validateSchema(schema as unknown as Schema);
|
|
24
|
+
|
|
25
|
+
this.#validatable = this.#schema.length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Does the value match the schema?
|
|
30
|
+
*/
|
|
31
|
+
is(value: unknown): value is Model {
|
|
32
|
+
return this.#validatable && validateValue(this.#schema, value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
10
35
|
|
|
11
36
|
/**
|
|
12
37
|
* Create a schematic from a schema
|
|
13
38
|
*/
|
|
14
|
-
export function schematic<Model extends Schema>(
|
|
15
|
-
schema: Model,
|
|
16
|
-
): Schematic<Inferred<Model>>;
|
|
17
|
-
|
|
18
|
-
export function schematic<Model extends Schema>(schema: Model) {
|
|
19
|
-
const validated = getValidatedSchema(schema);
|
|
39
|
+
export function schematic<Model extends Schema>(schema: Model): Schematic<Inferred<Model>>;
|
|
20
40
|
|
|
21
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Create a schematic from a typed schema
|
|
43
|
+
*/
|
|
44
|
+
export function schematic<Model extends Typed>(schema: TypedSchema<Model>): Schematic<Model>;
|
|
22
45
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
46
|
+
export function schematic<Model extends Schema>(schema: Model): Schematic<Model> {
|
|
47
|
+
return new Schematic<Model>(schema);
|
|
26
48
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {getTypes} from '../helpers';
|
|
3
|
+
import type {Schema, ValidatedPropertyType, ValidatedSchema} from '../model';
|
|
4
|
+
|
|
5
|
+
export function validateSchema(schema: unknown): ValidatedSchema {
|
|
6
|
+
if (validatedSchemas.has(schema as never)) {
|
|
7
|
+
return validatedSchemas.get(schema as never) as ValidatedSchema;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const validated: ValidatedSchema = {
|
|
11
|
+
keys: [],
|
|
12
|
+
length: 0,
|
|
13
|
+
properties: {},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
17
|
+
return validated;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const keys = Object.keys(schema);
|
|
21
|
+
const {length} = keys;
|
|
22
|
+
|
|
23
|
+
for (let index = 0; index < length; index += 1) {
|
|
24
|
+
const key = keys[index];
|
|
25
|
+
const value = (schema as Schema)[key];
|
|
26
|
+
|
|
27
|
+
let required = true;
|
|
28
|
+
let types: ValidatedPropertyType[];
|
|
29
|
+
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
types = getTypes(value);
|
|
32
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
33
|
+
if (typeof (value as PlainObject).required === 'boolean') {
|
|
34
|
+
required = (value as PlainObject).required as boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
types = getTypes((value as PlainObject).type);
|
|
38
|
+
} else {
|
|
39
|
+
types = getTypes(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (types.length > 0) {
|
|
43
|
+
if (!required && !types.includes('undefined')) {
|
|
44
|
+
types.push('undefined');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
validated.keys.push(key);
|
|
48
|
+
|
|
49
|
+
validated.properties[key] = {
|
|
50
|
+
required,
|
|
51
|
+
types,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
validated.length += 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
validatedSchemas.set(schema as never, validated);
|
|
59
|
+
|
|
60
|
+
return validated;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const validatedSchemas = new WeakMap<Schema, ValidatedSchema>();
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {isDateLike, isSchematic} from '../is';
|
|
3
|
+
import type {ValidatedPropertyType, ValidatedSchema, Values} from '../model';
|
|
4
|
+
|
|
5
|
+
export function validateType(type: ValidatedPropertyType, value: unknown): boolean {
|
|
6
|
+
if (typeof type === 'string') {
|
|
7
|
+
return validators[type](value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (isSchematic(type)) {
|
|
11
|
+
return type.is(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return validateValue(type as ValidatedSchema, value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function validateValue(validated: ValidatedSchema, obj: unknown): boolean {
|
|
18
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
outer: for (let keyIndex = 0; keyIndex < validated.length; keyIndex += 1) {
|
|
23
|
+
const key = validated.keys[keyIndex];
|
|
24
|
+
const property = validated.properties[key];
|
|
25
|
+
const value = (obj as PlainObject)[key];
|
|
26
|
+
|
|
27
|
+
if (value === undefined && property.required && !property.types.includes('undefined')) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const typesLength = property.types.length;
|
|
32
|
+
|
|
33
|
+
if (typesLength === 1) {
|
|
34
|
+
if (!validateType(property.types[0], value)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
42
|
+
if (validateType(property.types[typeIndex], value)) {
|
|
43
|
+
continue outer;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//
|
|
54
|
+
|
|
55
|
+
const validators: Record<keyof Values, (value: unknown) => boolean> = {
|
|
56
|
+
array: Array.isArray,
|
|
57
|
+
bigint: value => typeof value === 'bigint',
|
|
58
|
+
boolean: value => typeof value === 'boolean',
|
|
59
|
+
date: value => value instanceof Date,
|
|
60
|
+
'date-like': isDateLike,
|
|
61
|
+
function: value => typeof value === 'function',
|
|
62
|
+
null: value => value === null,
|
|
63
|
+
number: value => typeof value === 'number',
|
|
64
|
+
numerical: value => validators.bigint(value) || validators.number(value),
|
|
65
|
+
object: value => typeof value === 'object' && value !== null,
|
|
66
|
+
string: value => typeof value === 'string',
|
|
67
|
+
symbol: value => typeof value === 'symbol',
|
|
68
|
+
undefined: value => value === undefined,
|
|
69
|
+
};
|
package/types/helpers.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function getTypes(value: unknown):
|
|
1
|
+
import type { ValidatedPropertyType } from './model';
|
|
2
|
+
export declare function getTypes(value: unknown): ValidatedPropertyType[];
|
package/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { Schema,
|
|
2
|
-
export
|
|
1
|
+
export type { Schema, TypedSchema } from './model';
|
|
2
|
+
export { schematic, type Schematic } from './schematic';
|
package/types/is.d.ts
ADDED
package/types/model.d.ts
CHANGED
|
@@ -1,75 +1,93 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type
|
|
8
|
-
export type
|
|
9
|
-
export type Inferred<Model extends Schema> = {
|
|
1
|
+
import type { Simplify } from '@oscarpalmer/atoms/models';
|
|
2
|
+
import type { Schematic } from './schematic';
|
|
3
|
+
export type AutoInferExclude = 'date-like' | 'numerical';
|
|
4
|
+
export type InferProperty<Value> = Value extends Property ? InferPropertyType<Value['type']> : never;
|
|
5
|
+
type InferPropertyType<Value> = Value extends (infer Type)[] ? InferPropertyTypeValue<Type>[] : InferPropertyTypeValue<Value>;
|
|
6
|
+
type InferPropertyTypeValue<Value> = Value extends PropertyType ? Value extends Schema ? Inferred<Value> : Value extends Schematic<infer Model> ? Model : Value extends ValueKey ? Values[Value] : Value extends (infer NestedElementType)[] ? InferPropertyTypeValue<NestedElementType>[] : Value : never;
|
|
7
|
+
export type InferValue<Value> = Value extends ValueKey ? Values[Value] : Value extends ValueKey[] ? Values[Value[number]] : never;
|
|
8
|
+
export type Inferred<Model extends Schema> = Simplify<{
|
|
10
9
|
[Key in InferredRequiredProperties<Model>]: Model[Key] extends Property ? InferProperty<Model[Key]> : InferValue<Model[Key]>;
|
|
11
10
|
} & {
|
|
12
11
|
[Key in InferredOptionalProperties<Model>]?: Model[Key] extends Property ? InferProperty<Model[Key]> : never;
|
|
12
|
+
}>;
|
|
13
|
+
export type InferredOptionalProperties<Model extends Schema> = keyof {
|
|
14
|
+
[Key in keyof Model as IsOptionalInSchema<Model[Key]> extends true ? Key : never]: never;
|
|
15
|
+
};
|
|
16
|
+
export type InferredRequiredProperties<Model extends Schema> = keyof {
|
|
17
|
+
[Key in keyof Model as IsOptionalInSchema<Model[Key]> extends true ? never : Key]: never;
|
|
13
18
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}[keyof Model];
|
|
19
|
+
type IsNestedObject<Value> = Value extends object ? Value extends any[] | Date | Function ? false : true : false;
|
|
20
|
+
type IsOptionalInSchema<Prop> = Prop extends Property ? Prop['required'] extends false ? true : false : false;
|
|
21
|
+
export type OptionalKeys<Value> = {
|
|
22
|
+
[Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never;
|
|
23
|
+
}[keyof Value];
|
|
20
24
|
export type OptionalProperty<Type> = {
|
|
21
25
|
required: false;
|
|
22
|
-
type:
|
|
26
|
+
type: PropertyTypeFor<Type>;
|
|
23
27
|
};
|
|
24
28
|
export type Property = {
|
|
25
29
|
required?: boolean;
|
|
26
|
-
type:
|
|
30
|
+
type: PropertyType | PropertyType[];
|
|
27
31
|
};
|
|
32
|
+
type PropertyType = Schema | Schematic<unknown> | ValueKey;
|
|
33
|
+
type PropertyTypeFor<Value> = IsNestedObject<Value> extends true ? Value extends Typed ? TypedSchema<Value> : never : ValueToType<Value>;
|
|
34
|
+
type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
|
|
28
35
|
export type RequiredProperty<Type> = {
|
|
29
36
|
required: true;
|
|
30
|
-
type:
|
|
37
|
+
type: PropertyTypeFor<Type>;
|
|
31
38
|
};
|
|
32
39
|
/**
|
|
33
40
|
* A schema for validating objects
|
|
34
41
|
*/
|
|
35
|
-
export type Schema =
|
|
36
|
-
|
|
37
|
-
* A schematic for validating objects
|
|
38
|
-
*/
|
|
39
|
-
export type Schematic<Model> = {
|
|
40
|
-
/**
|
|
41
|
-
* Does the value match the schema?
|
|
42
|
-
*/
|
|
43
|
-
is(value: unknown): value is Model;
|
|
42
|
+
export type Schema = {
|
|
43
|
+
[key: string]: Property | ValueKey | ValueKey[];
|
|
44
44
|
};
|
|
45
|
+
export type TypeToValueKey<Value> = {
|
|
46
|
+
[Key in Exclude<ValueKey, AutoInferExclude>]: Value extends Values[Key] ? Key : never;
|
|
47
|
+
}[Exclude<ValueKey, AutoInferExclude>] extends infer SpecificKey ? SpecificKey extends never ? {
|
|
48
|
+
[Key in AutoInferExclude]: Value extends Values[Key] ? Key : never;
|
|
49
|
+
}[AutoInferExclude] : SpecificKey : never;
|
|
45
50
|
export type Typed = Record<string, unknown>;
|
|
46
51
|
/**
|
|
47
52
|
* A typed schema for validating objects
|
|
48
53
|
*/
|
|
49
54
|
export type TypedSchema<Model extends Typed> = Simplify<{
|
|
50
|
-
[Key in
|
|
55
|
+
[Key in RequiredKeys<Model>]: IsNestedObject<Model[Key]> extends true ? Model[Key] extends Typed ? TypedSchema<Model[Key]> : never : ValueToType<Model[Key]> | RequiredProperty<Model[Key]>;
|
|
51
56
|
} & {
|
|
52
|
-
[Key in
|
|
57
|
+
[Key in OptionalKeys<Model>]: IsNestedObject<Model[Key]> extends true ? Exclude<Model[Key], undefined> extends Typed ? TypedSchema<Exclude<Model[Key], undefined>> | OptionalProperty<Model[Key]> : never : OptionalProperty<Model[Key]>;
|
|
53
58
|
}>;
|
|
59
|
+
type UnionToIntersection<Value> = (Value extends any ? (value: Value) => void : never) extends (item: infer Item) => void ? Item : never;
|
|
60
|
+
type LastOfUnion<Value> = UnionToIntersection<Value extends any ? () => Value : never> extends () => infer Item ? Item : never;
|
|
61
|
+
type UnionToTuple<Value, Values extends any[] = []> = [Value] extends [never] ? Values : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Values]>;
|
|
54
62
|
export type ValidatedProperty = {
|
|
55
63
|
required: boolean;
|
|
56
|
-
types:
|
|
64
|
+
types: ValidatedPropertyType[];
|
|
57
65
|
};
|
|
66
|
+
export type ValidatedPropertyType = Schematic<unknown> | ValidatedSchema | ValueKey;
|
|
58
67
|
export type ValidatedSchema = {
|
|
59
68
|
keys: string[];
|
|
60
69
|
length: number;
|
|
61
|
-
properties:
|
|
70
|
+
properties: ValidatedSchemaProperties;
|
|
71
|
+
};
|
|
72
|
+
type ValidatedSchemaProperties = {
|
|
73
|
+
[key: string]: ValidatedProperty;
|
|
62
74
|
};
|
|
75
|
+
export type ValueKey = keyof Values;
|
|
76
|
+
export type ValueToType<Value> = ValueToTypes<UnionToTuple<TypeToValueKey<Value>>>;
|
|
77
|
+
export type ValueToTypes<Value extends unknown[]> = Value extends [infer Type] ? Type : Value;
|
|
63
78
|
export type Values = {
|
|
64
79
|
array: unknown[];
|
|
65
80
|
bigint: bigint;
|
|
66
81
|
boolean: boolean;
|
|
67
82
|
date: Date;
|
|
83
|
+
'date-like': number | string | Date;
|
|
68
84
|
function: Function;
|
|
69
85
|
null: null;
|
|
70
86
|
number: number;
|
|
87
|
+
numerical: bigint | number;
|
|
71
88
|
object: object;
|
|
72
89
|
string: string;
|
|
73
90
|
symbol: symbol;
|
|
74
91
|
undefined: undefined;
|
|
75
92
|
};
|
|
93
|
+
export {};
|
package/types/schematic.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
import type { Inferred, Schema,
|
|
1
|
+
import type { Inferred, Schema, Typed, TypedSchema } from './model';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* A schematic for validating objects
|
|
4
4
|
*/
|
|
5
|
-
export declare
|
|
5
|
+
export declare class Schematic<Model> {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly $schematic;
|
|
8
|
+
get validatable(): boolean;
|
|
9
|
+
constructor(schema: Model);
|
|
10
|
+
/**
|
|
11
|
+
* Does the value match the schema?
|
|
12
|
+
*/
|
|
13
|
+
is(value: unknown): value is Model;
|
|
14
|
+
}
|
|
6
15
|
/**
|
|
7
16
|
* Create a schematic from a schema
|
|
8
17
|
*/
|
|
9
18
|
export declare function schematic<Model extends Schema>(schema: Model): Schematic<Inferred<Model>>;
|
|
19
|
+
/**
|
|
20
|
+
* Create a schematic from a typed schema
|
|
21
|
+
*/
|
|
22
|
+
export declare function schematic<Model extends Typed>(schema: TypedSchema<Model>): Schematic<Model>;
|
package/dist/helpers.cjs
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
-
|
|
5
|
-
function getTypes(value) {
|
|
6
|
-
return (Array.isArray(value) ? value : [value]).filter(
|
|
7
|
-
(item) => types.has(item)
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
const types = /* @__PURE__ */ new Set([
|
|
11
|
-
"array",
|
|
12
|
-
"bigint",
|
|
13
|
-
"boolean",
|
|
14
|
-
"date",
|
|
15
|
-
"function",
|
|
16
|
-
"null",
|
|
17
|
-
"number",
|
|
18
|
-
"object",
|
|
19
|
-
"string",
|
|
20
|
-
"symbol",
|
|
21
|
-
"undefined"
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
exports.getTypes = getTypes;
|
package/dist/index.cjs
DELETED
package/dist/model.cjs
DELETED