@nocobase/database 0.19.0-alpha.9 → 0.20.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -84,14 +84,18 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
84
84
  findField(callback: (field: Field) => boolean): any;
85
85
  hasField(name: string): boolean;
86
86
  getField<F extends Field>(name: string): F;
87
+ getFields(): any[];
87
88
  addField(name: string, options: FieldOptions): Field;
88
89
  checkFieldType(name: string, options: FieldOptions): void;
90
+ correctOptions(options: any): void;
89
91
  setField(name: string, options: FieldOptions): Field;
90
92
  setFields(fields: FieldOptions[], resetFields?: boolean): void;
91
93
  resetFields(): void;
92
94
  remove(): Collection<any, any>;
93
95
  removeFieldFromDb(name: string, options?: QueryInterfaceOptions): Promise<void>;
94
- removeFromDb(options?: QueryInterfaceDropTableOptions): Promise<Collection<any, any>>;
96
+ removeFromDb(options?: QueryInterfaceDropTableOptions & {
97
+ dropCollection?: boolean;
98
+ }): Promise<Collection<any, any>>;
95
99
  existsInDb(options?: Transactionable): Promise<boolean>;
96
100
  removeField(name: string): void | Field;
97
101
  /**
package/lib/collection.js CHANGED
@@ -96,11 +96,15 @@ const _Collection = class _Collection extends import_events.EventEmitter {
96
96
  this.setSortable(options.sortable);
97
97
  }
98
98
  get filterTargetKey() {
99
- const targetKey = import_lodash.default.get(this.options, "filterTargetKey", this.model.primaryKeyAttribute);
100
- if (!targetKey && this.model.rawAttributes["id"]) {
101
- return "id";
99
+ var _a;
100
+ const targetKey = (_a = this.options) == null ? void 0 : _a.filterTargetKey;
101
+ if (targetKey && this.model.getAttributes()[targetKey]) {
102
+ return targetKey;
102
103
  }
103
- return targetKey;
104
+ if (this.model.primaryKeyAttributes.length > 1) {
105
+ return null;
106
+ }
107
+ return this.model.primaryKeyAttribute;
104
108
  }
105
109
  get name() {
106
110
  return this.options.name;
@@ -187,6 +191,9 @@ const _Collection = class _Collection extends import_events.EventEmitter {
187
191
  getField(name) {
188
192
  return this.fields.get(name);
189
193
  }
194
+ getFields() {
195
+ return [...this.fields.values()];
196
+ }
190
197
  addField(name, options) {
191
198
  return this.setField(name, options);
192
199
  }
@@ -211,6 +218,11 @@ const _Collection = class _Collection extends import_events.EventEmitter {
211
218
  throw new Error(`fields with same column must be of the same type ${JSON.stringify(options)}`);
212
219
  }
213
220
  }
221
+ correctOptions(options) {
222
+ if (options.primaryKey && options.autoIncrement) {
223
+ delete options.defaultValue;
224
+ }
225
+ }
214
226
  setField(name, options) {
215
227
  (0, import_utils.checkIdentifier)(name);
216
228
  this.checkFieldType(name, options);
@@ -233,6 +245,7 @@ const _Collection = class _Collection extends import_events.EventEmitter {
233
245
  }
234
246
  }
235
247
  }
248
+ this.correctOptions(options);
236
249
  this.emit("field.beforeAdd", name, options, { collection: this });
237
250
  const field = database.buildField(
238
251
  { name, ...options },
@@ -253,6 +266,10 @@ const _Collection = class _Collection extends import_events.EventEmitter {
253
266
  this.removeField(name);
254
267
  this.fields.set(name, field);
255
268
  this.emit("field.afterAdd", field);
269
+ this.db.emit("field.afterAdd", {
270
+ collection: this,
271
+ field
272
+ });
256
273
  if (this.isParent()) {
257
274
  for (const child of this.context.database.inheritanceMap.getChildren(this.name, {
258
275
  deep: false
@@ -306,9 +323,6 @@ const _Collection = class _Collection extends import_events.EventEmitter {
306
323
  field.remove();
307
324
  return;
308
325
  }
309
- if (this.model.primaryKeyAttributes.includes(this.name)) {
310
- return;
311
- }
312
326
  if (this.model.options.timestamps !== false) {
313
327
  let timestampsFields = ["createdAt", "updatedAt", "deletedAt"];
314
328
  if (this.db.options.underscored) {
@@ -341,8 +355,17 @@ const _Collection = class _Collection extends import_events.EventEmitter {
341
355
  if (await field.existsInDb({
342
356
  transaction: options == null ? void 0 : options.transaction
343
357
  }) && columnReferencesCount == 1) {
344
- const queryInterface = this.db.sequelize.getQueryInterface();
345
- await queryInterface.removeColumn(this.getTableNameWithSchema(), field.columnName(), options);
358
+ const columns = await this.model.sequelize.getQueryInterface().describeTable(this.getTableNameWithSchema(), options);
359
+ if (Object.keys(columns).length == 1) {
360
+ await this.removeFromDb({
361
+ ...options,
362
+ cascade: true,
363
+ dropCollection: false
364
+ });
365
+ } else {
366
+ const queryInterface = this.db.sequelize.getQueryInterface();
367
+ await queryInterface.removeColumn(this.getTableNameWithSchema(), field.columnName(), options);
368
+ }
346
369
  }
347
370
  field.remove();
348
371
  }
@@ -353,7 +376,9 @@ const _Collection = class _Collection extends import_events.EventEmitter {
353
376
  const queryInterface = this.db.sequelize.getQueryInterface();
354
377
  await queryInterface.dropTable(this.getTableNameWithSchema(), options);
355
378
  }
356
- return this.remove();
379
+ if ((options == null ? void 0 : options.dropCollection) !== false) {
380
+ return this.remove();
381
+ }
357
382
  }
358
383
  async existsInDb(options) {
359
384
  return this.db.queryInterface.collectionTableExists(this, options);
package/lib/database.js CHANGED
@@ -220,6 +220,9 @@ const _Database = class _Database extends import_events.EventEmitter {
220
220
  this.migrations = new import_migration.Migrations(context);
221
221
  this.sequelize.beforeDefine((model, opts2) => {
222
222
  if (this.options.tablePrefix) {
223
+ if (opts2.tableName && opts2.tableName.startsWith(this.options.tablePrefix)) {
224
+ return;
225
+ }
223
226
  opts2.tableName = `${this.options.tablePrefix}${opts2.tableName || opts2.modelName || opts2.name.plural}`;
224
227
  }
225
228
  });
@@ -445,7 +448,7 @@ const _Database = class _Database extends import_events.EventEmitter {
445
448
  return collection;
446
449
  }
447
450
  hasCollection(name) {
448
- return this.collections.has(name);
451
+ return !!this.getCollection(name);
449
452
  }
450
453
  removeCollection(name) {
451
454
  const collection = this.collections.get(name);
@@ -536,6 +539,9 @@ const _Database = class _Database extends import_events.EventEmitter {
536
539
  if (options.field && collection.options.underscored && !collection.isView()) {
537
540
  options.field = (0, import_utils2.snakeCase)(options.field);
538
541
  }
542
+ if (Object.prototype.hasOwnProperty.call(options, "defaultValue") && options.defaultValue === null) {
543
+ delete options.defaultValue;
544
+ }
539
545
  return new Field2(options, context);
540
546
  }
541
547
  async sync(options) {
@@ -91,6 +91,11 @@ const _EagerLoadingTree = class _EagerLoadingTree {
91
91
  continue;
92
92
  }
93
93
  const association = import_lodash.default.isString(include.association) ? eagerLoadingTreeParent.model.associations[include.association] : include.association;
94
+ if (!association) {
95
+ throw new Error(
96
+ `Association "${include.association}" not found in model "${eagerLoadingTreeParent.model.name}"`
97
+ );
98
+ }
94
99
  const associationType = association.associationType;
95
100
  const child = buildNode({
96
101
  model: association.target,
@@ -0,0 +1,2 @@
1
+ export declare class ZeroColumnTableError extends Error {
2
+ }
@@ -0,0 +1,31 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var zero_column_table_error_exports = {};
20
+ __export(zero_column_table_error_exports, {
21
+ ZeroColumnTableError: () => ZeroColumnTableError
22
+ });
23
+ module.exports = __toCommonJS(zero_column_table_error_exports);
24
+ const _ZeroColumnTableError = class _ZeroColumnTableError extends Error {
25
+ };
26
+ __name(_ZeroColumnTableError, "ZeroColumnTableError");
27
+ let ZeroColumnTableError = _ZeroColumnTableError;
28
+ // Annotate the CommonJS export names for ESM import in node:
29
+ 0 && (module.exports = {
30
+ ZeroColumnTableError
31
+ });
@@ -31,8 +31,11 @@ const _ArrayField = class _ArrayField extends import_field.Field {
31
31
  return import_sequelize.DataTypes.JSON;
32
32
  }
33
33
  sortValue = (model) => {
34
- const oldValue = model.get(this.options.name);
34
+ let oldValue = model.get(this.options.name);
35
35
  if (oldValue) {
36
+ if (typeof oldValue === "string") {
37
+ oldValue = JSON.parse(oldValue);
38
+ }
36
39
  const newValue = oldValue.sort();
37
40
  model.set(this.options.name, newValue);
38
41
  }
@@ -13,6 +13,7 @@ export declare class BelongsToField extends RelationField {
13
13
  onDelete: any;
14
14
  };
15
15
  reference(association: any): Reference;
16
+ checkAssociationKeys(): void;
16
17
  bind(): boolean;
17
18
  unbind(): void;
18
19
  }
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
  var __publicField = (obj, key, value) => {
21
31
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -26,7 +36,7 @@ __export(belongs_to_field_exports, {
26
36
  BelongsToField: () => BelongsToField
27
37
  });
28
38
  module.exports = __toCommonJS(belongs_to_field_exports);
29
- var import_lodash = require("lodash");
39
+ var import_lodash = __toESM(require("lodash"));
30
40
  var import_sequelize = require("sequelize");
31
41
  var import_utils = require("../utils");
32
42
  var import_relation_field = require("./relation-field");
@@ -51,6 +61,27 @@ const _BelongsToField = class _BelongsToField extends import_relation_field.Rela
51
61
  reference(association) {
52
62
  return _BelongsToField.toReference(this.database, association, this.options.onDelete);
53
63
  }
64
+ checkAssociationKeys() {
65
+ let { foreignKey, targetKey } = this.options;
66
+ if (!targetKey) {
67
+ targetKey = this.TargetModel.primaryKeyAttribute;
68
+ }
69
+ if (!foreignKey) {
70
+ foreignKey = import_lodash.default.camelCase(`${this.name}_${targetKey}`);
71
+ }
72
+ const targetKeyAttribute = this.TargetModel.rawAttributes[targetKey];
73
+ const foreignKeyAttribute = this.collection.model.rawAttributes[foreignKey];
74
+ if (!foreignKeyAttribute || !targetKeyAttribute) {
75
+ return;
76
+ }
77
+ const foreignKeyType = foreignKeyAttribute.type.constructor.toString();
78
+ const targetKeyType = targetKeyAttribute.type.constructor.toString();
79
+ if (!this.keyPairsTypeMatched(foreignKeyType, targetKeyType)) {
80
+ throw new Error(
81
+ `Foreign key "${foreignKey}" type "${foreignKeyType}" does not match target key "${targetKey}" type "${targetKeyType}" in belongs to relation "${this.name}" of collection "${this.collection.name}"`
82
+ );
83
+ }
84
+ }
54
85
  bind() {
55
86
  const { database, collection } = this.context;
56
87
  const Target = this.TargetModel;
@@ -58,6 +89,7 @@ const _BelongsToField = class _BelongsToField extends import_relation_field.Rela
58
89
  database.addPendingField(this);
59
90
  return false;
60
91
  }
92
+ this.checkAssociationKeys();
61
93
  if (collection.model.associations[this.name]) {
62
94
  delete collection.model.associations[this.name];
63
95
  }
@@ -70,6 +102,9 @@ const _BelongsToField = class _BelongsToField extends import_relation_field.Rela
70
102
  if (!this.options.foreignKey) {
71
103
  this.options.foreignKey = association.foreignKey;
72
104
  }
105
+ if (!this.options.targetKey) {
106
+ this.options.targetKey = association.targetKey;
107
+ }
73
108
  try {
74
109
  (0, import_utils.checkIdentifier)(this.options.foreignKey);
75
110
  } catch (error) {
@@ -1,4 +1,4 @@
1
- import { BelongsToManyOptions as SequelizeBelongsToManyOptions } from 'sequelize';
1
+ import { AssociationScope, BelongsToManyOptions as SequelizeBelongsToManyOptions } from 'sequelize';
2
2
  import { Reference } from '../features/ReferencesMap';
3
3
  import { MultipleRelationFieldOptions, RelationField } from './relation-field';
4
4
  export declare class BelongsToManyField extends RelationField {
@@ -6,6 +6,7 @@ export declare class BelongsToManyField extends RelationField {
6
6
  get through(): any;
7
7
  get otherKey(): any;
8
8
  references(association: any): Reference[];
9
+ checkAssociationKeys(database: any): void;
9
10
  bind(): boolean;
10
11
  unbind(): void;
11
12
  }
@@ -13,4 +14,7 @@ export interface BelongsToManyFieldOptions extends MultipleRelationFieldOptions,
13
14
  type: 'belongsToMany';
14
15
  target?: string;
15
16
  through?: string;
17
+ throughScope?: AssociationScope;
18
+ throughUnique?: boolean;
19
+ throughParanoid?: boolean;
16
20
  }
@@ -54,6 +54,47 @@ const _BelongsToManyField = class _BelongsToManyField extends import_relation_fi
54
54
  import_belongs_to_field.BelongsToField.toReference(db, sourceAssociation, onDelete)
55
55
  ];
56
56
  }
57
+ checkAssociationKeys(database) {
58
+ let { foreignKey, sourceKey, otherKey, targetKey } = this.options;
59
+ const through = this.through;
60
+ const throughCollection = database.getCollection(through);
61
+ if (!throughCollection) {
62
+ return;
63
+ }
64
+ if (!sourceKey) {
65
+ sourceKey = this.collection.model.primaryKeyAttribute;
66
+ }
67
+ if (!foreignKey) {
68
+ foreignKey = import_sequelize.Utils.camelize([import_sequelize.Utils.singularize(this.collection.model.name), sourceKey].join("_"));
69
+ }
70
+ if (!targetKey) {
71
+ targetKey = this.TargetModel.primaryKeyAttribute;
72
+ }
73
+ if (!otherKey) {
74
+ otherKey = import_sequelize.Utils.camelize([import_sequelize.Utils.singularize(this.TargetModel.name), targetKey].join("_"));
75
+ }
76
+ const foreignKeyAttribute = throughCollection.model.rawAttributes[foreignKey];
77
+ const otherKeyAttribute = throughCollection.model.rawAttributes[otherKey];
78
+ const sourceKeyAttribute = this.collection.model.rawAttributes[sourceKey];
79
+ const targetKeyAttribute = this.TargetModel.rawAttributes[targetKey];
80
+ if (!foreignKeyAttribute || !otherKeyAttribute || !sourceKeyAttribute || !targetKeyAttribute) {
81
+ return;
82
+ }
83
+ const foreignKeyType = foreignKeyAttribute.type.constructor.toString();
84
+ const otherKeyType = otherKeyAttribute.type.constructor.toString();
85
+ const sourceKeyType = sourceKeyAttribute.type.constructor.toString();
86
+ const targetKeyType = targetKeyAttribute.type.constructor.toString();
87
+ if (!this.keyPairsTypeMatched(foreignKeyType, sourceKeyType)) {
88
+ throw new Error(
89
+ `Foreign key "${foreignKey}" type "${foreignKeyType}" does not match source key "${sourceKey}" type "${sourceKeyType}" in belongs to many relation "${this.name}" of collection "${this.collection.name}"`
90
+ );
91
+ }
92
+ if (!this.keyPairsTypeMatched(otherKeyType, targetKeyType)) {
93
+ throw new Error(
94
+ `Other key "${otherKey}" type "${otherKeyType}" does not match target key "${targetKey}" type "${targetKeyType}" in belongs to many relation "${this.name}" of collection "${this.collection.name}"`
95
+ );
96
+ }
97
+ }
57
98
  bind() {
58
99
  const { database, collection } = this.context;
59
100
  const Target = this.TargetModel;
@@ -61,6 +102,13 @@ const _BelongsToManyField = class _BelongsToManyField extends import_relation_fi
61
102
  database.addPendingField(this);
62
103
  return false;
63
104
  }
105
+ if (!this.collection.model.primaryKeyAttribute) {
106
+ throw new Error(`Collection model ${this.collection.model.name} has no primary key attribute`);
107
+ }
108
+ if (!Target.primaryKeyAttribute) {
109
+ throw new Error(`Target model ${Target.name} has no primary key attribute`);
110
+ }
111
+ this.checkAssociationKeys(database);
64
112
  const through = this.through;
65
113
  let Through;
66
114
  if (database.hasCollection(through)) {
@@ -85,7 +133,12 @@ const _BelongsToManyField = class _BelongsToManyField extends import_relation_fi
85
133
  constraints: false,
86
134
  ...(0, import_lodash.omit)(this.options, ["name", "type", "target"]),
87
135
  as: this.name,
88
- through: Through.model
136
+ through: {
137
+ model: Through.model,
138
+ scope: this.options.throughScope,
139
+ paranoid: this.options.throughParanoid,
140
+ unique: this.options.throughUnique
141
+ }
89
142
  };
90
143
  const association = collection.model.belongsToMany(Target, belongsToManyOptions);
91
144
  database.removePendingField(this);
@@ -98,6 +151,9 @@ const _BelongsToManyField = class _BelongsToManyField extends import_relation_fi
98
151
  if (!this.options.otherKey) {
99
152
  this.options.otherKey = association.otherKey;
100
153
  }
154
+ if (!this.options.targetKey) {
155
+ this.options.targetKey = association.targetKey;
156
+ }
101
157
  try {
102
158
  (0, import_utils.checkIdentifier)(this.options.foreignKey);
103
159
  (0, import_utils.checkIdentifier)(this.options.otherKey);
@@ -6,6 +6,7 @@ export declare class DateField extends Field {
6
6
  getProps(): any;
7
7
  isDateOnly(): boolean;
8
8
  isGMT(): any;
9
+ bind(): void;
9
10
  }
10
11
  export interface DateFieldOptions extends BaseColumnFieldOptions {
11
12
  type: 'date';
@@ -42,6 +42,19 @@ const _DateField = class _DateField extends import_field.Field {
42
42
  const props = this.getProps();
43
43
  return props.gmt;
44
44
  }
45
+ bind() {
46
+ super.bind();
47
+ if (this.options.interface === "createdAt") {
48
+ const { model } = this.context.collection;
49
+ model._timestampAttributes.createdAt = this.name;
50
+ model.refreshAttributes();
51
+ }
52
+ if (this.options.interface === "updatedAt") {
53
+ const { model } = this.context.collection;
54
+ model._timestampAttributes.updatedAt = this.name;
55
+ model.refreshAttributes();
56
+ }
57
+ }
45
58
  };
46
59
  __name(_DateField, "DateField");
47
60
  let DateField = _DateField;
@@ -58,6 +58,7 @@ export declare class HasManyField extends RelationField {
58
58
  get dataType(): any;
59
59
  get foreignKey(): any;
60
60
  reference(association: any): Reference;
61
+ checkAssociationKeys(): void;
61
62
  bind(): boolean;
62
63
  unbind(): void;
63
64
  }
@@ -46,6 +46,27 @@ const _HasManyField = class _HasManyField extends import_relation_field.Relation
46
46
  onDelete: this.options.onDelete
47
47
  };
48
48
  }
49
+ checkAssociationKeys() {
50
+ let { foreignKey, sourceKey } = this.options;
51
+ if (!sourceKey) {
52
+ sourceKey = this.collection.model.primaryKeyAttribute;
53
+ }
54
+ if (!foreignKey) {
55
+ foreignKey = import_sequelize.Utils.camelize([import_sequelize.Utils.singularize(this.name), this.collection.model.primaryKeyAttribute].join("_"));
56
+ }
57
+ const foreignKeyAttribute = this.TargetModel.rawAttributes[foreignKey];
58
+ const sourceKeyAttribute = this.collection.model.rawAttributes[sourceKey];
59
+ if (!foreignKeyAttribute || !sourceKeyAttribute) {
60
+ return;
61
+ }
62
+ const foreignKeyType = foreignKeyAttribute.type.constructor.toString();
63
+ const sourceKeyType = sourceKeyAttribute.type.constructor.toString();
64
+ if (!this.keyPairsTypeMatched(foreignKeyType, sourceKeyType)) {
65
+ throw new Error(
66
+ `Foreign key "${foreignKey}" type "${foreignKeyType}" does not match source key "${sourceKey}" type "${sourceKeyType}" in has many relation "${this.name}" of collection "${this.collection.name}"`
67
+ );
68
+ }
69
+ }
49
70
  bind() {
50
71
  const { database, collection } = this.context;
51
72
  const Target = this.TargetModel;
@@ -53,6 +74,7 @@ const _HasManyField = class _HasManyField extends import_relation_field.Relation
53
74
  database.addPendingField(this);
54
75
  return false;
55
76
  }
77
+ this.checkAssociationKeys();
56
78
  if (collection.model.associations[this.name]) {
57
79
  delete collection.model.associations[this.name];
58
80
  }
@@ -66,6 +88,9 @@ const _HasManyField = class _HasManyField extends import_relation_field.Relation
66
88
  if (!this.options.foreignKey) {
67
89
  this.options.foreignKey = association.foreignKey;
68
90
  }
91
+ if (!this.options.sourceKey) {
92
+ this.options.sourceKey = association.sourceKey;
93
+ }
69
94
  try {
70
95
  (0, import_utils.checkIdentifier)(this.options.foreignKey);
71
96
  } catch (error) {
@@ -59,6 +59,7 @@ export declare class HasOneField extends RelationField {
59
59
  get target(): any;
60
60
  get foreignKey(): any;
61
61
  reference(association: any): Reference;
62
+ checkAssociationKeys(): void;
62
63
  bind(): boolean;
63
64
  unbind(): void;
64
65
  }
@@ -53,6 +53,27 @@ const _HasOneField = class _HasOneField extends import_relation_field.RelationFi
53
53
  onDelete: this.options.onDelete
54
54
  };
55
55
  }
56
+ checkAssociationKeys() {
57
+ let { foreignKey, sourceKey } = this.options;
58
+ if (!sourceKey) {
59
+ sourceKey = this.collection.model.primaryKeyAttribute;
60
+ }
61
+ if (!foreignKey) {
62
+ foreignKey = import_sequelize.Utils.camelize([import_sequelize.Utils.singularize(this.name), this.collection.model.primaryKeyAttribute].join("_"));
63
+ }
64
+ const foreignKeyAttribute = this.TargetModel.rawAttributes[foreignKey];
65
+ const sourceKeyAttribute = this.collection.model.rawAttributes[sourceKey];
66
+ if (!foreignKeyAttribute || !sourceKeyAttribute) {
67
+ return;
68
+ }
69
+ const foreignKeyType = foreignKeyAttribute.type.constructor.toString();
70
+ const sourceKeyType = sourceKeyAttribute.type.constructor.toString();
71
+ if (!this.keyPairsTypeMatched(foreignKeyType, sourceKeyType)) {
72
+ throw new Error(
73
+ `Foreign key "${foreignKey}" type "${foreignKeyType}" does not match source key "${sourceKey}" type "${sourceKeyType}" in has one relation "${this.name}" of collection "${this.collection.name}"`
74
+ );
75
+ }
76
+ }
56
77
  bind() {
57
78
  const { database, collection } = this.context;
58
79
  const Target = this.TargetModel;
@@ -60,6 +81,7 @@ const _HasOneField = class _HasOneField extends import_relation_field.RelationFi
60
81
  database.addPendingField(this);
61
82
  return false;
62
83
  }
84
+ this.checkAssociationKeys();
63
85
  const association = collection.model.hasOne(Target, {
64
86
  constraints: false,
65
87
  ...(0, import_lodash.omit)(this.options, ["name", "type", "target", "onDelete"]),
@@ -70,6 +92,9 @@ const _HasOneField = class _HasOneField extends import_relation_field.RelationFi
70
92
  if (!this.options.foreignKey) {
71
93
  this.options.foreignKey = association.foreignKey;
72
94
  }
95
+ if (!this.options.sourceKey) {
96
+ this.options.sourceKey = association.sourceKey;
97
+ }
73
98
  try {
74
99
  (0, import_utils.checkIdentifier)(this.options.foreignKey);
75
100
  } catch (error) {
@@ -16,5 +16,6 @@ export declare abstract class RelationField extends Field {
16
16
  * @constructor
17
17
  */
