@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
JavaScript
;
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
});