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.
- package/assets/{add-DOjh2NLm.js → add-CBlLpLr0.js} +1 -1
- package/assets/{add-group-B2FdiaVg.js → add-group-H0IKIsNm.js} +1 -1
- package/assets/{add-user-C07In-rU.js → add-user-CoTLmxtF.js} +1 -1
- package/assets/{app-layout-CvW8DKyk.js → app-layout-xxJpLAvb.js} +1 -1
- package/assets/app.js +2 -2
- package/assets/catalog-dk6a0I1u.js +6 -0
- package/assets/{dashboard-DRxpsbtY.js → dashboard-DVJS5EPP.js} +1 -1
- package/assets/{history-srOjJz94.js → history-bWRuzbye.js} +1 -1
- package/assets/{list-BObKdaDd.js → list-CcMtS4hD.js} +1 -1
- package/assets/manifest.json +24 -24
- package/assets/{module-CdaN8Pkb.js → module-CqO_BKsH.js} +1 -1
- package/assets/{notification-DMwIGHiv.js → notification-B5l2wwmi.js} +1 -1
- package/assets/{user-filters-list-CnRaXLuv.js → user-filters-list-C7kMKN5f.js} +1 -1
- package/assets/{welcome-DrLD1BEN.js → welcome-6z-7sXnQ.js} +1 -1
- package/lib/model/adapter/sequelize.d.ts +5 -3
- package/lib/model/adapter/sequelize.js +75 -44
- package/package.json +1 -1
- package/assets/catalog-CObbOCOb.js +0 -6
|
@@ -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
|
|
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] = { [
|
|
158
|
+
result[targetKey] = { [_Op.is]: null };
|
|
141
159
|
continue;
|
|
142
160
|
}
|
|
143
161
|
// 🔹 Handle Sequelize Op operators (already in Sequelize format)
|
|
144
|
-
if (key ===
|
|
145
|
-
result[
|
|
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 ===
|
|
151
|
-
result[
|
|
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 ===
|
|
157
|
-
result[
|
|
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] = { [
|
|
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 ===
|
|
177
|
-
op ===
|
|
178
|
-
op ===
|
|
179
|
-
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) {
|
|
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] = { [
|
|
227
|
+
result[targetKey] = { [_Op.eq]: val };
|
|
210
228
|
break;
|
|
211
229
|
case "ne":
|
|
212
|
-
result[targetKey] = { [
|
|
230
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
213
231
|
break;
|
|
214
232
|
case "gt":
|
|
215
|
-
result[targetKey] = { [
|
|
233
|
+
result[targetKey] = { [_Op.gt]: val };
|
|
216
234
|
break;
|
|
217
235
|
case "gte":
|
|
218
|
-
result[targetKey] = { [
|
|
236
|
+
result[targetKey] = { [_Op.gte]: val };
|
|
219
237
|
break;
|
|
220
238
|
case "lt":
|
|
221
|
-
result[targetKey] = { [
|
|
239
|
+
result[targetKey] = { [_Op.lt]: val };
|
|
222
240
|
break;
|
|
223
241
|
case "lte":
|
|
224
|
-
result[targetKey] = { [
|
|
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] = { [
|
|
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] = { [
|
|
270
|
+
result[targetKey] = { [_Op.like]: `%${val}%` };
|
|
253
271
|
}
|
|
254
272
|
break;
|
|
255
273
|
case "startsWith":
|
|
256
|
-
result[targetKey] = { [
|
|
274
|
+
result[targetKey] = { [_Op.startsWith]: val };
|
|
257
275
|
break;
|
|
258
276
|
case "endsWith":
|
|
259
|
-
result[targetKey] = { [
|
|
277
|
+
result[targetKey] = { [_Op.endsWith]: val };
|
|
260
278
|
break;
|
|
261
279
|
case ">":
|
|
262
|
-
result[targetKey] = { [
|
|
280
|
+
result[targetKey] = { [_Op.gt]: val };
|
|
263
281
|
break;
|
|
264
282
|
case ">=":
|
|
265
|
-
result[targetKey] = { [
|
|
283
|
+
result[targetKey] = { [_Op.gte]: val };
|
|
266
284
|
break;
|
|
267
285
|
case "<":
|
|
268
|
-
result[targetKey] = { [
|
|
286
|
+
result[targetKey] = { [_Op.lt]: val };
|
|
269
287
|
break;
|
|
270
288
|
case "<=":
|
|
271
|
-
result[targetKey] = { [
|
|
289
|
+
result[targetKey] = { [_Op.lte]: val };
|
|
272
290
|
break;
|
|
273
291
|
case "!=":
|
|
274
|
-
result[targetKey] = { [
|
|
292
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
275
293
|
break;
|
|
276
294
|
case "in":
|
|
277
|
-
result[targetKey] = { [
|
|
295
|
+
result[targetKey] = { [_Op.in]: val };
|
|
278
296
|
break;
|
|
279
297
|
case "notIn":
|
|
280
|
-
result[targetKey] = { [
|
|
298
|
+
result[targetKey] = { [_Op.notIn]: val };
|
|
281
299
|
break;
|
|
282
300
|
case "nin":
|
|
283
|
-
result[targetKey] = { [
|
|
301
|
+
result[targetKey] = { [_Op.notIn]: val };
|
|
284
302
|
break;
|
|
285
303
|
case "between":
|
|
286
|
-
result[targetKey] = { [
|
|
304
|
+
result[targetKey] = { [_Op.between]: val };
|
|
287
305
|
break;
|
|
288
306
|
case "isNull":
|
|
289
307
|
if (val) {
|
|
290
|
-
result[targetKey] = { [
|
|
308
|
+
result[targetKey] = { [_Op.is]: null };
|
|
291
309
|
}
|
|
292
310
|
break;
|
|
293
311
|
case "isNotNull":
|
|
294
312
|
if (val) {
|
|
295
|
-
result[targetKey] = { [
|
|
313
|
+
result[targetKey] = { [_Op.not]: null };
|
|
296
314
|
}
|
|
297
315
|
break;
|
|
298
316
|
case "regex":
|
|
299
|
-
result[targetKey] = { [
|
|
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[
|
|
305
|
-
...(result[
|
|
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] = { [
|
|
329
|
+
result[targetKey] = { [_Op.is]: val };
|
|
312
330
|
break;
|
|
313
331
|
case "$not":
|
|
314
|
-
result[targetKey] = { [
|
|
332
|
+
result[targetKey] = { [_Op.not]: val };
|
|
315
333
|
break;
|
|
316
334
|
case "$ne":
|
|
317
|
-
result[targetKey] = { [
|
|
335
|
+
result[targetKey] = { [_Op.ne]: val };
|
|
318
336
|
break;
|
|
319
337
|
case "not":
|
|
320
|
-
result[targetKey] = { [
|
|
338
|
+
result[targetKey] = { [_Op.not]: val };
|
|
321
339
|
break;
|
|
322
340
|
default:
|
|
323
|
-
result[targetKey] = { [
|
|
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
|
|
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) {
|