@nocobase/database 0.8.0-alpha.9 → 0.8.1-alpha.4

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.
Files changed (122) hide show
  1. package/lib/collection.d.ts +7 -3
  2. package/lib/collection.js +126 -22
  3. package/lib/database.d.ts +13 -7
  4. package/lib/database.js +39 -8
  5. package/lib/decorators/must-have-filter-decorator.js +4 -0
  6. package/lib/decorators/transaction-decorator.js +1 -0
  7. package/lib/features/ReferencesMap.js +14 -9
  8. package/lib/field-repository/array-field-repository.d.ts +28 -0
  9. package/lib/field-repository/array-field-repository.js +208 -0
  10. package/lib/fields/array-field.d.ts +1 -1
  11. package/lib/fields/array-field.js +15 -11
  12. package/lib/fields/belongs-to-field.d.ts +9 -1
  13. package/lib/fields/belongs-to-field.js +21 -7
  14. package/lib/fields/belongs-to-many-field.d.ts +4 -0
  15. package/lib/fields/belongs-to-many-field.js +24 -0
  16. package/lib/fields/field.d.ts +3 -2
  17. package/lib/fields/field.js +19 -13
  18. package/lib/fields/has-many-field.d.ts +2 -1
  19. package/lib/fields/has-many-field.js +18 -10
  20. package/lib/fields/has-one-field.d.ts +1 -0
  21. package/lib/fields/has-one-field.js +22 -10
  22. package/lib/fields/index.d.ts +3 -3
  23. package/lib/fields/index.js +14 -34
  24. package/lib/fields/relation-field.d.ts +2 -1
  25. package/lib/fields/relation-field.js +16 -0
  26. package/lib/fields/set-field.d.ts +10 -0
  27. package/lib/fields/set-field.js +35 -0
  28. package/lib/fields/sort-field.d.ts +1 -1
  29. package/lib/fields/sort-field.js +1 -1
  30. package/lib/filter-match.d.ts +1 -0
  31. package/lib/filter-match.js +84 -0
  32. package/lib/filter-parser.d.ts +2 -2
  33. package/lib/index.d.ts +5 -1
  34. package/lib/index.js +56 -0
  35. package/lib/inherited-collection.d.ts +13 -0
  36. package/lib/inherited-collection.js +210 -0
  37. package/lib/inherited-map.d.ts +21 -0
  38. package/lib/inherited-map.js +203 -0
  39. package/lib/model-hook.d.ts +1 -1
  40. package/lib/model.d.ts +1 -0
  41. package/lib/model.js +47 -0
  42. package/lib/operators/array.d.ts +1 -25
  43. package/lib/operators/association.d.ts +1 -9
  44. package/lib/operators/boolean.d.ts +1 -12
  45. package/lib/operators/date.d.ts +1 -33
  46. package/lib/operators/empty.d.ts +2 -25
  47. package/lib/operators/ne.d.ts +1 -13
  48. package/lib/operators/notIn.d.ts +1 -9
  49. package/lib/options-parser.d.ts +3 -2
  50. package/lib/options-parser.js +16 -1
  51. package/lib/relation-repository/relation-repository.d.ts +2 -2
  52. package/lib/relation-repository/single-relation-repository.js +2 -2
  53. package/lib/repository.d.ts +18 -10
  54. package/lib/repository.js +172 -38
  55. package/lib/sync-runner.d.ts +4 -0
  56. package/lib/sync-runner.js +181 -0
  57. package/lib/types.d.ts +2 -2
  58. package/lib/update-associations.d.ts +1 -0
  59. package/lib/update-associations.js +21 -2
  60. package/lib/update-guard.d.ts +3 -3
  61. package/lib/utils.js +5 -0
  62. package/package.json +4 -4
  63. package/src/__tests__/bigint.test.ts +48 -0
  64. package/src/__tests__/collection.test.ts +48 -13
  65. package/src/__tests__/database.test.ts +10 -0
  66. package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
  67. package/src/__tests__/fields/set.test.ts +37 -0
  68. package/src/__tests__/filter-match.test.ts +52 -0
  69. package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
  70. package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
  71. package/src/__tests__/inhertits/helper.ts +3 -0
  72. package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
  73. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
  74. package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
  75. package/src/__tests__/repository/destroy.test.ts +122 -1
  76. package/src/__tests__/repository/update-many.test.ts +57 -0
  77. package/src/__tests__/update-association-values.test.ts +232 -0
  78. package/src/__tests__/update-associations.test.ts +6 -1
  79. package/src/collection.ts +90 -8
  80. package/src/database.ts +53 -14
  81. package/src/decorators/must-have-filter-decorator.ts +4 -0
  82. package/src/decorators/transaction-decorator.ts +1 -0
  83. package/src/features/ReferencesMap.ts +20 -9
  84. package/src/features/referential-integrity-check.ts +1 -0
  85. package/src/field-repository/array-field-repository.ts +155 -0
  86. package/src/fields/array-field.ts +4 -4
  87. package/src/fields/belongs-to-field.ts +26 -10
  88. package/src/fields/belongs-to-many-field.ts +34 -0
  89. package/src/fields/field.ts +26 -13
  90. package/src/fields/has-many-field.ts +17 -9
  91. package/src/fields/has-one-field.ts +23 -9
  92. package/src/fields/index.ts +5 -5
  93. package/src/fields/relation-field.ts +16 -0
  94. package/src/fields/set-field.ts +25 -0
  95. package/src/fields/sort-field.ts +5 -4
  96. package/src/filter-match.ts +49 -0
  97. package/src/filter-parser.ts +2 -2
  98. package/src/index.ts +5 -2
  99. package/src/inherited-collection.ts +112 -0
  100. package/src/inherited-map.ts +97 -0
  101. package/src/model-hook.ts +2 -3
  102. package/src/model.ts +43 -3
  103. package/src/operators/array.ts +1 -1
  104. package/src/operators/association.ts +1 -1
  105. package/src/operators/boolean.ts +1 -1
  106. package/src/operators/date.ts +1 -1
  107. package/src/operators/empty.ts +1 -1
  108. package/src/operators/ne.ts +1 -1
  109. package/src/operators/notIn.ts +2 -1
  110. package/src/options-parser.ts +20 -4
  111. package/src/relation-repository/relation-repository.ts +2 -2
  112. package/src/relation-repository/single-relation-repository.ts +2 -2
  113. package/src/repository.ts +144 -30
  114. package/src/sync-runner.ts +162 -0
  115. package/src/types.ts +2 -2
  116. package/src/update-associations.ts +23 -7
  117. package/src/update-guard.ts +3 -3
  118. package/src/utils.ts +5 -1
  119. package/lib/fields/sequence-field.d.ts +0 -32
  120. package/lib/fields/sequence-field.js +0 -300
  121. package/src/__tests__/fields/sequence-field.test.ts +0 -480
  122. package/src/fields/sequence-field.ts +0 -202
