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) => { 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) { this.savedCalls.push({ name: propertyKey as string, args: args }) const res = original.call(this, ...args) return res } return descriptor } } interface SchemaItemStatic { new(...args: Array): SchemaItem } type ExtractGeneric = T extends SchemaItem ? U : never export default class Schema { private static registeredModules: Array = [ 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( ...inputs: ConstructorParameters> ): SchemaArray { return new SchemaArray(...inputs) } public static boolean( ...inputs: ConstructorParameters ): SchemaBoolean { return new SchemaBoolean(...inputs) } public static enum( ...inputs: ConstructorParameters> ): SchemaEnum { return new SchemaEnum(...inputs) } /** * * @param input the literal value (note: append `as const` else the typing won't work correctly) * @returns */ public static literal( input: Type ): SchemaLiteral { return new SchemaLiteral(input) } public static nullable( ...inputs: ConstructorParameters> ): SchemaNullable { return new SchemaNullable(...inputs) } public static number( ...inputs: ConstructorParameters ): SchemaNumber { return new SchemaNumber(...inputs) } public static object>>( ...inputs: ConstructorParameters> ): SchemaObject { return new SchemaObject(...inputs) } /** * See {@link SchemaString} */ public static string( ...inputs: ConstructorParameters ): SchemaString { return new SchemaString(...inputs) } public static union>>( ...inputs: ConstructorParameters> ): SchemaUnion { return new SchemaUnion(...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) => 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 } }