drapcode-utility 1.9.4 → 1.9.6

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,4 +1,4 @@
1
- export declare const queryParser: (collectionName: string, query: any, constants: any[], externalParams: any, currentUser: any, timezone: string, searchObj: any, refCollectionFieldsInItems: any, searchQueryTypeObj: any, currentTenant: any, currentUserSetting: any, lookupConfig: any, rowLevelSecurityFilter: any) => Promise<any>;
1
+ export declare const queryParser: (collectionName: string, query: any, constants: any[], externalParams: any, currentUser: any, timezone: string, searchObj: any, refCollectionFieldsInItems: any, searchQueryTypeObj: any, currentTenant: any, currentUserSetting: any, currentSubTenant: any, lookupConfig: any, rowLevelSecurityFilter: any) => Promise<any>;
2
2
  export declare const isEntityInCondition: (value: any) => boolean;
3
3
  export declare const isFixedValueQuery: (value: any) => boolean;
4
4
  export declare const getMinMaxValue: (value: any) => {
@@ -54,19 +54,20 @@ var drapcode_constant_1 = require("drapcode-constant");
54
54
  var date_util_1 = require("./date-util");
55
55
  var util_1 = require("./util");
56
56
  var prepare_query_1 = require("./prepare-query");
57
- var queryParser = function (collectionName, query, constants, externalParams, currentUser, timezone, searchObj, refCollectionFieldsInItems, searchQueryTypeObj, currentTenant, currentUserSetting, lookupConfig, rowLevelSecurityFilter) {
57
+ var queryParser = function (collectionName, query, constants, externalParams, currentUser, timezone, searchObj, refCollectionFieldsInItems, searchQueryTypeObj, currentTenant, currentUserSetting, currentSubTenant, lookupConfig, rowLevelSecurityFilter) {
58
58
  if (currentUser === void 0) { currentUser = {}; }
59
59
  if (searchObj === void 0) { searchObj = null; }
60
60
  if (refCollectionFieldsInItems === void 0) { refCollectionFieldsInItems = null; }
61
61
  if (searchQueryTypeObj === void 0) { searchQueryTypeObj = {}; }
62
62
  if (currentTenant === void 0) { currentTenant = {}; }
63
63
  if (currentUserSetting === void 0) { currentUserSetting = {}; }
64
+ if (currentSubTenant === void 0) { currentSubTenant = {}; }
64
65
  if (lookupConfig === void 0) { lookupConfig = []; }
65
66
  return __awaiter(void 0, void 0, void 0, function () {
66
- var filterQuery, rlsFilterQuery, aggregateQuery, queryStr, searchQueryObj, _a, searchAggregateQuery, likeQuery, finalQuery, _b, searchAggregateQuery, likeQuery, projection, finder, sortBy, orderBy, offset, limit, resetLimit, direction;
67
- var _c, _d, _e;
68
- return __generator(this, function (_f) {
69
- switch (_f.label) {
67
+ var filterQuery, rlsFilterQuery, aggregateQuery, queryStr, searchQueryObj, _a, searchAggregateQuery, likeQuery, finalQuery, addTenantQuery, addSubTenantQuery, additionalFilters, subTenantUuids, _b, searchAggregateQuery, likeQuery, projection, finder, sortBy, orderBy, offset, limit, resetLimit, direction;
68
+ var _c, _d, _e, _f;
69
+ return __generator(this, function (_g) {
70
+ switch (_g.label) {
70
71
  case 0:
71
72
  if (!collectionName)
72
73
  return [2 /*return*/, ""];
@@ -82,11 +83,11 @@ var queryParser = function (collectionName, query, constants, externalParams, cu
82
83
  console.log("currentUser", JSON.stringify(currentUser));
83
84
  console.log("searchObj", JSON.stringify(searchObj));
84
85
  console.log("searchQueryTypeObj", JSON.stringify(searchQueryTypeObj));
85
- filterQuery = mongoFilterQuery(query, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone);
86
+ filterQuery = mongoFilterQuery(query, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone);
86
87
  console.log("filterQuery", JSON.stringify(filterQuery));
87
88
  rlsFilterQuery = [];
88
89
  if (rowLevelSecurityFilter && rowLevelSecurityFilter.conditions.length) {
89
- rlsFilterQuery = mongoFilterQuery(rowLevelSecurityFilter, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone);
90
+ rlsFilterQuery = mongoFilterQuery(rowLevelSecurityFilter, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone);
90
91
  console.log("**************************");
91
92
  console.log("rlsFilterQuery", JSON.stringify(rlsFilterQuery));
92
93
  }
@@ -116,7 +117,24 @@ var queryParser = function (collectionName, query, constants, externalParams, cu
116
117
  if (searchQueryObj) {
117
118
  (_d = finalQuery.$and).push.apply(_d, searchQueryObj);
118
119
  }
119
- console.log("finalQuery after", JSON.stringify(finalQuery));
120
+ addTenantQuery = query.addTenantQuery, addSubTenantQuery = query.addSubTenantQuery;
121
+ additionalFilters = [];
122
+ if (addTenantQuery && (currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.uuid)) {
123
+ additionalFilters.push({ tenantId: currentTenant.uuid });
124
+ }
125
+ if (addSubTenantQuery &&
126
+ Array.isArray(currentUser.subTenantId) &&
127
+ currentUser.subTenantId.length > 0) {
128
+ subTenantUuids = currentUser.subTenantId
129
+ .map(function (item) { return item === null || item === void 0 ? void 0 : item.uuid; })
130
+ .filter(function (uuid) { return uuid; });
131
+ if (subTenantUuids.length > 0) {
132
+ additionalFilters.push({ subTenantId: { $in: subTenantUuids } });
133
+ }
134
+ }
135
+ if (additionalFilters.length > 0) {
136
+ (_e = finalQuery.$and).push.apply(_e, additionalFilters);
137
+ }
120
138
  aggregateQuery.push({ $match: finalQuery });
121
139
  }
122
140
  else if (searchObj) {
@@ -137,8 +155,8 @@ var queryParser = function (collectionName, query, constants, externalParams, cu
137
155
  return (0, prepare_query_1.commonLookupSetting)(field, lookupConfig, aggregateQuery);
138
156
  }))];
139
157
  case 1:
140
- _f.sent();
141
- _f.label = 2;
158
+ _g.sent();
159
+ _g.label = 2;
142
160
  case 2:
143
161
  if (finder != "COUNT" && !(0, util_1.isEmpty)(projection)) {
144
162
  aggregateQuery.push({ $project: projection });
@@ -160,7 +178,7 @@ var queryParser = function (collectionName, query, constants, externalParams, cu
160
178
  }
161
179
  if (finder != "COUNT" && sortBy) {
162
180
  direction = orderBy && orderBy == "asc" ? 1 : -1;
163
- aggregateQuery.push({ $sort: (_e = {}, _e[sortBy] = direction, _e._id = 1, _e) });
181
+ aggregateQuery.push({ $sort: (_f = {}, _f[sortBy] = direction, _f._id = 1, _f) });
164
182
  }
165
183
  if (finder != "COUNT" && limit) {
166
184
  aggregateQuery.push({ $skip: +offset }, { $limit: +limit });
@@ -186,17 +204,18 @@ var toBoolean = function (value) {
186
204
  return false;
187
205
  }
188
206
  };
189
- var mongoFilterQuery = function (query, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone) {
207
+ var mongoFilterQuery = function (query, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone) {
190
208
  if (currentUser === void 0) { currentUser = {}; }
191
209
  if (currentTenant === void 0) { currentTenant = {}; }
192
210
  if (currentUserSetting === void 0) { currentUserSetting = {}; }
211
+ if (currentSubTenant === void 0) { currentSubTenant = {}; }
193
212
  var filterQuery = { $or0: [] };
194
213
  var conditions = query.conditions;
195
214
  var queryConjunctions = [];
196
215
  conditions.forEach(function (queryObj, index) {
197
216
  var query = queryObj.query, requiredExternal = queryObj.requiredExternal;
198
217
  console.log("query :>> ", JSON.stringify(query));
199
- var mongoQuery = queryToMongo(query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone);
218
+ var mongoQuery = queryToMongo(query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone);
200
219
  console.log(":::::::::mongoQuery::::", JSON.stringify(mongoQuery));
201
220
  queryConjunctions.push(queryObj.conjunctionType);
202
221
  if (queryObj.conjunctionType == "OR") {
@@ -267,11 +286,12 @@ var modifyQuery = function (obj, hasAndQueryConjunction) {
267
286
  console.log("🚀 ~ file: query-parser.ts:303 ~ Object.entries ~ arr:", arr);
268
287
  return arr;
269
288
  };
270
- var queryToMongo = function (query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone) {
289
+ var queryToMongo = function (query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone) {
271
290
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13;
272
291
  if (currentUser === void 0) { currentUser = {}; }
273
292
  if (currentTenant === void 0) { currentTenant = {}; }
274
293
  if (currentUserSetting === void 0) { currentUserSetting = {}; }
294
+ if (currentSubTenant === void 0) { currentSubTenant = {}; }
275
295
  var key = query.key, field = query.field, value = query.value, fieldType = query.fieldType;
276
296
  if ([drapcode_constant_1.EQUALS, drapcode_constant_1.NOT_EQUAL, drapcode_constant_1.EQUALS_OR_IGNORE].includes(key) &&
277
297
  typeof value === "string" &&
@@ -280,7 +300,7 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
280
300
  console.log("\n ===final getDateRangeValue", final);
281
301
  return final;
282
302
  }
283
- var fieldValue = replaceExternalParams(query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone);
303
+ var fieldValue = replaceExternalParams(query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone);
284
304
  if (["undefined", "null", null, undefined, ""].includes(fieldValue)) {
285
305
  //TODO: In case field is not present in old record then fieldValue is undefined
286
306
  //TODO: In case of this, we assign some random string to avoid resulting all values
@@ -295,10 +315,10 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
295
315
  // if (isDate) fieldValue = new Date(fieldValue);
296
316
  console.log("fieldValue queryToMongo 1", fieldValue);
297
317
  var condition = [
298
- "dynamic_option",
299
- "reference",
300
- "static_option",
301
- "belongsTo",
318
+ drapcode_constant_1.FieldTypes.dynamic_option.id,
319
+ drapcode_constant_1.FieldTypes.reference.id,
320
+ drapcode_constant_1.FieldTypes.static_option.id,
321
+ drapcode_constant_1.FieldTypes.belongsTo.id,
302
322
  ].includes(fieldType);
303
323
  if (condition && key === drapcode_constant_1.EQUALS && !isDate) {
304
324
  if (!Array.isArray(fieldValue)) {
@@ -411,7 +431,7 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
411
431
  }
412
432
  : {};
413
433
  }
414
- if (key == drapcode_constant_1.EQUALS && fieldType === "boolean" && fieldValue)
434
+ if (key == drapcode_constant_1.EQUALS && fieldType === drapcode_constant_1.FieldTypes.boolean.id && fieldValue)
415
435
  return JSON.parse(fieldValue)
416
436
  ? (_z = {},
417
437
  _z[field] = JSON.parse(fieldValue),
@@ -421,7 +441,7 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
421
441
  (_1 = {}, _1[field] = { $exists: false }, _1),
422
442
  ],
423
443
  };
424
- if (key === drapcode_constant_1.EQUALS_OR_IGNORE && fieldType === "boolean") {
444
+ if (key === drapcode_constant_1.EQUALS_OR_IGNORE && fieldType === drapcode_constant_1.FieldTypes.boolean.id) {
425
445
  return !fieldValue.includes("random_string_since_field_not_present_1318874398806")
426
446
  ? JSON.parse(fieldValue)
427
447
  ? (_2 = {},
@@ -437,21 +457,21 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
437
457
  if (key == drapcode_constant_1.EQUALS) {
438
458
  console.log("I am query parser");
439
459
  console.log("fieldType", fieldType);
440
- if (fieldType === "boolean") {
460
+ if (fieldType === drapcode_constant_1.FieldTypes.boolean.id) {
441
461
  return _5 = {}, _5[field] = !!fieldValue, _5;
442
462
  }
443
- if (fieldType === "number") {
463
+ if (fieldType === drapcode_constant_1.FieldTypes.number.id) {
444
464
  return _6 = {}, _6[field] = +fieldValue, _6;
445
465
  }
446
466
  console.log("Nothing match");
447
467
  return _7 = {}, _7[field] = "".concat(fieldValue), _7;
448
468
  }
449
469
  if (key === drapcode_constant_1.EQUALS_OR_IGNORE) {
450
- if (fieldType === "boolean") {
470
+ if (fieldType === drapcode_constant_1.FieldTypes.boolean.id) {
451
471
  return !fieldValue.includes("random_string_since_field_not_present_1318874398806")
452
472
  ? (_8 = {}, _8[field] = !!fieldValue, _8) : {};
453
473
  }
454
- if (fieldType === "number") {
474
+ if (fieldType === drapcode_constant_1.FieldTypes.number.id) {
455
475
  return !fieldValue.includes("random_string_since_field_not_present_1318874398806")
456
476
  ? (_9 = {}, _9[field] = +fieldValue, _9) : {};
457
477
  }
@@ -461,13 +481,13 @@ var queryToMongo = function (query, requiredExternal, externalParams, constants,
461
481
  }
462
482
  }
463
483
  if (key == drapcode_constant_1.NOT_EQUAL)
464
- if (fieldType === "number") {
484
+ if (fieldType === drapcode_constant_1.FieldTypes.number.id) {
465
485
  return _11 = {}, _11[field] = { $ne: +fieldValue }, _11;
466
486
  }
467
487
  else
468
488
  return _12 = {},
469
489
  _12[field] = {
470
- $ne: fieldType === "boolean" && fieldValue
490
+ $ne: fieldType === drapcode_constant_1.FieldTypes.boolean.id && fieldValue
471
491
  ? JSON.parse(fieldValue)
472
492
  : fieldValue,
473
493
  },
@@ -513,16 +533,16 @@ var isFixedValueQuery = function (value) {
513
533
  return isFixeValue;
514
534
  };
515
535
  exports.isFixedValueQuery = isFixedValueQuery;
516
- var replaceExternalParams = function (query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, timezone) {
536
+ var replaceExternalParams = function (query, requiredExternal, externalParams, constants, currentUser, currentTenant, currentUserSetting, currentSubTenant, timezone) {
517
537
  var key = query.key, value = query.value, fieldType = query.fieldType;
518
538
  if (fieldType &&
519
539
  !requiredExternal &&
520
- ["dynamic_option", "reference", "static_option", "belongsTo"].includes(fieldType)) {
540
+ [drapcode_constant_1.FieldTypes.dynamic_option.id, drapcode_constant_1.FieldTypes.reference.id, drapcode_constant_1.FieldTypes.static_option.id, drapcode_constant_1.FieldTypes.belongsTo.id].includes(fieldType)) {
521
541
  //Todo: Remove if filter is working properly
522
542
  // return value === CURRENT_USER ? currentUser["uuid"] : value;
523
543
  var refCollection = query.refCollection, refField = query.refField;
524
544
  var finalValue = "";
525
- if (["dynamic_option", "static_option"].includes(fieldType)) {
545
+ if ([drapcode_constant_1.FieldTypes.dynamic_option.id, drapcode_constant_1.FieldTypes.static_option.id].includes(fieldType)) {
526
546
  if (!value) {
527
547
  if (refCollection === drapcode_constant_1.CURRENT_USER) {
528
548
  finalValue = currentUser[refField];
@@ -533,6 +553,9 @@ var replaceExternalParams = function (query, requiredExternal, externalParams, c
533
553
  if (refCollection === drapcode_constant_1.CURRENT_SETTINGS) {
534
554
  finalValue = currentUserSetting[refField];
535
555
  }
556
+ if (refCollection === drapcode_constant_1.CURRENT_SUB_TENANT) {
557
+ finalValue = currentSubTenant[refField];
558
+ }
536
559
  }
537
560
  }
538
561
  if (value === drapcode_constant_1.CURRENT_USER) {
@@ -543,7 +566,7 @@ var replaceExternalParams = function (query, requiredExternal, externalParams, c
543
566
  return finalValue;
544
567
  }
545
568
  if (fieldType &&
546
- fieldType === "boolean" &&
569
+ fieldType === drapcode_constant_1.FieldTypes.boolean.id &&
547
570
  ["true", "false", "", true, false].includes(value)) {
548
571
  return "" + value;
549
572
  }
@@ -563,6 +586,7 @@ var replaceExternalParams = function (query, requiredExternal, externalParams, c
563
586
  { constantName: drapcode_constant_1.CURRENT_USER, data: currentUser },
564
587
  { constantName: drapcode_constant_1.CURRENT_TENANT, data: currentTenant },
565
588
  { constantName: drapcode_constant_1.CURRENT_SETTINGS, data: currentUserSetting },
589
+ { constantName: drapcode_constant_1.CURRENT_SUB_TENANT, data: currentSubTenant },
566
590
  ];
567
591
  constantDataObj.forEach(function (cnst) {
568
592
  if (value.includes(cnst.constantName)) {
@@ -624,6 +648,9 @@ var replaceExternalParams = function (query, requiredExternal, externalParams, c
624
648
  console.log("\n currentUserSettingsValue :>> ", currentUserSettingsValue);
625
649
  if (currentUserSettingsValue)
626
650
  return currentUserSettingsValue;
651
+ var currentSubTenantValue = getCurrentDataValueFrom(value, drapcode_constant_1.CURRENT_SUB_TENANT, currentSubTenant);
652
+ if (currentSubTenantValue)
653
+ return currentSubTenantValue;
627
654
  if (drapcode_constant_1.DateConstant.some(function (cnst) { return value.includes(cnst); })) {
628
655
  return getValueOfProjectConstant(value, {}, timezone);
629
656
  }
@@ -702,10 +729,10 @@ var getSearchObjQuery = function (searchObj, searchQueryTypeObj, timezone) {
702
729
  _c));
703
730
  }
704
731
  else if ([
705
- "static_option",
706
- "dynamic_option",
707
- "reference",
708
- "belongsTo",
732
+ drapcode_constant_1.FieldTypes.static_option.id,
733
+ drapcode_constant_1.FieldTypes.dynamic_option.id,
734
+ drapcode_constant_1.FieldTypes.reference.id,
735
+ drapcode_constant_1.FieldTypes.belongsTo.id,
709
736
  ].includes(searchQueryTypeKey)) {
710
737
  // if input is string and db field type is array
711
738
  searchAggregateQuery.push({
@@ -758,7 +785,7 @@ var getSearchObjQuery = function (searchObj, searchQueryTypeObj, timezone) {
758
785
  _l));
759
786
  }
760
787
  }
761
- else if (["date", "createdAt", "time_slot"].includes(searchQueryTypeKey)) {
788
+ else if (__spreadArray(__spreadArray([drapcode_constant_1.FieldTypes.date.id], drapcode_constant_1.systemDateField, true), [drapcode_constant_1.FieldTypes.time_slot.id], false).includes(searchQueryTypeKey)) {
762
789
  if (key.startsWith("start_") || key.startsWith("end_")) {
763
790
  var fieldKey = key.replace("start_", "");
764
791
  fieldKey = fieldKey.replace("end_", "");
@@ -770,7 +797,7 @@ var getSearchObjQuery = function (searchObj, searchQueryTypeObj, timezone) {
770
797
  if (key.startsWith("start_")) {
771
798
  likeQuery.push((_o = {},
772
799
  _o["_".concat(key)] = {
773
- $gte: (0, date_util_1.timezoneDateParse)(value, false, true),
800
+ $gte: (0, date_util_1.timezoneDateParse)(value, true, true),
774
801
  },
775
802
  _o));
776
803
  }
@@ -20,7 +20,7 @@ export declare const arraysEqual: (array1: any, array2: any) => boolean;
20
20
  export declare const convertItemToArray: (itemValue: any) => any[];
21
21
  export declare const validateAlphanumericString: (str: string) => false | "String should contain only numbers and letters";
22
22
  export declare const formatCustomCSSClasses: (customClasses: any) => string;
23
- export declare const replaceDataValueIntoExpression: (expression: string, data: any, user: any, tenant: any, userSetting: any, sessionValue: any, envConstants: any, sessionFormValue: any, localStorageValue: any, cookiesValue: any) => string;
23
+ export declare const replaceDataValueIntoExpression: (expression: string, data: any, user: any, tenant: any, userSetting: any, subTenant: any, sessionValue: any, envConstants: any, sessionFormValue: any, localStorageValue: any, cookiesValue: any) => string;
24
24
  export declare const parseJsonString: (str: string) => any;
25
25
  export declare const parseValueFromData: (data: any, fieldName: string) => any;
26
26
  export declare const unflattenObject: (obj: any) => {};
@@ -294,7 +294,7 @@ var formatCustomCSSClasses = function (customClasses) {
294
294
  return projectCustomCSSClassesData;
295
295
  };
296
296
  exports.formatCustomCSSClasses = formatCustomCSSClasses;
297
- var replaceDataValueIntoExpression = function (expression, data, user, tenant, userSetting, sessionValue, envConstants, sessionFormValue, localStorageValue, cookiesValue) {
297
+ var replaceDataValueIntoExpression = function (expression, data, user, tenant, userSetting, subTenant, sessionValue, envConstants, sessionFormValue, localStorageValue, cookiesValue) {
298
298
  var _a;
299
299
  var contentList = (_a = expression
300
300
  .match(/{{(.*?)}}/g)) === null || _a === void 0 ? void 0 : _a.map(function (b) { return b.replace(/{{(.*?)}}/g, "$1"); });
@@ -324,6 +324,12 @@ var replaceDataValueIntoExpression = function (expression, data, user, tenant, u
324
324
  dataOfItem = (0, exports.parseValueFromData)(userSetting, prop);
325
325
  }
326
326
  }
327
+ else if (prop.includes("current_sub_tenant")) {
328
+ prop = prop.replace("current_sub_tenant.", "");
329
+ if (Object.keys(userSetting).length > 0) {
330
+ dataOfItem = (0, exports.parseValueFromData)(subTenant, prop);
331
+ }
332
+ }
327
333
  else if (prop.includes("current_session")) {
328
334
  console.log("Current Session");
329
335
  prop = prop.replace("current_session.", "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drapcode-utility",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -37,9 +37,9 @@
37
37
  "@types/voca": "^1.4.2",
38
38
  "axios": "^1.1.2",
39
39
  "dompurify": "^3.1.7",
40
- "drapcode-constant": "^1.6.7",
41
- "drapcode-logger": "^1.3.3",
42
- "drapcode-redis": "^1.2.3",
40
+ "drapcode-constant": "^1.6.8",
41
+ "drapcode-logger": "^1.3.4",
42
+ "drapcode-redis": "^1.2.4",
43
43
  "exiftool-vendored": "^28.2.1",
44
44
  "express": "^4.17.1",
45
45
  "gm": "^1.25.0",