芝麻web文件管理V1.00
编辑当前文件:/home/freeclou/app.optimyar.com/backend/node_modules/sequelize/lib/data-types.js
'use strict'; const util = require('util'); const _ = require('lodash'); const wkx = require('wkx'); const sequelizeErrors = require('./errors'); const Validator = require('./utils/validator-extras').validator; const momentTz = require('moment-timezone'); const moment = require('moment'); const { logger } = require('./utils/logger'); const warnings = {}; const { classToInvokable } = require('./utils/classToInvokable'); class ABSTRACT { toString(options) { return this.toSql(options); } toSql() { return this.key; } stringify(value, options) { if (this._stringify) { return this._stringify(value, options); } return value; } bindParam(value, options) { if (this._bindParam) { return this._bindParam(value, options); } return options.bindParam(this.stringify(value, options)); } static toString() { return this.name; } static warn(link, text) { if (!warnings[text]) { warnings[text] = true; logger.warn(`${text} \n>> Check: ${link}`); } } static extend(oldType) { return new this(oldType.options); } } ABSTRACT.prototype.dialectTypes = ''; /** * STRING A variable length string */ class STRING extends ABSTRACT { /** * @param {number} [length=255] length of string * @param {boolean} [binary=false] Is this binary? */ constructor(length, binary) { super(); const options = typeof length === 'object' && length || { length, binary }; this.options = options; this._binary = options.binary; this._length = options.length || 255; } toSql() { return `VARCHAR(${this._length})${this._binary ? ' BINARY' : ''}`; } validate(value) { if (Object.prototype.toString.call(value) !== '[object String]') { if (this.options.binary && Buffer.isBuffer(value) || typeof value === 'number') { return true; } throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); } return true; } get BINARY() { this._binary = true; this.options.binary = true; return this; } static get BINARY() { return new this().BINARY; } } /** * CHAR A fixed length string */ class CHAR extends STRING { /** * @param {number} [length=255] length of string * @param {boolean} [binary=false] Is this binary? */ constructor(length, binary) { super(typeof length === 'object' && length || { length, binary }); } toSql() { return `CHAR(${this._length})${this._binary ? ' BINARY' : ''}`; } } /** * Unlimited length TEXT column */ class TEXT extends ABSTRACT { /** * @param {string} [length=''] could be tiny, medium, long. */ constructor(length) { super(); const options = typeof length === 'object' && length || { length }; this.options = options; this._length = options.length || ''; } toSql() { switch (this._length.toLowerCase()) { case 'tiny': return 'TINYTEXT'; case 'medium': return 'MEDIUMTEXT'; case 'long': return 'LONGTEXT'; default: return this.key; } } validate(value) { if (typeof value !== 'string') { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); } return true; } } /** * An unlimited length case-insensitive text column. * Original case is preserved but acts case-insensitive when comparing values (such as when finding or unique constraints). * Only available in Postgres and SQLite. * */ class CITEXT extends ABSTRACT { toSql() { return 'CITEXT'; } validate(value) { if (typeof value !== 'string') { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); } return true; } } /** * Base number type which is used to build other types */ class NUMBER extends ABSTRACT { /** * @param {Object} options type options * @param {string|number} [options.length] length of type, like `INT(4)` * @param {boolean} [options.zerofill] Is zero filled? * @param {boolean} [options.unsigned] Is unsigned? * @param {string|number} [options.decimals] number of decimal points, used with length `FLOAT(5, 4)` * @param {string|number} [options.precision] defines precision for decimal type * @param {string|number} [options.scale] defines scale for decimal type */ constructor(options = {}) { super(); if (typeof options === 'number') { options = { length: options }; } this.options = options; this._length = options.length; this._zerofill = options.zerofill; this._decimals = options.decimals; this._precision = options.precision; this._scale = options.scale; this._unsigned = options.unsigned; } toSql() { let result = this.key; if (this._length) { result += `(${this._length}`; if (typeof this._decimals === 'number') { result += `,${this._decimals}`; } result += ')'; } if (this._unsigned) { result += ' UNSIGNED'; } if (this._zerofill) { result += ' ZEROFILL'; } return result; } validate(value) { if (!Validator.isFloat(String(value))) { throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value)); } return true; } _stringify(number) { if (typeof number === 'number' || typeof number === 'boolean' || number === null || number === undefined) { return number; } if (typeof number.toString === 'function') { return number.toString(); } return number; } get UNSIGNED() { this._unsigned = true; this.options.unsigned = true; return this; } get ZEROFILL() { this._zerofill = true; this.options.zerofill = true; return this; } static get UNSIGNED() { return new this().UNSIGNED; } static get ZEROFILL() { return new this().ZEROFILL; } } /** * A 32 bit integer */ class INTEGER extends NUMBER { validate(value) { if (!Validator.isInt(String(value))) { throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value)); } return true; } } /** * A 8 bit integer */ class TINYINT extends INTEGER { } /** * A 16 bit integer */ class SMALLINT extends INTEGER { } /** * A 24 bit integer */ class MEDIUMINT extends INTEGER { } /** * A 64 bit integer */ class BIGINT extends INTEGER { } /** * Floating point number (4-byte precision). */ class FLOAT extends NUMBER { /** * @param {string|number} [length] length of type, like `FLOAT(4)` * @param {string|number} [decimals] number of decimal points, used with length `FLOAT(5, 4)` */ constructor(length, decimals) { super(typeof length === 'object' && length || { length, decimals }); } validate(value) { if (!Validator.isFloat(String(value))) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid float', value)); } return true; } } /** * Floating point number (4-byte precision). */ class REAL extends NUMBER { /** * @param {string|number} [length] length of type, like `REAL(4)` * @param {string|number} [decimals] number of decimal points, used with length `REAL(5, 4)` */ constructor(length, decimals) { super(typeof length === 'object' && length || { length, decimals }); } } /** * Floating point number (8-byte precision). */ class DOUBLE extends NUMBER { /** * @param {string|number} [length] length of type, like `DOUBLE PRECISION(25)` * @param {string|number} [decimals] number of decimal points, used with length `DOUBLE PRECISION(25, 10)` */ constructor(length, decimals) { super(typeof length === 'object' && length || { length, decimals }); } } /** * Decimal type, variable precision, take length as specified by user */ class DECIMAL extends NUMBER { /** * @param {string|number} [precision] defines precision * @param {string|number} [scale] defines scale */ constructor(precision, scale) { super(typeof precision === 'object' && precision || { precision, scale }); } toSql() { if (this._precision || this._scale) { return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(',')})`; } return 'DECIMAL'; } validate(value) { if (!Validator.isDecimal(String(value))) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid decimal', value)); } return true; } } // TODO: Create intermediate class const protoExtensions = { escape: false, _value(value) { if (isNaN(value)) { return 'NaN'; } if (!isFinite(value)) { const sign = value < 0 ? '-' : ''; return `${sign}Infinity`; } return value; }, _stringify(value) { return `'${this._value(value)}'`; }, _bindParam(value, options) { return options.bindParam(this._value(value)); } }; for (const floating of [FLOAT, DOUBLE, REAL]) { Object.assign(floating.prototype, protoExtensions); } /** * A boolean / tinyint column, depending on dialect */ class BOOLEAN extends ABSTRACT { toSql() { return 'TINYINT(1)'; } validate(value) { if (!Validator.isBoolean(String(value))) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid boolean', value)); } return true; } _sanitize(value) { if (value !== null && value !== undefined) { if (Buffer.isBuffer(value) && value.length === 1) { // Bit fields are returned as buffers value = value[0]; } const type = typeof value; if (type === 'string') { // Only take action on valid boolean strings. return value === 'true' ? true : value === 'false' ? false : value; } if (type === 'number') { // Only take action on valid boolean integers. return value === 1 ? true : value === 0 ? false : value; } } return value; } } BOOLEAN.parse = BOOLEAN.prototype._sanitize; /** * A time column * */ class TIME extends ABSTRACT { toSql() { return 'TIME'; } } /** * Date column with timezone, default is UTC */ class DATE extends ABSTRACT { /** * @param {string|number} [length] precision to allow storing milliseconds */ constructor(length) { super(); const options = typeof length === 'object' && length || { length }; this.options = options; this._length = options.length || ''; } toSql() { return 'DATETIME'; } validate(value) { if (!Validator.isDate(String(value))) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid date', value)); } return true; } _sanitize(value, options) { if ((!options || options && !options.raw) && !(value instanceof Date) && !!value) { return new Date(value); } return value; } _isChanged(value, originalValue) { if (originalValue && !!value && (value === originalValue || value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime())) { return false; } // not changed when set to same empty value if (!originalValue && !value && originalValue === value) { return false; } return true; } _applyTimezone(date, options) { if (options.timezone) { if (momentTz.tz.zone(options.timezone)) { return momentTz(date).tz(options.timezone); } return date = moment(date).utcOffset(options.timezone); } return momentTz(date); } _stringify(date, options) { date = this._applyTimezone(date, options); // Z here means current timezone, _not_ UTC return date.format('YYYY-MM-DD HH:mm:ss.SSS Z'); } } /** * A date only column (no timestamp) */ class DATEONLY extends ABSTRACT { toSql() { return 'DATE'; } _stringify(date) { return moment(date).format('YYYY-MM-DD'); } _sanitize(value, options) { if ((!options || options && !options.raw) && !!value) { return moment(value).format('YYYY-MM-DD'); } return value; } _isChanged(value, originalValue) { if (originalValue && !!value && originalValue === value) { return false; } // not changed when set to same empty value if (!originalValue && !value && originalValue === value) { return false; } return true; } } /** * A key / value store column. Only available in Postgres. */ class HSTORE extends ABSTRACT { validate(value) { if (!_.isPlainObject(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid hstore', value)); } return true; } } /** * A JSON string column. Available in MySQL, Postgres and SQLite */ class JSONTYPE extends ABSTRACT { validate() { return true; } _stringify(value) { return JSON.stringify(value); } } /** * A binary storage JSON column. Only available in Postgres. */ class JSONB extends JSONTYPE { } /** * A default value of the current timestamp */ class NOW extends ABSTRACT { } /** * Binary storage */ class BLOB extends ABSTRACT { /** * @param {string} [length=''] could be tiny, medium, long. */ constructor(length) { super(); const options = typeof length === 'object' && length || { length }; this.options = options; this._length = options.length || ''; } toSql() { switch (this._length.toLowerCase()) { case 'tiny': return 'TINYBLOB'; case 'medium': return 'MEDIUMBLOB'; case 'long': return 'LONGBLOB'; default: return this.key; } } validate(value) { if (typeof value !== 'string' && !Buffer.isBuffer(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid blob', value)); } return true; } _stringify(value) { if (!Buffer.isBuffer(value)) { if (Array.isArray(value)) { value = Buffer.from(value); } else { value = Buffer.from(value.toString()); } } const hex = value.toString('hex'); return this._hexify(hex); } _hexify(hex) { return `X'${hex}'`; } _bindParam(value, options) { if (!Buffer.isBuffer(value)) { if (Array.isArray(value)) { value = Buffer.from(value); } else { value = Buffer.from(value.toString()); } } return options.bindParam(value); } } BLOB.prototype.escape = false; /** * Range types are data types representing a range of values of some element type (called the range's subtype). * Only available in Postgres. See [the Postgres documentation](http://www.postgresql.org/docs/9.4/static/rangetypes.html) for more details */ class RANGE extends ABSTRACT { /** * @param {ABSTRACT} subtype A subtype for range, like RANGE(DATE) */ constructor(subtype) { super(); const options = _.isPlainObject(subtype) ? subtype : { subtype }; if (!options.subtype) options.subtype = new INTEGER(); if (typeof options.subtype === 'function') { options.subtype = new options.subtype(); } this._subtype = options.subtype.key; this.options = options; } validate(value) { if (!Array.isArray(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value)); } if (value.length !== 2) { throw new sequelizeErrors.ValidationError('A range must be an array with two elements'); } return true; } } /** * A column storing a unique universal identifier. * Use with `UUIDV1` or `UUIDV4` for default values. */ class UUID extends ABSTRACT { validate(value, options) { if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value)); } return true; } } /** * A default unique universal identifier generated following the UUID v1 standard */ class UUIDV1 extends ABSTRACT { validate(value, options) { if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value)); } return true; } } /** * A default unique universal identifier generated following the UUID v4 standard */ class UUIDV4 extends ABSTRACT { validate(value, options) { if (typeof value !== 'string' || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuidv4', value)); } return true; } } /** * A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model that is returned to the user but not stored in the DB. * * You could also use it to validate a value before permuting and storing it. VIRTUAL also takes a return type and dependency fields as arguments * If a virtual attribute is present in `attributes` it will automatically pull in the extra fields as well. * Return type is mostly useful for setups that rely on types like GraphQL. * * @example
Checking password length before hashing it
* sequelize.define('user', { * password_hash: DataTypes.STRING, * password: { * type: DataTypes.VIRTUAL, * set: function (val) { * // Remember to set the data value, otherwise it won't be validated * this.setDataValue('password', val); * this.setDataValue('password_hash', this.salt + val); * }, * validate: { * isLongEnough: function (val) { * if (val.length < 7) { * throw new Error("Please choose a longer password") * } * } * } * } * }) * * # In the above code the password is stored plainly in the password field so it can be validated, but is never stored in the DB. * * @example
Virtual with dependency fields
* { * active: { * type: new DataTypes.VIRTUAL(DataTypes.BOOLEAN, ['createdAt']), * get: function() { * return this.get('createdAt') > Date.now() - (7 * 24 * 60 * 60 * 1000) * } * } * } * */ class VIRTUAL extends ABSTRACT { /** * @param {ABSTRACT} [ReturnType] return type for virtual type * @param {Array} [fields] array of fields this virtual type is dependent on */ constructor(ReturnType, fields) { super(); if (typeof ReturnType === 'function') ReturnType = new ReturnType(); this.returnType = ReturnType; this.fields = fields; } } /** * An enumeration, Postgres Only * * @example * DataTypes.ENUM('value', 'another value') * DataTypes.ENUM(['value', 'another value']) * DataTypes.ENUM({ * values: ['value', 'another value'] * }) */ class ENUM extends ABSTRACT { /** * @param {...any|{ values: any[] }|any[]} args either array of values or options object with values array. It also supports variadic values */ constructor(...args) { super(); const value = args[0]; const options = typeof value === 'object' && !Array.isArray(value) && value || { values: args.reduce((result, element) => { return result.concat(Array.isArray(element) ? element : [element]); }, []) }; this.values = options.values; this.options = options; } validate(value) { if (!this.values.includes(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid choice in %j', value, this.values)); } return true; } } /** * An array of `type`. Only available in Postgres. * * @example * DataTypes.ARRAY(DataTypes.DECIMAL) */ class ARRAY extends ABSTRACT { /** * @param {ABSTRACT} type type of array values */ constructor(type) { super(); const options = _.isPlainObject(type) ? type : { type }; this.options = options; this.type = typeof options.type === 'function' ? new options.type() : options.type; } toSql() { return `${this.type.toSql()}[]`; } validate(value) { if (!Array.isArray(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid array', value)); } return true; } static is(obj, type) { return obj instanceof ARRAY && obj.type instanceof type; } } /** * A column storing Geometry information. * It is only available in PostgreSQL (with PostGIS), MariaDB or MySQL. * * GeoJSON is accepted as input and returned as output. * * In PostGIS, the GeoJSON is parsed using the PostGIS function `ST_GeomFromGeoJSON`. * In MySQL it is parsed using the function `GeomFromText`. * * Therefore, one can just follow the [GeoJSON spec](http://geojson.org/geojson-spec.html) for handling geometry objects. See the following examples: * * @example
Defining a Geometry type attribute
* DataTypes.GEOMETRY * DataTypes.GEOMETRY('POINT') * DataTypes.GEOMETRY('POINT', 4326) * * @example
Create a new point
* const point = { type: 'Point', coordinates: [39.807222,-76.984722]}; * * User.create({username: 'username', geometry: point }); * * @example
Create a new linestring
* const line = { type: 'LineString', 'coordinates': [ [100.0, 0.0], [101.0, 1.0] ] }; * * User.create({username: 'username', geometry: line }); * * @example
Create a new polygon
* const polygon = { type: 'Polygon', coordinates: [ * [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], * [100.0, 1.0], [100.0, 0.0] ] * ]}; * * User.create({username: 'username', geometry: polygon }); * * @example
Create a new point with a custom SRID
* const point = { * type: 'Point', * coordinates: [39.807222,-76.984722], * crs: { type: 'name', properties: { name: 'EPSG:4326'} } * }; * * User.create({username: 'username', geometry: point }) * * * @see {@link DataTypes.GEOGRAPHY} */ class GEOMETRY extends ABSTRACT { /** * @param {string} [type] Type of geometry data * @param {string} [srid] SRID of type */ constructor(type, srid) { super(); const options = _.isPlainObject(type) ? type : { type, srid }; this.options = options; this.type = options.type; this.srid = options.srid; } _stringify(value, options) { return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; } _bindParam(value, options) { return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; } } GEOMETRY.prototype.escape = false; /** * A geography datatype represents two dimensional spacial objects in an elliptic coord system. * * __The difference from geometry and geography type:__ * * PostGIS 1.5 introduced a new spatial type called geography, which uses geodetic measurement instead of Cartesian measurement. * Coordinate points in the geography type are always represented in WGS 84 lon lat degrees (SRID 4326), * but measurement functions and relationships ST_Distance, ST_DWithin, ST_Length, and ST_Area always return answers in meters or assume inputs in meters. * * __What is best to use? It depends:__ * * When choosing between the geometry and geography type for data storage, you should consider what you’ll be using it for. * If all you do are simple measurements and relationship checks on your data, and your data covers a fairly large area, then most likely you’ll be better off storing your data using the new geography type. * Although the new geography data type can cover the globe, the geometry type is far from obsolete. * The geometry type has a much richer set of functions than geography, relationship checks are generally faster, and it has wider support currently across desktop and web-mapping tools * * @example
Defining a Geography type attribute
* DataTypes.GEOGRAPHY * DataTypes.GEOGRAPHY('POINT') * DataTypes.GEOGRAPHY('POINT', 4326) */ class GEOGRAPHY extends ABSTRACT { /** * @param {string} [type] Type of geography data * @param {string} [srid] SRID of type */ constructor(type, srid) { super(); const options = _.isPlainObject(type) ? type : { type, srid }; this.options = options; this.type = options.type; this.srid = options.srid; } _stringify(value, options) { return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; } _bindParam(value, options) { return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; } } GEOGRAPHY.prototype.escape = false; /** * The cidr type holds an IPv4 or IPv6 network specification. Takes 7 or 19 bytes. * * Only available for Postgres */ class CIDR extends ABSTRACT { validate(value) { if (typeof value !== 'string' || !Validator.isIPRange(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid CIDR', value)); } return true; } } /** * The INET type holds an IPv4 or IPv6 host address, and optionally its subnet. Takes 7 or 19 bytes * * Only available for Postgres */ class INET extends ABSTRACT { validate(value) { if (typeof value !== 'string' || !Validator.isIP(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid INET', value)); } return true; } } /** * The MACADDR type stores MAC addresses. Takes 6 bytes * * Only available for Postgres * */ class MACADDR extends ABSTRACT { validate(value) { if (typeof value !== 'string' || !Validator.isMACAddress(value)) { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid MACADDR', value)); } return true; } } /** * A convenience class holding commonly used data types. The data types are used when defining a new model using `Sequelize.define`, like this: * ```js * sequelize.define('model', { * column: DataTypes.INTEGER * }) * ``` * When defining a model you can just as easily pass a string as type, but often using the types defined here is beneficial. For example, using `DataTypes.BLOB`, mean * that that column will be returned as an instance of `Buffer` when being fetched by sequelize. * * To provide a length for the data type, you can invoke it like a function: `INTEGER(2)` * * Some data types have special properties that can be accessed in order to change the data type. * For example, to get an unsigned integer with zerofill you can do `DataTypes.INTEGER.UNSIGNED.ZEROFILL`. * The order you access the properties in do not matter, so `DataTypes.INTEGER.ZEROFILL.UNSIGNED` is fine as well. * * * All number types (`INTEGER`, `BIGINT`, `FLOAT`, `DOUBLE`, `REAL`, `DECIMAL`) expose the properties `UNSIGNED` and `ZEROFILL` * * The `CHAR` and `STRING` types expose the `BINARY` property * * Three of the values provided here (`NOW`, `UUIDV1` and `UUIDV4`) are special default values, that should not be used to define types. Instead they are used as shorthands for * defining default values. For example, to get a uuid field with a default value generated following v1 of the UUID standard: * ```js * sequelize.define('model', { * uuid: { * type: DataTypes.UUID, * defaultValue: DataTypes.UUIDV1, * primaryKey: true * } * }) * ``` * There may be times when you want to generate your own UUID conforming to some other algorithm. This is accomplished * using the defaultValue property as well, but instead of specifying one of the supplied UUID types, you return a value * from a function. * ```js * sequelize.define('model', { * uuid: { * type: DataTypes.UUID, * defaultValue: function() { * return generateMyId() * }, * primaryKey: true * } * }) * ``` */ const DataTypes = module.exports = { ABSTRACT, STRING, CHAR, TEXT, NUMBER, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, FLOAT, TIME, DATE, DATEONLY, BOOLEAN, NOW, BLOB, DECIMAL, NUMERIC: DECIMAL, UUID, UUIDV1, UUIDV4, HSTORE, JSON: JSONTYPE, JSONB, VIRTUAL, ARRAY, ENUM, RANGE, REAL, 'DOUBLE PRECISION': DOUBLE, DOUBLE, GEOMETRY, GEOGRAPHY, CIDR, INET, MACADDR, CITEXT }; _.each(DataTypes, (dataType, name) => { // guard for aliases if (!Object.prototype.hasOwnProperty.call(dataType, 'key')) { dataType.types = {}; dataType.key = dataType.prototype.key = name; } }); const dialectMap = {}; dialectMap.postgres = require('./dialects/postgres/data-types')(DataTypes); dialectMap.mysql = require('./dialects/mysql/data-types')(DataTypes); dialectMap.mariadb = require('./dialects/mariadb/data-types')(DataTypes); dialectMap.sqlite = require('./dialects/sqlite/data-types')(DataTypes); dialectMap.mssql = require('./dialects/mssql/data-types')(DataTypes); const dialectList = _.values(dialectMap); for (const dataTypes of dialectList) { _.each(dataTypes, (DataType, key) => { if (!DataType.key) { DataType.key = DataType.prototype.key = key; } }); } // Wrap all data types to not require `new` for (const dataTypes of [DataTypes, ...dialectList]) { _.each(dataTypes, (DataType, key) => { dataTypes[key] = classToInvokable(DataType); }); } Object.assign(DataTypes, dialectMap);