adminizer 5.0.0-build.8 → 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,22 +1,15 @@
1
- let Sequelize;
2
- let Op;
3
- let HasMany;
4
1
  import { AbstractAdapter, AbstractModel } from "../AbstractModel.js";
5
- try {
6
- const sequelize = require('sequelize');
7
- Sequelize = sequelize.Sequelize;
8
- Op = sequelize.Op;
9
- HasMany = sequelize.HasMany;
10
- }
11
- catch (error) {
12
- const stub = new Proxy({}, {
13
- get: () => () => {
14
- throw new Error('Sequelize is not installed. Run: npm install sequelize');
15
- }
16
- });
17
- Sequelize = stub;
18
- Op = stub;
19
- HasMany = stub;
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;
20
13
  }
21
14
  function resolveType(type) {
22
15
  const sqlType = typeof type.toString === "function"
@@ -112,6 +105,13 @@ function capitalize(str) {
112
105
  }
113
106
  export class SequelizeModel extends AbstractModel {
114
107
  model;
108
+ _init = false;
109
+ async _ensure() {
110
+ if (!this._init) {
111
+ await ensureSequelize();
112
+ this._init = true;
113
+ }
114
+ }
115
115
  constructor(modelName, model) {
116
116
  super(modelName, mapSequelizeToAdminizerAttributes(model), model.primaryKeyAttribute, model.name);
117
117
  this.model = model;
@@ -120,7 +120,7 @@ export class SequelizeModel extends AbstractModel {
120
120
  const pattern = `%${JSON.stringify(item).replace(/[%_]/g, "\\$&")}%`;
121
121
  const attribute = this.model.rawAttributes?.[targetKey];
122
122
  const columnName = typeof attribute?.field === "string" ? attribute.field : targetKey;
123
- 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 });
124
124
  }
125
125
  _convertCriteriaToSequelize(criteria) {
126
126
  // Special handling for Sequelize.literal() at the top level
@@ -155,24 +155,24 @@ export class SequelizeModel extends AbstractModel {
155
155
  }
156
156
  // Special handling for null values (IS NULL condition)
157
157
  if (value === null) {
158
- result[targetKey] = { [Op.is]: null };
158
+ result[targetKey] = { [_Op.is]: null };
159
159
  continue;
160
160
  }
161
161
  // 🔹 Handle Sequelize Op operators (already in Sequelize format)
162
- if (key === Op.and || key === 'and') {
163
- result[Op.and] = Array.isArray(value)
162
+ if (key === _Op.and || key === 'and') {
163
+ result[_Op.and] = Array.isArray(value)
164
164
  ? value.map((item) => this._convertCriteriaToSequelize(item))
165
165
  : [];
166
166
  continue;
167
167
  }
168
- if (key === Op.or || key === 'or') {
169
- result[Op.or] = Array.isArray(value)
168
+ if (key === _Op.or || key === 'or') {
169
+ result[_Op.or] = Array.isArray(value)
170
170
  ? value.map((item) => this._convertCriteriaToSequelize(item))
171
171
  : [];
172
172
  continue;
173
173
  }
174
- if (key === Op.not || key === 'not' || key === '$not') {
175
- result[Op.not] = this._convertCriteriaToSequelize(value);
174
+ if (key === _Op.not || key === 'not' || key === '$not') {
175
+ result[_Op.not] = this._convertCriteriaToSequelize(value);
176
176
  continue;
177
177
  }
178
178
  // Skip Symbol keys that are not handled above (should not happen, but safety check)
@@ -181,7 +181,7 @@ export class SequelizeModel extends AbstractModel {
181
181
  }
182
182
  if (Array.isArray(value)) {
183
183
  // ✅ Array processing - use the IN operator
184
- result[targetKey] = { [Op.in]: value };
184
+ result[targetKey] = { [_Op.in]: value };
185
185
  }
186
186
  else if (typeof value === "object" && !Array.isArray(value)) {
187
187
  // Check if value already contains Sequelize Op operators
@@ -191,10 +191,10 @@ export class SequelizeModel extends AbstractModel {
191
191
  // Helper function to check if a symbol/op is a Sequelize operator
192
192
  const isSequelizeOp = (op) => {
193
193
  // Check direct Symbol equality first
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) {
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) {
198
198
  return true;
199
199
  }
200
200
  // Fallback: check by Symbol description (handles different Symbol instances)
@@ -224,22 +224,22 @@ export class SequelizeModel extends AbstractModel {
224
224
  continue;
225
225
  switch (op) {
226
226
  case "eq":
227
- result[targetKey] = { [Op.eq]: val };
227
+ result[targetKey] = { [_Op.eq]: val };
228
228
  break;
229
229
  case "ne":
230
- result[targetKey] = { [Op.ne]: val };
230
+ result[targetKey] = { [_Op.ne]: val };
231
231
  break;
232
232
  case "gt":
233
- result[targetKey] = { [Op.gt]: val };
233
+ result[targetKey] = { [_Op.gt]: val };
234
234
  break;
235
235
  case "gte":
236
- result[targetKey] = { [Op.gte]: val };
236
+ result[targetKey] = { [_Op.gte]: val };
237
237
  break;
238
238
  case "lt":
239
- result[targetKey] = { [Op.lt]: val };
239
+ result[targetKey] = { [_Op.lt]: val };
240
240
  break;
241
241
  case "lte":
242
- result[targetKey] = { [Op.lte]: val };
242
+ result[targetKey] = { [_Op.lte]: val };
243
243
  break;
244
244
  case "contains":
245
245
  // Check if field is a date/datetime type
@@ -262,83 +262,83 @@ export class SequelizeModel extends AbstractModel {
262
262
  }
263
263
  else {
264
264
  // Valid date - use exact match
265
- result[targetKey] = { [Op.eq]: dateValue };
265
+ result[targetKey] = { [_Op.eq]: dateValue };
266
266
  }
267
267
  }
268
268
  else {
269
269
  // For time fields and non-date fields - use Op.like with wildcards
270
- result[targetKey] = { [Op.like]: `%${val}%` };
270
+ result[targetKey] = { [_Op.like]: `%${val}%` };
271
271
  }
272
272
  break;
273
273
  case "startsWith":
274
- result[targetKey] = { [Op.startsWith]: val };
274
+ result[targetKey] = { [_Op.startsWith]: val };
275
275
  break;
276
276
  case "endsWith":
277
- result[targetKey] = { [Op.endsWith]: val };
277
+ result[targetKey] = { [_Op.endsWith]: val };
278
278
  break;
279
279
  case ">":
280
- result[targetKey] = { [Op.gt]: val };
280
+ result[targetKey] = { [_Op.gt]: val };
281
281
  break;
282
282
  case ">=":
283
- result[targetKey] = { [Op.gte]: val };
283
+ result[targetKey] = { [_Op.gte]: val };
284
284
  break;
285
285
  case "<":
286
- result[targetKey] = { [Op.lt]: val };
286
+ result[targetKey] = { [_Op.lt]: val };
287
287
  break;
288
288
  case "<=":
289
- result[targetKey] = { [Op.lte]: val };
289
+ result[targetKey] = { [_Op.lte]: val };
290
290
  break;
291
291
  case "!=":
292
- result[targetKey] = { [Op.ne]: val };
292
+ result[targetKey] = { [_Op.ne]: val };
293
293
  break;
294
294
  case "in":
295
- result[targetKey] = { [Op.in]: val };
295
+ result[targetKey] = { [_Op.in]: val };
296
296
  break;
297
297
  case "notIn":
298
- result[targetKey] = { [Op.notIn]: val };
298
+ result[targetKey] = { [_Op.notIn]: val };
299
299
  break;
300
300
  case "nin":
301
- result[targetKey] = { [Op.notIn]: val };
301
+ result[targetKey] = { [_Op.notIn]: val };
302
302
  break;
303
303
  case "between":
304
- result[targetKey] = { [Op.between]: val };
304
+ result[targetKey] = { [_Op.between]: val };
305
305
  break;
306
306
  case "isNull":
307
307
  if (val) {
308
- result[targetKey] = { [Op.is]: null };
308
+ result[targetKey] = { [_Op.is]: null };
309
309
  }
310
310
  break;
311
311
  case "isNotNull":
312
312
  if (val) {
313
- result[targetKey] = { [Op.not]: null };
313
+ result[targetKey] = { [_Op.not]: null };
314
314
  }
315
315
  break;
316
316
  case "regex":
317
- result[targetKey] = { [Op.regexp]: val };
317
+ result[targetKey] = { [_Op.regexp]: val };
318
318
  break;
319
319
  case "jsonContains": {
320
320
  const values = Array.isArray(val) ? val : [val];
321
321
  const clauses = values.map((item) => this._buildJsonContainsCondition(targetKey, item));
322
- result[Op.and] = [
323
- ...(result[Op.and] ?? []),
322
+ result[_Op.and] = [
323
+ ...(result[_Op.and] ?? []),
324
324
  ...clauses
325
325
  ];
326
326
  break;
327
327
  }
328
328
  case "$is":
329
- result[targetKey] = { [Op.is]: val };
329
+ result[targetKey] = { [_Op.is]: val };
330
330
  break;
331
331
  case "$not":
332
- result[targetKey] = { [Op.not]: val };
332
+ result[targetKey] = { [_Op.not]: val };
333
333
  break;
334
334
  case "$ne":
335
- result[targetKey] = { [Op.ne]: val };
335
+ result[targetKey] = { [_Op.ne]: val };
336
336
  break;
337
337
  case "not":
338
- result[targetKey] = { [Op.not]: val };
338
+ result[targetKey] = { [_Op.not]: val };
339
339
  break;
340
340
  default:
341
- result[targetKey] = { [Op.eq]: val };
341
+ result[targetKey] = { [_Op.eq]: val };
342
342
  }
343
343
  }
344
344
  }
@@ -421,6 +421,7 @@ export class SequelizeModel extends AbstractModel {
421
421
  }
422
422
  // --- CREATE ---
423
423
  async _create(data) {
424
+ await this._ensure();
424
425
  // console.clear()
425
426
  const assocNames = Object.keys(this.model.associations);
426
427
  const plainData = {};
@@ -492,6 +493,7 @@ export class SequelizeModel extends AbstractModel {
492
493
  }
493
494
  // --- FIND ONE ---
494
495
  async _findOne(criteria) {
496
+ await this._ensure();
495
497
  // console.debug(">> _findOne: input criteria:", criteria);
496
498
  const { where, attributes } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
497
499
  const includes = criteria.populate
@@ -518,6 +520,7 @@ export class SequelizeModel extends AbstractModel {
518
520
  }
519
521
  // --- FIND MANY ---
520
522
  async _find(criteria = {}) {
523
+ await this._ensure();
521
524
  const assocNames = Object.keys(this.model.associations);
522
525
  // console.debug(">> _find: input criteria:", criteria, "options:", options);
523
526
  const { where, limit, offset, order, attributes } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
@@ -573,6 +576,7 @@ export class SequelizeModel extends AbstractModel {
573
576
  * Bypasses Adminizer criteria conversion for direct Sequelize usage
574
577
  */
575
578
  async findWithRawWhere(where, options = {}) {
579
+ await this._ensure();
576
580
  const assocNames = Object.keys(this.model.associations);
577
581
  const includes = options.populate !== false
578
582
  ? assocNames.map(a => ({ association: a }))
@@ -590,6 +594,7 @@ export class SequelizeModel extends AbstractModel {
590
594
  * Public method for count with raw SQL where clauses
591
595
  */
592
596
  async countWithRawWhere(where) {
597
+ await this._ensure();
593
598
  const assocNames = Object.keys(this.model.associations);
594
599
  const include = assocNames.map((association) => ({ association }));
595
600
  return await this.model.count({
@@ -601,6 +606,7 @@ export class SequelizeModel extends AbstractModel {
601
606
  }
602
607
  // --- UPDATE ONE ---
603
608
  async _updateOne(criteria, data) {
609
+ await this._ensure();
604
610
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
605
611
  const record = await this.model.findOne({ where });
606
612
  if (!record)
@@ -632,6 +638,7 @@ export class SequelizeModel extends AbstractModel {
632
638
  }
633
639
  // --- UPDATE MANY ---
634
640
  async _update(criteria, data) {
641
+ await this._ensure();
635
642
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
636
643
  const assocNames = Object.keys(this.model.associations);
637
644
  const plainData = {};
@@ -666,6 +673,7 @@ export class SequelizeModel extends AbstractModel {
666
673
  }
667
674
  // --- DESTROY ONE ---
668
675
  async _destroyOne(criteria) {
676
+ await this._ensure();
669
677
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
670
678
  const record = await this.model.findOne({ where });
671
679
  if (!record)
@@ -701,6 +709,7 @@ export class SequelizeModel extends AbstractModel {
701
709
  }
702
710
  // --- DESTROY MANY ---
703
711
  async _destroy(criteria) {
712
+ await this._ensure();
704
713
  const { where } = this._convertAdminizerCriteriaToSequelizeOptions(criteria);
705
714
  const records = await this.model.findAll({ where });
706
715
  const assocNames = Object.keys(this.model.associations);
@@ -805,7 +814,7 @@ export class SequelizeModel extends AbstractModel {
805
814
  }
806
815
  const association = this.model.associations[alias];
807
816
  const isUsedInWhere = usedRelationAliases.has(alias);
808
- const isHasMany = association instanceof HasMany || association?.associationType === 'HasMany';
817
+ const isHasMany = association instanceof _HasMany || association?.associationType === 'HasMany';
809
818
  // When filtering by relation path ($alias.field$), extra joined hasMany associations
810
819
  // can duplicate base rows and break page size. Load such associations separately.
811
820
  if (isHasMany && !isUsedInWhere) {
@@ -861,10 +870,14 @@ export class SequelizeAdapter extends AbstractAdapter {
861
870
  sequelize;
862
871
  Model = SequelizeModel;
863
872
  constructor(sequelize, options = {}) {
873
+ ensureSequelize();
864
874
  super("sequelize", sequelize, options);
875
+ // Note: ensureSequelize is fire-and-forget during construction
876
+ // Methods also call this._ensure() to guarantee initialization
865
877
  this.sequelize = sequelize;
866
878
  }
867
879
  get models() {
880
+ ``;
868
881
  return this.sequelize.models;
869
882
  }
870
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.8",
4
+ "version": "5.0.0-build.9",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": "./index.js",