@backland/schema
Version:
TypeScript schema declaration and validation library with static type inference
692 lines (687 loc) • 28.1 kB
JavaScript
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