diff --git a/.gitignore b/.gitignore
index 86aaedee1..382feba65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,5 @@ yarn.lock
/index.js
validator.js
validator.min.js
+.idea
diff --git a/README.md b/README.md
index 366036d43..41b7df69a 100644
--- a/README.md
+++ b/README.md
@@ -113,6 +113,7 @@ Validator | Description
**isFQDN(str [, options])** | check if the string is a fully qualified domain name (e.g. domain.com).
`options` is an object which defaults to `{ require_tld: true, allow_underscores: false, allow_trailing_dot: false, allow_numeric_tld: false, allow_wildcard: false, ignore_max_length: false }`.
`require_tld` - If set to false the validator will not check if the domain includes a TLD.
`allow_underscores` - if set to true, the validator will allow underscores in the domain.
`allow_trailing_dot` - if set to true, the validator will allow the domain to end with a `.` character.
`allow_numeric_tld` - if set to true, the validator will allow the TLD of the domain to be made up solely of numbers.
`allow_wildcard` - if set to true, the validator will allow domains starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`).
`ignore_max_length` - if set to true, the validator will not check for the standard max length of a domain.
**isFreightContainerID(str)** | alias for `isISO6346`, check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
**isFullWidth(str)** | check if the string contains any full-width chars.
+**isValidGraphQLQuery(str)** | check if the string is valid graphql query (note: uses parse function from graphql package).
**isHalfWidth(str)** | check if the string contains any half-width chars.
**isHash(str, algorithm)** | check if the string is a hash of type algorithm.
Algorithm is one of `['crc32', 'crc32b', 'md4', 'md5', 'ripemd128', 'ripemd160', 'sha1', 'sha256', 'sha384', 'sha512', 'tiger128', 'tiger160', 'tiger192']`.
**isHexadecimal(str)** | check if the string is a hexadecimal number.
diff --git a/package.json b/package.json
index 7e84ef71d..7badc3308 100644
--- a/package.json
+++ b/package.json
@@ -67,10 +67,13 @@
"build:node": "babel src -d .",
"build": "run-p build:*",
"pretest": "npm run build && npm run lint",
- "test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --reporter dot --recursive"
+ "test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --require ./test/setup.js --reporter dot --recursive"
},
"engines": {
"node": ">= 0.10"
},
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "graphql": "^16.11.0"
+ }
}
diff --git a/src/index.js b/src/index.js
index 87be7113c..57d205fa2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -129,6 +129,7 @@ import isLicensePlate from './lib/isLicensePlate';
import isStrongPassword from './lib/isStrongPassword';
import isVAT from './lib/isVAT';
+import isValidGraphQLQuery from './lib/isValidGraphQLQuery';
const version = '13.15.15';
@@ -245,6 +246,7 @@ const validator = {
isLicensePlate,
isVAT,
ibanLocales,
+ isValidGraphQLQuery,
};
export default validator;
diff --git a/src/lib/isValidGraphQLQuery.js b/src/lib/isValidGraphQLQuery.js
new file mode 100644
index 000000000..98a6d434b
--- /dev/null
+++ b/src/lib/isValidGraphQLQuery.js
@@ -0,0 +1,42 @@
+import assertString from './util/assertString';
+
+let isGraphQLAvailable = false;
+let parseFunction = null;
+
+// Attempt to load GraphQL parse function
+/* istanbul ignore if */
+if (typeof process === 'undefined' || !process.versions || !process.versions.node) {
+ // Skip initialization in non-Node environments
+} else {
+ const nodeVersion = process.versions.node.split('.')[0];
+ /* istanbul ignore else */
+ if (parseInt(nodeVersion, 10) >= 10) {
+ try {
+ // eslint-disable-next-line global-require
+ const { parse } = require('graphql');
+ parseFunction = parse;
+ isGraphQLAvailable = true;
+ } catch (e) {
+ /* istanbul ignore next */
+ // GraphQL loading failed
+ isGraphQLAvailable = false;
+ }
+ }
+}
+
+export default function isValidGraphQLQuery(input) {
+ assertString(input);
+
+ /* istanbul ignore if */
+ if (!isGraphQLAvailable || !parseFunction) {
+ /* istanbul ignore next */
+ return false;
+ }
+
+ try {
+ const obj = parseFunction(input);
+ return (!!obj && typeof obj === 'object');
+ } catch (e) {
+ return false;
+ }
+}
diff --git a/test/setup.js b/test/setup.js
new file mode 100644
index 000000000..236ed6f7e
--- /dev/null
+++ b/test/setup.js
@@ -0,0 +1,14 @@
+// Polyfill for globalThis (needed for Node < 12)
+if (typeof globalThis === 'undefined') {
+ (function () {
+ if (typeof global !== 'undefined') {
+ global.globalThis = global;
+ } else if (typeof window !== 'undefined') {
+ window.globalThis = window;
+ } else if (typeof self !== 'undefined') {
+ self.globalThis = self;
+ } else {
+ throw new Error('Unable to locate global object');
+ }
+ }());
+}
diff --git a/test/validators.test.js b/test/validators.test.js
index 299af27d8..2d6682ac4 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -7140,8 +7140,15 @@ describe('Validators', () => {
it('should define the module using an AMD-compatible loader', () => {
let window = {
validator: null,
- define(module) {
- window.validator = module();
+ define(deps, factory) {
+ // Handle AMD define with dependencies
+ if (Array.isArray(deps) && typeof factory === 'function') {
+ // Mock the graphql dependency as null/undefined since it's optional
+ window.validator = factory(null);
+ } else if (typeof deps === 'function') {
+ // Handle define without dependencies
+ window.validator = deps();
+ }
},
};
window.define.amd = true;
@@ -15652,4 +15659,31 @@ describe('Validators', () => {
],
});
});
+ it('should validate graphQL', () => {
+ // Skip test on Node.js < 10 due to graphql module incompatibility
+ const nodeVersion = parseInt(process.version.match(/^v(\d+)/)[1], 10);
+ if (nodeVersion < 10) {
+ console.log(' ⚠️ Skipping GraphQL test on Node.js', process.version);
+ return;
+ }
+
+ test({
+ validator: 'isValidGraphQLQuery',
+ valid: [
+ 'query StudentName {\n' +
+ ' student {\n' +
+ ' name\n' +
+ ' }\n' +
+ ' }',
+ ],
+ invalid: [
+ 'query StudentName {\n'
+ + ' student {\n'
+ + ' name\n'
+ + ' }',
+ 'null',
+ '2432',
+ ],
+ });
+ });
});