adminizer 5.0.0-build.7 → 5.0.0-build.9

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.
@@ -1,5 +1,16 @@
1
- import { Sequelize, Op, HasMany } from "sequelize";
2
1
  import { AbstractAdapter, AbstractModel } from "../AbstractModel.js";
2
+ let _Sequelize = null;
3
+ let _Op = null;
4
+ let _HasMany = null;
5
+ async function ensureSequelize() {
6
+ if (_Sequelize)
7
+ return _Sequelize;
8
+ const sequelize = await import('sequelize');
9
+ _Sequelize = sequelize.Sequelize;
10
+ _Op = sequelize.Op;
11
+ _HasMany = sequelize.HasMany;
12
+ return _Sequelize;
13
+ }
3
14
  function resolveType(type) {
4
15
  const sqlType = typeof type.toString === "function"
5
16
  ? type.toString().toLowerCase()
@@ -94,6 +105,13 @@ function capitalize(str) {
94
105
  }
95
106
  export class SequelizeModel extends AbstractModel {
96
107
  model;
108
+ _init = false;
109
+ async _ensure() {
110
+ if (!this._init) {
111
+ await ensureSequelize();
112
+ this._init = true;
113
+ }
114
+ }
97
115
  constructor(modelName, model) {
98
116
  super(modelName, mapSequelizeToAdminizerAttributes(model), model.primaryKeyAttribute, model.name);
99
117
  this.model = model;
@@ -102,7 +120,7 @@ export class SequelizeModel extends AbstractModel {
102
120
  const pattern = `%${JSON.stringify(item).replace(/[%_]/g, "\\$&")}%`;
103
121
  const attribute = this.model.rawAttributes?.[targetKey];
104
122
  const columnName = typeof attribute?.field === "string" ? attribute.field : targetKey;
105
- return Sequelize.where(Sequelize.cast(Sequelize.col(`${this.model.name}.${columnName}`), "TEXT"), { [Op.like]: pattern });
123
+ return _Sequelize.where(_Sequelize.cast(_Sequelize.col(`${this.model.name}.${columnName}`), "TEXT"), { [_Op.like]: pattern });
106
124
  }
107
125
  _convertCriteriaToSequelize(criteria) {
108
126
  // Special handling for Sequelize.literal() at the top level
@@ -137,24 +155,24 @@ export class SequelizeModel extends AbstractModel {
137
155
  }
138
156
  // Special handling for null values (IS NULL condition)
139
157
  if (value === null) {
140
- result[targetKey] = { [Op.is]: null };
158
+ result[targetKey] = { [_Op.is]: null };
141
159
  continue;
142
160
  }
143
161
  // 🔹 Handle Sequelize Op operators (already in Sequelize format)
144
- if (key === Op.and || key === 'and') {
145
- result[Op.and] = Array.isArray(value)
162
+ if (key === _Op.and || key === 'and') {
163
+ result[_Op.and] = Array.isArray(value)
146
164
  ? value.map((item) => this._convertCriteriaToSequelize(item))
147
165
  : [];
148
166
  continue;
149
167
  }
150
- if (key === Op.or || key === 'or') {
151
- result[Op.or] = Array.isArray(value)
168
+ if (key === _Op.or || key === 'or') {
169
+ result[_Op.or] = Array.isArray(value)
152
170
  ? value.map((item) => this._convertCriteriaToSequelize(item))
153
171
  : [];
154
172
  continue;
155
173
  }
156
- if (key === Op.not || key === 'not' || key === '$not') {
157
- result[Op.not] = this._convertCriteriaToSequelize(value);
174
+ if (key === _Op.not || key === 'not' || key === '$not') {
175
+ result[_Op.not] = this._convertCriteriaToSequelize(value);
158
176
  continue;
159
177
  }
160
178
  // Skip Symbol keys that are not handled above (should not happen, but safety check)
@@ -163,7 +181,7 @@ export class SequelizeModel extends AbstractModel {
163
181
  }
164
182
  if (Array.isArray(value)) {
165
183
  // ✅ Array processing - use the IN operator
166
- result[targetKey] = { [Op.in]: value };
184
+ result[targetKey] = { [_Op.in]: value };
167
185
  }
168
186
  else if (typeof value === "object" && !Array.isArray(value)) {
169
187
  // Check if value already contains Sequelize Op operators
@@ -173,10 +191,10 @@ export class SequelizeModel extends AbstractModel {
173
191
  // Helper function to check if a symbol/op is a Sequelize operator
174
192
  const isSequelizeOp = (op) => {
175
193
  // Check direct Symbol equality first
176
- if (op === Op.gt || op === Op.gte || op === Op.lt || op === Op.lte ||
177
- op === Op.eq || op === Op.ne || op === Op.in || op === Op.notIn ||
178
- op === Op.like || op === Op.iLike || op === Op.between ||
179
- op === Op.startsWith || op === Op.endsWith || op === Op.is || op === Op.not) {
194
+ if (op === _Op.gt || op === _Op.gte || op === _Op.lt || op === _Op.lte ||
195
+ op === _Op.eq || op === _Op.ne || op === _Op.in || op === _Op.notIn ||
196
+ op === _Op.like || op === _Op.iLike || op === _Op.between ||
197
+ op === _Op.startsWith || op === _Op.endsWith || op === _Op.is || op === _Op.not) {
180
198
  return true;
181
199
  }
182
200
  // Fallback: check by Symbol description (handles different Symbol instances)
@@ -206,22 +224,22 @@ export class SequelizeModel extends AbstractModel {
206
224
  continue;
207
225
  switch (op) {
208
226
  case "eq":
209
- result[targetKey] = { [Op.eq]: val };
227
+ result[targetKey] = { [_Op.eq]: val };
210
228
  break;
211
229
  case "ne":
212
- result[targetKey] = { [Op.ne]: val };
230
+ result[targetKey] = { [_Op.ne]: val };
213
231
  break;
214
232
  case "gt":
215
- result[targetKey] = { [Op.gt]: val };
233
+ result[targetKey] = { [_Op.gt]: val };
216
234
  break;
217
235
  case "gte":
218
- result[targetKey] = { [Op.gte]: val };
236
+ result[targetKey] = { [_Op.gte]: val };
219
237
  break;
220
238
  case "lt":
221
- result[targetKey] = { [Op.lt]: val };
239
+ result[targetKey] = { [_Op.lt]: val };
222
240
  break;
223
241
  case "lte":
224
- result[targetKey] = { [Op.lte]: val };
242
+ result[targetKey] = { [_Op.lte]: val };
225
243
  break;
226
244
  case "contains":
227
245
  // Check if field is a date/datetime type
@@ -244,83 +262,83 @@ export class SequelizeModel extends AbstractModel {
244
262
  }
245
263
  else {
246
264
  // Valid date - use exact match
247
- result[targetKey] = { [Op.eq]: dateValue };
265
+ result[targetKey] = { [_Op.eq]: dateValue };
248
266
  }
249
267
  }
250
268
  else {
251
269
  // For time fields and non-date fields - use Op.like with wildcards
252
- result[targetKey] = { [Op.like]: `%${val}%` };
270
+ result[targetKey] = { [_Op.like]: `%${val}%` };
253
271
  }
254
272
  break;
255
273
  case "startsWith":
256
- result[targetKey] = { [Op.startsWith]: val };
274
+ result[targetKey] = { [_Op.startsWith]: val };
257
275
  break;
258
276
  case "endsWith":
259
- result[targetKey] = { [Op.endsWith]: val };
277
+ result[targetKey] = { [_Op.endsWith]: val };
260
278
  break;
261
279
  case ">":
262
- result[targetKey] = { [Op.gt]: val };
280
+ result[targetKey] = { [_Op.gt]: val };
263
281
  break;
264
282
  case ">=":
265
- result[targetKey] = { [Op.gte]: val };
283
+ result[targetKey] = { [_Op.gte]: val };
266
284
  break;
267
285
  case "<":
268
- result[targetKey] = { [Op.lt]: val };
286
+ result[targetKey] = { [_Op.lt]: val };
269
287
  break;
270
288
  case "<=":
271
- result[targetKey] = { [Op.lte]: val };
289
+ result[targetKey] = { [_Op.lte]: val };
272
290
  break;
273
291
  case "!=":
274
- result[targetKey] = { [Op.ne]: val };
292
+ result[targetKey] = { [_Op.ne]: val };
275
293
  break;
276
294
  case "in":
277
- result[targetKey] = { [Op.in]: val };
295
+ result[targetKey] = { [_Op.in]: val };
278
296
  break;
279
297
  case "notIn":
280
- result[targetKey] = { [Op.notIn]: val };
298
+ result[targetKey] = { [_Op.notIn]: val };
281
299
  break;
282
300
  case "nin":
283
- result[targetKey] = { [Op.notIn]: val };
301
+ result[targetKey] = { [_Op.notIn]: val };
284
302
  break;
285
303
  case "between":
286
- result[targetKey] = { [Op.between]: val };
304
+ result[targetKey] = { [_Op.between]: val };
287
305
  break;
288
306
  case "isNull":
289
307
  if (val) {
290
- result[targetKey] = { [Op.is]: null };
308
+ result[targetKey] = { [_Op.is]: null };
291
309
  }
292
310
  break;
293
311
  case "isNotNull":
294
312
  if (val) {
295
- result[targetKey] = { [Op.not]: null };
313
+ result[targetKey] = { [_Op.not]: null };
296
314
  }
297
315
  break;
298
316
  case "regex":
299
- result[targetKey] = { [Op.regexp]: val };
317
+ result[targetKey] = { [_Op.regexp]: val };
300
318
  break;
301
319
  case "jsonContains": {
302
320
  const values = Array.isArray(val) ? val : [val];
303
321
  const clauses = values.map((item) => this._buildJsonContainsCondition(targetKey, item));
304
- result[Op.and] = [
305
- ...(result[Op.and] ?? []),
322
+ result[_Op.and] = [
323
+ ...(result[_Op.and] ?? []),
306
324
  ...clauses
307
325
  ];
308
326
  break;
309
327
  }
310
328
  case "$is":
311
- result[targetKey] = { [Op.is]: val };
329
+ result[targetKey] = { [_Op.is]: val };
312
330
  break;
313
331
  case "$not":
314
- result[targetKey] = { [Op.not]: val };
332
+ result[targetKey] = { [_Op.not]: val };
315
333
  break;
316
334
  case "$ne":
317
- result[targetKey] = { [Op.ne]: val };
335
+ result[targetKey] = { [_Op.ne]: val };
318
336
  break;
319
337
  case "not":
320
- result[targetKey] = { [Op.not]: val };
338
+ result[targetKey] = { [_Op.not]: val };
321
339
  break;
322
340
  default:
323
- result[targetKey] = { [Op.eq]: val };
341
+ result[targetKey] = { [_Op.eq]: val };
324
342
  }
325
343
  }
326
344
  }
@@ -403,6 +421,7 @@ export class SequelizeModel extends AbstractModel {
403
421
  }
404
422
  // --- CREATE ---
405
423
  async _create(data) {
424
+ await this._ensure();
406
425
  // console.clear()
407
426
  const assocNames = Object.keys(this.model.associations);
408
427
  const plainData = {};
@@ -474,6 +493,7 @@ export class SequelizeModel extends AbstractModel {
474
493
  }
475
494
  // --- FIND ONE ---
476
495
  async _findOne(criteria) {
496
+ await this._ensure();
477
497
  // console.debug(">> _findOne: input criteria:", criteria);
478
498
  const { where, attributes } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
479
499
  const includes = criteria.populate
@@ -500,6 +520,7 @@ export class SequelizeModel extends AbstractModel {
500
520
  }
501
521
  // --- FIND MANY ---
502
522
  async _find(criteria = {}) {
523
+ await this._ensure();
503
524
  const assocNames = Object.keys(this.model.associations);
504
525
  // console.debug(">> _find: input criteria:", criteria, "options:", options);
505
526
  const { where, limit, offset, order, attributes } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
@@ -555,6 +576,7 @@ export class SequelizeModel extends AbstractModel {
555
576
  * Bypasses Adminizer criteria conversion for direct Sequelize usage
556
577
  */
557
578
  async findWithRawWhere(where, options = {}) {
579
+ await this._ensure();
558
580
  const assocNames = Object.keys(this.model.associations);
559
581
  const includes = options.populate !== false
560
582
  ? assocNames.map(a => ({ association: a }))
@@ -572,6 +594,7 @@ export class SequelizeModel extends AbstractModel {
572
594
  * Public method for count with raw SQL where clauses
573
595
  */
574
596
  async countWithRawWhere(where) {
597
+ await this._ensure();
575
598
  const assocNames = Object.keys(this.model.associations);
576
599
  const include = assocNames.map((association) => ({ association }));
577
600
  return await this.model.count({
@@ -583,6 +606,7 @@ export class SequelizeModel extends AbstractModel {
583
606
  }
584
607
  // --- UPDATE ONE ---
585
608
  async _updateOne(criteria, data) {
609
+ await this._ensure();
586
610
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
587
611
  const record = await this.model.findOne({ where });
588
612
  if (!record)
@@ -614,6 +638,7 @@ export class SequelizeModel extends AbstractModel {
614
638
  }
615
639
  // --- UPDATE MANY ---
616
640
  async _update(criteria, data) {
641
+ await this._ensure();
617
642
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
618
643
  const assocNames = Object.keys(this.model.associations);
619
644
  const plainData = {};
@@ -648,6 +673,7 @@ export class SequelizeModel extends AbstractModel {
648
673
  }
649
674
  // --- DESTROY ONE ---
650
675
  async _destroyOne(criteria) {
676
+ await this._ensure();
651
677
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
652
678
  const record = await this.model.findOne({ where });
653
679
  if (!record)
@@ -683,6 +709,7 @@ export class SequelizeModel extends AbstractModel {
683
709
  }
684
710
  // --- DESTROY MANY ---
685
711
  async _destroy(criteria) {
712
+ await this._ensure();
686
713
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
687
714
  const records = await this.model.findAll({ where });
688
715
  const assocNames = Object.keys(this.model.associations);
@@ -787,7 +814,7 @@ export class SequelizeModel extends AbstractModel {
787
814
  }
788
815
  const association = this.model.associations[alias];
789
816
  const isUsedInWhere = usedRelationAliases.has(alias);
790
- const isHasMany = association instanceof HasMany || association?.associationType === 'HasMany';
817
+ const isHasMany = association instanceof _HasMany || association?.associationType === 'HasMany';
791
818
  // When filtering by relation path ($alias.field$), extra joined hasMany associations
792
819
  // can duplicate base rows and break page size. Load such associations separately.
793
820
  if (isHasMany && !isUsedInWhere) {
@@ -843,10 +870,14 @@ export class SequelizeAdapter extends AbstractAdapter {
843
870
  sequelize;
844
871
  Model = SequelizeModel;
845
872
  constructor(sequelize, options = {}) {
873
+ ensureSequelize();
846
874
  super("sequelize", sequelize, options);
875
+ // Note: ensureSequelize is fire-and-forget during construction
876
+ // Methods also call this._ensure() to guarantee initialization
847
877
  this.sequelize = sequelize;
848
878
  }
849
879
  get models() {
880
+ ``;
850
881
  return this.sequelize.models;
851
882
  }
852
883
  getModel(modelName) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "adminizer",
3
3
  "type": "module",
4
- "version": "5.0.0-build.7",
4
+ "version": "5.0.0-build.9",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": "./index.js",