UNPKG

@backland/schema

Version:

TypeScript schema declaration and validation library with static type inference

474 lines (463 loc) 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _exportNames = { ObjectType: true, BacklandObject: true, createObjectType: true, createBacklandObject: true, createSchema: true, resetTypesCache: true }; exports.createBacklandObject = exports.ObjectType = exports.BacklandObject = void 0; exports.createObjectType = createObjectType; exports.resetTypesCache = exports.createSchema = void 0; var _utils = require("@backland/utils"); var _CircularDeps = require("./CircularDeps"); var _applyValidator = require("./applyValidator"); var _extendObjectDefinition = require("./extendObjectDefinition"); var _MetaFieldField = require("./fields/MetaFieldField"); var _fieldDefinitions = require("./fields/_fieldDefinitions"); Object.keys(_fieldDefinitions).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _fieldDefinitions[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _fieldDefinitions[key]; } }); }); var _getObjectErrors = require("./getObjectErrors"); var _getObjectHelpers = require("./getObjectHelpers"); var _implementObject = require("./implementObject"); Object.keys(_implementObject).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _implementObject[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _implementObject[key]; } }); }); var _objectInferenceUtils = require("./objectInferenceUtils"); Object.keys(_objectInferenceUtils).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _objectInferenceUtils[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _objectInferenceUtils[key]; } }); }); var _parseObjectDefinition = require("./parseObjectDefinition"); Object.keys(_parseObjectDefinition).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _parseObjectDefinition[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _parseObjectDefinition[key]; } }); }); var _withCache = require("./withCache"); var _parseFields = require("./fields/_parseFields"); Object.keys(_parseFields).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _parseFields[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _parseFields[key]; } }); }); class ObjectType { get __isBacklandObject() { return true; } static __isBacklandObject = true; constructor(objectDef) { this.inputDefinition = objectDef; this.__withCache = (0, _withCache.withCache)(this); } get definition() { return this.__definitionCache = this.__definitionCache || (() => { const objectDef = typeof this.inputDefinition === 'function' ? this.inputDefinition(_CircularDeps.CircularDeps) : this.inputDefinition; if (!objectDef || typeof objectDef !== 'object') { throw new Error('Expected object definition to be an object'); } return (0, _parseObjectDefinition.parseObjectDefinition)(objectDef).definition; })(); } get description() { return this.meta.description; } __hidden = false; set hidden(value) { this.__hidden = value; } get hidden() { return this.__hidden; } // definition without metadata (name, etc) cleanDefinition() { // @ts-ignore return (0, _MetaFieldField.cleanMetaField)(this.clone(el => el.def())); } edit() { return (0, _extendObjectDefinition.extendObjectDefinition)(this); } get meta() { // @ts-ignore return this.definition[_MetaFieldField.objectMetaFieldKey].def; } __setMetaData(k, value) { // @ts-ignore this.definition[_MetaFieldField.objectMetaFieldKey].def[k] = value; } parse(input, options) { const { customMessage, customErrorMessage } = options || {}; const { errors, parsed } = this.safeParse(input, options); if (errors.length) { let e_message = errors.join(' \n'); if (this.id) { e_message = `${this.id}: ${e_message}`; } const err = (0, _applyValidator.parseValidationError)(input, customMessage || customErrorMessage, e_message); err.isObjectValidationError = true; err.fieldErrors = errors; throw err; } return parsed; } softParse = (input, options = {}) => { return this.parse(input, { ...options, allowExtraFields: true }); }; validate(input) { try { this.parse(input); return true; } catch (e) { return false; } } safeParse(input, options) { const { partial = false, excludeInvalidListItems, includeHidden = true, allowExtraFields, exclude } = options || {}; const objectDef = { ...this.definition }; if (this.__hidden && !includeHidden) return { errors: [], parsed: {} }; const errors = []; const parsed = {}; const fieldInputsList = []; if (!input || typeof input !== 'object' || Array.isArray(input)) { throw new _utils.RuntimeError(`Invalid input. Expected object, found ${(0, _utils.getTypeName)(input)}`, { input }); } input = { ...input }; const inputKeys = (value => { if (exclude) { return value.filter(el => !exclude.includes(el)); } return value; })(Object.keys(input)); // @ts-ignore let fields = (options === null || options === void 0 ? void 0 : options.fields) || Object.keys(this.definition); // === Start handling {[K: string}: any}|{[K: number}: any} === const anyStringKey = fields.find(field => field === _fieldDefinitions.SpecialObjectKeyEnum.$string); const anyNumberKey = fields.find(field => field === _fieldDefinitions.SpecialObjectKeyEnum.$number); if (anyNumberKey || anyStringKey) { const allFieldsSet = new Set(fields); const keysNotDefined = inputKeys.filter(k => !allFieldsSet.has(k)); fields = fields.filter(k => !_fieldDefinitions.SpecialObjectKeyEnum.list.includes(k)); if (anyStringKey) { const def = objectDef[anyStringKey]; keysNotDefined.forEach(key => { objectDef[key] = def; }); } else if (anyNumberKey) { const 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 ((0, _MetaFieldField.isMetaFieldKey)(currField)) return; if (exclude && exclude.includes(currField)) return; // @ts-ignore const fieldDef = objectDef[currField]; if (!includeHidden && fieldDef.hidden) return; if (fieldDef.type === 'alias') { const instance = (0, _parseObjectDefinition.__getCachedFieldInstance)(fieldDef); return fieldInputsList.push({ composer: instance.composer, fieldDef, key: currField, value: undefined }); } const value = input[currField]; const 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 const notAliasFieldsResults = fieldInputsList.map(entry => { const { key, fieldDef, value } = entry; const result = (0, _getObjectErrors.validateObjectFields)({ definition: fieldDef, fieldName: key, fieldParserOptions: { excludeInvalidListItems }, value }); if (result.parsed !== undefined) { parsed[key] = result.parsed; } if (!entry.composer) { errors.unshift(...result.errors); } return { ...entry, ...result }; }); // handling aliases notAliasFieldsResults.forEach(field => { let { key, composer } = field; if (!composer) return; const value = composer.compose(parsed); const fieldDef = composer.def; const result = (0, _getObjectErrors.validateObjectFields)({ definition: fieldDef, fieldName: key, fieldParserOptions: { excludeInvalidListItems }, value }); if (result.parsed !== undefined) { parsed[key] = result.parsed; } errors.unshift(...result.errors); }); const resulting = allowExtraFields ? { ...input, ...parsed } : parsed; return { errors, parsed: resulting }; } describe(...descriptions) { if (descriptions.length === 1 && typeof descriptions[0] === 'string') { this.__setMetaData('description', descriptions[0]); return this; } const commentsConfig = descriptions[0]; (0, _utils.invariantType)({ commentsConfig }, 'object', { commentsConfig }); const definition = this.definition; Object.entries(commentsConfig).forEach(([name, comment]) => { (0, _utils.invariantType)({ [name]: definition[name] }, 'object', `"${name}" is not in object definition.`); definition[name].description = comment || ''; }); return this; } clone(handler) { const parsed = (0, _parseObjectDefinition.parseField)(this); const input = (0, _extendObjectDefinition.extendObjectDefinition)(parsed); return handler(input); } get id() { return this.meta.id; } get nonNullId() { const id = this.meta.id; if (!id) { throw new _utils.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 "${this.id}" with "${id}". You can clone it to create a new Object.`); } (0, _utils.expectedType)({ id }, 'string', 'truthy'); this.__setMetaData('id', id); ObjectType.register.set(id, this); return this; } helpers = () => { return this.__withCache('helpers', () => (0, _getObjectHelpers.getObjectHelpers)(this)); }; toGraphQL = name => { if (name) { this.identify(name); } if (!this.id) { throw new _utils.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 const { GraphQLParser } = _CircularDeps.CircularDeps.GraphQLParser; return GraphQLParser.objectToGraphQL({ object: this }); }; graphqlType = options => { return this.toGraphQL().getType(options); }; graphqlInterfaceType = options => { return this.toGraphQL().interfaceType(options); }; graphqlPrint = () => { return this.toGraphQL().typeToString(); }; typescriptPrint = options => { // @ts-ignore circular return _CircularDeps.CircularDeps.objectToTypescript(this.nonNullId, // @ts-ignore this, options); }; graphqlTypeToString = () => { return this.toGraphQL().typeToString(); }; graphqlInputType = options => { return this.toGraphQL().getInputType(options); }; implement = (name, ...parents) => { return (0, _implementObject.implementObject)(name, this.definition, ...parents); }; static async reset() { ObjectType.register.clear(); const promises = []; try { // only available server side or in tests const { GraphQLParser, GraphType } = _CircularDeps.CircularDeps; promises.push(GraphQLParser.reset(), GraphType.reset()); } catch (e) { if (typeof window === 'undefined') { throw e; } } await Promise.all(promises); } static register = (0, _utils.createStore)(); /** * Get an Object with the provided id * or set a new Object in the register if not found. * @param id * @param def */ static getOrSet = (id, def) => { const 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); }; graphQLMiddleware = []; addGraphQLMiddleware = middleware => { this.graphQLMiddleware.push(...(0, _utils.ensureArray)(middleware)); }; static is(input) { return (0, _objectInferenceUtils.isObject)(input); } } exports.ObjectType = ObjectType; const BacklandObject = ObjectType; exports.BacklandObject = BacklandObject; function createObjectType(...args) { var _getObjectDefinitionM, _getObjectDefinitionM2; const fields = args.length === 2 ? args[1] : args[0]; const id = args.length === 2 ? args[0] : undefined; if (id) { // @ts-ignore return ObjectType.getOrSet(id, fields); } const idFromDefinition = (_getObjectDefinitionM = (0, _MetaFieldField.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); } const createBacklandObject = createObjectType; exports.createBacklandObject = createBacklandObject; const createSchema = createObjectType; exports.createSchema = createSchema; const resetTypesCache = ObjectType.reset; exports.resetTypesCache = resetTypesCache; //# sourceMappingURL=ObjectType.js.map