18
18
  get TargetModel(): import("sequelize").ModelCtor<import("sequelize").Model<any, any>>;
19
+ keyPairsTypeMatched(type1: any, type2: any): boolean;
19
20
  protected clearAccessors(): void;
20
21
  }
@@ -46,6 +46,19 @@ const _RelationField = class _RelationField extends import_field.Field {
46
46
  get TargetModel() {
47
47
  return this.context.database.sequelize.models[this.target];
48
48
  }
49
+ keyPairsTypeMatched(type1, type2) {
50
+ type1 = type1.toLowerCase();
51
+ type2 = type2.toLowerCase();
52
+ const numberTypeGroups = ["integer", "bigint", "decimal", "float", "real", "double", "smallint", "tinyint"];
53
+ const stringTypeGroups = ["string", "char", "text"];
54
+ if (numberTypeGroups.includes(type1) && numberTypeGroups.includes(type2)) {
55
+ return true;
56
+ }
57
+ if (stringTypeGroups.includes(type1) && stringTypeGroups.includes(type2)) {
58
+ return true;
59
+ }
60
+ return type1 === type2;
61
+ }
49
62
  clearAccessors() {
50
63
  const { collection } = this.context;
51
64
  const association = collection.model.associations[this.name];
@@ -3,11 +3,12 @@ import { Collection, CollectionContext, CollectionOptions } from './collection';
3
3
  export declare class InheritedCollection extends Collection {
4
4
  parents?: Collection[];
5
5
  constructor(options: CollectionOptions, context: CollectionContext);
6
- protected bindParents(): void;
7
- protected setParents(inherits: string | string[]): void;
8
- protected setParentFields(): void;
9
6
  getParents(): Collection<any, any>[];
7
+ getFlatParents(): any[];
10
8
  parentFields(): Map<string, Field>;
11
9
  parentAttributes(): {};
12
10
  isInherited(): boolean;
11
+ protected bindParents(): void;
12
+ protected setParents(inherits: string | string[]): void;
13
+ protected setParentFields(): void;
13
14
  }
@@ -57,31 +57,18 @@ const _InheritedCollection = class _InheritedCollection extends import_collectio
57
57
  }
58
58
  }
