UNPKG

@palmares/schemas

Version:

This defines a default schema definition for validation of data, it abstract popular schema validation libraries like zod, yup, valibot and others"

166 lines (164 loc) 8.06 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/validators/utils.ts var utils_exports = {}; __export(utils_exports, { Validator: () => Validator }); module.exports = __toCommonJS(utils_exports); var priorityByType = { low: 0, medium: 1, high: 2 }; var typeByPriority = Object.entries(priorityByType).reduce((acc, [key, value]) => { acc[value] = key; return acc; }, {}); var Validator = class _Validator { static { __name(this, "Validator"); } $$type = "$PValidator"; child; parent; fallbackNamesAdded = /* @__PURE__ */ new Set(); priority; fallbacks = []; constructor(type) { this.fallbackNamesAdded = /* @__PURE__ */ new Set(); this.fallbacks = []; this.priority = priorityByType[type]; } /** * We create all of the validators on the schema in order, i actually didn't want to go on that route but i * found it easier to do so. * * The logic here is simple, if it's not the same priority we will walk on the linked list until we find * a validator that matches the priority we are expecting. If we can't walk anymore, we create the next * priority validator and append it to the linked list. Be aware that it's a double linked list, so we * can walk both ways, from the end to the start and from the start to the end. * So you don't really need to start from the root, the linked list can start from anywhere and it will * find it's way through. * * I know there are better ways to do this instead of walking through the linked list, but like i explained * before, this is enough for now. * * If the priority is higher than the current priority saved on the schema, we should substitute the * rootValidator on the schema with the new one. * * @param schema - The schema that we are working on right now, all fallbacks are tied to that specific schema. * @param type - The type of the fallback that we are adding. * @param fallback - The fallback function that we are adding. * @param childOrParent - If we are adding a fallback to the child or to the parent. * @param options - The options that we are passing to the fallback. */ checkAppendOrCreate(schema, type, fallbackName, fallback, childOrParent, options) { const schemaWithProtected = schema; if (this[childOrParent]) this[childOrParent].addFallback(schemaWithProtected, type, fallbackName, fallback, options); else { const nextPriority = childOrParent === "child" ? this.priority - 1 : this.priority + 1; if (Object.keys(typeByPriority).includes(String(nextPriority))) { const nextType = typeByPriority[nextPriority]; const validatorInstance = new _Validator(nextType); this[childOrParent] = validatorInstance; this[childOrParent][childOrParent === "parent" ? "child" : "parent"] = this; this[childOrParent].addFallback(schemaWithProtected, type, fallbackName, fallback, options); if (nextPriority > schemaWithProtected.__rootFallbacksValidator.priority) schemaWithProtected.__rootFallbacksValidator = validatorInstance; } } } addFallback(schema, type, fallbackName, fallback, options) { if (this.fallbackNamesAdded.has(fallbackName) && options?.removeCurrent !== true) return; this.fallbackNamesAdded.add(fallbackName); const priority = priorityByType[type]; if (this.priority === priority) { if (typeof options?.at === "number") this.fallbacks.splice(options.at, options.removeCurrent === true ? 1 : 0, fallback); else this.fallbacks.push(fallback); } else if (priority > this.priority) this.checkAppendOrCreate(schema, type, fallbackName, fallback, "parent", options); else if (priority < this.priority) this.checkAppendOrCreate(schema, type, fallbackName, fallback, "child", options); } /** * Validates the value against all of the fallbacks, the fallbacks are executed in order, from the highest * priority to the lowest priority. A validator can stop the execution of the other validators if it feels * like so. Like on the example of a value being null or undefined. * * @param errorsAsHashedSet - This is a set that contains all of the errors that we already found, this is * used to avoid duplicated errors. * @param path - The path that we are validating right now. * @param parseResult - The result of the parsing, it contains the parsed value and the errors that we found. * @param options - The options that we are passing to the fallback. */ async validate(errorsAsHashedSet, path, parseResult, options) { let doesItShouldPreventChildValidation = false; for (const fallback of this.fallbacks) { const { parsed, errors, preventChildValidation } = await fallback(parseResult.parsed, path, options); parseResult.parsed = parsed; for (const error of errors) { if (error.isValid === false) { const sortedError = Object.fromEntries(Object.entries(error).sort(([a], [b]) => a.localeCompare(b))); const hashedError = JSON.stringify(sortedError); if (errorsAsHashedSet.has(hashedError)) continue; errorsAsHashedSet.add(hashedError); if (!Array.isArray(parseResult.errors)) parseResult.errors = []; parseResult.errors.push({ ...error, received: parseResult.parsed }); } } doesItShouldPreventChildValidation = doesItShouldPreventChildValidation || preventChildValidation || false; } if (this.child && doesItShouldPreventChildValidation === false) return await this.child.validate(errorsAsHashedSet, path, parseResult, options); return parseResult; } /** * This static method takes care of everything for you. This means that you should only call this method * for appending new fallbacks, it takes care of creating the root validator and making sure that the * rootValidator on the schema is the highest priority one. * * @param schema - The schema that we are working on right now, all fallbacks are tied to that specific * schema. We automatically define the rootValidator on the schema so you don't need to worry about that. * @param fallback - The fallback that we are adding. This is an object that contains the type of the * fallback and the callback that we are adding. * @param options - The options that we are passing to the fallback. Options like `at` and `removeCurrent` * are passed to the `addFallback` method. */ static createAndAppendFallback(schema, fallback, options) { const schemaWithProtected = schema; let validatorInstance = schemaWithProtected.__rootFallbacksValidator; if (schemaWithProtected.__rootFallbacksValidator === void 0) { validatorInstance = new _Validator(fallback.type); schemaWithProtected.__rootFallbacksValidator = validatorInstance; } validatorInstance.addFallback(schema, fallback.type, fallback.name, fallback.callback, options); return validatorInstance; } toString(ident = 0) { return `Priority: ${this.priority} Fallbacks: ${this.fallbacks.length} ${this.child ? `Children: ${this.child.toString(ident + 2)}` : ""}`; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Validator });