valia
Version:
A runtime data validator in TypeScript with advanced type inference, built-in validation functions, and seamless integration for server and client environments.
81 lines (71 loc) • 2.24 kB
text/typescript
import type { FormatTemplate, MountedCriteria } from "../types";
import type { StructSetableCriteria, StructCriteria } from "./types";
import { isPlainObject } from "../../../testers";
export interface CustomProperties {
hasRequiredKeys(
mountedCriteria: MountedCriteria<StructSetableCriteria>,
value: (string | symbol)[]
): boolean;
hasAcceptedKeys(
mountedCriteria: MountedCriteria<StructSetableCriteria>,
value: (string | symbol)[]
): boolean;
}
function isSubStruct(obj: object): obj is StructCriteria {
return (isPlainObject(obj) && typeof obj?.type !== "string");
}
export const StructFormat: FormatTemplate<StructSetableCriteria, CustomProperties> = {
defaultCriteria: {},
mounting(queue, path, criteria) {
const acceptedKeys = Reflect.ownKeys(criteria.struct);
const requiredKeys = acceptedKeys.filter(key => !criteria?.optional?.includes(key));
Object.assign(criteria, { acceptedKeys, requiredKeys });
for (let i = 0; i < acceptedKeys.length; i++) {
const key = acceptedKeys[i];
if (isSubStruct(criteria.struct[key])) {
criteria.struct[key] = {
type: "struct",
struct: criteria.struct[key]
}
}
queue.push({
prevNode: criteria,
prevPath: path,
currNode: criteria.struct[key],
partPath: {
explicit: ["struct", key],
implicit: ["&", key]
}
});
}
},
hasRequiredKeys(criteria, keys) {
const requiredKeys = criteria.requiredKeys;
return (requiredKeys.length <= keys.length && requiredKeys.every((key) => keys.includes(key)));
},
hasAcceptedKeys(criteria, keys) {
const acceptedKeys = criteria.acceptedKeys;
return (keys.length <= acceptedKeys.length && keys.every((key) => acceptedKeys.includes(key)));
},
checking(queue, path, criteria, value) {
if (!isPlainObject(value)) {
return ("TYPE_NOT_PLAIN_OBJECT");
}
const keys = Reflect.ownKeys(value);
if (!this.hasAcceptedKeys(criteria, keys)) {
return ("VALUE_INVALID_KEY");
}
else if (!this.hasRequiredKeys(criteria, keys)) {
return ("VALUE_MISSING_KEY");
}
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
queue.push({
prevPath: path,
currNode: criteria.struct[key],
value: value[key],
});
}
return (null);
}
}