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.
- package/assets/{add-DED_wpga.js → add-CBlLpLr0.js} +1 -1
- package/assets/{add-group-CL80RdYb.js → add-group-H0IKIsNm.js} +1 -1
- package/assets/{add-user-BRmwvQB8.js → add-user-CoTLmxtF.js} +1 -1
- package/assets/{app-layout-86yxiZOy.js → app-layout-xxJpLAvb.js} +1 -1
- package/assets/app.js +2 -2
- package/assets/{catalog-BEvJZYJP.js → catalog-dk6a0I1u.js} +1 -1
- package/assets/{dashboard-ZP6K2s_8.js → dashboard-DVJS5EPP.js} +1 -1
- package/assets/{history-M61Bcv02.js → history-bWRuzbye.js} +1 -1
- package/assets/{list-DhK0McVn.js → list-CcMtS4hD.js} +1 -1
- package/assets/manifest.json +24 -24
- package/assets/{module-DfBneaUl.js → module-CqO_BKsH.js} +1 -1
- package/assets/{notification-CVnQf3ef.js → notification-B5l2wwmi.js} +1 -1
- package/assets/{user-filters-list-DXhGLFTn.js → user-filters-list-C7kMKN5f.js} +1 -1
- package/assets/{welcome-CLxvXkcg.js → welcome-6z-7sXnQ.js} +1 -1
- package/lib/model/adapter/sequelize.d.ts +3 -2
- package/lib/model/adapter/sequelize.js +74 -61
- package/package.json +1 -1
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
let Sequelize;
|
|
2
|
-
let Op;
|
|
3
|
-
let HasMany;
|
|
4
1
|
import { AbstractAdapter, AbstractModel } from "../AbstractModel.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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] = { [
|
|
158
|
+
result[targetKey] = { [_Op.is]: null };
|
|
159
159
|
continue;
|
|
160
160
|
}
|
|
161
161
|
// 🔹 Handle Sequelize Op operators (already in Sequelize format)
|
|
162
|
-
if (key ===
|
|
163
|
-
result[
|
|
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 ===
|
|
169
|
-
result[
|
|
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 ===
|
|
175
|
-
result[
|
|
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] = { [
|
|
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 ===
|
|
195
|
-
op ===
|
|
196
|
-
op ===
|
|
197
|
-
op ===
|
|
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] = { [
|
|
227
|
+
result[targetKey] = { [_Op.eq]: val };
|
|
228
228
|
break;
|
|
229
229
|
case "ne":
|
|
230
|
-
result[targetKey] = { [
|
|
230
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
231
231
|
break;
|
|
232
232
|
case "gt":
|
|
233
|
-
result[targetKey] = { [
|
|
233
|
+
result[targetKey] = { [_Op.gt]: val };
|
|
234
234
|
break;
|
|
235
235
|
case "gte":
|
|
236
|
-
result[targetKey] = { [
|
|
236
|
+
result[targetKey] = { [_Op.gte]: val };
|
|
237
237
|
break;
|
|
238
238
|
case "lt":
|
|
239
|
-
result[targetKey] = { [
|
|
239
|
+
result[targetKey] = { [_Op.lt]: val };
|
|
240
240
|
break;
|
|
241
241
|
case "lte":
|
|
242
|
-
result[targetKey] = { [
|
|
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] = { [
|
|
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] = { [
|
|
270
|
+
result[targetKey] = { [_Op.like]: `%${val}%` };
|
|
271
271
|
}
|
|
272
272
|
break;
|
|
273
273
|
case "startsWith":
|
|
274
|
-
result[targetKey] = { [
|
|
274
|
+
result[targetKey] = { [_Op.startsWith]: val };
|
|
275
275
|
break;
|
|
276
276
|
case "endsWith":
|
|
277
|
-
result[targetKey] = { [
|
|
277
|
+
result[targetKey] = { [_Op.endsWith]: val };
|
|
278
278
|
break;
|
|
279
279
|
case ">":
|
|
280
|
-
result[targetKey] = { [
|
|
280
|
+
result[targetKey] = { [_Op.gt]: val };
|
|
281
281
|
break;
|
|
282
282
|
case ">=":
|
|
283
|
-
result[targetKey] = { [
|
|
283
|
+
result[targetKey] = { [_Op.gte]: val };
|
|
284
284
|
break;
|
|
285
285
|
case "<":
|
|
286
|
-
result[targetKey] = { [
|
|
286
|
+
result[targetKey] = { [_Op.lt]: val };
|
|
287
287
|
break;
|
|
288
288
|
case "<=":
|
|
289
|
-
result[targetKey] = { [
|
|
289
|
+
result[targetKey] = { [_Op.lte]: val };
|
|
290
290
|
break;
|
|
291
291
|
case "!=":
|
|
292
|
-
result[targetKey] = { [
|
|
292
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
293
293
|
break;
|
|
294
294
|
case "in":
|
|
295
|
-
result[targetKey] = { [
|
|
295
|
+
result[targetKey] = { [_Op.in]: val };
|
|
296
296
|
break;
|
|
297
297
|
case "notIn":
|
|
298
|
-
result[targetKey] = { [
|
|
298
|
+
result[targetKey] = { [_Op.notIn]: val };
|
|
299
299
|
break;
|
|
300
300
|
case "nin":
|
|
301
|
-
result[targetKey] = { [
|
|
301
|
+
result[targetKey] = { [_Op.notIn]: val };
|
|
302
302
|
break;
|
|
303
303
|
case "between":
|
|
304
|
-
result[targetKey] = { [
|
|
304
|
+
result[targetKey] = { [_Op.between]: val };
|
|
305
305
|
break;
|
|
306
306
|
case "isNull":
|
|
307
307
|
if (val) {
|
|
308
|
-
result[targetKey] = { [
|
|
308
|
+
result[targetKey] = { [_Op.is]: null };
|
|
309
309
|
}
|
|
310
310
|
break;
|
|
311
311
|
case "isNotNull":
|
|
312
312
|
if (val) {
|
|
313
|
-
result[targetKey] = { [
|
|
313
|
+
result[targetKey] = { [_Op.not]: null };
|
|
314
314
|
}
|
|
315
315
|
break;
|
|
316
316
|
case "regex":
|
|
317
|
-
result[targetKey] = { [
|
|
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[
|
|
323
|
-
...(result[
|
|
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] = { [
|
|
329
|
+
result[targetKey] = { [_Op.is]: val };
|
|
330
330
|
break;
|
|
331
331
|
case "$not":
|
|
332
|
-
result[targetKey] = { [
|
|
332
|
+
result[targetKey] = { [_Op.not]: val };
|
|
333
333
|
break;
|
|
334
334
|
case "$ne":
|
|
335
|
-
result[targetKey] = { [
|
|
335
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
336
336
|
break;
|
|
337
337
|
case "not":
|
|
338
|
-
result[targetKey] = { [
|
|
338
|
+
result[targetKey] = { [_Op.not]: val };
|
|
339
339
|
break;
|
|
340
340
|
default:
|
|
341
|
-
result[targetKey] = { [
|
|
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
|
|
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) {
|