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