nox-validation 1.7.1 → 1.7.3

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/index.js CHANGED
@@ -5,19 +5,34 @@ const {
5
5
  buildNestedStructure,
6
6
  getValue,
7
7
  setValue,
8
- getDefaultValues
8
+ getDefaultValues,
9
9
  } = require("./lib/helpers");
10
10
 
11
+ const optimizedValidate = require("./lib/validate.optimized");
12
+ const helpersOptimized = require("./lib/helpers.optimized");
13
+
11
14
  module.exports = {
12
15
  validate,
13
16
  CONSTANTS: constants,
14
17
  helpers: {
15
18
  validateSingleField: validateField,
16
19
  validateType: typeChecks,
17
- fieldsMapping:getFieldsGroupBySchemaId,
18
- convertTree:buildNestedStructure,
19
- getValueByDynamicKey:getValue,
20
- setValueByDynamicKey:setValue,
21
- getTreeDefaultValues:getDefaultValues
20
+ fieldsMapping: getFieldsGroupBySchemaId,
21
+ convertTree: buildNestedStructure,
22
+ getValueByDynamicKey: getValue,
23
+ setValueByDynamicKey: setValue,
24
+ getTreeDefaultValues: getDefaultValues,
25
+ },
26
+ optimized: {
27
+ validate: optimizedValidate.validate,
28
+ helpers: {
29
+ validateSingleField: helpersOptimized.validateField,
30
+ validateType: helpersOptimized.typeChecks,
31
+ fieldsMapping: helpersOptimized.getFieldsGroupBySchemaId,
32
+ convertTree: helpersOptimized.buildNestedStructure,
33
+ getValueByDynamicKey: helpersOptimized.getValue,
34
+ setValueByDynamicKey: helpersOptimized.setValue,
35
+ getTreeDefaultValues: helpersOptimized.getDefaultValues,
36
+ },
22
37
  },
23
38
  };
