166 lines
4.1 KiB
TypeScript
166 lines
4.1 KiB
TypeScript
import SchemaArray from "./items/array"
|
|
import SchemaBoolean from "./items/boolean"
|
|
import SchemaEnum, { EnumLike } from "./items/enum"
|
|
import SchemaLiteral from "./items/literal"
|
|
import SchemaNullable from "./items/nullable"
|
|
import SchemaNumber from "./items/number"
|
|
import SchemaObject from "./items/object"
|
|
import SchemaString from "./items/string"
|
|
import { SchemaUnion } from "./items/union"
|
|
import SchemaItem from "./SchemaItem"
|
|
import { SchemaJSON } from "./types"
|
|
|
|
export function parceable() {
|
|
|
|
return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => {
|
|
if (!(target instanceof SchemaItem)) {
|
|
throw new Error('the decorator is only usable on Schema')
|
|
}
|
|
|
|
|
|
const original = target[propertyKey]
|
|
|
|
const t = function () { }
|
|
|
|
descriptor.value = function (this: SchemaItem, ...args: Array<any>) {
|
|
this.savedCalls.push({ name: propertyKey as string, args: args })
|
|
const res = original.call(this, ...args)
|
|
return res
|
|
}
|
|
|
|
return descriptor
|
|
}
|
|
}
|
|
|
|
interface SchemaItemStatic {
|
|
new(...args: Array<any>): SchemaItem
|
|
}
|
|
|
|
type ExtractGeneric<T> = T extends SchemaItem<infer U> ? U : never
|
|
|
|
export default class Schema {
|
|
private static registeredModules: Array<SchemaItemStatic> = [
|
|
SchemaArray,
|
|
SchemaBoolean,
|
|
SchemaEnum,
|
|
SchemaLiteral,
|
|
SchemaNullable,
|
|
SchemaObject,
|
|
SchemaString,
|
|
SchemaUnion
|
|
]
|
|
|
|
public static register(module: SchemaItemStatic) {
|
|
this.registeredModules.push(module)
|
|
}
|
|
|
|
public static getModule(name: string) {
|
|
return this.registeredModules.find((it) => it.name === name)
|
|
}
|
|
|
|
public static array<Type extends SchemaItem>(
|
|
...inputs: ConstructorParameters<typeof SchemaArray<Type>>
|
|
): SchemaArray<Type> {
|
|
return new SchemaArray<Type>(...inputs)
|
|
}
|
|
|
|
public static boolean(
|
|
...inputs: ConstructorParameters<typeof SchemaBoolean>
|
|
): SchemaBoolean {
|
|
return new SchemaBoolean(...inputs)
|
|
}
|
|
|
|
public static enum<Type extends EnumLike>(
|
|
...inputs: ConstructorParameters<typeof SchemaEnum<Type>>
|
|
): SchemaEnum<Type> {
|
|
return new SchemaEnum<Type>(...inputs)
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param input the literal value (note: append `as const` else the typing won't work correctly)
|
|
* @returns
|
|
*/
|
|
public static literal<Type>(
|
|
input: Type
|
|
): SchemaLiteral<Type> {
|
|
return new SchemaLiteral<Type>(input)
|
|
}
|
|
|
|
public static nullable<Type extends SchemaItem>(
|
|
...inputs: ConstructorParameters<typeof SchemaNullable<Type>>
|
|
): SchemaNullable<Type> {
|
|
return new SchemaNullable<Type>(...inputs)
|
|
}
|
|
|
|
public static number(
|
|
...inputs: ConstructorParameters<typeof SchemaNumber>
|
|
): SchemaNumber {
|
|
return new SchemaNumber(...inputs)
|
|
}
|
|
|
|
public static object<Type extends Record<string, SchemaItem<any>>>(
|
|
...inputs: ConstructorParameters<typeof SchemaObject<Type>>
|
|
): SchemaObject<Type> {
|
|
return new SchemaObject<Type>(...inputs)
|
|
}
|
|
|
|
/**
|
|
* See {@link SchemaString}
|
|
*/
|
|
public static string(
|
|
...inputs: ConstructorParameters<typeof SchemaString>
|
|
): SchemaString {
|
|
return new SchemaString(...inputs)
|
|
}
|
|
|
|
public static union<Type extends Array<SchemaItem<any>>>(
|
|
...inputs: ConstructorParameters<typeof SchemaUnion<Type>>
|
|
): SchemaUnion<Type> {
|
|
return new SchemaUnion<Type>(...inputs)
|
|
}
|
|
|
|
public static fromJSON(json: SchemaJSON): SchemaItem {
|
|
|
|
// get the module
|
|
const fn = this.getModule(json.i)
|
|
|
|
// handle module not detected
|
|
if (!fn) {
|
|
throw new Error(`Schema cannot parse ${json.i}`)
|
|
}
|
|
|
|
// init the module
|
|
const item = new fn(...(json.c?.map((it) => this.isSchemaJSON(it) ? Schema.fromJSON(it) : it) ?? []))
|
|
|
|
// handle validations
|
|
for (const validation of (json.f ?? [])) {
|
|
// validation not found in item :(
|
|
if (!(validation.n in item)) {
|
|
throw new Error('validation not available in Schema Item')
|
|
}
|
|
|
|
// init the validation
|
|
(item[validation.n] as (...params: Array<unknown>) => void)(...validation.a.map((it) => Schema.isSchemaJSON(it) ? Schema.fromJSON(it) : it))
|
|
}
|
|
|
|
// add the attributes
|
|
item.attrs(...json.a ?? [])
|
|
|
|
|
|
return item
|
|
}
|
|
|
|
public static isSchemaJSON(data: unknown): data is SchemaJSON {
|
|
if (typeof data !== 'object' || data === null) {
|
|
return false
|
|
}
|
|
|
|
if (!('i' in data)) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|