schema/src/helpers.ts

65 lines
2.4 KiB
TypeScript

import { objectGet, objectLoop, objectSet } from '@dzeio/object-util'
import SchemaArray from 'items/array'
import SchemaBoolean from 'items/boolean'
import SchemaNullable from 'items/nullable'
import SchemaObject from 'items/object'
import type SchemaItem from 'SchemaItem'
export function isNull(value: unknown): value is undefined | null {
return typeof value === 'undefined' || value === null
}
export function parseQuery<T extends SchemaItem>(model: T, query: URLSearchParams, opts?: Parameters<T['parse']>[1]): ReturnType<T['parse']> {
const record: Record<string, unknown> = {}
for (const [key, value] of query) {
record[key] = value
}
return model.parse(record, opts) as ReturnType<T['parse']>
}
export function parseFormData<T extends SchemaItem>(model: T, data: FormData, opts?: Parameters<T['parse']>[1]): ReturnType<T['parse']> {
const record: Record<string, unknown> = {}
// console.log('VALIDATE FORM DATA data', data)
for (const [key, value] of data) {
const hasMultipleValues = data.getAll(key).length > 1
objectSet(record, key.split('.').map((it) => /^\d+$/g.test(it) ? parseInt(it, 10) : it), hasMultipleValues ? data.getAll(key) : value)
}
// quick hack to handle FormData not returning Checkboxes
const handleBoolean = (value: SchemaItem, keys: Array<string | number>) => {
if (value instanceof SchemaNullable) {
handleBoolean(value.unwrap(), keys)
}
if (value instanceof SchemaArray) {
const elements: Array<unknown> | undefined = objectGet(record, keys)
for (let it = 0; it < (elements?.length ?? 0); it++) {
handleBoolean(value.unwrap(), [...keys, it])
}
}
if (value instanceof SchemaObject) {
handleSchemaForBoolean(value.model as Record<string, SchemaItem>, keys)
}
if (value instanceof SchemaBoolean) {
objectSet(record, keys, !!data.get(keys.join('.')))
}
}
const handleSchemaForBoolean = (modl: Record<string, SchemaItem>, keys: Array<string | number> = []) => {
objectLoop(modl, (value, key) => {
handleBoolean(value as unknown as SchemaItem, [...keys, key])
})
}
if (model instanceof SchemaObject) {
handleSchemaForBoolean(model.model)
}
return model.parse(record, opts) as ReturnType<T['parse']>
}
export function parseForm<T extends SchemaItem>(model: T, form: HTMLFormElement, opts?: Parameters<T['parse']>[1]): ReturnType<T['parse']> {
return parseFormData(model, new FormData(form), opts)
}