UNPKG

@backland/schema

Version:

TypeScript schema declaration and validation library with static type inference

692 lines (687 loc) 28.1 kB
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } import { assertEqual, BJSON, createStore, hooks, nonNullValues, RuntimeError } from '@backland/utils'; import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLSchema, GraphQLString, GraphQLUnionType, isNullableType, printSchema } from 'graphql'; import { __getCachedFieldInstance, isObject, ObjectType } from '../ObjectType'; import { AliasField } from '../fields/AliasField'; import { LiteralField } from '../fields/LiteralField'; import { cleanMetaField, getObjectDefinitionMetaField } from '../fields/MetaFieldField'; import { ObjectField } from '../fields/ObjectField'; import { UnionField } from '../fields/UnionField'; import { isHiddenFieldName } from '../isHiddenFieldName'; import { parseTypeName } from '../parseTypeName'; import { GraphQLDateType } from './GraphQLDateType'; import { GraphQLNullType } from './GraphQLNullType'; import { GraphQLPhoneType } from './GraphQLPhoneType'; import { GraphQLUlidType } from './GraphQLUlidType'; export function createHooks() { return { onField: hooks.parallel(), onFieldConfigMap: hooks.parallel(), onFieldResult: hooks.parallel(), willCreateObjectType: hooks.parallel() }; } var resultsCache = createStore(); var graphqlTypesRegister = createStore(); var fieldsRegister = createStore(); function wrapCreationWithCache(name, create) { return function creator() { if (graphqlTypesRegister.has(name)) { return graphqlTypesRegister.get(name); } var result = create.apply(void 0, arguments); graphqlTypesRegister.set(name, result); return graphqlTypesRegister.get(name); }; } export var GraphQLParser = /*#__PURE__*/function () { function GraphQLParser() { _classCallCheck(this, GraphQLParser); } _createClass(GraphQLParser, null, [{ key: "objectToGraphQL", value: function objectToGraphQL(init) { var _this = this; var path = init.path; var objectInput = init.object; var _this$objectIds = this.objectIds(objectInput), cacheId = _this$objectIds.cacheId, objectId = _this$objectIds.objectId; var parents = objectInput.meta.implements; if (resultsCache.has(cacheId)) { return resultsCache.get(cacheId); } // save reference for circular dependencies var graphqlParsed = {}; resultsCache.set(cacheId, graphqlParsed); var buildFields = function buildFields(options, _getType) { var currentInterfacesOption = options.interfaces; options.interfaces = function () { var currentInterfaces = typeof currentInterfacesOption === 'function' ? currentInterfacesOption() : currentInterfacesOption || []; var types = parents ? parents.map(function (parent) { return ObjectType.register.get(parent); }) : []; return [].concat(_toConsumableArray(currentInterfaces), _toConsumableArray(types.map(function (type) { return type.graphqlInterfaceType(); }))); }; options.fields = function () { var _options$middleware; var helpers = objectInput.helpers(); var objectMiddleware = objectInput.graphQLMiddleware || []; var builders = []; var hooks = createHooks(); (_options$middleware = options.middleware) === null || _options$middleware === void 0 ? void 0 : _options$middleware.call(options, hooks); objectMiddleware.forEach(function (fn) { return fn(hooks); }); var fieldsConfigMap = {}; var aliases = []; helpers.list.forEach(function (_ref) { var fieldName = _ref.name, instance = _ref.instance, plainField = _ref.plainField; if (isHiddenFieldName(fieldName)) return; if (plainField.hidden) return; if (plainField.type === 'alias') { AliasField.assert(instance); var subTypeName = parseTypeName({ field: plainField, fieldName: fieldName, parentName: objectId }); aliases.push({ fieldName: subTypeName, instance: instance, parentName: objectId, path: [objectId, fieldName] }); return; } var field = _this.fieldToGraphQL({ field: instance, fieldName: fieldName, parentName: objectId, path: path || [objectId] }); hooks.onFieldResult.exec(field); builders.push(field); }); function _useConvertFieldResult(next) { var field = { description: next.description, type: _getType(next) }; var origin = // @ts-ignore objectInput.definition[next.fieldName] || undefined; if (origin !== null && origin !== void 0 && origin.hidden) return; if ((origin === null || origin === void 0 ? void 0 : origin.defaultValue) !== undefined) { // @ts-ignore field.defaultValue = origin.defaultValue; } fieldsConfigMap[next.fieldName] = field; hooks.onField.exec(next, field); } builders.forEach(_useConvertFieldResult); aliases.forEach(function (_ref2) { var instance = _ref2.instance, fieldName = _ref2.fieldName, parentName = _ref2.parentName, path = _ref2.path; var _ref3 = instance.asFinalFieldDef.def, type = _ref3.type; _useConvertFieldResult(_this.fieldToGraphQL({ field: __getCachedFieldInstance(type), fieldName: fieldName, parentName: parentName, path: path })); }); hooks.onFieldConfigMap.exec(fieldsConfigMap); hooks.willCreateObjectType.exec(fieldsConfigMap); return fieldsConfigMap; }; return options; }; function getType() { var _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var __options = _objectSpread({ fields: {}, name: objectId }, _options); var name = __options.name; if (graphqlTypesRegister.has(name)) { return graphqlTypesRegister.get(name); } buildFields(__options, function (el) { return el.type(); }); var result = new GraphQLObjectType(__options); graphqlTypesRegister.set(__options.name, result); return result; } function getInputType() { var _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var options = _objectSpread({ fields: {}, name: "".concat(objectId, "Input") }, _options); var name = options.name; if (graphqlTypesRegister.has(name)) { return graphqlTypesRegister.get(name); } buildFields(options, function (el) { return el.inputType(); }); var result = new GraphQLInputObjectType(options); graphqlTypesRegister.set(name, result); return result; } function interfaceType() { var _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var options = _objectSpread({ fields: {}, name: "".concat(objectId, "Interface") }, _options); var name = options.name; if (graphqlTypesRegister.has(name)) { return graphqlTypesRegister.get(name); } buildFields(options, function (el) { return el.type(); }); var result = new GraphQLInterfaceType(options); graphqlTypesRegister.set(name, result); return result; } function getSDL() { var result = getType(); var object = new GraphQLSchema({ types: [result] }); return printSchema(object); } function getInputSDL() { var object = new GraphQLSchema({ types: [getInputType()] }); return printSchema(object); } var parsed = { getInputType: getInputType, getType: getType, inputToString: getInputSDL, interfaceType: interfaceType, object: objectInput, typeToString: getSDL }; Object.assign(graphqlParsed, parsed); return graphqlParsed; } }, { key: "fieldToGraphQL", value: function fieldToGraphQL(init) { var fieldName = init.fieldName, parentName = init.parentName; var fieldClone = init.field; var plainField = fieldClone.asFinalFieldDef; var optional = fieldClone.optional, typeName = fieldClone.typeName; var description = plainField.description; var path = [].concat(_toConsumableArray(init.path), [fieldName]); var cacheId = path.join('--'); if (resultsCache.has(cacheId)) { return fieldsRegister.get(cacheId); } // save reference for circular dependencies var fieldParsed = {}; fieldsRegister.set(cacheId, fieldParsed); var subTypeName = parseTypeName({ field: fieldClone, fieldName: fieldName, parentName: parentName }); var self = this; // @ts-ignore var creators = { ID: function ID() { return { inputType: function inputType() { return GraphQLID; }, type: function type() { return GraphQLID; } }; }, alias: function alias() { return {}; // handled in object parser; }, any: function any() { var create = wrapCreationWithCache('Any', function () { return new GraphQLScalarType({ name: 'Any' }); }); return { inputType: create, type: create }; }, array: function array() { var _ref4 = fieldClone, innerFieldType = _ref4.utils.listItemType; var id = parseTypeName({ field: innerFieldType, fieldName: fieldName, parentName: parentName }); var convertFieldResult = self.fieldToGraphQL({ field: innerFieldType, fieldName: fieldName, parentName: parentName, path: path }); return { inputType: wrapCreationWithCache("".concat(id, "ListInput"), function () { return new GraphQLList(convertFieldResult.inputType.apply(convertFieldResult, arguments)); }), type: wrapCreationWithCache("".concat(id, "List"), function () { return new GraphQLList(convertFieldResult.type.apply(convertFieldResult, arguments)); }) }; }, boolean: function boolean() { return { inputType: function inputType() { return GraphQLBoolean; }, type: function type() { return GraphQLBoolean; } }; }, cursor: function cursor() { var cursor = fieldClone; return { inputType: cursor.utils.object.graphqlInputType, type: cursor.utils.object.graphqlType }; }, date: function date() { return { inputType: function inputType() { return GraphQLDateType; }, type: function type() { return GraphQLDateType; } }; }, email: function email() { return { inputType: function inputType() { return GraphQLString; }, type: function type() { return GraphQLString; } }; }, enum: function _enum() { function createEnum(options) { var values = {}; assertEqual(fieldClone.type, 'enum'); fieldClone.def.forEach(function (key) { values[key] = { value: key }; }); return new GraphQLEnumType(_objectSpread(_objectSpread({ name: subTypeName }, options), {}, { values: values })); } return { inputType: wrapCreationWithCache(subTypeName, createEnum), type: wrapCreationWithCache(subTypeName, createEnum) }; }, float: function float() { return { inputType: function inputType() { return GraphQLFloat; }, type: function type() { return GraphQLFloat; } }; }, int: function int() { return { inputType: function inputType() { return GraphQLInt; }, type: function type() { return GraphQLInt; } }; }, literal: function literal() { if (!LiteralField.is(fieldClone)) throw new Error('ts'); var description = fieldClone.description, def = fieldClone.def; var recordName = parseTypeName({ field: fieldClone, fieldName: fieldName, parentName: parentName }); function createLiteral(options) { return new GraphQLScalarType(_objectSpread(_objectSpread({}, options), {}, { description: JSON.stringify(description || "Literal value: ".concat(def.value)), name: recordName, parseValue: function parseValue(value) { return LiteralField.utils.deserialize(_objectSpread(_objectSpread({}, def), {}, { value: value })); }, serialize: function serialize(value) { return LiteralField.utils.deserialize(_objectSpread(_objectSpread({}, def), {}, { value: value })); } })); } return { inputType: wrapCreationWithCache(recordName, createLiteral), type: wrapCreationWithCache(recordName, createLiteral) }; }, meta: function meta() { throw new Error('meta field'); }, null: function _null() { return { inputType: function inputType() { return GraphQLNullType; }, type: function type() { return GraphQLNullType; } }; }, object: function object() { assertEqual(fieldClone.type, 'object'); var id = parseTypeName({ field: fieldClone, fieldName: fieldName, parentName: parentName }); var def = ObjectType.is(fieldClone.def) ? fieldClone.def.clone(function (el) { return el.def(); }) : fieldClone.def; // @ts-ignore var object = ObjectType.getOrSet(id, def); var res = self.objectToGraphQL({ object: object }); return { inputType: res.getInputType, type: res.getType }; }, phone: function phone() { return { inputType: function inputType() { return new GraphQLPhoneType(); }, type: function type() { return new GraphQLPhoneType(); } }; }, record: function record() { var recordName = parseTypeName({ field: fieldClone, fieldName: fieldName, parentName: parentName }); function createRecord(options) { return new GraphQLScalarType(_objectSpread(_objectSpread({}, options), {}, { name: recordName, parseValue: function parseValue(value) { return fieldClone.parse(value); }, serialize: function serialize(value) { return fieldClone.parse(value); } })); } return { inputType: wrapCreationWithCache(recordName, createRecord), type: wrapCreationWithCache(recordName, createRecord) }; }, string: function string() { return { inputType: function inputType() { return GraphQLString; }, type: function type() { return GraphQLString; } }; }, ulid: function ulid() { return { inputType: function inputType() { return GraphQLUlidType; }, type: function type() { return GraphQLUlidType; } }; }, undefined: function undefined() { var create = wrapCreationWithCache('Undefined', function () { return new GraphQLScalarType({ name: 'Undefined' }); }); return { inputType: create, type: create }; }, union: function union() { if (!UnionField.is(fieldClone)) throw fieldClone; var descriptions = []; // if all types are objects it can be used as normal GraphQL union // otherwise we need to create a scalar, since GraphQL only accepts unions of objects // - https://github.com/graphql/graphql-js/issues/207 // GraphQL union items cannot be used as input, so we always use scalars for inputs: // - https://github.com/graphql/graphql-spec/issues/488 var areAllObjects = true; fieldClone.utils.fieldTypes.forEach(function (field) { if (field.type !== 'object') areAllObjects = false; descriptions.push(describeField(field.definition)); }); var description = undefined; descriptions = descriptions.map(function (el) { return el.trim(); }); description = descriptions.join(' | '); if (description.length > 100) { description = "Union of:\n".concat(descriptions.map(function (el) { return " - ".concat(el); }).join('\n')); } else { description = "Union of ".concat(descriptions.join(' | ')).trim(); } description = description.replace(/ /g, ' '); var scalarUnion = new GraphQLScalarType({ description: description, name: subTypeName, parseValue: function parseValue(value) { return fieldClone.parse(value); }, serialize: function serialize(value) { return fieldClone.parse(value); } }); return { inputType: wrapCreationWithCache(subTypeName, function () { return scalarUnion; }), type: wrapCreationWithCache(subTypeName, function () { if (!areAllObjects) return scalarUnion; for (var _len = arguments.length, options = new Array(_len), _key = 0; _key < _len; _key++) { options[_key] = arguments[_key]; } return new GraphQLUnionType(_objectSpread(_objectSpread({ name: subTypeName }, options), {}, { types: fieldClone.utils.fieldTypes.map(function (field, index) { if (!ObjectField.is(field)) throw field; var object = field.utils.object; if (!object.id) { object = object.clone(function (el) { return el.objectType("".concat(subTypeName, "_").concat(index)); }); } return object.graphqlType(); }) })); }) }; }, unknown: function unknown() { var GraphQLUnknownType = new GraphQLScalarType({ name: subTypeName }); return { inputType: function inputType() { return GraphQLUnknownType; }, type: function type() { return GraphQLUnknownType; } }; } }; var result = { description: description, fieldName: fieldName, inputType: function inputType() { var _creators$typeName; if (typeName === 'alias') { // dev only assertion, aliasing should be handled in parseObject throw new Error("can't handle alias in convertField."); } var _result = (_creators$typeName = creators[typeName]()).inputType.apply(_creators$typeName, arguments); if (fieldClone.list) { _result = new GraphQLList(_result); } if (!optional && fieldClone.defaultValue === undefined) { _result = new GraphQLNonNull(_result); } return _result; }, plainField: plainField, type: function type() { var _creators$typeName2; if (typeName === 'alias') { // dev only assertion, aliasing should be handled in parseObject throw new Error("can't handle alias in convertField."); } var _result = (_creators$typeName2 = creators[typeName]()).type.apply(_creators$typeName2, arguments); if (fieldClone.list) { _result = new GraphQLList(_result); } if (!optional && isNullableType(_result)) { _result = new GraphQLNonNull(_result); } return _result; }, typeName: typeName }; Object.assign(fieldParsed, result); return result; } }]); return GraphQLParser; }(); _defineProperty(GraphQLParser, "resultsCache", resultsCache); _defineProperty(GraphQLParser, "graphqlTypesRegister", graphqlTypesRegister); _defineProperty(GraphQLParser, "fieldsRegister", fieldsRegister); _defineProperty(GraphQLParser, "reset", function () { resultsCache.clear(); graphqlTypesRegister.clear(); fieldsRegister.clear(); }); _defineProperty(GraphQLParser, "objectIds", function (object) { if (!isObject(object)) { throw new RuntimeError("Invalid Object.", { object: object }); } var _nonNullValues = nonNullValues({ id: object.id }, 'The provided object should be identified before converting. ' + 'You can use object.identify("abc")'), objectId = _nonNullValues.id; var cacheId = "".concat([objectId].filter(Boolean).join()); // return { cacheId: cacheId, object: object, objectId: objectId }; }); export function describeField(field) { if (field.name) return field.name; if (field.type === 'literal') { var value = BJSON.stringify(LiteralField.utils.deserialize(field.def), { quoteKeys: function quoteKeys(key) { return " ".concat(key); } }); return "".concat(value); } return BJSON.stringify(field, { handler: function handler(payload) { var value = payload.value; if ((value === null || value === void 0 ? void 0 : value.type) === 'object' && value.def) { var meta = getObjectDefinitionMetaField((value === null || value === void 0 ? void 0 : value.def) || {}); if (meta !== null && meta !== void 0 && meta.def.id) return meta.def.id; return describeField(cleanMetaField(value.def)); } return undefined; }, quoteKeys: function quoteKeys(key) { return " ".concat(key); }, quoteValues: function quoteValues(value, _ref5) { var key = _ref5.key; if (value === false) return ''; if (key === 'type') return " ".concat(value, " "); return "".concat(value); } }); } //# sourceMappingURL=GraphQLParser.js.map