/* eslint-disable id-blacklist */ import { parseForm, parseFormData, parseQuery } from 'helpers' import SchemaAny from 'items/any' import SchemaDate from 'items/date' import SchemaRecord from 'items/record' 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) => { // make sure the target is of SchemaItem if (!(target instanceof SchemaItem)) { throw new Error('the decorator is only usable on Schema') } // make sur the property exists in the target if (!(propertyKey in target)) { throw new Error('property not set in object') } // @ts-expect-error call a function defined from calls of collable const original = target[propertyKey] as (...args: Array) => unknown // replace original function with modified one descriptor.value = function(this: SchemaItem, ...args: Array) { this.savedCalls.push({ name: propertyKey as string, args: args as Array }) const res: unknown = original.call(this, ...args) return res } // return the modified descriptor return descriptor } } type SchemaItemStatic = new (...args: Array) => SchemaItem export const Types = { Any: SchemaAny, Array: SchemaArray, Boolean: SchemaBoolean, Date: SchemaDate, Enum: SchemaEnum, Literal: SchemaLiteral, Nullable: SchemaNullable, Object: SchemaObject, Record: SchemaRecord, String: SchemaString, Union: SchemaUnion } as const export default class Schema = Record> extends SchemaObject { private static registeredModules: Array = [ SchemaArray, SchemaBoolean, SchemaDate, SchemaEnum, SchemaLiteral, SchemaNullable, SchemaObject, SchemaRecord, 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 any() { return new SchemaAny() } public static array( ...inputs: ConstructorParameters> ): SchemaArray { return new SchemaArray(...inputs) } public static boolean( ...inputs: ConstructorParameters ): SchemaBoolean { return new SchemaBoolean(...inputs) } public static date( ...inputs: ConstructorParameters ): SchemaDate { return new SchemaDate(...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) } public static record( ...inputs: ConstructorParameters> ): SchemaRecord { return new SchemaRecord(...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 // @ts-expect-error call a function defined from calls of collable (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 } /** * @deprecated use helper `parseQuery` */ public validateQuery(query: URLSearchParams, fast = false) { return parseQuery(this, query, { fast }) } /** * @deprecated use `parse` */ public validate(input: unknown, fast = false) { return this.parse(input, { fast }) } /** * @deprecated use helper `parseForm` */ public validateForm(form: HTMLFormElement, fast = false) { return parseForm(this, form, { fast }) } /** * @deprecated use helper `parseFormData` */ public validateFormData(data: FormData, fast = false) { return parseFormData(this, data, { fast }) } } export const s = Schema export * from './helpers' export type * from './types.d.ts' export { SchemaAny, SchemaArray, SchemaBoolean, SchemaDate, SchemaEnum, SchemaItem, SchemaLiteral, SchemaNullable, SchemaNumber, SchemaObject, SchemaRecord, SchemaString, SchemaUnion }