UNPKG

graphql-scalars

Version:

A collection of scalar types not included in base GraphQL.

173 lines (172 loc) 6.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processValue = processValue; exports.isDecimal = isDecimal; exports.isSexagesimal = isSexagesimal; exports.sexagesimalToDecimal = sexagesimalToDecimal; exports.isObjectLike = isObjectLike; exports.serializeObject = serializeObject; const error_js_1 = require("../error.js"); var VALUE_RANGES; (function (VALUE_RANGES) { VALUE_RANGES[VALUE_RANGES["NEGATIVE"] = 0] = "NEGATIVE"; VALUE_RANGES[VALUE_RANGES["NON_NEGATIVE"] = 1] = "NON_NEGATIVE"; VALUE_RANGES[VALUE_RANGES["POSITIVE"] = 2] = "POSITIVE"; VALUE_RANGES[VALUE_RANGES["NON_POSITIVE"] = 3] = "NON_POSITIVE"; })(VALUE_RANGES || (VALUE_RANGES = {})); var VALUE_TYPES; (function (VALUE_TYPES) { VALUE_TYPES[VALUE_TYPES["INT"] = 0] = "INT"; VALUE_TYPES[VALUE_TYPES["FLOAT"] = 1] = "FLOAT"; })(VALUE_TYPES || (VALUE_TYPES = {})); // More info about Sexagesimal: https://en.wikipedia.org/wiki/Sexagesimal const SEXAGESIMAL_REGEX = /^([0-9]{1,3})°\s*([0-9]{1,3}(?:\.(?:[0-9]{1,}))?)['′]\s*(([0-9]{1,3}(\.([0-9]{1,}))?)["″]\s*)?([NEOSW]?)$/; // TODO: Consider implementing coercion like this... // See: https://github.com/graphql/graphql-js/blob/master/src/type/scalars.js#L13 // See: https://github.com/graphql/graphql-js/blob/master/src/type/scalars.js#L60 function _validateInt(value) { if (!Number.isFinite(value)) { throw (0, error_js_1.createGraphQLError)(`Value is not a finite number: ${value}`); } if (!Number.isInteger(value)) { throw (0, error_js_1.createGraphQLError)(`Value is not an integer: ${value}`); } if (!Number.isSafeInteger(value)) { throw (0, error_js_1.createGraphQLError)(`Value is not a safe integer: ${value}`); } } function _validateFloat(value) { if (!Number.isFinite(value)) { throw (0, error_js_1.createGraphQLError)(`Value is not a finite number: ${value}`); } } const VALIDATIONS = { NonPositiveInt: { range: VALUE_RANGES.NON_POSITIVE, type: VALUE_TYPES.INT, }, PositiveInt: { range: VALUE_RANGES.POSITIVE, type: VALUE_TYPES.INT, }, NonNegativeInt: { range: VALUE_RANGES.NON_NEGATIVE, type: VALUE_TYPES.INT, }, NegativeInt: { range: VALUE_RANGES.NEGATIVE, type: VALUE_TYPES.INT, }, NonPositiveFloat: { range: VALUE_RANGES.NON_POSITIVE, type: VALUE_TYPES.FLOAT, }, PositiveFloat: { range: VALUE_RANGES.POSITIVE, type: VALUE_TYPES.FLOAT, }, NonNegativeFloat: { range: VALUE_RANGES.NON_NEGATIVE, type: VALUE_TYPES.FLOAT, }, NegativeFloat: { range: VALUE_RANGES.NEGATIVE, type: VALUE_TYPES.FLOAT, }, }; function processValue(value, scalarName) { const { range, type } = VALIDATIONS[scalarName]; /* eslint-disable no-restricted-globals */ /* eslint-disable use-isnan */ if (value === null || typeof value === 'undefined' || isNaN(value) || Number.isNaN(value) || value === Number.NaN) { throw (0, error_js_1.createGraphQLError)(`Value is not a number: ${value}`); } /* eslint-enable */ let parsedValue; switch (type) { case VALUE_TYPES.FLOAT: parsedValue = parseFloat(value); _validateFloat(parsedValue); break; case VALUE_TYPES.INT: parsedValue = parseInt(value, 10); _validateInt(parsedValue); break; default: // no -op, return undefined } if ((range === VALUE_RANGES.NEGATIVE && !(parsedValue < 0)) || (range === VALUE_RANGES.NON_NEGATIVE && !(parsedValue >= 0)) || (range === VALUE_RANGES.POSITIVE && !(parsedValue > 0)) || (range === VALUE_RANGES.NON_POSITIVE && !(parsedValue <= 0))) { throw (0, error_js_1.createGraphQLError)(`Value is not a ${VALUE_RANGES[range].toLowerCase().replace('_', '-')} number: ${value}`); } return parsedValue; } /** * Check if the value is in decimal format. * * @param value - Value to check * @returns True if is decimal, false otherwise */ function isDecimal(value) { const checkedValue = value.toString().trim(); if (Number.isNaN(Number.parseFloat(checkedValue))) { return false; } return Number.parseFloat(checkedValue) === Number(checkedValue); } /** * Check if the value is in sexagesimal format. * * @param value - Value to check * @returns True if sexagesimal, false otherwise */ function isSexagesimal(value) { if (typeof value !== 'string') return false; return SEXAGESIMAL_REGEX.test(value.toString().trim()); } /** * Converts a sexagesimal coordinate to decimal format. * * @param value - Value to convert * @returns Decimal coordinate * @throws {TypeError} if the value is not in sexagesimal format */ function sexagesimalToDecimal(value) { const data = SEXAGESIMAL_REGEX.exec(value); if (typeof data === 'undefined' || data === null) { throw (0, error_js_1.createGraphQLError)(`Value is not in sexagesimal format: ${value}`); } const min = Number(data[2]) / 60 || 0; const sec = Number(data[4]) / 3600 || 0; const decimal = Number.parseFloat(data[1]) + min + sec; // Southern and western coordinates must be negative decimals return ['S', 'W'].includes(data[7]) ? -decimal : decimal; } function isObjectLike(value) { return typeof value === 'object' && value !== null; } // Taken from https://github.com/graphql/graphql-js/blob/30b446938a9b5afeb25c642d8af1ea33f6c849f3/src/type/scalars.ts#L267 // Support serializing objects with custom valueOf() or toJSON() functions - // a common way to represent a complex value which can be represented as // a string (ex: MongoDB id objects). function serializeObject(outputValue) { if (isObjectLike(outputValue)) { if (typeof outputValue.valueOf === 'function') { const valueOfResult = outputValue.valueOf(); if (!isObjectLike(valueOfResult)) { return valueOfResult; } } if (typeof outputValue.toJSON === 'function') { return outputValue.toJSON(); } } return outputValue; }