UNPKG

bguard

Version:

**bguard** is a powerful, flexible, and type-safe validation library for TypeScript. It allows developers to define validation schemas for their data structures and ensures that data conforms to the expected types and constraints.

302 lines (300 loc) 10.1 kB
import { isValidDateInner } from "./chunk-YBVMP4EI.mjs"; import { getTranslationByLocale } from "./chunk-OE3L7EKN.mjs"; import { BuildSchemaError, ValidationError } from "./chunk-ZTCXXAKD.mjs"; import { ctxSymbol } from "./chunk-2ANPCB4O.mjs"; // src/core.ts var replacePlaceholdersRegex = /{{(.*?)}}/g; function replacePlaceholders(template, replacements) { return template.replace(replacePlaceholdersRegex, (_, key) => { return key in replacements ? `${replacements[key]}` : `{{${key}}}`; }); } var ExceptionContext = class _ExceptionContext { constructor(initialReceived, t, pathToError, errors, meta) { this.initialReceived = initialReceived; this.t = t; this.pathToError = pathToError; this.errors = errors; this.meta = meta; } createChild(childPathToError, childMeta) { return new _ExceptionContext(this.initialReceived, this.t, childPathToError, this.errors, childMeta); } ref(path) { let ref = this.initialReceived; const parsedRefPath = path.split("."); parsedRefPath.forEach((el) => { ref = ref[el]; }); return ref; } addIssue(expected, received, messageKey) { const rawMessage = this.t[messageKey] ?? messageKey; const message = replacePlaceholders(rawMessage, { e: expected, r: received, p: this.pathToError }); if (this.errors) { this.errors.push({ expected, received, pathToError: this.pathToError, message }); return; } throw new ValidationError(expected, received, this.pathToError, message, this.meta); } }; function innerCheck(schema, receivedValue, exCtx) { const commonTmap = exCtx.t; const schemaData = schema[ctxSymbol]; schemaData.transformListBefore?.forEach((transformCallback) => { receivedValue = transformCallback(receivedValue); }); if (receivedValue === void 0) { if (schemaData.defaultValue !== void 0) return schemaData.defaultValue; if (!schemaData.isOptional) exCtx.addIssue("Required", receivedValue, commonTmap["c:optional"]); return receivedValue; } if (receivedValue === null) { if (!schemaData.isNullable) exCtx.addIssue("Not null", receivedValue, commonTmap["c:nullable"]); return receivedValue; } if (schemaData.date) { if (!isValidDateInner(receivedValue)) exCtx.addIssue("Date", receivedValue, commonTmap["c:date"]); } const typeOfVal = typeof receivedValue; if (schemaData.type.length) { if (!schemaData.type.includes(typeOfVal)) exCtx.addIssue(schemaData.type, typeOfVal, commonTmap["c:invalidType"]); } if (schemaData.array) { if (!Array.isArray(receivedValue)) return exCtx.addIssue("Array", receivedValue, commonTmap["c:array"]); schemaData.requiredValidations.forEach((requiredValidation) => { requiredValidation(receivedValue, exCtx); }); const schema2 = schemaData.array; const pathToError = exCtx.pathToError; const parsedReceivedValue = []; receivedValue.forEach((elem, i) => { const parsedElement = innerCheck(schema2, elem, exCtx.createChild(`${pathToError}[${i}]`, schemaData.meta)); parsedReceivedValue.push(parsedElement); }); return parsedReceivedValue; } if (schemaData.object) { if (typeOfVal !== "object") exCtx.addIssue("Object", receivedValue, commonTmap["c:objectType"]); if (Array.isArray(receivedValue)) exCtx.addIssue("Object", receivedValue, commonTmap["c:objectTypeAsArray"]); schemaData.requiredValidations.forEach((requiredValidation) => { requiredValidation(receivedValue, exCtx); }); const shapeSchema = schemaData.object; const parsedReceivedValue = {}; if (!schemaData.allowUnrecognizedObjectProps) { for (const keyPerReceivedValue of Object.keys(receivedValue)) { if (shapeSchema[keyPerReceivedValue] === void 0) exCtx.addIssue("Unrecognized property", keyPerReceivedValue, commonTmap["c:unrecognizedProperty"]); } } const pathToError = exCtx.pathToError; for (const [keyOfSchema, valueOfSchema] of Object.entries(shapeSchema)) { const receivedObjectValuePropery = receivedValue[keyOfSchema]; if (receivedObjectValuePropery === void 0) { if (!valueOfSchema[ctxSymbol].isOptional) exCtx.addIssue("Required", receivedObjectValuePropery, commonTmap["c:requiredProperty"]); } const parsedReceivedObjectValuePropery = innerCheck( valueOfSchema, receivedObjectValuePropery, exCtx.createChild(`${pathToError}.${keyOfSchema}`, schemaData.meta) ); parsedReceivedValue[keyOfSchema] = parsedReceivedObjectValuePropery; } return parsedReceivedValue; } schemaData.requiredValidations.forEach((requiredValidation) => { requiredValidation(receivedValue, exCtx); }); return receivedValue; } var CommonSchema = class { [ctxSymbol]; constructor(ctx) { this[ctxSymbol] = ctx; } /** * @param validators - One or more custom validation functions. * @returns {this} The schema instance with the added custom validation. */ custom(...validators) { this.defaultValueCheck(); this[ctxSymbol].requiredValidations.push(...validators); return this; } /** * Marks the schema as nullable, allowing the value to be `null`. * * @returns {WithNull<this>} The schema instance marked as nullable. */ nullable() { this.defaultValueCheck(); this[ctxSymbol].isNullable = true; return this; } /** * Marks the schema as optional, allowing the value to be `undefined`. * * @returns {WithUndefined<this>} The schema instance marked as optional. */ optional() { this.defaultValueCheck(); this[ctxSymbol].isOptional = true; return this; } /** * Marks the schema as optional, allowing the value to be `undefined`. * * @returns {this} The schema instance. This method should be used as a last one because it does the check of previous methods and */ default(defaultValue) { const ctx = this[ctxSymbol]; if (ctx.isOptional) { throw new BuildSchemaError(`Cannot call method 'default' after method 'optional'`); } try { parseOrFail(this, defaultValue); } catch (e) { throw new BuildSchemaError(e.message); } this[ctxSymbol].defaultValue = defaultValue; return this; } /** * Applies a transformation to the input value before any validation occurs. * The transformation should return a value of the same type as the inferred type of the schema, * ensuring that the overall type is not altered. * * @template In - The type of the input value before transformation (defaults to `unknown`). * @param {TransformCallback<In, InferType<this>>} cb - The callback function that performs the transformation. * @returns {this} The updated schema with the applied transformation. * * @example * const schema = string() * .nullable() * .transformBeforeValidation((val) => val + '') // Ensure the value is a string * .transformBeforeValidation((val: string) => (val === '' ? null : val)); // Convert empty strings to null * * // Parse 'test' will pass as 'test' is a valid string longer than 3 characters. * parseOrFail(schema, 'test'); * * // Parsing '' will be transformed to null and will pass due to .nullable(). * parseOrFail(schema, ''); */ transformBeforeValidation(cb) { const ctx = this[ctxSymbol]; if (ctx.transformListBefore) { ctx.transformListBefore.push(cb); } else { ctx.transformListBefore = [cb]; } return this; } /** * Assigns a unique identifier to the schema. * This ID can be used to track or map validation errors back to specific fields * in a form or other structures. * * @param {string} value - The unique identifier for the schema. * @returns {this} The updated schema with the assigned ID. * * @example * const schema = string().id('username'); */ id(value) { return this.meta("id", value); } /** * Provides a description for the schema, offering additional context or information. * The description can be used when displaying validation errors or for documentation purposes. * * @param {string} value - The description for the schema. * @returns {this} The updated schema with the added description. * * @example * const schema = string().description('The username of the account holder.'); */ description(value) { return this.meta("description", value); } meta(key, value) { const ctx = this[ctxSymbol]; ctx.meta = { ...ctx.meta, [key]: value }; return this; } defaultValueCheck() { if (this[ctxSymbol].defaultValue !== void 0) { throw new BuildSchemaError("Default value must be the last method called in schema"); } } }; function parseOrFail(schema, receivedValue, options) { try { const ctx = new ExceptionContext( receivedValue, getTranslationByLocale(options?.lng), "", void 0, schema[ctxSymbol].meta ); return innerCheck(schema, receivedValue, ctx); } catch (e) { if (e instanceof ValidationError) throw e; throw new Error("Something unexpected happened"); } } function parse(schema, receivedValue, options) { try { const ctx = new ExceptionContext( receivedValue, getTranslationByLocale(options?.lng), "", options?.getAllErrors ? [] : void 0, schema[ctxSymbol].meta ); const parsedValue = innerCheck(schema, receivedValue, ctx); if (ctx.errors?.length) { return [ctx.errors, void 0]; } return [void 0, parsedValue]; } catch (e) { if (e instanceof ValidationError) { delete e.stack; return [[e], void 0]; } return [ [ { message: "Something unexpected happened", expected: "", received: "", pathToError: "", meta: void 0 } ], void 0 ]; } } export { ExceptionContext, CommonSchema, parseOrFail, parse }; //# sourceMappingURL=chunk-QIPGUTIG.mjs.map