@@ -1105,21 +1105,53 @@ const buildNestedStructure = ({
1105
1105
  const root = {};
1106
1106
  const nodeMap = new Map();
1107
1107
 
1108
- // Sort fields by path depth once
1109
1108
  const sortedFields = [...schemaFields].sort(
1110
1109
  (a, b) => a.path.split(".").length - b.path.split(".").length
1111
1110
  );
1112
1111
 
1113
- // Pre-compute interface checks
1114
- const isV2FileInterface = (interface) =>
1112
+ const isV2FileInterface = (interfaceName) =>
1115
1113
  apiVersion === constants.API_VERSION.V2 &&
1116
1114
  [
1117
1115
  constants.interfaces.FILES,
1118
1116
  constants.interfaces.FILE,
1119
1117
  constants.interfaces.FILE_IMAGE,
1120
- ].includes(interface);
1118
+ ].includes(interfaceName);
1119
+
1120
+ // utility to adjust children keys as required
1121
+ function adjustChildKeys(
1122
+ children,
1123
+ parentType,
1124
+ parentKey,
1125
+ parentMetaInterface
1126
+ ) {
1127
+ if (!Array.isArray(children) || children.length === 0) return children;
1128
+ // For array parent type
1129
+ if (parentType === constants.types.ARRAY) {
1130
+ // If translation
1131
+ if (parentMetaInterface === constants.interfaces.TRANSLATIONS) {
1132
+ // e.g., parent.key[lang_code].child.key
1133
+ return children.map((child) => ({
1134
+ ...child,
1135
+ key: `${parentKey}[lang_code].${child.key.split(".").pop()}`,
1136
+ value: `${parentKey}[lang_code].${child.key.split(".").pop()}`,
1137
+ }));
1138
+ } else {
1139
+ // e.g., parent.key[idx].child.key
1140
+ return children.map((child) => ({
1141
+ ...child,
1142
+ key: `${parentKey}[idx].${child.key.split(".").pop()}`,
1143
+ value: `${parentKey}[idx].${child.key.split(".").pop()}`,
1144
+ }));
1145
+ }
1146
+ }
1147
+ // For normal object type, just parent.key.child.key
1148
+ return children.map((child) => ({
1149
+ ...child,
1150
+ key: `${parentKey}.${child.key.split(".").pop()}`,
1151
+ value: `${parentKey}.${child.key.split(".").pop()}`,
1152
+ }));
1153
+ }
1121
1154
 
1122
- // Process each field in a single pass
1123
1155
  for (const item of sortedFields) {
1124
1156
  const pathParts = item.path.split(".");
1125
1157
  const key = pathParts.join(".");
@@ -1215,6 +1247,13 @@ const buildNestedStructure = ({
1215
1247
  } else {
1216
1248
  children = childFields;
1217
1249
  }
1250
+ // Now, adjust children keys as required
1251
+ children = adjustChildKeys(
1252
+ children,
1253
+ definedType.type,
1254
+ key,
1255
+ item.meta?.interface
1256
+ );
1218
1257
  }
1219
1258
 
1220
1259
  // Add _id field for arrays
@@ -1382,7 +1421,7 @@ const RULE_TRANSFORM_MAP = {
1382
1421
  case: constants.rulesTypes.REGEX,
1383
1422
  options: (rule) => ({
1384
1423
  type: constants.regexTypes.CONTAINS,
1385
- case_sensitive: !rule[rule.type].insensitive,
1424
+ case_sensitive: !rule[rule.type]?.insensitive,
1386
1425
  multiline: false,
1387
1426
  global: false,
1388
1427
  }),
@@ -1391,7 +1430,7 @@ const RULE_TRANSFORM_MAP = {
1391
1430
  case: constants.rulesTypes.REGEX,
1392
1431
  options: (rule) => ({
1393
1432
  type: constants.regexTypes.NOT_CONTAINS,
1394
- case_sensitive: !rule[rule.type].insensitive,
1433
+ case_sensitive: !rule[rule.type]?.insensitive,
1395
1434
  multiline: false,
1396
1435
  global: false,
1397
1436
  }),
@@ -1400,28 +1439,28 @@ const RULE_TRANSFORM_MAP = {
1400
1439
  case: constants.rulesTypes.REGEX,
1401
1440
  options: (rule) => ({
1402
1441
  type: constants.regexTypes.START_WITH,
1403
- case_sensitive: !rule[rule.type].insensitive,
1442
+ case_sensitive: !rule[rule.type]?.insensitive,
1404
1443
  }),
1405
1444
  },
1406
1445
  doesNotStartWith: {
1407
1446
  case: constants.rulesTypes.REGEX,
1408
1447
  options: (rule) => ({
1409
1448
  type: constants.regexTypes.NOT_START_WITH,
1410
- case_sensitive: !rule[rule.type].insensitive,
1449
+ case_sensitive: !rule[rule.type]?.insensitive,
1411
1450
  }),
1412
1451
  },
1413
1452
  endsWith: {
1414
1453
  case: constants.rulesTypes.REGEX,
1415
1454
  options: (rule) => ({
1416
1455
  type: constants.regexTypes.ENDS_WITH,
1417
- case_sensitive: !rule[rule.type].insensitive,
1456
+ case_sensitive: !rule[rule.type]?.insensitive,
1418
1457
  }),
1419
1458
  },
1420
1459
  doesNotEndWith: {
1421
1460
  case: constants.rulesTypes.REGEX,
1422
1461
  options: (rule) => ({
1423
1462
  type: constants.regexTypes.NOT_ENDS_WITH,
1424
- case_sensitive: !rule[rule.type].insensitive,
1463
+ case_sensitive: !rule[rule.type]?.insensitive,
1425
1464
  }),
1426
1465
  },
1427
1466
  matchesRegExp: {
@@ -1465,12 +1504,12 @@ const RULE_TRANSFORM_MAP = {
1465
1504
  isOneOf: {
1466
1505
  case: constants.rulesTypes.ONE_OF,
1467
1506
  options: () => ({}),
1468
- value: (rule) => rule[rule.type].value,
1507
+ value: (rule) => rule[rule.type]?.value,
1469
1508
  },
1470
1509
  isNotOneOf: {
1471
1510
  case: constants.rulesTypes.NOT_ONE_OF,
1472
1511
  options: () => ({}),
1473
- value: (rule) => [rule[rule.type].value],
1512
+ value: (rule) => [rule[rule.type]?.value],
1474
1513
  },
1475
1514
  };
1476
1515
 
package/lib/validate.js CHANGED
@@ -224,7 +224,18 @@ const isEmptyRelational = ({
224
224
  }
225
225
  } else if (api_version === constants.API_VERSION.V2) {
226
226
  if (isEmpty(existingValue)) return true;
227
-
227
+ let all = Array.isArray(existingValue)
228
+ ? existingValue.length === 0
229
+ ? []
230
+ : typeof existingValue[0] === "string"
231
+ ? existingValue
232
+ : existingValue.map((item) => item._id)
233
+ : typeof existingValue === "string"
234
+ ? [existingValue]
235
+ : existingValue && typeof existingValue === "object"
236
+ ? [existingValue._id]
237
+ : [];
238
+ all = Array.isArray(all) ? all?.filter(Boolean) : all;
228
239
  switch (interface) {
229
240
  case constants.interfaces.FILE:
230
241
  case constants.interfaces.FILE_IMAGE:
@@ -232,30 +243,17 @@ const isEmptyRelational = ({
232
243
  case constants.interfaces.MANY_TO_MANY:
233
244
  case constants.interfaces.ONE_TO_MANY:
234
245
  case constants.interfaces.MANY_TO_ONE:
235
- case constants.interfaces.SEO:
246
+ case constants.interfaces.MANY_TO_ANY:
236
247
  return remainingItems({
237
- all: Array.isArray(existingValue)
238
- ? existingValue.length === 0
239
- ? []
240
- : typeof existingValue[0] === "string"
241
- ? existingValue
242
- : existingValue.map((item) => item._id)
243
- : typeof existingValue === "string"
244
- ? [existingValue]
245
- : existingValue && typeof existingValue === "object"
246
- ? [existingValue._id]
247
- : [],
248
+ all: all,
248
249
  obj: value,
250
+ isM2A: interface === constants.interfaces.MANY_TO_ANY,
249
251
  });
250
- case constants.interfaces.MANY_TO_ANY:
252
+ case constants.interfaces.SEO:
251
253
  return remainingItems({
252
- all: Array.isArray(existingValue)
253
- ? existingValue?.map((item) => item.item)
254
- : [],
254
+ all: all,
255
255
  obj: value,
256
- isM2A: true,
257
256
  });
258
-
259
257
  // constants.interfaces.TRANSLATIONS,
260
258
  default:
261
259
  return true;
@@ -361,12 +359,21 @@ const validateMetaRules = (
361
359
  const isValidRelational =
362
360
  required && isRelational
363
361
  ? isEmptyRelational({
364
- api_version: apiVersion,
365
- value: fieldValue,
366
- interface: field?.meta?.interface,
367
- onlyFormFields,
368
- existingValue: getValue(existingForm, currentPath),
369
- })
362
+ api_version: apiVersion,
363
+ value: fieldValue,
364
+ interface: field?.meta?.interface,
365
+ onlyFormFields,
366
+ existingValue:
367
+ getValue(existingForm, currentPath) ||
368
+ getValue(
369
+ existingForm,
370
+ currentPath
371
+ ?.replace(".create", "")
372
+ ?.replace(".update", "")
373
+ ?.replace(".existing", "")
374
+ ?.replace(".delete", "")
375
+ ), // TODO: Need to Generate Form Path Without create, update, existing, delete
376
+ })
370
377
  : true;
371
378
 
372
379
  const isEmptyVal = is_m2a_item ? !fieldValue : isEmpty(fieldValue);
@@ -385,29 +392,29 @@ const validateMetaRules = (
385
392
  if (invalidRequire || !isValidRelational) {
386
393
  const message = field?.meta?.required
387
394
  ? error_messages.REQUIRED.replace(
388
- `{field}`,
389
- formatLabel(field.display_label)
390
- )
395
+ `{field}`,
396
+ formatLabel(field.display_label)
397
+ )
391
398
  : error_messages.NOT_EMPTY.replace(
392
- `{field}`,
393
- formatLabel(field.display_label)
394
- );
399
+ `{field}`,
400
+ formatLabel(field.display_label)
401
+ );
395
402
  addError(
396
403
  currentPath,
397
404
  translatedPath
398
405
  ? {
399
- label: formatLabel(field.display_label),
400
- fieldPath: currentPath,
401
- description: "",
402
- translation_path: translatedPath,
403
- message,
404
- }
406
+ label: formatLabel(field.display_label),
407
+ fieldPath: currentPath,
408
+ description: "",
409
+ translation_path: translatedPath,
410
+ message,
411
+ }
405
412
  : {
406
- label: formatLabel(field.display_label),
407
- fieldPath: currentPath,
408
- description: "",
409
- message,
410
- },
413
+ label: formatLabel(field.display_label),
414
+ fieldPath: currentPath,
415
+ description: "",
416
+ message,
417
+ },
411
418
  field
412
419
  );
413
420
  }
@@ -463,8 +470,19 @@ const validateMetaRules = (
463
470
  ) {
464
471
  getSelectedNodes({
465
472
  node: field,
466
- skipFn: (node) =>
467
- node.meta?.interface === constants.interfaces.MANY_TO_ANY,
473
+ skipFn: (node) => {
474
+ let status = false;
475
+ if (
476
+ node.meta?.interface === constants.interfaces.MANY_TO_ANY
477
+ ) status = true
478
+
479
+ if ([constants.interfaces.MANY_TO_MANY,
480
+ constants.interfaces.ONE_TO_MANY,
481
+ constants.interfaces.MANY_TO_ONE,
482
+ constants.interfaces.SEO,
483
+ ].includes(node.meta?.interface) && (!node.meta?.required || !node.meta?.hidden)) status = true
484
+ return status
485
+ },
468
486
  conditionFn: (node) =>
469
487
  node.meta?.required === true && !node.isRelationalUpdate,
470
488
  mapFn: (node) => ({
@@ -475,6 +493,7 @@ const validateMetaRules = (
475
493
  const isTranslationNode =
476
494
  node?.meta?.parentInterface === constants.interfaces.TRANSLATIONS ||
477
495
  field?.meta?.interface === constants.interfaces.TRANSLATIONS;
496
+
478
497
  let fPath = node.key?.replace("[0][0]", "[0]");
479
498
  const types = ["update", "delete", "existing", "create"];
480
499
  const extractFirstType = (key) => {
@@ -587,8 +606,8 @@ const validateMetaRules = (
587
606
  const validType =
588
607
  field?.alternateType?.length > 0
589
608
  ? [field.type, ...field?.alternateType].some((type) =>
590
- typeChecks[type](fieldValue)
591
- )
609
+ typeChecks[type](fieldValue)
610
+ )
592
611
  : typeChecks[field.type](fieldValue, { key: currentPath, updateValue });
593
612
 
594
613
  if (!isEmpty(fieldValue) && !validType) {
@@ -626,9 +645,8 @@ const handleRegexValidation = (
626
645
  const fieldLabel = formatLabel(field.display_label);
627
646
 
628
647
  // Determine regex flags
629
- const flags = `${rule.options?.case_sensitive ? "" : "i"}${
630
- rule.options?.multiline ? "m" : ""
631
- }${rule.options?.global ? "g" : ""}`;
648
+ const flags = `${rule.options?.case_sensitive ? "" : "i"}${rule.options?.multiline ? "m" : ""
649
+ }${rule.options?.global ? "g" : ""}`;
632
650
 
633
651
  // Build regex for MATCH/CONTAINS/NOT_CONTAINS
634
652
  let regex;
@@ -1203,10 +1221,12 @@ const validateField = (
1203
1221
  if (itemType) {
1204
1222
  value.forEach((item, index) => {
1205
1223
  const itemPath = `${currentPath}[${index}]`;
1206
-
1207
1224
  if (
1208
1225
  (choices.includes(field?.meta?.interface) && !isEmpty(item)) ||
1209
- (["tags", "array_of_values"].includes(field?.meta?.interface) &&
1226
+ ([
1227
+ constants.interfaces.TAGS,
1228
+ constants.interfaces.ARRAY_OF_VALUES,
1229
+ ].includes(field?.meta?.interface) &&
1210
1230
  !isEmpty(item))
1211
1231
  ) {
1212
1232
  applyValidations(
@@ -1404,11 +1424,14 @@ const validate = (data) => {
1404
1424
  };
1405
1425
 
1406
1426
  const addError = (fieldPath, obj, field) => {
1407
- fieldPath = [...choices, "tags", "array_of_values"].includes(
1408
- field?.meta?.interface
1409
- )
1410
- ? fieldPath?.replace(/\[\d+\].*$/, "")
1411
- : fieldPath;
1427
+ // fieldPath = [
1428
+ // ...choices,
1429
+ // constants.interfaces.TAGS,
1430
+ // constants.interfaces.ARRAY_OF_VALUES,
1431
+ // ].includes(field?.meta?.interface)
1432
+ // ? fieldPath?.replace(/\[\d+\].*$/, "")
1433
+ // : fieldPath;
1434
+ // TODO: Path is Sometimes Become Wrong (Like Unflatten) if we uncomment this
1412
1435
 
1413
1436
  const fieldKey = getLastChildKey(fieldPath);
1414
1437
  const isBypass = byPassKeys?.some((key) => key === fieldKey);
@@ -1422,7 +1445,7 @@ const validate = (data) => {
1422
1445
  if (
1423
1446
  secondParentField &&
1424
1447
  secondParentField?.meta?.interface ===
1425
- constants.interfaces.TRANSLATIONS
1448
+ constants.interfaces.TRANSLATIONS
1426
1449
  ) {
1427
1450
  const languageKey = secondParentField?.meta?.options?.language_field;
1428
1451
  const firstParentValue = getValue(formData, firstParent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nox-validation",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "validate dynamic schema",
5
5
  "main": "index.js",
6
6
  "scripts": {