Repository: icebob/fastest-validator
Pull request #229 (2021-04-04 11:56:27)

Passing meta information for custom validators
You can pass any extra meta information for the custom validators which is available via `context.meta`.

```js
const schema = {
    name: { type: "string", custom: (value, errors, schema, name, parent, context) => {
        // Access to the meta
        return context.meta.a;
    } },
};
const check = v.compile(schema);

const res = check(obj, {
    // Passes meta information
    meta: { a: "from-meta" }
});
```


Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 3372100..0630b24 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,7 +8,7 @@
 			"type": "node",
 			"request": "launch",
 			"name": "Launch dev",
-			"program": "${workspaceRoot}\\examples\\custom.js"
+			"program": "${workspaceRoot}\\examples\\async.js"
 		},
 		{
 			"type": "node",
diff --git a/README.md b/README.md
index 92c9223..6dd765f 100644
--- a/README.md
+++ b/README.md
@@ -117,8 +117,8 @@ import FastestValidator from "https://esm.sh/fastest-validator@1"
 
 const v = new FastestValidator();
 const check = v.compile({
-	name: "string",
-	age: "number",
+    name: "string",
+    age: "number",
 });
 
 console.log(check({ name: "Erf", age: 18 })); //true
@@ -1177,20 +1177,20 @@ const v = new Validator({
 });
 
 const schema = {
-	$$async: true,
-	name: { type: "string" },
-	username: {
-		type: "string",
+    $$async: true,
+    name: { type: "string" },
+    username: {
+        type: "string",
         min: 2,
-		custom: async (v, errors) => {
-			// E.g. checking in the DB that the value is unique.
-			const res = await DB.checkUsername(v);
+        custom: async (v, errors) => {
+            // E.g. checking in the DB that the value is unique.
+            const res = await DB.checkUsername(v);
             if (!res) 
                 errors.push({ type: "unique", actual: value });
 
             return v;
-		}
-	}
+        }
+    }
     // ...
 };
 
@@ -1207,6 +1207,24 @@ const check = v.compile(schema);
 console.log("Is async?", check.async);
 ```
 
+## Meta information for custom validators
+You can pass any extra meta information for the custom validators which is available via `context.meta`.
+
+```js
+const schema = {
+    name: { type: "string", custom: (value, errors, schema, name, parent, context) => {
+        // Access to the meta
+        return context.meta.a;
+    } },
+};
+const check = v.compile(schema);
+
+const res = check(obj, {
+    // Passes meta information
+    meta: { a: "from-meta" }
+});
+```
+
 # Custom error messages (l10n)
 You can set your custom messages in the validator constructor.
 
diff --git a/examples/async.js b/examples/async.js
index bbb35fe..bd63146 100644
--- a/examples/async.js
+++ b/examples/async.js
@@ -26,9 +26,9 @@ const schema = {
 		type: "string",
 		min: 4,
 		max: 25,
-		custom: async (v) => {
+		custom: async (v, errors, schema, name, parent, context) => {
 			await new Promise(resolve => setTimeout(resolve, 1000));
-			return v.toUpperCase();
+			return context.meta.name;
 		}
 	},
 
@@ -55,5 +55,5 @@ console.log("Is async?", check.async);
 		username: "johndoe   ",
 		age: 21
 	};
-	console.log(await check(data), data);
+	console.log(await check(data, { meta: { name: "Jane Doe" }}), data);
 })();
diff --git a/index.d.ts b/index.d.ts
index 997d2bc..46c964f 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -894,11 +894,13 @@ declare module "fastest-validator" {
 
 	export interface Context {
 		index: number;
+		async: boolean;
 		rules: ValidationRuleObject[];
 		fn: Function[];
 		customs: {
 			[ruleName: string]: { schema: RuleCustom; messages: MessagesType };
 		};
+		meta?: object;
 	}
 
 	export interface CheckerFunctionError {
@@ -942,14 +944,17 @@ declare module "fastest-validator" {
 		toHexString(): string;
 	}
 
+	export interface CheckFunctionOptions {
+		meta?: object | null;
+	}
 
 	export interface SyncCheckFunction {
-		(value: any): true | ValidationError[]
+		(value: any, opts?: CheckFunctionOptions): true | ValidationError[]
 		async: false
 	 }
 
 	 export interface AsyncCheckFunction {
-		(value: any): Promise<true | ValidationError[]>
+		(value: any, opts?: CheckFunctionOptions): Promise<true | ValidationError[]>
 		async: true
 	 }
 
diff --git a/lib/validator.js b/lib/validator.js
index 78982a3..693ca38 100644
--- a/lib/validator.js
+++ b/lib/validator.js
@@ -162,6 +162,7 @@ class Validator {
 		this.cache.clear();
 		delete schema.$$async;
 
+		/* istanbul ignore next */
 		if (context.async && !AsyncFunction) {
 			throw new Error("Asynchronous mode is not supported.");
 		}
@@ -224,8 +225,10 @@ class Validator {
 
 		this.cache.clear();
 
-		const resFn = function (data) {
+		const resFn = function (data, opts) {
 			context.data = data;
+			if (opts && opts.meta)
+				context.meta = opts.meta;
 			return checkFn.call(self, data, context);
 		};
 		resFn.async = context.async;


Test patch:
diff --git a/test/integration.spec.js b/test/integration.spec.js
index 7ee3be9..adfa637 100644
--- a/test/integration.spec.js
+++ b/test/integration.spec.js
@@ -1225,7 +1225,7 @@ describe("Test async mode", () => {
 	});
 
 	it("should call custom async validators", async () => {
-		let obj = {
+		const obj = {
 			id: 3,
 			name: "John",
 			username: "   john.doe  ",
@@ -1260,3 +1260,28 @@ describe("Test async mode", () => {
 	});
 
 });
+
+describe("Test context meta", () => {
+	const v = new Validator({ useNewCustomCheckerFunction: true });
+
+	const schema = {
+		name: { type: "string", custom: (value, errors, schema, name, parent, context) => {
+			expect(context.meta).toEqual({ a: "from-meta" });
+			return context.meta.a;
+		} },
+	};
+	const check = v.compile(schema);
+
+	it("should call custom async validators", () => {
+		const obj = {
+			name: "John"
+		};
+
+		const res = check(obj, {
+			meta: { a: "from-meta" }
+		});
+
+		expect(res).toBe(true);
+		expect(obj).toEqual({ name: "from-meta" });
+	});
+});
diff --git a/test/typescript/integration.spec.ts b/test/typescript/integration.spec.ts
index bb6cf12..ecf18f2 100644
--- a/test/typescript/integration.spec.ts
+++ b/test/typescript/integration.spec.ts
@@ -1283,5 +1283,29 @@ describe("Test async mode", () => {
 			message: "The 'age' field must be an even number! Actual: 31",
 		}]);
 	});
+});
+
+describe("Test context meta", () => {
+	const v = new Validator({ useNewCustomCheckerFunction: true });
+
+	const schema = {
+		name: { type: "string", custom: (value, errors, schema, name, parent, context) => {
+			expect(context.meta).toEqual({ a: "from-meta" });
+			return context.meta.a;
+		} },
+	};
+	const check = v.compile(schema);
 
+	it("should call custom async validators", () => {
+		const obj = {
+			name: "John"
+		};
+
+		const res = check(obj, {
+			meta: { a: "from-meta" }
+		});
+
+		expect(res).toBe(true);
+		expect(obj).toEqual({ name: "from-meta" });
+	});
 });