pols-validator 4.1.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # pols-validator
2
+
3
+ Un validador y sanitizador de datos ligero, síncrono, fluido y altamente extensible para **JavaScript** y **TypeScript**.
4
+
5
+ Inspirado en la filosofía de validación secuencial y filtrado en línea, `pols-validator` te permite validar la estructura de tus datos y sanitizarlos o transformarlos en un solo paso utilizando una API fluida basada en encadenamiento de métodos.
6
+
7
+ ---
8
+
9
+ ## Características Principales
10
+
11
+ * 🔗 **API Fluida**: Encadenamiento intuitivo de reglas que se lee como lenguaje natural.
12
+ * 🧼 **Híbrido de Validación y Sanitización**: Valida y transforma tus datos (mayúsculas, redondeos, limpieza de espacios, etc.) en un solo flujo.
13
+ * 📦 **Coerción de Tipos Inteligente**: Convierte cadenas a objetos `Date`, números o valores booleanos automáticamente.
14
+ * 🔒 **Reutilización Segura de Esquemas**: Sin mutaciones colaterales ni acumulación de prefijos en las etiquetas al validar múltiples registros.
15
+ * 🚀 **Extensible y DX Avanzado**: Añade validaciones rápidas en línea con `.custom()` o extiende la librería con tus propios métodos autocompletados mediante `createRulesCreator()`.
16
+ * 🛡️ **Seguridad contra Parameter Pollution**: Al validar objetos con un esquema, los campos adicionales no definidos son ignorados automáticamente en la salida sanitizada.
17
+
18
+ ---
19
+
20
+ ## Instalación
21
+
22
+ ```bash
23
+ npm install pols-validator
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Uso Básico
29
+
30
+ ```typescript
31
+ import { rules } from 'pols-validator';
32
+
33
+ // 1. Definir el esquema
34
+ const schema = rules().isObject({
35
+ nombre: rules('Nombre').isString().capitalize().trim(),
36
+ correo: rules('Correo').isEmailAddress().lower(),
37
+ edad: rules('Edad').required().isInteger().isGte(18),
38
+ activo: rules('Estado').isBoolean()
39
+ });
40
+
41
+ // 2. Validar
42
+ const resultado = schema.validate({
43
+ nombre: " jean sanchez ",
44
+ correo: "JEAN@ASD.COM",
45
+ edad: "25", // Se coerciona a número automáticamente
46
+ activo: "SÍ", // Se coerciona a true automáticamente
47
+ extra: "este campo se eliminará" // Campo no definido
48
+ });
49
+
50
+ if (resultado.success) {
51
+ console.log(resultado.sanitized);
52
+ /* Salida:
53
+ {
54
+ nombre: "Jean sanchez",
55
+ correo: "jean@asd.com",
56
+ edad: 25,
57
+ activo: true
58
+ }
59
+ */
60
+ } else {
61
+ console.error(resultado.messages); // Array de strings con los mensajes de error en español
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Métodos de Validación Disponibles
68
+
69
+ ### Cadenas y Texto
70
+ * `.isString()`: Valida que sea una cadena de texto (o convierte números a texto).
71
+ * `.isAlphanumeric()`: Valida que contenga solo letras y números (soporta caracteres del español como `ñ`, tildes y diéresis).
72
+ * `.isEmailAddress()`: Valida formato de correo electrónico (soporta subdireccionamiento de correo `+`).
73
+ * `.match(pattern: RegExp)`: Valida contra una expresión regular personalizada.
74
+ * `.maxLength(limit: number)`: Valida la longitud máxima del texto (o elementos de un array).
75
+ * `.minLength(limit: number)`: Valida la longitud mínima del texto (o elementos de un array).
76
+ * `.hasFixedLength(limit: number)`: Valida la longitud exacta del texto (o elementos de un array).
77
+
78
+ ### Números
79
+ * `.isNumber()`: Valida y coerciona a tipo numérico.
80
+ * `.isInteger()`: Valida que sea un número entero.
81
+ * `.isNumeric()`: Valida que la cadena contenga únicamente dígitos numéricos (`0-9`).
82
+ * `.isNatural()`: Valida enteros mayores o iguales a 0.
83
+ * `.isNaturalNoZero()`: Valida enteros mayores a 0.
84
+ * `.isGt(limit: number)`: Mayor que.
85
+ * `.isGte(limit: number)`: Mayor o igual que.
86
+ * `.isLt(limit: number)`: Menor que.
87
+ * `.isLte(limit: number)`: Menor o igual que.
88
+
89
+ ### Fechas y Horas
90
+ * `.isDateTime()`: Valida y coerciona a un objeto `PDate` (de `pols-date`).
91
+ * `.isDate()`: Valida, coerciona a `PDate` y limpia la hora (`clearClockTime`).
92
+ * `.isTime()`: Valida formatos de hora (AM/PM y duración) y los estandariza al formato de 24 horas `HH:MM:SS`.
93
+ * `.beforeOrSameAsNow()`: Valida que la fecha/hora sea anterior o igual al momento actual.
94
+
95
+ ### Colecciones y Estructuras
96
+ * `.isObject(schema?: Record<string, PRules>)`: Valida objetos y aplica esquemas anidados.
97
+ * `.isArray(rulesGenerator?: (index: number) => PRules)`: Valida arrays y aplica reglas opcionales por elemento.
98
+ * `.hasElements()`: Valida que el array no esté vacío.
99
+ * `.isIn(...elements: any[])`: Valida que el valor (o elementos del array) esté en la lista permitida.
100
+ * `.isNotIn(...elements: any[])`: Valida que el valor no esté en la lista prohibida.
101
+
102
+ ### Booleanos
103
+ * `.isBoolean()`: Valida y coerciona valores a booleanos (`true` para `1`, `"S"`, `"SÍ"`, `"Y"`, `"YES"`, `"TRUE"`; `false` para `0`, `"N"`, `"NO"`, `"FALSE"`).
104
+
105
+ ### Transformaciones y Filtros
106
+ * `.upper()`: Convierte el texto a mayúsculas.
107
+ * `.lower()`: Convierte el texto a minúsculas.
108
+ * `.capitalize()`: Capitaliza la primera letra del texto.
109
+ * `.trim()`: Limpia espacios en blanco iniciales y finales (se ejecuta por defecto al iniciar `validate`).
110
+ * `.cleanDoubleSpaces()`: Reemplaza múltiples espacios consecutivos por un espacio simple.
111
+ * `.noSpaces()`: Valida que la cadena no tenga ningún espacio.
112
+ * `.replace(search, replace)`: Reemplaza coincidencias en cadenas.
113
+ * `.split(separator)`: Divide una cadena en un array.
114
+ * `.join(separator)`: Une un array en una cadena.
115
+ * `.round(decimals)`: Redondea un número a ciertos decimales.
116
+ * `.floor()`: Aplica redondeo hacia abajo (`Math.floor`).
117
+ * `.ceil()`: Aplica redondeo hacia arriba (`Math.ceil`).
118
+ * `.sanitize(params?: PSanitizeParams)`: Limpia código HTML malicioso usando `isomorphic-dompurify`.
119
+
120
+ ---
121
+
122
+ ## Características Avanzadas
123
+
124
+ ### 1. Validaciones Personalizadas en Línea (`.custom`)
125
+ Puedes inyectar una validación dinámica en cualquier cadena de reglas sin extender la librería:
126
+
127
+ ```typescript
128
+ const schema = rules("Puerto").isInteger().custom(
129
+ (value) => value >= 1024 && value <= 65535,
130
+ "El puerto debe estar en el rango de puertos de usuario (1024-65535)"
131
+ );
132
+ ```
133
+
134
+ ### 2. Extender con Métodos Propios (`createRulesCreator`)
135
+ Si deseas añadir métodos con nombre propio a tu validador y que tu editor de código (como VS Code) los **reconozca y autocomplete automáticamente**, puedes extender la clase `PRules` y generar tu propio constructor `rules` usando `createRulesCreator`:
136
+
137
+ ```typescript
138
+ import { PRules, createRulesCreator } from 'pols-validator';
139
+
140
+ // 1. Extender PRules con tus propios métodos
141
+ class MisReglas extends PRules {
142
+ isDNI() {
143
+ return this.add('isDNI', (wrapper) => {
144
+ if (!(wrapper.value as string).match(/^[0-9]{8}[A-Z]$/)) {
145
+ return `'${wrapper.label}' no es un DNI español válido`;
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ // 2. Crear tu constructor personalizado (TypeScript infiere el tipo completo)
152
+ export const myRules = createRulesCreator(MisReglas);
153
+
154
+ // 3. Uso con autocompletado nativo
155
+ const resultado = myRules("Documento").isString().isDNI().validate("12345678Z");
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Licencia
161
+
162
+ ISC
package/dist/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export type PRulesCreator<T> = {
8
8
  export declare const rules: PRulesCreator<PRules>;
9
9
  export { PRules, PRulesParams, PSanitizeParams };
10
10
  export { PRulesResponse, PRulesWrapper, PRulesFunction } from './rulesEngine';
11
+ export declare function createRulesCreator<T>(ctor: new (...args: any[]) => T): PRulesCreator<T>;
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC9B,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;IAC1D,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;IAC1C,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,CAAC,CAAA;CAC1B,CAAA;AAED,eAAO,MAAM,KAAK,EAAE,aAAa,CAAC,MAAM,CAA2C,CAAA;AAEnF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC9B,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;IAC1D,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;IAC1C,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,CAAC,CAAA;CAC1B,CAAA;AAED,eAAO,MAAM,KAAK,EAAE,aAAa,CAAC,MAAM,CAA2C,CAAA;AAEnF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE7E,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAEvF"}
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PRules = exports.rules = void 0;
4
+ exports.createRulesCreator = createRulesCreator;
4
5
  const rules_1 = require("./rules");
5
6
  Object.defineProperty(exports, "PRules", { enumerable: true, get: function () { return rules_1.PRules; } });
6
7
  const rules = (...args) => new rules_1.PRules(...args);
7
8
  exports.rules = rules;
9
+ function createRulesCreator(ctor) {
10
+ return (...args) => new ctor(...args);
11
+ }
package/dist/rules.d.ts CHANGED
@@ -44,5 +44,6 @@ export declare class PRules extends PRulesEngine {
44
44
  floor(): this;
45
45
  ceil(): this;
46
46
  sanitize(params?: PSanitizeParams): this;
47
+ custom(validationFn: (value: any) => boolean | string | string[], errorMessage?: string): this;
47
48
  }
48
49
  //# sourceMappingURL=rules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAA;AAK3D,MAAM,MAAM,eAAe,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB,CAAA;AAED,qBAAa,MAAO,SAAQ,YAAY;IACvC,QAAQ;IAUR,cAAc;IAQd,cAAc;IAad,UAAU;IASV,MAAM;IAMN,MAAM;IA+BN,KAAK,CAAC,OAAO,EAAE,MAAM;IAMrB,QAAQ;IASR,SAAS;IAMT,SAAS;IAIT,eAAe;IAIf,SAAS;IAMT,SAAS,CAAC,KAAK,EAAE,MAAM;IAYvB,SAAS,CAAC,KAAK,EAAE,MAAM;IAYvB,cAAc,CAAC,KAAK,EAAE,MAAM;IAY5B,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM;IAoClD,IAAI,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE;IAY3B,OAAO,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE;IAM9B,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM;IAMtD,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,KAAK,CAAC,KAAK,EAAE,MAAM;IAMnB,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,KAAK,CAAC,KAAK,EAAE,MAAM;IAMnB,iBAAiB;IAOjB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IA4CxC,SAAS;IAiDT,KAAK;IAML,KAAK;IAML,SAAS;IAMT,KAAK,CAAC,QAAQ,EAAE,MAAM;IAOtB,iBAAiB;IAMjB,QAAQ;IAOR,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC;IAMlG,UAAU;IAMV,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMhC,IAAI,CAAC,SAAS,EAAE,MAAM;IAMtB,KAAK;IAML,IAAI;IAMJ,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe;CAajC"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAA;AAK3D,MAAM,MAAM,eAAe,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB,CAAA;AAED,qBAAa,MAAO,SAAQ,YAAY;IACvC,QAAQ;IAUR,cAAc;IAQd,cAAc;IAad,UAAU;IASV,MAAM;IAMN,MAAM;IA+BN,KAAK,CAAC,OAAO,EAAE,MAAM;IAMrB,QAAQ;IASR,SAAS;IAMT,SAAS;IAIT,eAAe;IAIf,SAAS;IAMT,SAAS,CAAC,KAAK,EAAE,MAAM;IAYvB,SAAS,CAAC,KAAK,EAAE,MAAM;IAYvB,cAAc,CAAC,KAAK,EAAE,MAAM;IAY5B,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM;IAoClD,IAAI,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE;IAY3B,OAAO,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE;IAM9B,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM;IAMtD,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,KAAK,CAAC,KAAK,EAAE,MAAM;IAMnB,IAAI,CAAC,KAAK,EAAE,MAAM;IAMlB,KAAK,CAAC,KAAK,EAAE,MAAM;IAMnB,iBAAiB;IAOjB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IA4CxC,SAAS;IAiDT,KAAK;IAML,KAAK;IAML,SAAS;IAMT,KAAK,CAAC,QAAQ,EAAE,MAAM;IAOtB,iBAAiB;IAMjB,QAAQ;IAOR,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC;IAMlG,UAAU;IAMV,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMhC,IAAI,CAAC,SAAS,EAAE,MAAM;IAMtB,KAAK;IAML,IAAI;IAMJ,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe;IAcjC,MAAM,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM;CAWvF"}
package/dist/rules.js CHANGED
@@ -437,5 +437,16 @@ class PRules extends rulesEngine_1.PRulesEngine {
437
437
  wrapper.value = (0, isomorphic_dompurify_1.sanitize)(wrapper.value.trim(), config);
438
438
  });
439
439
  }
440
+ custom(validationFn, errorMessage) {
441
+ return this.add('custom', (wrapper) => {
442
+ const result = validationFn(wrapper.value);
443
+ if (result === false) {
444
+ return errorMessage ?? `'${wrapper.label}' no es válido`;
445
+ }
446
+ if (typeof result === 'string' || Array.isArray(result)) {
447
+ return result;
448
+ }
449
+ });
450
+ }
440
451
  }
441
452
  exports.PRules = PRules;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pols-validator",
3
- "version": "4.1.0",
3
+ "version": "4.2.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",