59
59
  }
60
- bindParents() {
61
- this.setParents(this.options.inherits);
62
- this.setParentFields();
63
- this.setFields(this.options.fields, false);
64
- this.db.inheritanceMap.setInheritance(this.name, this.options.inherits);
60
+ getParents() {
61
+ return this.parents;
65
62
  }
66
- setParents(inherits) {
67
- this.parents = import_lodash.default.castArray(inherits).map((name) => {
68
- const existCollection = this.db.collections.get(name);
69
- if (!existCollection) {
70
- throw new ParentCollectionNotFound(name);
63
+ getFlatParents() {
64
+ const parents = [];
65
+ for (const parent of this.parents) {
66
+ if (parent.isInherited()) {
67
+ parents.push(...parent.getFlatParents());
71
68
  }
72
- return existCollection;
73
- });
74
- }
75
- setParentFields() {
76
- for (const [name, fieldOptions] of this.parentFields()) {
77
- this.setField(name, {
78
- ...fieldOptions,
79
- inherit: true
80
- });
69
+ parents.push(parent);
81
70
  }
82
- }
83
- getParents() {
84
- return this.parents;
71
+ return parents;
85
72
  }
86
73
  parentFields() {
87
74
  const fields = /* @__PURE__ */ new Map();
@@ -112,6 +99,29 @@ const _InheritedCollection = class _InheritedCollection extends import_collectio
112
99
  isInherited() {
113
100
  return true;
114
101
  }
102
+ bindParents() {
103
+ this.setParents(this.options.inherits);
104
+ this.setParentFields();
105
+ this.setFields(this.options.fields, false);
106
+ this.db.inheritanceMap.setInheritance(this.name, this.options.inherits);
107
+ }
108
+ setParents(inherits) {
109
+ this.parents = import_lodash.default.castArray(inherits).map((name) => {
110
+ const existCollection = this.db.collections.get(name);
111
+ if (!existCollection) {
112
+ throw new ParentCollectionNotFound(name);
113
+ }
114
+ return existCollection;
115
+ });
116
+ }
117
+ setParentFields() {
118
+ for (const [name, fieldOptions] of this.parentFields()) {
119
+ this.setField(name, {
120
+ ...fieldOptions,
121
+ inherit: true
122
+ });
123
+ }
124
+ }
115
125
  };
116
126
  __name(_InheritedCollection, "InheritedCollection");
117
127
  let InheritedCollection = _InheritedCollection;