@@ -8,9 +8,9 @@ import {
8
8
  Utils,
9
9
  } from 'sequelize';
10
10
  import { Collection } from '../collection';
11
+ import { Reference } from '../features/ReferencesMap';
11
12
  import { checkIdentifier } from '../utils';
12
13
  import { MultipleRelationFieldOptions, RelationField } from './relation-field';
13
- import { Reference } from '../features/ReferencesMap';
14
14
 
15
15
  export interface HasManyFieldOptions extends HasManyOptions {
16
16
  /**
@@ -74,6 +74,10 @@ export interface HasManyFieldOptions extends HasManyOptions {
74
74
  }
75
75
 
76
76
  export class HasManyField extends RelationField {
77
+ get dataType(): any {
78
+ return 'HasMany';
79
+ }
80
+
77
81
  get foreignKey() {
78
82
  if (this.options.foreignKey) {
79
83
  return this.options.foreignKey;
@@ -155,15 +159,18 @@ export class HasManyField extends RelationField {
155
159
  database.removePendingField(this);
156
160
  // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
157
161
  const tcoll = database.getCollection(this.target);
158
- const foreignKey = this.options.foreignKey;
159
- const field = tcoll.findField((field) => {
160
- if (field.name === foreignKey) {
161
- return true;
162
+
163
+ if (tcoll) {
164
+ const foreignKey = this.options.foreignKey;
165
+ const field = tcoll.findField((field) => {
166
+ if (field.name === foreignKey) {
167
+ return true;
168
+ }
169
+ return field.type === 'belongsTo' && field.foreignKey === foreignKey;
170
+ });
171
+ if (!field) {
172
+ tcoll.model.removeAttribute(foreignKey);
162
173
  }
163
- return field.type === 'belongsTo' && field.foreignKey === foreignKey;
164
- });
165
- if (!field) {
166
- tcoll.model.removeAttribute(foreignKey);
167
174
  }
168
175
 
169
176
  const association = collection.model.associations[this.name];
@@ -172,6 +179,7 @@ export class HasManyField extends RelationField {
172
179
  this.database.referenceMap.removeReference(this.reference(association));
173
180
  }
174
181
 
182
+ this.clearAccessors();
175
183
  // 删掉 model 的关联字段
176
184
  delete collection.model.associations[this.name];
177
185
  // @ts-ignore
@@ -74,6 +74,10 @@ export interface HasOneFieldOptions extends HasOneOptions {
74
74
  }
75
75
 
76
76
  export class HasOneField extends RelationField {
77
+ get dataType(): any {
78
+ return 'HasOne';
79
+ }
80
+
77
81
  get target() {
78
82
  const { target, name } = this.options;
79
83
  return target || Utils.pluralize(name);
@@ -154,20 +158,30 @@ export class HasOneField extends RelationField {
154
158
  database.removePendingField(this);
155
159
  // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
156
160
  const tcoll = database.collections.get(this.target);
157
- const foreignKey = this.options.foreignKey;
158
- const field = tcoll.findField((field) => {
159
- if (field.name === foreignKey) {
160
- return true;
161
+
162
+ if (tcoll && !this.options.inherit) {
163
+ const foreignKey = this.options.foreignKey;
164
+
165
+ const field = tcoll.findField((field) => {
166
+ if (field.name === foreignKey) {
167
+ return true;
168
+ }
169
+
170
+ return field.type === 'belongsTo' && field.foreignKey === foreignKey;
171
+ });
172
+
173
+ if (!field) {
174
+ tcoll.model.removeAttribute(foreignKey);
161
175
  }
162
- return field.type === 'belongsTo' && field.foreignKey === foreignKey;
163
- });
164
- if (!field) {
165
- tcoll.model.removeAttribute(foreignKey);
166
176
  }
167
177
 
168
178
  const association = collection.model.associations[this.name];
169
- this.database.referenceMap.removeReference(this.reference(association));
170
179
 
180
+ if (association) {
181
+ this.database.referenceMap.removeReference(this.reference(association));
182
+ }
183
+
184
+ this.clearAccessors();
171
185
  // 删掉 model 的关联字段
172
186
  delete collection.model.associations[this.name];
173
187
  // @ts-ignore
@@ -13,7 +13,7 @@ import {
13
13
  DoubleFieldOptions,
14
14
  FloatFieldOptions,
15
15
  IntegerFieldOptions,
16
- RealFieldOptions
16
+ RealFieldOptions,
17
17
  } from './number-field';
18
18
  import { PasswordFieldOptions } from './password-field';
19
19
  import { RadioFieldOptions } from './radio-field';
@@ -25,9 +25,10 @@ import { UidFieldOptions } from './uid-field';
25
25
  import { UUIDFieldOptions } from './uuid-field';
26
26
  import { VirtualFieldOptions } from './virtual-field';
27
27
  import { FormulaFieldOptions } from './formula-field';
28
- import { SequenceFieldOptions } from './sequence-field';
28
+ import { SetFieldOptions } from './set-field';
29
29
 
30
30
  export * from './array-field';
31
+ export * from './set-field';
31
32
  export * from './belongs-to-field';
32
33
  export * from './belongs-to-many-field';
33
34
  export * from './boolean-field';
@@ -49,7 +50,6 @@ export * from './uid-field';
49
50
  export * from './uuid-field';
50
51
  export * from './virtual-field';
51
52
  export * from './formula-field';
52
- export { SequenceField } from './sequence-field';
53
53
 
54
54
  export type FieldOptions =
55
55
  | BaseFieldOptions
@@ -68,6 +68,7 @@ export type FieldOptions =
68
68
  | VirtualFieldOptions
69
69
  | FormulaFieldOptions
70
70
  | ArrayFieldOptions
71
+ | SetFieldOptions
71
72
  | TimeFieldOptions
72
73
  | DateFieldOptions
73
74
  | UidFieldOptions
@@ -77,5 +78,4 @@ export type FieldOptions =
77
78
  | BelongsToFieldOptions
78
79
  | HasOneFieldOptions
79
80
  | HasManyFieldOptions
80
- | BelongsToManyFieldOptions
81
- | SequenceFieldOptions;
81
+ | BelongsToManyFieldOptions;
@@ -34,4 +34,20 @@ export abstract class RelationField extends Field {
34
34
  get TargetModel() {
35
35
  return this.context.database.sequelize.models[this.target];
36
36
  }
37
+
38
+ protected clearAccessors() {
39
+ const { collection } = this.context;
40
+ const association = collection.model.associations[this.name];
41
+ if (!association) {
42
+ return;
43
+ }
44
+
45
+ // @ts-ignore
46
+ const accessors = Object.values(association.accessors);
47
+
48
+ accessors.forEach((accessor) => {
49
+ // @ts-ignore
50
+ delete collection.model.prototype[accessor];
51
+ });
52
+ }
37
53
  }
@@ -0,0 +1,25 @@
1
+ import { ArrayField } from './array-field';
2
+ import { BaseColumnFieldOptions } from './field';
3
+
4
+ export interface SetFieldOptions extends BaseColumnFieldOptions {
5
+ type: 'set';
6
+ }
7
+
8
+ export class SetField extends ArrayField {
9
+ beforeSave = (model) => {
10
+ const oldValue = model.get(this.options.name);
11
+ if (oldValue) {
12
+ model.set(this.options.name, [...new Set(oldValue)]);
13
+ }
14
+ };
15
+
16
+ bind() {
17
+ super.bind();
18
+ this.on('beforeSave', this.beforeSave);
19
+ }
20
+
21
+ unbind() {
22
+ super.unbind();
23
+ this.off('beforeSave', this.beforeSave);
24
+ }
25
+ }
@@ -7,7 +7,7 @@ const sortFieldMutex = new Mutex();
7
7
 
8
8
  export class SortField extends Field {
9
9
  get dataType() {
10
- return DataTypes.INTEGER;
10
+ return DataTypes.BIGINT;
11
11
  }
12
12
 
13
13
  setSortValue = async (instance, options) => {
@@ -32,19 +32,20 @@ export class SortField extends Field {
32
32
  const newValue = (max || 0) + 1;
33
33
  instance.set(name, newValue);
34
34
  });
35
- }
35
+ };
36
36
 
37
37
  onScopeChange = async (instance, options) => {
38
38
  const { scopeKey } = this.options;
39
39
  if (scopeKey && !instance.isNewRecord && instance._previousDataValues[scopeKey] != instance[scopeKey]) {
40
40
  await this.setSortValue(instance, options);
41
41
  }
42
- }
42
+ };
43
43
 
44
44
  initRecordsSortValue = async ({ transaction }) => {
45
45
  const totalCount = await this.collection.repository.count({
46
46
  transaction,
47
47
  });
48
+
48
49
  const emptyCount = await this.collection.repository.count({
49
50
  filter: {
50
51
  [this.name]: null,
@@ -73,7 +74,7 @@ export class SortField extends Field {
73
74
  start += 1;
74
75
  }
75
76
  }
76
- }
77
+ };
77
78
 
78
79
  bind() {
79
80
  super.bind();
@@ -0,0 +1,49 @@
1
+ import { filter } from 'mathjs';
2
+
3
+ export function filterMatch(model, where) {
4
+ if (where.filter !== undefined) {
5
+ where = filter;
6
+ }
7
+
8
+ // Create an object that maps operator names to functions
9
+ const operatorFunctions = {
10
+ $eq: (value, condition) => value === condition,
11
+ $not: (value, condition) => !filterMatch(model, condition),
12
+ $gt: (value, condition) => value > condition,
13
+ $gte: (value, condition) => value >= condition,
14
+ $lt: (value, condition) => value < condition,
15
+ $lte: (value, condition) => value <= condition,
16
+ $ne: (value, condition) => value !== condition,
17
+ $in: (value, condition) => condition.includes(value),
18
+ $or: (model, conditions) => Object.values(conditions).some((condition) => filterMatch(model, condition)),
19
+ $and: (model, conditions) => Object.values(conditions).every((condition) => filterMatch(model, condition)),
20
+ };
21
+
22
+ for (const [key, value] of Object.entries(where)) {
23
+ // Check if the property value contains a logical operator
24
+ if (operatorFunctions[key] !== undefined) {
25
+ // Check if the conditions specified in the property value are satisfied
26
+ if (!operatorFunctions[key](model, value)) {
27
+ return false;
28
+ }
29
+ } else {
30
+ // Check if the property value is an object (which would contain operators)
31
+ if (typeof value === 'object') {
32
+ // Loop through each operator in the property value
33
+ for (const [operator, condition] of Object.entries(value)) {
34
+ // Check if the property value satisfies the condition
35
+ if (!operatorFunctions[operator](model[key], condition)) {
36
+ return false;
37
+ }
38
+ }
39
+ } else {
40
+ // Assume the default operator is "eq"
41
+ if (!operatorFunctions['$eq'](model[key], value)) {
42
+ return false;
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ return true;
49
+ }
@@ -1,6 +1,6 @@
1
1
  import { flatten, unflatten } from 'flat';
2
2
  import { default as lodash, default as _ } from 'lodash';
3
- import { ModelCtor } from 'sequelize';
3
+ import { ModelStatic } from 'sequelize';
4
4
  import { Collection } from './collection';
5
5
  import { Database } from './database';
6
6
  import { Model } from './model';
@@ -17,7 +17,7 @@ interface FilterParserContext {
17
17
  export default class FilterParser {
18
18
  collection: Collection;
19
19
  database: Database;
20
- model: ModelCtor<Model>;
20
+ model: ModelStatic<Model>;
21
21
  filter: FilterType;
22
22
  context: FilterParserContext;
23
23
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { DataTypes, ModelCtor, Op, SyncOptions } from 'sequelize';
1
+ export { DataTypes, ModelStatic, Op, SyncOptions } from 'sequelize';
2
2
  export * from './collection';
3
+ export * from './inherited-collection';
3
4
  export * from './database';
4
5
  export { Database as default } from './database';
5
6
  export * from './fields';
@@ -14,4 +15,6 @@ export * from './relation-repository/multiple-relation-repository';
14
15
  export * from './relation-repository/single-relation-repository';
15
16
  export * from './repository';
16
17
  export * from './update-associations';
17
-
18
+ export * from './collection-importer';
19
+ export * from './filter-match';
20
+ export * from './field-repository/array-field-repository';
@@ -0,0 +1,112 @@
1
+ import { Collection, CollectionContext, CollectionOptions } from './collection';
2
+ import { default as lodash } from 'lodash';
3
+ import { Field } from '.';
4
+
5
+ export class InheritedCollection extends Collection {
6
+ parents?: Collection[];
7
+ constructor(options: CollectionOptions, context: CollectionContext) {
8
+ if (!options.inherits) {
9
+ throw new Error('InheritedCollection must have inherits option');
10
+ }
11
+
12
+ options.inherits = lodash.castArray(options.inherits);
13
+
14
+ super(options, context);
15
+
16
+ try {
17
+ this.bindParents();
18
+ } catch (err) {
19
+ if (err instanceof ParentCollectionNotFound) {
20
+ const listener = (collection) => {
21
+ if (
22
+ options.inherits.includes(collection.name) &&
23
+ (options.inherits as Array<string>).every((name) => this.db.collections.has(name))
24
+ ) {
25
+ this.bindParents();
26
+ this.db.removeListener('afterDefineCollection', listener);
27
+ }
28
+ };
29
+
30
+ this.db.addListener('afterDefineCollection', listener);
31
+ } else {
32
+ throw err;
33
+ }
34
+ }
35
+ }
36
+
37
+ protected bindParents() {
38
+ this.setParents(this.options.inherits);
39
+ this.setParentFields();
40
+ this.setFields(this.options.fields, false);
41
+ this.db.inheritanceMap.setInheritance(this.name, this.options.inherits);
42
+ }
43
+
44
+ protected setParents(inherits: string | string[]) {
45
+ this.parents = lodash.castArray(inherits).map((name) => {
46
+ const existCollection = this.db.collections.get(name);
47
+ if (!existCollection) {
48
+ throw new ParentCollectionNotFound(name);
49
+ }
50
+
51
+ return existCollection;
52
+ });
53
+ }
54
+
55
+ protected setParentFields() {
56
+ for (const [name, fieldOptions] of this.parentFields()) {
57
+ this.setField(name, {
58
+ ...fieldOptions,
59
+ inherit: true,
60
+ });
61
+ }
62
+ }
63
+
64
+ getParents() {
65
+ return this.parents;
66
+ }
67
+
68
+ parentFields() {
69
+ const fields = new Map<string, Field>();
70
+
71
+ for (const parent of this.parents) {
72
+ if (parent.isInherited()) {
73
+ for (const [name, field] of (<InheritedCollection>parent).parentFields()) {
74
+ fields.set(name, field.options);
75
+ }
76
+ }
77
+
78
+ const parentFields = parent.fields;
79
+ for (const [name, field] of parentFields) {
80
+ fields.set(name, field.options);
81
+ }
82
+ }
83
+
84
+ return fields;
85
+ }
86
+
87
+ parentAttributes() {
88
+ const attributes = {};
89
+
90
+ for (const parent of this.parents) {
91
+ if (parent.isInherited()) {
92
+ Object.assign(attributes, (<InheritedCollection>parent).parentAttributes());
93
+ }
94
+
95
+ const parentAttributes = (<any>parent.model).tableAttributes;
96
+
97
+ Object.assign(attributes, parentAttributes);
98
+ }
99
+
100
+ return attributes;
101
+ }
102
+
103
+ isInherited() {
104
+ return true;
105
+ }
106
+ }
107
+
108
+ class ParentCollectionNotFound extends Error {
109
+ constructor(name: string) {
110
+ super(`parent collection ${name} not found`);
111
+ }
112
+ }
@@ -0,0 +1,97 @@
1
+ import lodash from 'lodash';
2
+ import Database from './database';
3
+
4
+ class TableNode {
5
+ name: string;
6
+ parents: Set<TableNode>;
7
+ children: Set<TableNode>;
8
+ constructor(name: string) {
9
+ this.name = name;
10
+ this.parents = new Set();
11
+ this.children = new Set();
12
+ }
13
+ }
14
+
15
+ export default class InheritanceMap {
16
+ nodes: Map<string, TableNode> = new Map<string, TableNode>();
17
+
18
+ removeNode(name: string) {
19
+ const node = this.nodes.get(name);
20
+ if (!node) return;
21
+
22
+ for (const parent of node.parents) {
23
+ parent.children.delete(node);
24
+ }
25
+
26
+ for (const child of node.children) {
27
+ child.parents.delete(node);
28
+ }
29
+
30
+ this.nodes.delete(name);
31
+ }
32
+
33
+ getOrCreateNode(name: string) {
34
+ if (!this.nodes.has(name)) {
35
+ this.nodes.set(name, new TableNode(name));
36
+ }
37
+ return this.getNode(name);
38
+ }
39
+
40
+ getNode(name: string) {
41
+ return this.nodes.get(name);
42
+ }
43
+
44
+ setInheritance(name: string, inherits: string | string[]) {
45
+ const node = this.getOrCreateNode(name);
46
+ const parents = lodash.castArray(inherits).map((name) => this.getOrCreateNode(name));
47
+
48
+ node.parents = new Set(parents);
49
+
50
+ for (const parent of parents) {
51
+ parent.children.add(node);
52
+ }
53
+ }
54
+
55
+ isParentNode(name: string) {
56
+ const node = this.getNode(name);
57
+ return node && node.children.size > 0;
58
+ }
59
+
60
+ getChildren(name: string, options: { deep: boolean } = { deep: true }): Set<string> {
61
+ const results = new Set<string>();
62
+ const node = this.getNode(name);
63
+ if (!node) return results;
64
+
65
+ for (const child of node.children) {
66
+ results.add(child.name);
67
+ if (!options.deep) {
68
+ continue;
69
+ }
70
+
71
+ for (const grandchild of this.getChildren(child.name)) {
72
+ results.add(grandchild);
73
+ }
74
+ }
75
+
76
+ return results;
77
+ }
78
+
79
+ getParents(name: string, options: { deep: boolean } = { deep: true }): Set<string> {
80
+ const results = new Set<string>();
81
+ const node = this.getNode(name);
82
+ if (!node) return results;
83
+
84
+ for (const parent of node.parents) {
85
+ results.add(parent.name);
86
+ if (!options.deep) {
87
+ continue;
88
+ }
89
+
90
+ for (const grandparent of this.getParents(parent.name)) {
91
+ results.add(grandparent);
92
+ }
93
+ }
94
+
95
+ return results;
96
+ }
97
+ }
package/src/model-hook.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import lodash from 'lodash';
2
- import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
2
+ import { SequelizeHooks } from 'sequelize/types/hooks';
3
+
3
4
  import Database from './database';
4
5
  import { Model } from './model';
5
6
 
6
7
  const { hooks } = require('sequelize/lib/hooks');
7
8
 
8
-
9
-
10
9
  export class ModelHook {
11
10
  database: Database;
12
11
 
package/src/model.ts CHANGED
@@ -1,15 +1,19 @@
1
1
  import lodash from 'lodash';
2
- import { Model as SequelizeModel, ModelCtor } from 'sequelize';
2
+ import { DataTypes, Model as SequelizeModel, ModelStatic } from 'sequelize';
3
3
  import { Collection } from './collection';
4
4
  import { Database } from './database';
5
5
  import { Field } from './fields';
6
+ import type { InheritedCollection } from './inherited-collection';
7
+ import { SyncRunner } from './sync-runner';
8
+
9
+ const _ = lodash;
6
10
 
7
11
  interface IModel {
8
12
  [key: string]: any;
9
13
  }
10
14
 
11
15
  interface JSONTransformerOptions {
12
- model: ModelCtor<any>;
16
+ model: ModelStatic<any>;
13
17
  collection: Collection;
14
18
  db: Database;
15
19
  key?: string;
@@ -78,7 +82,7 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
78
82
  };
79
83
 
80
84
  const opts = {
81
- model: this.constructor as ModelCtor<any>,
85
+ model: this.constructor as ModelStatic<any>,
82
86
  collection: (this.constructor as any).collection,
83
87
  db: (this.constructor as any).database as Database,
84
88
  };
@@ -145,4 +149,40 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
145
149
 
146
150
  return lodash.orderBy(data, orderItems, orderDirections);
147
151
  }
152
+
153
+ static async sync(options) {
154
+ const model = this as any;
155
+
156
+ // fix sequelize sync with model that not have any column
157
+ if (Object.keys(model.tableAttributes).length === 0) {
158
+ if (this.database.inDialect('sqlite', 'mysql')) {
159
+ throw new Error(`Zero-column tables aren't supported in ${this.database.sequelize.getDialect()}`);
160
+ }
161
+
162
+ // @ts-ignore
163
+ const queryInterface = this.sequelize.queryInterface;
164
+
165
+ if (!queryInterface.patched) {
166
+ const oldDescribeTable = queryInterface.describeTable;
167
+ queryInterface.describeTable = async function (...args) {
168
+ try {
169
+ return await oldDescribeTable.call(this, ...args);
170
+ } catch (err) {
171
+ if (err.message.includes('No description found for')) {
172
+ return [];
173
+ } else {
174
+ throw err;
175
+ }
176
+ }
177
+ };
178
+ queryInterface.patched = true;
179
+ }
180
+ }
181
+
182
+ if (this.collection.isInherited()) {
183
+ return SyncRunner.syncInheritModel(model, options);
184
+ }
185
+
186
+ return SequelizeModel.sync.call(this, options);
187
+ }
148
188
  }
@@ -149,4 +149,4 @@ export default {
149
149
  [Op.and]: [Sequelize.literal(`${subQuery}`)],
150
150
  };
151
151
  },
152
- };
152
+ } as Record<string, any>;
@@ -11,4 +11,4 @@ export default {
11
11
  [Op.is]: null,
12
12
  };
13
13
  },
14
- };
14
+ } as Record<string, any>;
@@ -15,4 +15,4 @@ export default {
15
15
  [Op.eq]: true,
16
16
  };
17
17
  },
18
- };
18
+ } as Record<string, any>;
@@ -38,4 +38,4 @@ export default {
38
38
  $dateNotAfter(value) {
39
39
  return { [Op.lt]: getNextDay(value) };
40
40
  },
41
- };
41
+ } as Record<string, any>;
@@ -1,4 +1,4 @@
1
- import { DataTypes, Op } from 'sequelize';
1
+ import { Op } from 'sequelize';
2
2
  import { ArrayField, StringField } from '../fields';
3
3
  import arrayOperators from './array';
4
4
  import lodash, { parseInt } from 'lodash';
@@ -13,4 +13,4 @@ export default {
13
13
  },
14
14
  };
15
15
  },
16
- };
16
+ } as Record<string, any>;
@@ -9,4 +9,5 @@ export default {
9
9
  },
10
10
  };
11
11
  },
12
- };
12
+ } as Record<string, any>;
13
+