@nocobase/database 2.3.0-beta.6 → 2.4.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.
@@ -136,6 +136,8 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
136
136
  setSortable(sortable: any): void;
137
137
  updateField(name: string, options: FieldOptions): void;
138
138
  private normalizeFieldName;
139
+ private findAttributeByIndexField;
140
+ private normalizeIndexFields;
139
141
  addIndex(index: string | string[] | {
140
142
  fields: string[];
141
143
  unique?: boolean;
package/lib/collection.js CHANGED
@@ -607,6 +607,20 @@ const _Collection = class _Collection extends import_events.EventEmitter {
607
607
  }
608
608
  return Array.isArray(val) ? val.map((v) => (0, import_utils.snakeCase)(v)) : (0, import_utils.snakeCase)(val);
609
609
  }
610
+ findAttributeByIndexField(field) {
611
+ var _a;
612
+ const attributes = this.model.getAttributes();
613
+ const normalizedField = this.normalizeFieldName(field);
614
+ return (_a = Object.entries(attributes).find(([name, attribute]) => {
615
+ return this.normalizeFieldName(name) === normalizedField || attribute.field === normalizedField;
616
+ })) == null ? void 0 : _a[1];
617
+ }
618
+ normalizeIndexFields(fields) {
619
+ return fields.map((field) => {
620
+ const attribute = this.findAttributeByIndexField(field);
621
+ return (attribute == null ? void 0 : attribute.field) || this.normalizeFieldName(field);
622
+ });
623
+ }
610
624
  addIndex(index) {
611
625
  if (!index) {
612
626
  return;
@@ -628,19 +642,22 @@ const _Collection = class _Collection extends import_events.EventEmitter {
628
642
  indexItem = index;
629
643
  indexName = index.fields;
630
644
  }
631
- if (import_lodash.default.isEqual(this.model.primaryKeyAttributes, indexName)) {
645
+ const normalizedIndexName = this.normalizeIndexFields(indexName);
646
+ const normalizedPrimaryKeyAttributes = this.normalizeIndexFields(this.model.primaryKeyAttributes);
647
+ if (import_lodash.default.isEqual(normalizedPrimaryKeyAttributes, normalizedIndexName)) {
632
648
  return;
633
649
  }
634
- const name = this.model.primaryKeyAttributes.join(",");
635
- if (name.startsWith(`${indexName.join(",")},`)) {
650
+ const name = normalizedPrimaryKeyAttributes.join(",");
651
+ if (name.startsWith(`${normalizedIndexName.join(",")},`)) {
636
652
  return;
637
653
  }
638
654
  for (const item of indexes) {
639
- if (import_lodash.default.isEqual(this.normalizeFieldName(item.fields), this.normalizeFieldName(indexName))) {
655
+ const normalizedFields = this.normalizeIndexFields(item.fields);
656
+ if (import_lodash.default.isEqual(normalizedFields, normalizedIndexName)) {
640
657
  return;
641
658
  }
642
- const name2 = item.fields.join(",");
643
- if (name2.startsWith(`${indexName.join(",")},`)) {
659
+ const name2 = normalizedFields.join(",");
660
+ if (name2.startsWith(`${normalizedIndexName.join(",")},`)) {
644
661
  return;
645
662
  }
646
663
  }
@@ -675,6 +692,7 @@ const _Collection = class _Collection extends import_events.EventEmitter {
675
692
  const attributes = {};
676
693
  for (const [name, field] of Object.entries(this.model.getAttributes())) {
677
694
  attributes[this.normalizeFieldName(name)] = field;
695
+ attributes[field.field] = field;
678
696
  }
679
697
  this.model._indexes = import_lodash.default.uniqBy(
680
698
  indexes.filter((item) => {
@@ -41,7 +41,8 @@ __export(field_exports, {
41
41
  });
42
42
  module.exports = __toCommonJS(field_exports);
43
43
  var import_lodash = __toESM(require("lodash"));
44
- var import_utils = require("../utils");
44
+ var import_utils = require("@nocobase/utils");
45
+ var import_utils2 = require("../utils");
45
46
  const _Field = class _Field {
46
47
  options;
47
48
  context;
@@ -94,7 +95,7 @@ const _Field = class _Field {
94
95
  return this.options.field;
95
96
  }
96
97
  if (this.database.options.underscored) {
97
- return (0, import_utils.snakeCase)(this.name);
98
+ return (0, import_utils2.snakeCase)(this.name);
98
99
  }
99
100
  return this.name;
100
101
  }
@@ -156,6 +157,23 @@ const _Field = class _Field {
156
157
  Object.assign(opts, { type: this.database.sequelize.normalizeDataType(this.dataType) });
157
158
  }
158
159
  Object.assign(opts, this.additionalSequelizeOptions());
160
+ if (this.options.interface === "richText") {
161
+ const { name } = this;
162
+ const originalSetter = opts.set;
163
+ const sanitizeValue = /* @__PURE__ */ __name((value) => typeof value === "string" ? (0, import_utils.sanitizeRichTextHtml)(value) : value, "sanitizeValue");
164
+ opts.set = function(value) {
165
+ if (typeof originalSetter === "function") {
166
+ originalSetter.call(this, sanitizeValue(value));
167
+ const currentValue = this.getDataValue(name);
168
+ const sanitizedValue = sanitizeValue(currentValue);
169
+ if (!Object.is(sanitizedValue, currentValue)) {
170
+ this.setDataValue(name, sanitizedValue);
171
+ }
172
+ return;
173
+ }
174
+ this.setDataValue(name, sanitizeValue(value));
175
+ };
176
+ }
159
177
  return opts;
160
178
  }
161
179
  additionalSequelizeOptions() {
package/lib/model.js CHANGED
@@ -43,6 +43,7 @@ __export(model_exports, {
43
43
  });
44
44
  module.exports = __toCommonJS(model_exports);
45
45
  var import_lodash = __toESM(require("lodash"));
46
+ var import_utils = require("@nocobase/utils");
46
47
  var import_sequelize = require("sequelize");
47
48
  var import_sync_runner = require("./sync-runner");
48
49
  const _ = import_lodash.default;
@@ -65,6 +66,9 @@ const _Model = class _Model extends import_sequelize.Model {
65
66
  } else {
66
67
  result[key] = values[key];
67
68
  }
69
+ if ((field == null ? void 0 : field.options.interface) === "richText" && typeof result[key] === "string") {
70
+ result[key] = (0, import_utils.sanitizeRichTextHtml)(result[key]);
71
+ }
68
72
  }
69
73
  return result;
70
74
  }
@@ -32,7 +32,11 @@ __export(string_exports, {
32
32
  module.exports = __toCommonJS(string_exports);
33
33
  var import_sequelize = require("sequelize");
34
34
  var import_utils = require("./utils");
35
- function escapeLike(value) {
35
+ function escapeLike(value, ctx) {
36
+ if (ctx.db.sequelize.getDialect() === "mssql") {
37
+ const escapedValue = value.replaceAll("[", "[[]");
38
+ return escapedValue.replace(/[%_]/g, (character) => `[${character}]`);
39
+ }
36
40
  return value.replace(/[_%]/g, "\\$&");
37
41
  }
38
42
  __name(escapeLike, "escapeLike");
@@ -85,13 +89,13 @@ var string_default = {
85
89
  }
86
90
  if (Array.isArray(value)) {
87
91
  const conditions = value.map(
88
- (item) => getFieldExpression(`%${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
92
+ (item) => getFieldExpression(`%${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
89
93
  );
90
94
  return {
91
95
  [import_sequelize.Op.or]: conditions
92
96
  };
93
97
  }
94
- return getFieldExpression(`%${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
98
+ return getFieldExpression(`%${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
95
99
  },
96
100
  $notIncludes(value, ctx) {
97
101
  if (value === null) {
@@ -101,56 +105,56 @@ var string_default = {
101
105
  }
102
106
  if (Array.isArray(value)) {
103
107
  const conditions = value.map(
104
- (item) => getFieldExpression(`%${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
108
+ (item) => getFieldExpression(`%${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
105
109
  );
106
110
  return {
107
111
  [import_sequelize.Op.and]: conditions
108
112
  };
109
113
  }
110
- return getFieldExpression(`%${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
114
+ return getFieldExpression(`%${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
111
115
  },
112
116
  $startsWith(value, ctx) {
113
117
  if (Array.isArray(value)) {
114
118
  const conditions = value.map(
115
- (item) => getFieldExpression(`${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
119
+ (item) => getFieldExpression(`${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
116
120
  );
117
121
  return {
118
122
  [import_sequelize.Op.or]: conditions
119
123
  };
120
124
  }
121
- return getFieldExpression(`${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
125
+ return getFieldExpression(`${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
122
126
  },
123
127
  $notStartsWith(value, ctx) {
124
128
  if (Array.isArray(value)) {
125
129
  const conditions = value.map(
126
- (item) => getFieldExpression(`${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
130
+ (item) => getFieldExpression(`${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
127
131
  );
128
132
  return {
129
133
  [import_sequelize.Op.and]: conditions
130
134
  };
131
135
  }
132
- return getFieldExpression(`${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
136
+ return getFieldExpression(`${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
133
137
  },
134
138
  $endWith(value, ctx) {
135
139
  if (Array.isArray(value)) {
136
140
  const conditions = value.map(
137
- (item) => getFieldExpression(`%${escapeLike(item)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
141
+ (item) => getFieldExpression(`%${escapeLike(item, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
138
142
  );
139
143
  return {
140
144
  [import_sequelize.Op.or]: conditions
141
145
  };
142
146
  }
143
- return getFieldExpression(`%${escapeLike(value)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
147
+ return getFieldExpression(`%${escapeLike(value, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
144
148
  },
145
149
  $notEndWith(value, ctx) {
146
150
  if (Array.isArray(value)) {
147
151
  const conditions = value.map(
148
- (item) => getFieldExpression(`%${escapeLike(item)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
152
+ (item) => getFieldExpression(`%${escapeLike(item, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
149
153
  );
150
154
  return {
151
155
  [import_sequelize.Op.and]: conditions
152
156
  };
153
157
  }
154
- return getFieldExpression(`%${escapeLike(value)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
158
+ return getFieldExpression(`%${escapeLike(value, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
155
159
  }
156
160
  };
@@ -35,6 +35,10 @@ export declare class SyncRunner {
35
35
  isParentColumn(columnName: string, options: any): Promise<boolean>;
36
36
  removeUnusedColumns(columns: any, options: any): Promise<void>;
37
37
  findAttributeByColumnName(columnName: string): any;
38
+ private getIndexFields;
39
+ private indexExists;
40
+ private deduplicateIndexes;
41
+ private filterExistingIndexesBeforeSync;
38
42
  performSync(options: any): Promise<any>;
39
43
  handleZeroColumnModel(options: any): Promise<void>;
40
44
  handleSchema(options: any): Promise<void>;
@@ -286,8 +286,52 @@ const _SyncRunner = class _SyncRunner {
286
286
  return attribute.field == columnName;
287
287
  });
288
288
  }
289
+ getIndexFields(index) {
290
+ return (index.fields || []).map((field) => {
291
+ if (typeof field === "string") {
292
+ return field;
293
+ }
294
+ return field.attribute || field.name;
295
+ });
296
+ }
297
+ indexExists(modelIndex, existingIndexes) {
298
+ const modelIndexFields = this.getIndexFields(modelIndex);
299
+ return existingIndexes.some((existingIndex) => {
300
+ if (modelIndex.name && existingIndex.name === modelIndex.name) {
301
+ return true;
302
+ }
303
+ return Boolean(modelIndex.unique) === Boolean(existingIndex.unique) && JSON.stringify(modelIndexFields) === JSON.stringify(this.getIndexFields(existingIndex));
304
+ });
305
+ }
306
+ deduplicateIndexes(indexes) {
307
+ return indexes.filter((index, indexPosition) => {
308
+ return indexes.findIndex((item) => this.indexExists(index, [item])) === indexPosition;
309
+ });
310
+ }
311
+ async filterExistingIndexesBeforeSync(options) {
312
+ const indexes = this.model["_indexes"];
313
+ if (!(indexes == null ? void 0 : indexes.length)) {
314
+ return;
315
+ }
316
+ const deduplicatedIndexes = this.deduplicateIndexes(indexes);
317
+ this.model["_indexes"] = deduplicatedIndexes;
318
+ try {
319
+ const existingIndexes = await this.queryInterface.showIndex(this.tableName, options);
320
+ this.model["_indexes"] = deduplicatedIndexes.filter((index) => !this.indexExists(index, existingIndexes));
321
+ } catch (error) {
322
+ }
323
+ }
289
324
  async performSync(options) {
290
- return this.collection.isInherited() ? await import_inherited_sync_runner.InheritedSyncRunner.syncInheritModel(this.model, options) : await import_sequelize.Model.sync.call(this.model, options);
325
+ if (this.collection.isInherited()) {
326
+ return await import_inherited_sync_runner.InheritedSyncRunner.syncInheritModel(this.model, options);
327
+ }
328
+ const indexes = this.model["_indexes"];
329
+ try {
330
+ await this.filterExistingIndexesBeforeSync(options);
331
+ return await import_sequelize.Model.sync.call(this.model, options);
332
+ } finally {
333
+ this.model["_indexes"] = indexes;
334
+ }
291
335
  }
292
336
  async handleZeroColumnModel(options) {
293
337
  if (Object.keys(this.model.tableAttributes).length === 0) {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "2.3.0-beta.6",
3
+ "version": "2.4.0-alpha.2",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "license": "Apache-2.0",
8
8
  "dependencies": {
9
- "@nocobase/logger": "2.3.0-beta.6",
10
- "@nocobase/utils": "2.3.0-beta.6",
9
+ "@nocobase/logger": "2.4.0-alpha.2",
10
+ "@nocobase/utils": "2.4.0-alpha.2",
11
11
  "async-mutex": "^0.3.2",
12
12
  "chalk": "^4.1.1",
13
13
  "cron-parser": "4.4.0",
@@ -38,5 +38,5 @@
38
38
  "url": "git+https://github.com/nocobase/nocobase.git",
39
39
  "directory": "packages/database"
40
40
  },
41
- "gitHead": "45b23791f5245d1417047b122d1a76764aaf5473"
41
+ "gitHead": "706bb81047a05f39f2ddce781ebcafeea4ec9cd5"
42
42
  }