@nocobase/database 1.7.0-beta.9 → 1.8.0-beta.1

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 (47) hide show
  1. package/lib/belongs-to-array/belongs-to-array-repository.d.ts +2 -2
  2. package/lib/belongs-to-array/belongs-to-array-repository.js +6 -7
  3. package/lib/cursor-builder.d.ts +38 -0
  4. package/lib/cursor-builder.js +307 -0
  5. package/lib/dialects/mariadb-dialect.d.ts +2 -0
  6. package/lib/dialects/mariadb-dialect.js +4 -0
  7. package/lib/eager-loading/eager-loading-tree.js +20 -10
  8. package/lib/fields/array-field.d.ts +1 -1
  9. package/lib/fields/array-field.js +12 -7
  10. package/lib/fields/date-field.js +14 -9
  11. package/lib/fields/datetime-no-tz-field.d.ts +1 -1
  12. package/lib/fields/datetime-no-tz-field.js +24 -18
  13. package/lib/fields/encryption-field/encryption-field.js +2 -0
  14. package/lib/fields/nanoid-field.js +12 -5
  15. package/lib/fields/set-field.d.ts +1 -1
  16. package/lib/fields/set-field.js +9 -4
  17. package/lib/fields/string-field.d.ts +4 -0
  18. package/lib/fields/string-field.js +18 -0
  19. package/lib/fields/text-field.d.ts +4 -0
  20. package/lib/fields/text-field.js +18 -0
  21. package/lib/fields/uuid-field.js +11 -4
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +3 -1
  24. package/lib/interfaces/index.d.ts +1 -0
  25. package/lib/interfaces/index.js +3 -1
  26. package/lib/interfaces/input-interface.d.ts +13 -0
  27. package/lib/interfaces/input-interface.js +64 -0
  28. package/lib/interfaces/time-interface.d.ts +13 -0
  29. package/lib/interfaces/time-interface.js +65 -0
  30. package/lib/interfaces/utils.js +4 -1
  31. package/lib/options-parser.d.ts +3 -1
  32. package/lib/options-parser.js +3 -2
  33. package/lib/query-interface/mysql-query-interface.d.ts +1 -0
  34. package/lib/query-interface/mysql-query-interface.js +3 -0
  35. package/lib/query-interface/postgres-query-interface.d.ts +1 -0
  36. package/lib/query-interface/postgres-query-interface.js +3 -0
  37. package/lib/query-interface/query-interface.d.ts +1 -0
  38. package/lib/query-interface/query-interface.js +4 -0
  39. package/lib/query-interface/sqlite-query-interface.d.ts +1 -0
  40. package/lib/query-interface/sqlite-query-interface.js +3 -0
  41. package/lib/relation-repository/hasmany-repository.js +7 -1
  42. package/lib/relation-repository/multiple-relation-repository.d.ts +4 -1
  43. package/lib/relation-repository/multiple-relation-repository.js +2 -2
  44. package/lib/repository.d.ts +21 -0
  45. package/lib/repository.js +30 -4
  46. package/lib/view-collection.js +1 -1
  47. package/package.json +6 -6
@@ -31,8 +31,8 @@ export declare class BelongsToArrayAssociation {
31
31
  targetKey: string;
32
32
  });
33
33
  get target(): import("sequelize/types").ModelStatic<Model<any, any>>;
34
- generateInclude(): {
35
- on: import("sequelize/types/utils").Literal;
34
+ generateInclude(parentAs?: string): {
35
+ on: void;
36
36
  };
37
37
  update(instance: Model, value: any, options?: UpdateAssociationOptions): Promise<void>;
38
38
  }
