UNPKG

@backland/schema

Version:

TypeScript schema declaration and validation library with static type inference

408 lines (399 loc) 15.5 kB
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } 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 _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 { createStore, RuntimeError } from '@backland/utils'; import { ensureArray } from '@backland/utils'; import { expectedType } from '@backland/utils'; import { getTypeName } from '@backland/utils'; import { invariantType } from '@backland/utils'; import { CircularDeps } from './CircularDeps'; import { parseValidationError } from './applyValidator'; import { extendObjectDefinition } from './extendObjectDefinition'; import { cleanMetaField, getObjectDefinitionMetaField, isMetaFieldKey, objectMetaFieldKey } from './fields/MetaFieldField'; import { SpecialObjectKeyEnum } from './fields/_fieldDefinitions'; import { validateObjectFields } from './getObjectErrors'; import { getObjectHelpers } from './getObjectHelpers'; import { implementObject } from './implementObject'; import { isObject } from './objectInferenceUtils'; import { __getCachedFieldInstance, parseObjectDefinition } from './parseObjectDefinition'; import { parseField } from './parseObjectDefinition'; import { withCache } from './withCache'; export * from './parseObjectDefinition'; export * from './objectInferenceUtils'; export * from './implementObject'; export * from './fields/_parseFields'; export * from './fields/_fieldDefinitions'; export * from './fields/_parseFields'; export class ObjectType { get __isBacklandObject() { return true; } constructor(objectDef) { var _this = this; _defineProperty(this, "__withCache", void 0); _defineProperty(this, "inputDefinition", void 0); _defineProperty(this, "__definitionCache", void 0); _defineProperty(this, "__hidden", false); _defineProperty(this, "softParse", function (input) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return _this.parse(input, _objectSpread(_objectSpread({}, options), {}, { allowExtraFields: true })); }); _defineProperty(this, "helpers", () => { return this.__withCache('helpers', () => getObjectHelpers(this)); }); _defineProperty(this, "toGraphQL", name => { if (name) { this.identify(name); } if (!this.id) { throw new RuntimeError('Should object.identify() before converting to Graphql.' + '\nYou can call object.clone() to choose a different identification.', { 'used definition': this.definition }); } // @ts-ignore circular var { GraphQLParser } = CircularDeps.GraphQLParser; return GraphQLParser.objectToGraphQL({ object: this }); }); _defineProperty(this, "graphqlType", options => { return this.toGraphQL().getType(options); }); _defineProperty(this, "graphqlInterfaceType", options => { return this.toGraphQL().interfaceType(options); }); _defineProperty(this, "graphqlPrint", () => { return this.toGraphQL().typeToString(); }); _defineProperty(this, "typescriptPrint", options => { // @ts-ignore circular return CircularDeps.objectToTypescript(this.nonNullId, // @ts-ignore this, options); }); _defineProperty(this, "graphqlTypeToString", () => { return this.toGraphQL().typeToString(); }); _defineProperty(this, "graphqlInputType", options => { return this.toGraphQL().getInputType(options); }); _defineProperty(this, "implement", function (name) { for (var _len = arguments.length, parents = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { parents[_key - 1] = arguments[_key]; } return implementObject(name, _this.definition, ...parents); }); _defineProperty(this, "graphQLMiddleware", []); _defineProperty(this, "addGraphQLMiddleware", middleware => { this.graphQLMiddleware.push(...ensureArray(middleware)); }); this.inputDefinition = objectDef; this.__withCache = withCache(this); } get definition() { return this.__definitionCache = this.__definitionCache || (() => { var objectDef = typeof this.inputDefinition === 'function' ? this.inputDefinition(CircularDeps) : this.inputDefinition; if (!objectDef || typeof objectDef !== 'object') { throw new Error('Expected object definition to be an object'); } return parseObjectDefinition(objectDef).definition; })(); } get description() { return this.meta.description; } set hidden(value) { this.__hidden = value; } get hidden() { return this.__hidden; } // definition without metadata (name, etc) cleanDefinition() { // @ts-ignore return cleanMetaField(this.clone(el => el.def())); } edit() { return extendObjectDefinition(this); } get meta() { // @ts-ignore return this.definition[objectMetaFieldKey].def; } __setMetaData(k, value) { // @ts-ignore this.definition[objectMetaFieldKey].def[k] = value; } parse(input, options) { var { customMessage, customErrorMessage } = options || {}; var { errors, parsed } = this.safeParse(input, options); if (errors.length) { var e_message = errors.join(' \n'); if (this.id) { e_message = "".concat(this.id, ": ").concat(e_message); } var err = parseValidationError(input, customMessage || customErrorMessage, e_message); err.isObjectValidationError = true; err.fieldErrors = errors; throw err; } return parsed; } validate(input) { try { this.parse(input); return true; } catch (e) { return false; } } safeParse(input, options) { var { partial = false, excludeInvalidListItems, includeHidden = true, allowExtraFields, exclude } = options || {}; var objectDef = _objectSpread({}, this.definition); if (this.__hidden && !includeHidden) return { errors: [], parsed: {} }; var errors = []; var parsed = {}; var fieldInputsList = []; if (!input || typeof input !== 'object' || Array.isArray(input)) { throw new RuntimeError("Invalid input. Expected object, found ".concat(getTypeName(input)), { input }); } input = _objectSpread({}, input); var inputKeys = (value => { if (exclude) { return value.filter(el => !exclude.includes(el)); } return value; })(Object.keys(input)); // @ts-ignore var fields = (options === null || options === void 0 ? void 0 : options.fields) || Object.keys(this.definition); // === Start handling {[K: string}: any}|{[K: number}: any} === var anyStringKey = fields.find(field => field === SpecialObjectKeyEnum.$string); var anyNumberKey = fields.find(field => field === SpecialObjectKeyEnum.$number); if (anyNumberKey || anyStringKey) { var allFieldsSet = new Set(fields); var keysNotDefined = inputKeys.filter(k => !allFieldsSet.has(k)); fields = fields.filter(k => !SpecialObjectKeyEnum.list.includes(k)); if (anyStringKey) { var def = objectDef[anyStringKey]; keysNotDefined.forEach(key => { objectDef[key] = def; }); } else if (anyNumberKey) { var _def = objectDef[anyNumberKey]; keysNotDefined.forEach(key => { if (!key.match(/\d*/)) return; objectDef[key] = _def; }); } } // === End handling {[K: string}: any}|{[K: number}: any} === fields.forEach(currField => { var _fieldDef$def; if (currField.startsWith('$')) return; // special field if (isMetaFieldKey(currField)) return; if (exclude && exclude.includes(currField)) return; // @ts-ignore var fieldDef = objectDef[currField]; if (!includeHidden && fieldDef.hidden) return; if (fieldDef.type === 'alias') { var instance = __getCachedFieldInstance(fieldDef); return fieldInputsList.push({ composer: instance.composer, fieldDef, key: currField, value: undefined }); } var value = input[currField]; var hasAutoCreateOption = (fieldDef === null || fieldDef === void 0 ? void 0 : (_fieldDef$def = fieldDef.def) === null || _fieldDef$def === void 0 ? void 0 : _fieldDef$def['autoCreate']) === true; if ((value === undefined || value === null) && partial && !hasAutoCreateOption) { return; } fieldInputsList.push({ composer: undefined, fieldDef, key: currField, value }); }); // parsing ignoring aliases var notAliasFieldsResults = fieldInputsList.map(entry => { var { key, fieldDef, value } = entry; var result = validateObjectFields({ definition: fieldDef, fieldName: key, fieldParserOptions: { excludeInvalidListItems }, value }); if (result.parsed !== undefined) { parsed[key] = result.parsed; } if (!entry.composer) { errors.unshift(...result.errors); } return _objectSpread(_objectSpread({}, entry), result); }); // handling aliases notAliasFieldsResults.forEach(field => { var { key, composer } = field; if (!composer) return; var value = composer.compose(parsed); var fieldDef = composer.def; var result = validateObjectFields({ definition: fieldDef, fieldName: key, fieldParserOptions: { excludeInvalidListItems }, value }); if (result.parsed !== undefined) { parsed[key] = result.parsed; } errors.unshift(...result.errors); }); var resulting = allowExtraFields ? _objectSpread(_objectSpread({}, input), parsed) : parsed; return { errors, parsed: resulting }; } describe() { if (arguments.length === 1 && typeof (arguments.length <= 0 ? undefined : arguments[0]) === 'string') { this.__setMetaData('description', arguments.length <= 0 ? undefined : arguments[0]); return this; } var commentsConfig = arguments.length <= 0 ? undefined : arguments[0]; invariantType({ commentsConfig }, 'object', { commentsConfig }); var definition = this.definition; Object.entries(commentsConfig).forEach(_ref => { var [name, comment] = _ref; invariantType({ [name]: definition[name] }, 'object', "\"".concat(name, "\" is not in object definition.")); definition[name].description = comment || ''; }); return this; } clone(handler) { var parsed = parseField(this); var input = extendObjectDefinition(parsed); return handler(input); } get id() { return this.meta.id; } get nonNullId() { var id = this.meta.id; if (!id) { throw new RuntimeError('Expected object to be identified.', { definition: this.definition }); } return id; } identify(id) { if (id && id === this.id) return this; if (this.id) { throw new Error("Trying to replace existing id \"".concat(this.id, "\" with \"").concat(id, "\". You can clone it to create a new Object.")); } expectedType({ id }, 'string', 'truthy'); this.__setMetaData('id', id); ObjectType.register.set(id, this); return this; } static reset() { return _asyncToGenerator(function* () { ObjectType.register.clear(); var promises = []; try { // only available server side or in tests var { GraphQLParser, GraphType } = CircularDeps; promises.push(GraphQLParser.reset(), GraphType.reset()); } catch (e) { if (typeof window === 'undefined') { throw e; } } yield Promise.all(promises); })(); } static is(input) { return isObject(input); } } _defineProperty(ObjectType, "__isBacklandObject", true); _defineProperty(ObjectType, "register", createStore()); _defineProperty(ObjectType, "getOrSet", (id, def) => { var existing = ObjectType.register.has(id) && ObjectType.register.get(id); if (existing) { return existing; } // @ts-ignore return new ObjectType(() => { def = typeof def === 'function' ? def() : def; return def; }).identify(id); }); export var BacklandObject = ObjectType; export function createObjectType() { var _getObjectDefinitionM, _getObjectDefinitionM2; var fields = arguments.length === 2 ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 0 ? undefined : arguments[0]; var id = arguments.length === 2 ? arguments.length <= 0 ? undefined : arguments[0] : undefined; if (id) { // @ts-ignore return ObjectType.getOrSet(id, fields); } var idFromDefinition = (_getObjectDefinitionM = getObjectDefinitionMetaField(fields)) === null || _getObjectDefinitionM === void 0 ? void 0 : (_getObjectDefinitionM2 = _getObjectDefinitionM.def) === null || _getObjectDefinitionM2 === void 0 ? void 0 : _getObjectDefinitionM2.id; if (idFromDefinition) { return ObjectType.getOrSet(idFromDefinition, fields); } return new ObjectType(fields); } export var createBacklandObject = createObjectType; export var createSchema = createObjectType; export var resetTypesCache = ObjectType.reset; //# sourceMappingURL=ObjectType.js.map