sequelize
Version:
Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more.
1,228 lines • 113 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
const assert = require("assert");
const _ = require("lodash");
const Dottie = require("dottie");
const Utils = require("./utils");
const { logger } = require("./utils/logger");
const BelongsTo = require("./associations/belongs-to");
const BelongsToMany = require("./associations/belongs-to-many");
const InstanceValidator = require("./instance-validator");
const QueryTypes = require("./query-types");
const sequelizeErrors = require("./errors");
const Association = require("./associations/base");
const HasMany = require("./associations/has-many");
const DataTypes = require("./data-types");
const Hooks = require("./hooks");
const associationsMixin = require("./associations/mixin");
const Op = require("./operators");
const { noDoubleNestedGroup } = require("./utils/deprecations");
const validQueryKeywords = /* @__PURE__ */ new Set([
"where",
"attributes",
"paranoid",
"include",
"order",
"limit",
"offset",
"transaction",
"lock",
"raw",
"logging",
"benchmark",
"having",
"searchPath",
"rejectOnEmpty",
"plain",
"scope",
"group",
"through",
"defaults",
"distinct",
"primary",
"exception",
"type",
"hooks",
"force",
"name"
]);
const nonCascadingOptions = ["include", "attributes", "originalAttributes", "order", "where", "limit", "offset", "plain", "group", "having"];
class Model {
static get queryInterface() {
return this.sequelize.getQueryInterface();
}
static get queryGenerator() {
return this.queryInterface.queryGenerator;
}
get sequelize() {
return this.constructor.sequelize;
}
constructor(values = {}, options = {}) {
if (!this.constructor._overwrittenAttributesChecked) {
this.constructor._overwrittenAttributesChecked = true;
setTimeout(() => {
const overwrittenAttributes = [];
for (const key of Object.keys(this.constructor._attributeManipulation)) {
if (Object.prototype.hasOwnProperty.call(this, key)) {
overwrittenAttributes.push(key);
}
}
if (overwrittenAttributes.length > 0) {
logger.warn(`Model ${JSON.stringify(this.constructor.name)} is declaring public class fields for attribute(s): ${overwrittenAttributes.map((attr) => JSON.stringify(attr)).join(", ")}.
These class fields are shadowing Sequelize's attribute getters & setters.
See https://sequelize.org/main/manual/model-basics.html#caveat-with-public-class-fields`);
}
}, 0);
}
options = __spreadValues({
isNewRecord: true,
_schema: this.constructor._schema,
_schemaDelimiter: this.constructor._schemaDelimiter
}, options);
if (options.attributes) {
options.attributes = options.attributes.map((attribute) => Array.isArray(attribute) ? attribute[1] : attribute);
}
if (!options.includeValidated) {
this.constructor._conformIncludes(options, this.constructor);
if (options.include) {
this.constructor._expandIncludeAll(options);
this.constructor._validateIncludedElements(options);
}
}
this.dataValues = {};
this._previousDataValues = {};
this.uniqno = 1;
this._changed = /* @__PURE__ */ new Set();
this._options = options;
this.isNewRecord = options.isNewRecord;
this._initValues(values, options);
}
_initValues(values, options) {
let defaults;
let key;
values = __spreadValues({}, values);
if (options.isNewRecord) {
defaults = {};
if (this.constructor._hasDefaultValues) {
defaults = _.mapValues(this.constructor._defaultValues, (valueFn) => {
const value = valueFn();
return value && value instanceof Utils.SequelizeMethod ? value : _.cloneDeep(value);
});
}
if (this.constructor.primaryKeyAttributes.length) {
this.constructor.primaryKeyAttributes.forEach((primaryKeyAttribute) => {
if (!Object.prototype.hasOwnProperty.call(defaults, primaryKeyAttribute)) {
defaults[primaryKeyAttribute] = null;
}
});
}
if (this.constructor._timestampAttributes.createdAt && defaults[this.constructor._timestampAttributes.createdAt]) {
this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.createdAt];
}
if (this.constructor._timestampAttributes.updatedAt && defaults[this.constructor._timestampAttributes.updatedAt]) {
this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.updatedAt];
}
if (this.constructor._timestampAttributes.deletedAt && defaults[this.constructor._timestampAttributes.deletedAt]) {
this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.deletedAt];
}
for (key in defaults) {
if (values[key] === void 0) {
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), { raw: true });
delete values[key];
}
}
}
this.set(values, options);
}
static _paranoidClause(model, options = {}) {
if (options.include) {
for (const include of options.include) {
this._paranoidClause(include.model, include);
}
}
if (_.get(options, "groupedLimit.on.options.paranoid")) {
const throughModel = _.get(options, "groupedLimit.on.through.model");
if (throughModel) {
options.groupedLimit.through = this._paranoidClause(throughModel, options.groupedLimit.through);
}
}
if (!model.options.timestamps || !model.options.paranoid || options.paranoid === false) {
return options;
}
const deletedAtCol = model._timestampAttributes.deletedAt;
const deletedAtAttribute = model.rawAttributes[deletedAtCol];
const deletedAtObject = {};
let deletedAtDefaultValue = Object.prototype.hasOwnProperty.call(deletedAtAttribute, "defaultValue") ? deletedAtAttribute.defaultValue : null;
deletedAtDefaultValue = deletedAtDefaultValue || {
[Op.eq]: null
};
deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
if (Utils.isWhereEmpty(options.where)) {
options.where = deletedAtObject;
} else {
options.where = { [Op.and]: [deletedAtObject, options.where] };
}
return options;
}
static _addDefaultAttributes() {
const tail = {};
let head = {};
if (!_.some(this.rawAttributes, "primaryKey")) {
if ("id" in this.rawAttributes) {
throw new Error(`A column called 'id' was added to the attributes of '${this.tableName}' but not marked with 'primaryKey: true'`);
}
head = {
id: {
type: new DataTypes.INTEGER(),
allowNull: false,
primaryKey: true,
autoIncrement: true,
_autoGenerated: true
}
};
}
if (this._timestampAttributes.createdAt) {
tail[this._timestampAttributes.createdAt] = {
type: DataTypes.DATE,
allowNull: false,
_autoGenerated: true
};
}
if (this._timestampAttributes.updatedAt) {
tail[this._timestampAttributes.updatedAt] = {
type: DataTypes.DATE,
allowNull: false,
_autoGenerated: true
};
}
if (this._timestampAttributes.deletedAt) {
tail[this._timestampAttributes.deletedAt] = {
type: DataTypes.DATE,
_autoGenerated: true
};
}
if (this._versionAttribute) {
tail[this._versionAttribute] = {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
_autoGenerated: true
};
}
const newRawAttributes = __spreadValues(__spreadValues({}, head), this.rawAttributes);
_.each(tail, (value, attr) => {
if (newRawAttributes[attr] === void 0) {
newRawAttributes[attr] = value;
}
});
this.rawAttributes = newRawAttributes;
if (!Object.keys(this.primaryKeys).length) {
this.primaryKeys.id = this.rawAttributes.id;
}
}
static getAttributes() {
return this.rawAttributes;
}
static _findAutoIncrementAttribute() {
this.autoIncrementAttribute = null;
for (const name in this.rawAttributes) {
if (Object.prototype.hasOwnProperty.call(this.rawAttributes, name)) {
const definition = this.rawAttributes[name];
if (definition && definition.autoIncrement) {
if (this.autoIncrementAttribute) {
throw new Error("Invalid Instance definition. Only one autoincrement field allowed.");
}
this.autoIncrementAttribute = name;
}
}
}
}
static _conformIncludes(options, self) {
if (!options.include)
return;
if (!Array.isArray(options.include)) {
options.include = [options.include];
} else if (!options.include.length) {
delete options.include;
return;
}
options.include = options.include.map((include) => this._conformInclude(include, self));
}
static _transformStringAssociation(include, self) {
if (self && typeof include === "string") {
if (!Object.prototype.hasOwnProperty.call(self.associations, include)) {
throw new Error(`Association with alias "${include}" does not exist on ${self.name}`);
}
return self.associations[include];
}
return include;
}
static _conformInclude(include, self) {
if (include) {
let model;
if (include._pseudo)
return include;
include = this._transformStringAssociation(include, self);
if (include instanceof Association) {
if (self && include.target.name === self.name) {
model = include.source;
} else {
model = include.target;
}
return { model, association: include, as: include.as };
}
if (include.prototype && include.prototype instanceof Model) {
return { model: include };
}
if (_.isPlainObject(include)) {
if (include.association) {
include.association = this._transformStringAssociation(include.association, self);
if (self && include.association.target.name === self.name) {
model = include.association.source;
} else {
model = include.association.target;
}
if (!include.model)
include.model = model;
if (!include.as)
include.as = include.association.as;
this._conformIncludes(include, model);
return include;
}
if (include.model) {
this._conformIncludes(include, include.model);
return include;
}
if (include.all) {
this._conformIncludes(include);
return include;
}
}
}
throw new Error("Include unexpected. Element has to be either a Model, an Association or an object.");
}
static _expandIncludeAllElement(includes, include) {
let all = include.all;
delete include.all;
if (all !== true) {
if (!Array.isArray(all)) {
all = [all];
}
const validTypes = {
BelongsTo: true,
HasOne: true,
HasMany: true,
One: ["BelongsTo", "HasOne"],
Has: ["HasOne", "HasMany"],
Many: ["HasMany"]
};
for (let i = 0; i < all.length; i++) {
const type = all[i];
if (type === "All") {
all = true;
break;
}
const types = validTypes[type];
if (!types) {
throw new sequelizeErrors.EagerLoadingError(`include all '${type}' is not valid - must be BelongsTo, HasOne, HasMany, One, Has, Many or All`);
}
if (types !== true) {
all.splice(i, 1);
i--;
for (let j = 0; j < types.length; j++) {
if (!all.includes(types[j])) {
all.unshift(types[j]);
i++;
}
}
}
}
}
const nested = include.nested;
if (nested) {
delete include.nested;
if (!include.include) {
include.include = [];
} else if (!Array.isArray(include.include)) {
include.include = [include.include];
}
}
const used = [];
(function addAllIncludes(parent, includes2) {
_.forEach(parent.associations, (association) => {
if (all !== true && !all.includes(association.associationType)) {
return;
}
const model = association.target;
const as = association.options.as;
const predicate = { model };
if (as) {
predicate.as = as;
}
if (_.some(includes2, predicate)) {
return;
}
if (nested && used.includes(model)) {
return;
}
used.push(parent);
const thisInclude = Utils.cloneDeep(include);
thisInclude.model = model;
if (as) {
thisInclude.as = as;
}
includes2.push(thisInclude);
if (nested) {
addAllIncludes(model, thisInclude.include);
if (thisInclude.include.length === 0)
delete thisInclude.include;
}
});
used.pop();
})(this, includes);
}
static _validateIncludedElements(options, tableNames) {
if (!options.model)
options.model = this;
tableNames = tableNames || {};
options.includeNames = [];
options.includeMap = {};
options.hasSingleAssociation = false;
options.hasMultiAssociation = false;
if (!options.parent) {
options.topModel = options.model;
options.topLimit = options.limit;
}
options.include = options.include.map((include) => {
include = this._conformInclude(include);
include.parent = options;
include.topLimit = options.topLimit;
this._validateIncludedElement.call(options.model, include, tableNames, options);
if (include.duplicating === void 0) {
include.duplicating = include.association.isMultiAssociation;
}
include.hasDuplicating = include.hasDuplicating || include.duplicating;
include.hasRequired = include.hasRequired || include.required;
options.hasDuplicating = options.hasDuplicating || include.hasDuplicating;
options.hasRequired = options.hasRequired || include.required;
options.hasWhere = options.hasWhere || include.hasWhere || !!include.where;
return include;
});
for (const include of options.include) {
include.hasParentWhere = options.hasParentWhere || !!options.where;
include.hasParentRequired = options.hasParentRequired || !!options.required;
if (include.subQuery !== false && options.hasDuplicating && options.topLimit) {
if (include.duplicating) {
include.subQuery = include.subQuery || false;
include.subQueryFilter = include.hasRequired;
} else {
include.subQuery = include.hasRequired;
include.subQueryFilter = false;
}
} else {
include.subQuery = include.subQuery || false;
if (include.duplicating) {
include.subQueryFilter = include.subQuery;
} else {
include.subQueryFilter = false;
include.subQuery = include.subQuery || include.hasParentRequired && include.hasRequired && !include.separate;
}
}
options.includeMap[include.as] = include;
options.includeNames.push(include.as);
if (options.topModel === options.model && options.subQuery === void 0 && options.topLimit) {
if (include.subQuery) {
options.subQuery = include.subQuery;
} else if (include.hasDuplicating) {
options.subQuery = true;
}
}
options.hasIncludeWhere = options.hasIncludeWhere || include.hasIncludeWhere || !!include.where;
options.hasIncludeRequired = options.hasIncludeRequired || include.hasIncludeRequired || !!include.required;
if (include.association.isMultiAssociation || include.hasMultiAssociation) {
options.hasMultiAssociation = true;
}
if (include.association.isSingleAssociation || include.hasSingleAssociation) {
options.hasSingleAssociation = true;
}
}
if (options.topModel === options.model && options.subQuery === void 0) {
options.subQuery = false;
}
return options;
}
static _validateIncludedElement(include, tableNames, options) {
tableNames[include.model.getTableName()] = true;
if (include.attributes && !options.raw) {
include.model._expandAttributes(include);
include.originalAttributes = include.model._injectDependentVirtualAttributes(include.attributes);
include = Utils.mapFinderOptions(include, include.model);
if (include.attributes.length) {
_.each(include.model.primaryKeys, (attr, key) => {
if (!include.attributes.some((includeAttr) => {
if (attr.field !== key) {
return Array.isArray(includeAttr) && includeAttr[0] === attr.field && includeAttr[1] === key;
}
return includeAttr === key;
})) {
include.attributes.unshift(key);
}
});
}
} else {
include = Utils.mapFinderOptions(include, include.model);
}
if (include._pseudo) {
if (!include.attributes) {
include.attributes = Object.keys(include.model.tableAttributes);
}
return Utils.mapFinderOptions(include, include.model);
}
const association = include.association || this._getIncludedAssociation(include.model, include.as);
include.association = association;
include.as = association.as;
if (include.association.through && Object(include.association.through.model) === include.association.through.model) {
if (!include.include)
include.include = [];
const through = include.association.through;
include.through = _.defaults(include.through || {}, {
model: through.model,
as: through.model.name,
association: {
isSingleAssociation: true
},
_pseudo: true,
parent: include
});
if (through.scope) {
include.through.where = include.through.where ? { [Op.and]: [include.through.where, through.scope] } : through.scope;
}
include.include.push(include.through);
tableNames[through.tableName] = true;
}
let model;
if (include.model.scoped === true) {
model = include.model;
} else {
model = include.association.target.name === include.model.name ? include.association.target : include.association.source;
}
model._injectScope(include);
if (!include.attributes) {
include.attributes = Object.keys(include.model.tableAttributes);
}
include = Utils.mapFinderOptions(include, include.model);
if (include.required === void 0) {
include.required = !!include.where;
}
if (include.association.scope) {
include.where = include.where ? { [Op.and]: [include.where, include.association.scope] } : include.association.scope;
}
if (include.limit && include.separate === void 0) {
include.separate = true;
}
if (include.separate === true) {
if (!(include.association instanceof HasMany)) {
throw new Error("Only HasMany associations support include.separate");
}
include.duplicating = false;
if (options.attributes && options.attributes.length && !_.flattenDepth(options.attributes, 2).includes(association.sourceKey)) {
options.attributes.push(association.sourceKey);
}
if (include.attributes && include.attributes.length && !_.flattenDepth(include.attributes, 2).includes(association.foreignKey)) {
include.attributes.push(association.foreignKey);
}
}
if (Object.prototype.hasOwnProperty.call(include, "include")) {
this._validateIncludedElements.call(include.model, include, tableNames);
}
return include;
}
static _getIncludedAssociation(targetModel, targetAlias) {
const associations = this.getAssociations(targetModel);
let association = null;
if (associations.length === 0) {
throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is not associated to ${this.name}!`);
}
if (associations.length === 1) {
association = this.getAssociationForAlias(targetModel, targetAlias);
if (association) {
return association;
}
if (targetAlias) {
const existingAliases = this.getAssociations(targetModel).map((association2) => association2.as);
throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} using an alias. You've included an alias (${targetAlias}), but it does not match the alias(es) defined in your association (${existingAliases.join(", ")}).`);
}
throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} using an alias. You must use the 'as' keyword to specify the alias within your include statement.`);
}
association = this.getAssociationForAlias(targetModel, targetAlias);
if (!association) {
throw new sequelizeErrors.EagerLoadingError(`${targetModel.name} is associated to ${this.name} multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include.`);
}
return association;
}
static _expandIncludeAll(options) {
const includes = options.include;
if (!includes) {
return;
}
for (let index = 0; index < includes.length; index++) {
const include = includes[index];
if (include.all) {
includes.splice(index, 1);
index--;
this._expandIncludeAllElement(includes, include);
}
}
includes.forEach((include) => {
this._expandIncludeAll.call(include.model, include);
});
}
static _conformIndex(index) {
if (!index.fields) {
throw new Error('Missing "fields" property for index definition');
}
index = _.defaults(index, {
type: "",
parser: null
});
if (index.type && index.type.toLowerCase() === "unique") {
index.unique = true;
delete index.type;
}
return index;
}
static _uniqIncludes(options) {
if (!options.include)
return;
options.include = _(options.include).groupBy((include) => `${include.model && include.model.name}-${include.as}`).map((includes) => this._assignOptions(...includes)).value();
}
static _baseMerge(...args) {
_.assignWith(...args);
this._conformIncludes(args[0], this);
this._uniqIncludes(args[0]);
return args[0];
}
static _mergeFunction(objValue, srcValue, key) {
if (Array.isArray(objValue) && Array.isArray(srcValue)) {
return _.union(objValue, srcValue);
}
if (["where", "having"].includes(key)) {
if (this.options && this.options.whereMergeStrategy === "and") {
return combineWheresWithAnd(objValue, srcValue);
}
if (srcValue instanceof Utils.SequelizeMethod) {
srcValue = { [Op.and]: srcValue };
}
if (_.isPlainObject(objValue) && _.isPlainObject(srcValue)) {
return Object.assign(objValue, srcValue);
}
} else if (key === "attributes" && _.isPlainObject(objValue) && _.isPlainObject(srcValue)) {
return _.assignWith(objValue, srcValue, (objValue2, srcValue2) => {
if (Array.isArray(objValue2) && Array.isArray(srcValue2)) {
return _.union(objValue2, srcValue2);
}
});
}
if (srcValue) {
return Utils.cloneDeep(srcValue, true);
}
return srcValue === void 0 ? objValue : srcValue;
}
static _assignOptions(...args) {
return this._baseMerge(...args, this._mergeFunction.bind(this));
}
static _defaultsOptions(target, opts) {
return this._baseMerge(target, opts, (srcValue, objValue, key) => {
return this._mergeFunction(objValue, srcValue, key);
});
}
static init(attributes, options = {}) {
if (!options.sequelize) {
throw new Error("No Sequelize instance passed");
}
this.sequelize = options.sequelize;
const globalOptions = this.sequelize.options;
options = Utils.merge(_.cloneDeep(globalOptions.define), options);
if (!options.modelName) {
options.modelName = this.name;
}
options = Utils.merge({
name: {
plural: Utils.pluralize(options.modelName),
singular: Utils.singularize(options.modelName)
},
indexes: [],
omitNull: globalOptions.omitNull,
schema: globalOptions.schema
}, options);
this.sequelize.runHooks("beforeDefine", attributes, options);
if (options.modelName !== this.name) {
Object.defineProperty(this, "name", { value: options.modelName });
}
delete options.modelName;
this.options = __spreadValues({
timestamps: true,
validate: {},
freezeTableName: false,
underscored: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: null,
schema: null,
schemaDelimiter: "",
defaultScope: {},
scopes: {},
indexes: [],
whereMergeStrategy: "overwrite"
}, options);
if (this.sequelize.isDefined(this.name)) {
this.sequelize.modelManager.removeModel(this.sequelize.modelManager.getModel(this.name));
}
this.associations = {};
this._setupHooks(options.hooks);
this.underscored = this.options.underscored;
if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? this.name : Utils.underscoredIf(Utils.pluralize(this.name), this.underscored);
} else {
this.tableName = this.options.tableName;
}
this._schema = this.options.schema;
this._schemaDelimiter = this.options.schemaDelimiter;
_.each(options.validate, (validator, validatorType) => {
if (Object.prototype.hasOwnProperty.call(attributes, validatorType)) {
throw new Error(`A model validator function must not have the same name as a field. Model: ${this.name}, field/validation name: ${validatorType}`);
}
if (typeof validator !== "function") {
throw new Error(`Members of the validate option must be functions. Model: ${this.name}, error with validate member ${validatorType}`);
}
});
if (!_.includes(["and", "overwrite"], this.options && this.options.whereMergeStrategy)) {
throw new Error(`Invalid value ${this.options && this.options.whereMergeStrategy} for whereMergeStrategy. Allowed values are 'and' and 'overwrite'.`);
}
this.rawAttributes = _.mapValues(attributes, (attribute, name) => {
attribute = this.sequelize.normalizeAttribute(attribute);
if (attribute.type === void 0) {
throw new Error(`Unrecognized datatype for attribute "${this.name}.${name}"`);
}
if (attribute.allowNull !== false && _.get(attribute, "validate.notNull")) {
throw new Error(`Invalid definition for "${this.name}.${name}", "notNull" validator is only allowed with "allowNull:false"`);
}
if (_.get(attribute, "references.model.prototype") instanceof Model) {
attribute.references.model = attribute.references.model.getTableName();
}
return attribute;
});
const tableName = this.getTableName();
this._indexes = this.options.indexes.map((index) => Utils.nameIndex(this._conformIndex(index), tableName));
this.primaryKeys = {};
this._readOnlyAttributes = /* @__PURE__ */ new Set();
this._timestampAttributes = {};
if (this.options.timestamps) {
for (const key of ["createdAt", "updatedAt", "deletedAt"]) {
if (!["undefined", "string", "boolean"].includes(typeof this.options[key])) {
throw new Error(`Value for "${key}" option must be a string or a boolean, got ${typeof this.options[key]}`);
}
if (this.options[key] === "") {
throw new Error(`Value for "${key}" option cannot be an empty string`);
}
}
if (this.options.createdAt !== false) {
this._timestampAttributes.createdAt = typeof this.options.createdAt === "string" ? this.options.createdAt : "createdAt";
this._readOnlyAttributes.add(this._timestampAttributes.createdAt);
}
if (this.options.updatedAt !== false) {
this._timestampAttributes.updatedAt = typeof this.options.updatedAt === "string" ? this.options.updatedAt : "updatedAt";
this._readOnlyAttributes.add(this._timestampAttributes.updatedAt);
}
if (this.options.paranoid && this.options.deletedAt !== false) {
this._timestampAttributes.deletedAt = typeof this.options.deletedAt === "string" ? this.options.deletedAt : "deletedAt";
this._readOnlyAttributes.add(this._timestampAttributes.deletedAt);
}
}
if (this.options.version) {
this._versionAttribute = typeof this.options.version === "string" ? this.options.version : "version";
this._readOnlyAttributes.add(this._versionAttribute);
}
this._hasReadOnlyAttributes = this._readOnlyAttributes.size > 0;
this._addDefaultAttributes();
this.refreshAttributes();
this._findAutoIncrementAttribute();
this._scope = this.options.defaultScope;
this._scopeNames = ["defaultScope"];
this.sequelize.modelManager.addModel(this);
this.sequelize.runHooks("afterDefine", this);
return this;
}
static refreshAttributes() {
const attributeManipulation = {};
this.prototype._customGetters = {};
this.prototype._customSetters = {};
["get", "set"].forEach((type) => {
const opt = `${type}terMethods`;
const funcs = __spreadValues({}, this.options[opt]);
const _custom = type === "get" ? this.prototype._customGetters : this.prototype._customSetters;
_.each(funcs, (method, attribute) => {
_custom[attribute] = method;
if (type === "get") {
funcs[attribute] = function() {
return this.get(attribute);
};
}
if (type === "set") {
funcs[attribute] = function(value) {
return this.set(attribute, value);
};
}
});
_.each(this.rawAttributes, (options, attribute) => {
if (Object.prototype.hasOwnProperty.call(options, type)) {
_custom[attribute] = options[type];
}
if (type === "get") {
funcs[attribute] = function() {
return this.get(attribute);
};
}
if (type === "set") {
funcs[attribute] = function(value) {
return this.set(attribute, value);
};
}
});
_.each(funcs, (fct, name) => {
if (!attributeManipulation[name]) {
attributeManipulation[name] = {
configurable: true
};
}
attributeManipulation[name][type] = fct;
});
});
this._dataTypeChanges = {};
this._dataTypeSanitizers = {};
this._hasBooleanAttributes = false;
this._hasDateAttributes = false;
this._jsonAttributes = /* @__PURE__ */ new Set();
this._virtualAttributes = /* @__PURE__ */ new Set();
this._defaultValues = {};
this.prototype.validators = {};
this.fieldRawAttributesMap = {};
this.primaryKeys = {};
this.uniqueKeys = {};
_.each(this.rawAttributes, (definition, name) => {
definition.type = this.sequelize.normalizeDataType(definition.type);
definition.Model = this;
definition.fieldName = name;
definition._modelAttribute = true;
if (definition.field === void 0) {
definition.field = Utils.underscoredIf(name, this.underscored);
}
if (definition.primaryKey === true) {
this.primaryKeys[name] = definition;
}
this.fieldRawAttributesMap[definition.field] = definition;
if (definition.type._sanitize) {
this._dataTypeSanitizers[name] = definition.type._sanitize;
}
if (definition.type._isChanged) {
this._dataTypeChanges[name] = definition.type._isChanged;
}
if (definition.type instanceof DataTypes.BOOLEAN) {
this._hasBooleanAttributes = true;
} else if (definition.type instanceof DataTypes.DATE || definition.type instanceof DataTypes.DATEONLY) {
this._hasDateAttributes = true;
} else if (definition.type instanceof DataTypes.JSON) {
this._jsonAttributes.add(name);
} else if (definition.type instanceof DataTypes.VIRTUAL) {
this._virtualAttributes.add(name);
}
if (Object.prototype.hasOwnProperty.call(definition, "defaultValue")) {
this._defaultValues[name] = () => Utils.toDefaultValue(definition.defaultValue, this.sequelize.options.dialect);
}
if (Object.prototype.hasOwnProperty.call(definition, "unique") && definition.unique) {
let idxName;
if (typeof definition.unique === "object" && Object.prototype.hasOwnProperty.call(definition.unique, "name")) {
idxName = definition.unique.name;
} else if (typeof definition.unique === "string") {
idxName = definition.unique;
} else {
idxName = `${this.tableName}_${name}_unique`;
}
const idx = this.uniqueKeys[idxName] || { fields: [] };
idx.fields.push(definition.field);
idx.msg = idx.msg || definition.unique.msg || null;
idx.name = idxName || false;
idx.column = name;
idx.customIndex = definition.unique !== true;
this.uniqueKeys[idxName] = idx;
}
if (Object.prototype.hasOwnProperty.call(definition, "validate")) {
this.prototype.validators[name] = definition.validate;
}
if (definition.index === true && definition.type instanceof DataTypes.JSONB) {
this._indexes.push(Utils.nameIndex(this._conformIndex({
fields: [definition.field || name],
using: "gin"
}), this.getTableName()));
delete definition.index;
}
});
this.fieldAttributeMap = _.reduce(this.fieldRawAttributesMap, (map, value, key) => {
if (key !== value.fieldName) {
map[key] = value.fieldName;
}
return map;
}, {});
this._hasJsonAttributes = !!this._jsonAttributes.size;
this._hasVirtualAttributes = !!this._virtualAttributes.size;
this._hasDefaultValues = !_.isEmpty(this._defaultValues);
this.tableAttributes = _.omitBy(this.rawAttributes, (_a, key) => this._virtualAttributes.has(key));
this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
this.prototype._hasCustomSetters = Object.keys(this.prototype._customSetters).length;
for (const key of Object.keys(attributeManipulation)) {
if (Object.prototype.hasOwnProperty.call(Model.prototype, key)) {
this.sequelize.log(`Not overriding built-in method from model attribute: ${key}`);
continue;
}
Object.defineProperty(this.prototype, key, attributeManipulation[key]);
}
this.prototype.rawAttributes = this.rawAttributes;
this.prototype._isAttribute = (key) => Object.prototype.hasOwnProperty.call(this.prototype.rawAttributes, key);
this.primaryKeyAttributes = Object.keys(this.primaryKeys);
this.primaryKeyAttribute = this.primaryKeyAttributes[0];
if (this.primaryKeyAttribute) {
this.primaryKeyField = this.rawAttributes[this.primaryKeyAttribute].field || this.primaryKeyAttribute;
}
this._hasPrimaryKeys = this.primaryKeyAttributes.length > 0;
this._isPrimaryKey = (key) => this.primaryKeyAttributes.includes(key);
this._attributeManipulation = attributeManipulation;
}
static removeAttribute(attribute) {
delete this.rawAttributes[attribute];
this.refreshAttributes();
}
static async sync(options) {
options = __spreadValues(__spreadValues({}, this.options), options);
options.hooks = options.hooks === void 0 ? true : !!options.hooks;
const attributes = this.tableAttributes;
const rawAttributes = this.fieldRawAttributesMap;
if (options.hooks) {
await this.runHooks("beforeSync", options);
}
const tableName = this.getTableName(options);
let tableExists;
if (options.force) {
await this.drop(options);
tableExists = false;
} else {
tableExists = await this.queryInterface.tableExists(tableName, options);
}
if (!tableExists) {
await this.queryInterface.createTable(tableName, attributes, options, this);
} else {
await this.queryInterface.ensureEnums(tableName, attributes, options, this);
}
if (tableExists && options.alter) {
const tableInfos = await Promise.all([
this.queryInterface.describeTable(tableName, options),
this.queryInterface.getForeignKeyReferencesForTable(tableName, options)
]);
const columns = tableInfos[0];
const foreignKeyReferences = tableInfos[1];
const removedConstraints = {};
for (const columnName in attributes) {
if (!Object.prototype.hasOwnProperty.call(attributes, columnName))
continue;
if (!columns[columnName] && !columns[attributes[columnName].field]) {
await this.queryInterface.addColumn(tableName, attributes[columnName].field || columnName, attributes[columnName], options);
}
}
if (options.alter === true || typeof options.alter === "object" && options.alter.drop !== false) {
for (const columnName in columns) {
if (!Object.prototype.hasOwnProperty.call(columns, columnName))
continue;
const currentAttribute = rawAttributes[columnName];
if (!currentAttribute) {
await this.queryInterface.removeColumn(tableName, columnName, options);
continue;
}
if (currentAttribute.primaryKey)
continue;
const references = currentAttribute.references;
if (currentAttribute.references) {
const database = this.sequelize.config.database;
const schema = this.sequelize.config.schema;
for (const foreignKeyReference of foreignKeyReferences) {
const constraintName = foreignKeyReference.constraintName;
if (!!constraintName && foreignKeyReference.tableCatalog === database && (schema ? foreignKeyReference.tableSchema === schema : true) && foreignKeyReference.referencedTableName === references.model && foreignKeyReference.referencedColumnName === references.key && (schema ? foreignKeyReference.referencedTableSchema === schema : true) && !removedConstraints[constraintName]) {
await this.queryInterface.removeConstraint(tableName, constraintName, options);
removedConstraints[constraintName] = true;
}
}
}
await this.queryInterface.changeColumn(tableName, columnName, currentAttribute, options);
}
}
}
const existingIndexes = await this.queryInterface.showIndex(tableName, options);
const missingIndexes = this._indexes.filter((item1) => !existingIndexes.some((item2) => item1.name === item2.name)).sort((index1, index2) => {
if (this.sequelize.options.dialect === "postgres") {
if (index1.concurrently === true)
return 1;
if (index2.concurrently === true)
return -1;
}
return 0;
});
for (const index of missingIndexes) {
await this.queryInterface.addIndex(tableName, __spreadValues(__spreadValues({}, options), index));
}
if (options.hooks) {
await this.runHooks("afterSync", options);
}
return this;
}
static async drop(options) {
return await this.queryInterface.dropTable(this.getTableName(options), options);
}
static async dropSchema(schema) {
return await this.queryInterface.dropSchema(schema);
}
static schema(schema, options) {
const clone = class extends this {
};
Object.defineProperty(clone, "name", { value: this.name });
clone._schema = schema;
if (options) {
if (typeof options === "string") {
clone._schemaDelimiter = options;
} else if (options.schemaDelimiter) {
clone._schemaDelimiter = options.schemaDelimiter;
}
}
return clone;
}
static getTableName() {
return this.queryGenerator.addSchema(this);
}
static unscoped() {
return this.scope();
}
static addScope(name, scope, options) {
options = __spreadValues({ override: false }, options);
if ((name === "defaultScope" && Object.keys(this.options.defaultScope).length > 0 || name in this.options.scopes) && options.override === false) {
throw new Error(`The scope ${name} already exists. Pass { override: true } as options to silence this error`);
}
if (name === "defaultScope") {
this.options.defaultScope = this._scope = scope;
} else {
this.options.scopes[name] = scope;
}
}
static scope(option) {
const self = class extends this {
};
let scope;
let scopeName;
Object.defineProperty(self, "name", { value: this.name });
self._scope = {};
self._scopeNames = [];
self.scoped = true;
if (!option) {
return self;
}
const options = _.flatten(arguments);
for (const option2 of options) {
scope = null;
scopeName = null;
if (_.isPlainObject(option2)) {
if (option2.method) {
if (Array.isArray(option2.method) && !!self.options.scopes[option2.method[0]]) {
scopeName = option2.method[0];
scope = self.options.scopes[scopeName].apply(self, option2.method.slice(1));
} else if (self.options.scopes[option2.method]) {
scopeName = option2.method;
scope = self.options.scopes[scopeName].apply(self);
}
} else {
scope = option2;
}
} else if (option2 === "defaultScope" && _.isPlainObject(self.options.defaultScope)) {
scope = self.options.defaultScope;
} else {
scopeName = option2;
scope = self.options.scopes[scopeName];
if (typeof scope === "function") {
scope = scope();
}
}
if (scope) {
this._conformIncludes(scope, this);
this._assignOptions(self._scope, Utils.cloneDeep(scope));
self._scopeNames.push(scopeName ? scopeName : "defaultScope");
} else {
throw new sequelizeErrors.SequelizeScopeError(`Invalid scope ${scopeName} called.`);
}
}
return self;
}
static async findAll(options) {
if (options !== void 0 && !_.isPlainObject(options)) {
throw new sequelizeErrors.QueryError("The argument passed to findAll must be an options object, use findByPk if you wish to pass a single primary key value");
}
if (options !== void 0 && options.attributes) {
if (!Array.isArray(options.attributes) && !_.isPlainObject(options.attributes)) {
throw new sequelizeErrors.QueryError("The attributes option must be an array of column names or an object");
}
}
this.warnOnInvalidOptions(options, Object.keys(this.rawAttributes));
const tableNames = {};
tableNames[this.getTableName(options)] = true;
options = Utils.cloneDeep(options);
if (options.transaction === void 0 && this.sequelize.constructor._cls) {
const t = this.sequelize.constructor._cls.get("transaction");
if (t) {
options.transaction = t;
}
}
_.defaults(options, { hooks: true });
options.rejectOnEmpty = Object.prototype.hasOwnProperty.call(options, "rejectOnEmpty") ? options.rejectOnEmpty : this.options.rejectOnEmpty;
this._injectScope(options);
if (options.hooks) {
await this.runHooks("beforeFind", options);
}
this._conformIncludes(options, this);
this._expandAttributes(options);
this._expandIncludeAll(options);
if (options.hooks) {
await this.runHooks("beforeFindAfterExpandIncludeAll", options);
}
options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
if (options.include) {
options.hasJoin = true;
this._validateIncludedElements(options, tableNames);
if (options.attributes && !options.raw && this.primaryKeyAttribute && !options.attributes.includes(this.primaryKeyAttribute) && (!options.group || !options.hasSingleAssociation || options.hasMultiAssociation)) {
options.attributes = [this.primaryKeyAttribute].concat(options.attributes);
}
}
if (!options.attributes) {
options.attributes = Object.keys(this.rawAttributes);
options.originalAttributes = this._injectDependentVirtualAttributes(options.attributes);
}
this.options.whereCollection = options.where || null;
Utils.mapFinderOptions(options, this);
options = this._paranoidClause(this, options);
if (options.hooks) {
await this.runHooks("beforeFindAfterOptions", options);
}
const selectOptions = __spreadProps(__spreadValues({}, options), { tableNames: Object.keys(tableNames) });
const results = await this.queryInterface.select(this, this.getTableName(selectOptions), selectOptions);
if (options.hooks) {
await this.runHooks("afterFind", results, options);
}
if (_.isEmpty(results) && options.rejectOnEmpty) {
if (typeof options.rejectOnEmpty === "function") {
throw new options.rejectOnEmpty();
}
if (typeof options.rejectOnEmpty === "object") {
throw options.rejectOnEmpty;
}
throw new sequelizeErrors.EmptyResultError();
}
return await Model._findSeparate(results, options);
}
static warnOnInvalidOptions(options, validColumnNames) {
if (!_.isPlainObject(options)) {
return;
}
const unrecognizedOptions = Object.keys(options).filter((k) => !validQueryKeywords.has(k));
const unexpectedModelAttributes = _.intersection(unrecognizedOptions, validColumnNames);
if (!options.where && unexpectedModelAttributes.length > 0) {
logger.warn(`Model attributes (${unexpectedModelAttributes.join(", ")}) passed into finder method options of model ${this.name}, but the options.where object is empty. Did you forget to use options.where?`);
}
}
static _injectDependentVirtualAttributes(attributes) {
if (!this._hasVirtualAttributes)
return attributes;
if (!attributes || !Array.isArray(attributes))
return attributes;
for (const attribute of attributes) {
if (this._virtualAttributes.has(attribute) && this.rawAttributes[attribute].type.fields) {
attributes = attributes.concat(this.rawAttributes[attribute].type.fields);
}
}
attributes = _.uniq(attributes);
return attributes;
}
static async _findSeparate(results, options) {
if (!options.include || options.raw || !results)
return results;
const original = results;
if (options.plain)
results = [results];
if (!results.length)
return original;
await Promise.all(options.include.map(async (include) => {
if (!include.separate) {
return await Model._findSeparate(results.reduce((memo, result) => {
let associations = result.get(include.association.as);
if (!associations)
return memo;
if (!Array.isArray(associations))
associations = [associations];
for (let i = 0, len = associations.length; i !== len; ++i) {
memo.push(associations[i]);
}
return memo;
}, []), __spreadProps(__spreadValues({}, _.omit(options, "include", "attributes", "order", "where", "limit", "offset", "plain", "scope")), {
include: include.include || []
}));
}
const map = await include.association.get(results, __spreadValues(__spreadValues({}, _.omit(options, nonCascadingOptions)), _.omit(include, ["parent", "association", "as", "originalAttributes"])));
for (const result of results) {
result.set(include.association.as, map[result.get(include.association.sourceKey)], { raw: true });
}
}));
return original;
}
static async findByPk(param, options) {
if ([null, void 0].includes(param)) {
return null;
}
options = Utils.cloneDeep(options) || {};
if (typeof param === "number" || typeof param === "bigint" || typeof param === "string" || Buffer.isBuffer(param)) {
options.where = {
[this.primaryKeyAttribute]: param
};
} else {
throw new Error(`Argument passed to findByPk is invalid: ${param}`);
}
return await this.findOne(options);
}
static async findOne(options) {
if (options !== void 0 && !_.isPlainObject(options)) {
throw new Error("The argument passed to findOne must be an options object, use findByPk if you wish to pass a single primary key value");
}
options = Utils.cloneDeep(options);
if (options.transaction === void 0 && this.sequeliz