@@ -78,19 +78,18 @@ const _BelongsToArrayAssociation = class _BelongsToArrayAssociation {
78
78
  get target() {
79
79
  return this.db.getModel(this.targetName);
80
80
  }
81
- generateInclude() {
82
- if (this.db.sequelize.getDialect() !== "postgres") {
83
- throw new Error("Filtering by many to many (array) associations is only supported on postgres");
84
- }
81
+ generateInclude(parentAs) {
85
82
  const targetCollection = this.db.getCollection(this.targetName);
86
83
  const targetField = targetCollection.getField(this.targetKey);
87
84
  const sourceCollection = this.db.getCollection(this.source.name);
88
85
  const foreignField = sourceCollection.getField(this.foreignKey);
89
86
  const queryInterface = this.db.sequelize.getQueryInterface();
90
- const left = queryInterface.quoteIdentifiers(`${this.as}.${targetField.columnName()}`);
91
- const right = queryInterface.quoteIdentifiers(`${this.source.collection.name}.${foreignField.columnName()}`);
87
+ const asLeft = parentAs ? `${parentAs}->${this.as}` : this.as;
88
+ const asRight = parentAs || this.source.collection.name;
89
+ const left = queryInterface.quoteIdentifiers(`${asLeft}.${targetField.columnName()}`);
90
+ const right = queryInterface.quoteIdentifiers(`${asRight}.${foreignField.columnName()}`);
92
91
  return {
93
- on: this.db.sequelize.literal(`${left}=any(${right})`)
92
+ on: this.db.queryInterface.generateJoinOnForJSONArray(left, right)
94
93
  };
95
94
  }
96
95
  async update(instance, value, options = {}) {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Sequelize } from 'sequelize';
10
+ import { FindOptions } from './repository';
11
+ import { Model } from './model';
12
+ import { Collection } from './collection';
13
+ export declare class SmartCursorBuilder {
14
+ private sequelize;
15
+ private tableName;
16
+ private collection;
17
+ constructor(sequelize: Sequelize, tableName: string, collection: Collection);
18
+ /**
19
+ * 根据表结构自动选择最优游标策略
20
+ */
21
+ private getBestCursorStrategy;
22
+ /**
23
+ * Cursor-based pagination query function.
24
+ * Ideal for large datasets (e.g., millions of rows)
25
+ * Note:
26
+ * 1. does not support jumping to arbitrary pages (e.g., "Page 5")
27
+ * 2. Requires a stable, indexed sort field (e.g. ID, createdAt)
28
+ * 3. If custom orderBy is used, it must match the cursor field(s) and direction, otherwise results may be incorrect or unstable.
29
+ * @param options
30
+ */
31
+ chunk(options: FindOptions & {
32
+ chunkSize: number;
33
+ callback: (rows: Model[], options: FindOptions) => Promise<void>;
34
+ find: (options: FindOptions) => Promise<any[]>;
35
+ beforeFind?: (options: FindOptions) => Promise<void>;
36
+ afterFind?: (rows: Model[], options: FindOptions) => Promise<void>;
37
+ }): Promise<void>;
38
+ }
@@ -0,0 +1,307 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+ var __copyProps = (to, from, except, desc) => {
22
+ if (from && typeof from === "object" || typeof from === "function") {
23
+ for (let key of __getOwnPropNames(from))
24
+ if (!__hasOwnProp.call(to, key) && key !== except)
25
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
+ }
27
+ return to;
28
+ };
29
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
+ // If the importer is in node compatibility mode or this is not an ESM
31
+ // file that has been converted to a CommonJS file using a Babel-
32
+ // compatible transform (i.e. "__esModule" has not been set), then set
33
+ // "default" to the CommonJS "module.exports" for node compatibility.
34
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
+ mod
36
+ ));
37
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
+ var cursor_builder_exports = {};
39
+ __export(cursor_builder_exports, {
40
+ SmartCursorBuilder: () => SmartCursorBuilder
41
+ });
42
+ module.exports = __toCommonJS(cursor_builder_exports);
43
+ var import_sequelize = require("sequelize");
44
+ var import_lodash = __toESM(require("lodash"));
45
+ const _SmartCursorBuilder = class _SmartCursorBuilder {
46
+ sequelize;
47
+ tableName;
48
+ collection;
49
+ constructor(sequelize, tableName, collection) {
50
+ this.sequelize = sequelize;
51
+ this.tableName = tableName;
52
+ this.collection = collection;
53
+ }
54
+ /**
55
+ * 根据表结构自动选择最优游标策略
56
+ */
57
+ async getBestCursorStrategy() {
58
+ let indexInfoSql = "";
59
+ const dialect = this.sequelize.getDialect();
60
+ if (dialect === "postgres") {
61
+ indexInfoSql = `
62
+ SELECT
63
+ t.relname AS table_name,
64
+ i.relname AS index_name,
65
+ a.attname AS column_name,
66
+ array_position(ix.indkey, a.attnum) + 1 AS seq_in_index,
67
+ CASE
68
+ WHEN ix.indisprimary THEN 1
69
+ WHEN ix.indisunique THEN 2
70
+ ELSE 3
71
+ END AS index_type,
72
+ -- \u5224\u65AD\u7D22\u5F15\u6392\u5E8F\u65B9\u5411 (0=ASC, 1=DESC)
73
+ CASE WHEN (ix.indoption[array_position(ix.indkey, a.attnum) - 1] & 1) = 1
74
+ THEN 'DESC' ELSE 'ASC'
75
+ END AS direction
76
+ FROM
77
+ pg_class t,
78
+ pg_class i,
79
+ pg_index ix,
80
+ pg_attribute a,
81
+ pg_namespace n
82
+ WHERE
83
+ t.oid = ix.indrelid
84
+ AND i.oid = ix.indexrelid
85
+ AND a.attrelid = t.oid
86
+ AND t.relnamespace = n.oid
87
+ AND a.attnum = ANY(ix.indkey)
88
+ AND t.relkind = 'r'
89
+ AND n.nspname = current_schema()
90
+ AND t.relname = $1
91
+ ORDER BY
92
+ i.relname,
93
+ array_position(ix.indkey, a.attnum)
94
+ `;
95
+ } else if (dialect === "mariadb" || dialect === "mysql") {
96
+ indexInfoSql = `
97
+ SELECT
98
+ i.TABLE_NAME,
99
+ i.INDEX_NAME,
100
+ i.COLUMN_NAME,
101
+ i.SEQ_IN_INDEX,
102
+ CASE
103
+ WHEN i.INDEX_NAME = 'PRIMARY' THEN 1
104
+ WHEN i.NON_UNIQUE = 0 THEN 2
105
+ ELSE 3
106
+ END as INDEX_TYPE
107
+ FROM
108
+ information_schema.STATISTICS i
109
+ WHERE
110
+ i.TABLE_SCHEMA = DATABASE()
111
+ AND i.TABLE_NAME = ?
112
+ ORDER BY
113
+ i.INDEX_NAME,
114
+ i.SEQ_IN_INDEX;
115
+ `;
116
+ }
117
+ const indexRows = await this.sequelize.query(indexInfoSql, {
118
+ type: import_sequelize.QueryTypes.SELECT,
119
+ replacements: [this.tableName],
120
+ raw: true
121
+ });
122
+ const indexes = /* @__PURE__ */ new Map();
123
+ const indexDirections = /* @__PURE__ */ new Map();
124
+ if (!indexRows || indexRows.length === 0) {
125
+ if (Array.isArray(this.collection.filterTargetKey)) {
126
+ return new CompositeKeyCursorStrategy(this.collection.filterTargetKey);
127
+ }
128
+ return new SingleColumnCursorStrategy(this.collection.filterTargetKey);
129
+ }
130
+ for (const row of indexRows) {
131
+ const indexName = dialect === "postgres" ? row.index_name : row.INDEX_NAME;
132
+ const columnName = dialect === "postgres" ? row.column_name : row.COLUMN_NAME;
133
+ const indexType = dialect === "postgres" ? row.index_type : row.INDEX_TYPE;
134
+ if (dialect === "postgres" && row.direction) {
135
+ if (!indexDirections.has(indexName)) {
136
+ indexDirections.set(indexName, /* @__PURE__ */ new Map());
137
+ }
138
+ indexDirections.get(indexName).set(columnName, row.direction);
139
+ }
140
+ if (!indexes.has(indexName)) {
141
+ indexes.set(indexName, {
142
+ name: indexName,
143
+ columns: [],
144
+ isPrimary: dialect === "postgres" ? indexType === 1 : indexName === "PRIMARY",
145
+ isUnique: indexType < 3
146
+ });
147
+ }
148
+ const index = indexes.get(row.INDEX_NAME);
149
+ index.columns[row.SEQ_IN_INDEX - 1] = row.COLUMN_NAME;
150
+ }
151
+ for (const index of indexes.values()) {
152
+ if (index.isPrimary) {
153
+ if (index.columns.length === 1) {
154
+ return new SingleColumnCursorStrategy(index.columns[0]);
155
+ } else {
156
+ if (dialect === "postgres" && indexDirections.has(index.name)) {
157
+ const directions = index.columns.map((col) => indexDirections.get(index.name).get(col) || "ASC");
158
+ return new CompositeKeyCursorStrategy(index.columns, directions);
159
+ } else {
160
+ return new CompositeKeyCursorStrategy(index.columns);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ let singleColumnUniqueIndex = null;
166
+ let multiColumnUniqueIndex = null;
167
+ for (const index of indexes.values()) {
168
+ if (index.isUnique && !index.isPrimary) {
169
+ if (index.columns.length === 1 && !singleColumnUniqueIndex) {
170
+ singleColumnUniqueIndex = index;
171
+ } else if (index.columns.length > 1 && !multiColumnUniqueIndex) {
172
+ multiColumnUniqueIndex = index;
173
+ }
174
+ }
175
+ }
176
+ if (singleColumnUniqueIndex) {
177
+ return new SingleColumnCursorStrategy(singleColumnUniqueIndex.columns[0]);
178
+ }
179
+ if (multiColumnUniqueIndex) {
180
+ return new CompositeKeyCursorStrategy(multiColumnUniqueIndex.columns);
181
+ }
182
+ let anyIndex = null;
183
+ for (const index of indexes.values()) {
184
+ if (index.columns.length > 0 && !index.isPrimary && !index.isUnique) {
185
+ anyIndex = index;
186
+ break;
187
+ }
188
+ }
189
+ if (anyIndex) {
190
+ if (anyIndex.columns.length === 1) {
191
+ return new SingleColumnCursorStrategy(anyIndex.columns[0]);
192
+ } else {
193
+ return new CompositeKeyCursorStrategy(anyIndex.columns);
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Cursor-based pagination query function.
199
+ * Ideal for large datasets (e.g., millions of rows)
200
+ * Note:
201
+ * 1. does not support jumping to arbitrary pages (e.g., "Page 5")
202
+ * 2. Requires a stable, indexed sort field (e.g. ID, createdAt)
203
+ * 3. If custom orderBy is used, it must match the cursor field(s) and direction, otherwise results may be incorrect or unstable.
204
+ * @param options
205
+ */
206
+ async chunk(options) {
207
+ const cursorStrategy = await this.getBestCursorStrategy();
208
+ let cursorRecord = null;
209
+ let hasMoreData = true;
210
+ let isFirst = true;
211
+ options.order = cursorStrategy.buildSort();
212
+ options["parseSort"] = false;
213
+ while (hasMoreData) {
214
+ if (!isFirst) {
215
+ options.where = cursorStrategy.buildWhere(options.where, cursorRecord);
216
+ }
217
+ if (isFirst) {
218
+ isFirst = false;
219
+ }
220
+ options.limit = options.chunkSize || 1e3;
221
+ if (options.beforeFind) {
222
+ await options.beforeFind(options);
223
+ }
224
+ const records = await options.find(import_lodash.default.omit(options, "callback", "beforeFind", "afterFind", "chunkSize", "find"));
225
+ if (options.afterFind) {
226
+ await options.afterFind(records, options);
227
+ }
228
+ if (records.length === 0) {
229
+ hasMoreData = false;
230
+ continue;
231
+ }
232
+ await options.callback(records, options);
233
+ cursorRecord = records[records.length - 1];
234
+ }
235
+ }
236
+ };
237
+ __name(_SmartCursorBuilder, "SmartCursorBuilder");
238
+ let SmartCursorBuilder = _SmartCursorBuilder;
239
+ const _SingleColumnCursorStrategy = class _SingleColumnCursorStrategy {
240
+ columnName;
241
+ constructor(columnName) {
242
+ this.columnName = columnName;
243
+ }
244
+ buildSort() {
245
+ return [[this.columnName, "ASC"]];
246
+ }
247
+ buildWhere(baseWhere, record) {
248
+ if (!record) {
249
+ return baseWhere;
250
+ }
251
+ return { ...baseWhere, [this.columnName]: { [import_sequelize.Op.gt]: record[this.columnName] } };
252
+ }
253
+ };
254
+ __name(_SingleColumnCursorStrategy, "SingleColumnCursorStrategy");
255
+ let SingleColumnCursorStrategy = _SingleColumnCursorStrategy;
256
+ const _CompositeKeyCursorStrategy = class _CompositeKeyCursorStrategy {
257
+ columns;
258
+ directions;
259
+ constructor(columns, directions) {
260
+ this.columns = columns;
261
+ this.directions = directions || Array(columns.length).fill("ASC");
262
+ }
263
+ buildSort() {
264
+ const orderBy = [];
265
+ for (let i = 0; i < this.columns.length; i++) {
266
+ orderBy.push([this.columns[i], this.directions[i]]);
267
+ }
268
+ return orderBy;
269
+ }
270
+ buildWhere(baseWhere, record) {
271
+ if (!record) {
272
+ return baseWhere;
273
+ }
274
+ const whereConditions = [];
275
+ for (let i = 0; i < this.columns.length; i++) {
276
+ const column = this.columns[i];
277
+ if (i > 0) {
278
+ const equalConditions = {};
279
+ for (let j = 0; j < i; j++) {
280
+ equalConditions[this.columns[j]] = record[this.columns[j]];
281
+ }
282
+ whereConditions.push({
283
+ ...equalConditions,
284
+ [column]: {
285
+ [import_sequelize.Op.gt]: record[column]
286
+ }
287
+ });
288
+ } else {
289
+ whereConditions.push({
290
+ [column]: {
291
+ [import_sequelize.Op.gt]: record[column]
292
+ }
293
+ });
294
+ }
295
+ }
296
+ const cursorCondition = {
297
+ [import_sequelize.Op.or]: whereConditions
298
+ };
299
+ return baseWhere ? { [import_sequelize.Op.and]: [baseWhere, cursorCondition] } : cursorCondition;
300
+ }
301
+ };
302
+ __name(_CompositeKeyCursorStrategy, "CompositeKeyCursorStrategy");
303
+ let CompositeKeyCursorStrategy = _CompositeKeyCursorStrategy;
304
+ // Annotate the CommonJS export names for ESM import in node:
305
+ 0 && (module.exports = {
306
+ SmartCursorBuilder
307
+ });
@@ -6,9 +6,11 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
+ import { DatabaseOptions } from '../database';
9
10
  import { BaseDialect } from './base-dialect';
10
11
  export declare class MariadbDialect extends BaseDialect {
11
12
  static dialectName: string;
13
+ getSequelizeOptions(options: DatabaseOptions): import("../database").IDatabaseOptions;
12
14
  getVersionGuard(): {
13
15
  sql: string;
14
16
  get: (v: string) => string;
@@ -34,6 +34,10 @@ __export(mariadb_dialect_exports, {
34
34
  module.exports = __toCommonJS(mariadb_dialect_exports);
35
35
  var import_base_dialect = require("./base-dialect");
36
36
  const _MariadbDialect = class _MariadbDialect extends import_base_dialect.BaseDialect {
37
+ getSequelizeOptions(options) {
38
+ options.dialectOptions = { ...options.dialectOptions || {}, supportBigNumbers: true, bigNumberStrings: true };
39
+ return options;
40
+ }
37
41
  getVersionGuard() {
38
42
  return {
39
43
  sql: "select version() as version",
@@ -85,6 +85,25 @@ const queryParentSQL = /* @__PURE__ */ __name((options) => {
85
85
  )
86
86
  SELECT ${q(targetKeyField)} AS ${q(targetKey)}, ${q(foreignKeyField)} AS ${q(foreignKey)} FROM cte`;
87
87
  }, "queryParentSQL");
88
+ const processIncludes = /* @__PURE__ */ __name((includes, model, parentAs = "") => {
89
+ includes.forEach((include, index) => {
90
+ const association = model.associations[include.association];
91
+ if (association == null ? void 0 : association.generateInclude) {
92
+ includes[index] = {
93
+ ...include,
94
+ ...association.generateInclude(parentAs)
95
+ };
96
+ }
97
+ if (include.include && Array.isArray(include.include) && include.include.length > 0) {
98
+ const nextModel = association == null ? void 0 : association.target;
99
+ if (!nextModel) {
100
+ return;
101
+ }
102
+ processIncludes(include.include, nextModel, parentAs ? `${parentAs}->${association.as}` : association.as);
103
+ }
104
+ });
105
+ return includes;
106
+ }, "processIncludes");
88
107
  const _EagerLoadingTree = class _EagerLoadingTree {
89
108
  root;
90
109
  db;
@@ -208,22 +227,13 @@ const _EagerLoadingTree = class _EagerLoadingTree {
208
227
  if (!primaryKeyField) {
209
228
  throw new Error(`Model ${node.model.name} does not have primary key`);
210
229
  }
211
- includeForFilter.forEach((include, index) => {
212
- const association = node.model.associations[include.association];
213
- if ((association == null ? void 0 : association.associationType) == "BelongsToArray") {
214
- includeForFilter[index] = {
215
- ...include,
216
- ...association.generateInclude()
217
- };
218
- }
219
- });
220
230
  const ids2 = (await node.model.findAll({
221
231
  ...this.rootQueryOptions,
222
232
  includeIgnoreAttributes: false,
223
233
  attributes: [primaryKeyField],
224
234
  group: `${node.model.name}.${primaryKeyField}`,
225
235
  transaction,
226
- include: includeForFilter
236
+ include: processIncludes(includeForFilter, node.model)
227
237
  })).map((row) => {
228
238
  return { row, pk: row[primaryKeyField] };
229
239
  });
@@ -10,7 +10,7 @@ import { DataTypes } from 'sequelize';
10
10
  import { BaseColumnFieldOptions, Field } from './field';
11
11
  export declare class ArrayField extends Field {
12
12
  get dataType(): DataTypes.AbstractDataTypeConstructor | DataTypes.ArrayDataType<any>;
13
- sortValue: (model: any) => void;
13
+ sortValue: (instances: any) => void;
14
14
  bind(): void;
15
15
  unbind(): void;
16
16
  }
@@ -43,23 +43,28 @@ const _ArrayField = class _ArrayField extends import_field.Field {
43
43
  }
44
44
  return import_sequelize.DataTypes.JSON;
45
45
  }
46
- sortValue = /* @__PURE__ */ __name((model) => {
47
- let oldValue = model.get(this.options.name);
48
- if (oldValue) {
49
- if (typeof oldValue === "string") {
50
- oldValue = JSON.parse(oldValue);
46
+ sortValue = /* @__PURE__ */ __name((instances) => {
47
+ instances = Array.isArray(instances) ? instances : [instances];
48
+ for (const instance of instances) {
49
+ let oldValue = instance.get(this.options.name);
50
+ if (oldValue) {
51
+ if (typeof oldValue === "string") {
52
+ oldValue = JSON.parse(oldValue);
53
+ }
54
+ const newValue = oldValue.sort();
55
+ instance.set(this.options.name, newValue);
51
56
  }
52
- const newValue = oldValue.sort();
53
- model.set(this.options.name, newValue);
54
57
  }
55
58
  }, "sortValue");
56
59
  bind() {
57
60
  super.bind();
58
61
  this.on("beforeSave", this.sortValue);
62
+ this.on("beforeBulkCreate", this.sortValue);
59
63
  }
60
64
  unbind() {
61
65
  super.unbind();
62
66
  this.off("beforeSave", this.sortValue);
67
+ this.off("beforeBulkCreate", this.sortValue);
63
68
  }
64
69
  };
65
70
  __name(_ArrayField, "ArrayField");
@@ -82,15 +82,18 @@ const _DateField = class _DateField extends import_field.Field {
82
82
  }
83
83
  return serverTimeZone;
84
84
  };
85
- this.beforeSave = async (instance, options) => {
86
- const value = instance.get(name);
87
- if (!value && instance.isNewRecord && defaultToCurrentTime) {
88
- instance.set(name, /* @__PURE__ */ new Date());
89
- return;
90
- }
91
- if (onUpdateToCurrentTime) {
92
- instance.set(name, /* @__PURE__ */ new Date());
93
- return;
85
+ this.beforeSave = async (instances, options) => {
86
+ instances = Array.isArray(instances) ? instances : [instances];
87
+ for (const instance of instances) {
88
+ const value = instance.get(name);
89
+ if (!value && instance.isNewRecord && defaultToCurrentTime) {
90
+ instance.set(name, /* @__PURE__ */ new Date());
91
+ continue;
92
+ }
93
+ if (onUpdateToCurrentTime) {
94
+ instance.set(name, /* @__PURE__ */ new Date());
95
+ continue;
96
+ }
94
97
  }
95
98
  };
96
99
  if (this.options.defaultValue && this.database.isMySQLCompatibleDialect()) {
@@ -143,10 +146,12 @@ const _DateField = class _DateField extends import_field.Field {
143
146
  model.refreshAttributes();
144
147
  }
145
148
  this.on("beforeSave", this.beforeSave);
149
+ this.on("beforeBulkCreate", this.beforeSave);
146
150
  }
147
151
  unbind() {
148
152
  super.unbind();
149
153
  this.off("beforeSave", this.beforeSave);
154
+ this.off("beforeBulkCreate", this.beforeSave);
150
155
  }
151
156
  };
152
157
  __name(_DateField, "DateField");
@@ -13,7 +13,7 @@ declare class DatetimeNoTzTypeMySQL extends DataTypes.ABSTRACT {
13
13
  }
14
14
  export declare class DatetimeNoTzField extends Field {
15
15
  get dataType(): typeof DatetimeNoTzTypeMySQL;
16
- init(): void;
16
+ beforeSave: (instances: any, options: any) => Promise<void>;
17
17
  additionalSequelizeOptions(): {};
18
18
  bind(): void;
19
19
  unbind(): void;
@@ -61,58 +61,64 @@ const _DatetimeNoTzField = class _DatetimeNoTzField extends import_field.Field {
61
61
  if (this.database.isMySQLCompatibleDialect()) {
62
62
  return DatetimeNoTzTypeMySQL;
63
63
  }
64
- return import_sequelize.DataTypes.STRING;
64
+ return import_sequelize.DataTypes.DATE;
65
65
  }
66
- init() {
66
+ beforeSave = /* @__PURE__ */ __name(async (instances, options) => {
67
+ instances = Array.isArray(instances) ? instances : [instances];
67
68
  const { name, defaultToCurrentTime, onUpdateToCurrentTime } = this.options;
68
- this.beforeSave = async (instance, options) => {
69
+ for (const instance of instances) {
69
70
  const value = instance.get(name);
70
71
  if (!value && instance.isNewRecord && defaultToCurrentTime) {
71
72
  instance.set(name, /* @__PURE__ */ new Date());
72
- return;
73
+ continue;
73
74
  }
74
75
  if (onUpdateToCurrentTime) {
75
76
  instance.set(name, /* @__PURE__ */ new Date());
76
- return;
77
+ continue;
77
78
  }
78
- };
79
- }
79
+ }
80
+ }, "beforeSave");
80
81
  additionalSequelizeOptions() {
81
82
  const { name } = this.options;
82
83
  const timezone = this.database.options.rawTimezone || "+00:00";
83
84
  const isPg = this.database.inDialect("postgres");
85
+ const isMySQLCompatibleDialect = this.database.isMySQLCompatibleDialect();
84
86
  return {
85
87
  get() {
86
88
  const val = this.getDataValue(name);
87
89
  if (val instanceof Date) {
88
- if (isPg) {
89
- return (0, import_moment.default)(val).format("YYYY-MM-DD HH:mm:ss");
90
- }
91
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
90
+ const momentVal = (0, import_moment.default)(val);
92
91
  return momentVal.format("YYYY-MM-DD HH:mm:ss");
93
92
  }
94
93
  return val;
95
94
  },
96
95
  set(val) {
97
- if (typeof val === "string" && isIso8601(val)) {
98
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
99
- val = momentVal.format("YYYY-MM-DD HH:mm:ss");
96
+ if (val == null) {
97
+ return this.setDataValue(name, null);
98
+ }
99
+ const dateOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
100
+ const momentVal = (0, import_moment.default)(val);
101
+ if (typeof val === "string" && isIso8601(val) || val instanceof Date) {
102
+ momentVal.utcOffset(timezone);
103
+ momentVal.utcOffset(-dateOffset, true);
100
104
  }
101
- if (val && val instanceof Date) {
102
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
103
- val = momentVal.format("YYYY-MM-DD HH:mm:ss");
105
+ if (isMySQLCompatibleDialect) {
106
+ momentVal.millisecond(0);
104
107
  }
105
- return this.setDataValue(name, val);
108
+ const date = momentVal.toDate();
109
+ return this.setDataValue(name, date);
106
110
  }
107
111
  };
108
112
  }
109
113
  bind() {
110
114
  super.bind();
111
115
  this.on("beforeSave", this.beforeSave);
116
+ this.on("beforeBulkCreate", this.beforeSave);
112
117
  }
113
118
  unbind() {
114
119
  super.unbind();
115
120
  this.off("beforeSave", this.beforeSave);
121
+ this.off("beforeBulkCreate", this.beforeSave);
116
122
  }
117
123
  };
118
124
  __name(_DatetimeNoTzField, "DatetimeNoTzField");
@@ -93,11 +93,13 @@ const _EncryptionField = class _EncryptionField extends import_field.Field {
93
93
  super.bind();
94
94
  this.on("afterFind", this.findListener);
95
95
  this.on("beforeSave", this.writeListener);
96
+ this.on("beforeBulkCreate", this.writeListener);
96
97
  }
97
98
  unbind() {
98
99
  super.unbind();
99
100
  this.off("afterFind", this.findListener);
100
101
  this.off("beforeSave", this.writeListener);
102
+ this.off("beforeBulkCreate", this.writeListener);
101
103
  }
102
104
  };
103
105
  __name(_EncryptionField, "EncryptionField");