strapi-plugin-field-clearer 1.0.10 → 1.0.12

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.
@@ -30,7 +30,7 @@ const config = {
30
30
  };
31
31
  const contentTypes = {};
32
32
  const PLUGIN_ID = "field-clearer";
33
- const VALID_FIELD_PATH_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?(\.[a-zA-Z_][a-zA-Z0-9_]*){0,2}$/;
33
+ const VALID_FIELD_PATH_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?(\.[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?){0,2}$/;
34
34
  const MAX_FIELD_PATH_LENGTH = 100;
35
35
  const MAX_DOCUMENT_ID_LENGTH = 50;
36
36
  const getAllowedContentTypes = (strapi) => {
@@ -188,17 +188,23 @@ const parseFieldPath = (path) => {
188
188
  if (bracketMatch) {
189
189
  const [, fieldName, indicesStr, nestedField, deepNestedField] = bracketMatch;
190
190
  const indices = indicesStr.split(",").map(Number);
191
- return { fieldName, nestedField, deepNestedField, indices };
191
+ return { fieldName, nestedField, deepNestedField, indices, middleIndices: null };
192
+ }
193
+ const middleBracketMatch = path.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)\[(\d+(?:,\d+)*)\]\.([a-zA-Z_][a-zA-Z0-9_]*)$/);
194
+ if (middleBracketMatch) {
195
+ const [, fieldName, nestedField, indicesStr, deepNestedField] = middleBracketMatch;
196
+ const middleIndices = indicesStr.split(",").map(Number);
197
+ return { fieldName, nestedField, deepNestedField, indices: null, middleIndices };
192
198
  }
193
199
  const parts = path.split(".");
194
200
  if (parts.length === 1) {
195
- return { fieldName: parts[0], indices: null };
201
+ return { fieldName: parts[0], indices: null, middleIndices: null };
196
202
  }
197
203
  if (parts.length === 2) {
198
- return { fieldName: parts[0], nestedField: parts[1], indices: null };
204
+ return { fieldName: parts[0], nestedField: parts[1], indices: null, middleIndices: null };
199
205
  }
200
206
  if (parts.length === 3) {
201
- return { fieldName: parts[0], nestedField: parts[1], deepNestedField: parts[2], indices: null };
207
+ return { fieldName: parts[0], nestedField: parts[1], deepNestedField: parts[2], indices: null, middleIndices: null };
202
208
  }
203
209
  throw new Error(`Invalid path format: "${path}"`);
204
210
  };
@@ -252,7 +258,7 @@ const service = ({ strapi }) => ({
252
258
  if (!trimmedPath) {
253
259
  throw new Error("Field path cannot be empty");
254
260
  }
255
- const { fieldName, nestedField, deepNestedField, indices } = parseFieldPath(trimmedPath);
261
+ const { fieldName, nestedField, deepNestedField, indices, middleIndices } = parseFieldPath(trimmedPath);
256
262
  if (!fieldName) {
257
263
  throw new Error("Field path cannot be empty");
258
264
  }
@@ -260,7 +266,7 @@ const service = ({ strapi }) => ({
260
266
  return this.previewTopLevelField(contentType, documentId, fieldName);
261
267
  }
262
268
  if (deepNestedField) {
263
- return this.previewDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices);
269
+ return this.previewDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices, middleIndices);
264
270
  }
265
271
  return this.previewNestedField(contentType, documentId, fieldName, nestedField, indices);
266
272
  },
@@ -350,14 +356,17 @@ const service = ({ strapi }) => ({
350
356
  };
351
357
  }
352
358
  const componentArray = Array.isArray(components) ? components : [components];
359
+ let targetIndices;
353
360
  if (indices) {
354
- for (const idx of indices) {
361
+ targetIndices = indices.map((idx) => idx - 1);
362
+ for (const idx of targetIndices) {
355
363
  if (idx < 0 || idx >= componentArray.length) {
356
- throw new Error(`Index ${idx} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 0-${componentArray.length - 1})`);
364
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 1-${componentArray.length})`);
357
365
  }
358
366
  }
367
+ } else {
368
+ targetIndices = componentArray.map((_, i) => i);
359
369
  }
360
- const targetIndices = indices || componentArray.map((_, i) => i);
361
370
  let totalCount = 0;
362
371
  let nestedFieldExists = false;
363
372
  const allItems = [];
@@ -401,7 +410,7 @@ const service = ({ strapi }) => ({
401
410
  * Preview a deep nested field deletion (3 levels: e.g., "blocks[0].items.subfield")
402
411
  * Used for fields inside components within dynamic zones or repeatable components
403
412
  */
404
- async previewDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null) {
413
+ async previewDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null, middleIndices = null) {
405
414
  const populate = {
406
415
  [parentField]: {
407
416
  populate: {
@@ -436,7 +445,14 @@ const service = ({ strapi }) => ({
436
445
  throw new Error("Document not found");
437
446
  }
438
447
  const parentComponents = document[parentField];
439
- const displayPath = indices ? `${parentField}[${indices.join(",")}].${componentField}.${nestedField}` : `${parentField}.${componentField}.${nestedField}`;
448
+ let displayPath;
449
+ if (indices) {
450
+ displayPath = `${parentField}[${indices.join(",")}].${componentField}.${nestedField}`;
451
+ } else if (middleIndices) {
452
+ displayPath = `${parentField}.${componentField}[${middleIndices.join(",")}].${nestedField}`;
453
+ } else {
454
+ displayPath = `${parentField}.${componentField}.${nestedField}`;
455
+ }
440
456
  if (parentComponents === void 0) {
441
457
  throw new Error(`Field "${parentField}" does not exist on this content type`);
442
458
  }
@@ -451,25 +467,53 @@ const service = ({ strapi }) => ({
451
467
  };
452
468
  }
453
469
  const parentArray = Array.isArray(parentComponents) ? parentComponents : [parentComponents];
470
+ let targetParentIndices;
454
471
  if (indices) {
455
- for (const idx of indices) {
472
+ targetParentIndices = indices.map((idx) => idx - 1);
473
+ for (const idx of targetParentIndices) {
456
474
  if (idx < 0 || idx >= parentArray.length) {
457
- throw new Error(`Index ${idx} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 0-${parentArray.length - 1})`);
475
+ throw new Error(`Index ${idx + 1} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 1-${parentArray.length})`);
458
476
  }
459
477
  }
478
+ } else {
479
+ targetParentIndices = parentArray.map((_, i) => i);
460
480
  }
461
- const targetIndices = indices || parentArray.map((_, i) => i);
481
+ const targetMiddleIndices = middleIndices ? middleIndices.map((idx) => idx - 1) : null;
462
482
  let totalCount = 0;
463
483
  let fieldExists = false;
464
484
  const allItems = [];
465
485
  let fieldType = "unknown";
466
- for (const i of targetIndices) {
486
+ let maxSubArrayLength = 0;
487
+ let subFieldFound = false;
488
+ for (const i of targetParentIndices) {
489
+ const parentComp = parentArray[i];
490
+ if (!parentComp) continue;
491
+ const subComponent = parentComp[componentField];
492
+ if (subComponent === void 0) continue;
493
+ subFieldFound = true;
494
+ const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
495
+ if (subArray.length > maxSubArrayLength) {
496
+ maxSubArrayLength = subArray.length;
497
+ }
498
+ }
499
+ if (!subFieldFound) {
500
+ throw new Error(`Field "${componentField}" does not exist inside "${parentField}"`);
501
+ }
502
+ if (targetMiddleIndices) {
503
+ for (const idx of targetMiddleIndices) {
504
+ if (idx < 0 || idx >= maxSubArrayLength) {
505
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${maxSubArrayLength} item${maxSubArrayLength !== 1 ? "s" : ""} (indices 1-${maxSubArrayLength})`);
506
+ }
507
+ }
508
+ }
509
+ for (const i of targetParentIndices) {
467
510
  const parentComp = parentArray[i];
468
511
  if (!parentComp) continue;
469
512
  const subComponent = parentComp[componentField];
470
513
  if (subComponent === void 0) continue;
471
514
  const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
472
515
  for (let j = 0; j < subArray.length; j++) {
516
+ if (targetMiddleIndices && !targetMiddleIndices.includes(j)) continue;
473
517
  const sub = subArray[j];
474
518
  if (sub && sub[nestedField] !== void 0) {
475
519
  fieldExists = true;
@@ -492,15 +536,15 @@ const service = ({ strapi }) => ({
492
536
  throw new Error(`Field "${nestedField}" does not exist inside "${parentField}.${componentField}"`);
493
537
  }
494
538
  const isEmpty = totalCount === 0;
495
- const targetDescription = indices ? `${targetIndices.length} selected "${parentField}"` : `${parentArray.length} "${parentField}"`;
539
+ const targetDescription = indices ? `${targetParentIndices.length} selected "${parentField}"` : `${parentArray.length} "${parentField}"`;
496
540
  return {
497
541
  fieldPath: displayPath,
498
542
  fieldType,
499
543
  isEmpty,
500
544
  itemCount: totalCount,
501
- componentCount: targetIndices.length,
545
+ componentCount: targetParentIndices.length,
502
546
  totalComponentCount: parentArray.length,
503
- targetIndices,
547
+ targetIndices: targetParentIndices,
504
548
  items: allItems,
505
549
  message: isEmpty ? `"${nestedField}" is already empty in ${targetDescription}` : `Will delete ${totalCount} item${totalCount !== 1 ? "s" : ""} from "${nestedField}" across ${targetDescription}`
506
550
  };
@@ -603,7 +647,7 @@ const service = ({ strapi }) => ({
603
647
  if (!trimmedPath) {
604
648
  throw new Error("Field path cannot be empty");
605
649
  }
606
- const { fieldName, nestedField, deepNestedField, indices } = parseFieldPath(trimmedPath);
650
+ const { fieldName, nestedField, deepNestedField, indices, middleIndices } = parseFieldPath(trimmedPath);
607
651
  if (!fieldName) {
608
652
  throw new Error("Field path cannot be empty");
609
653
  }
@@ -611,7 +655,7 @@ const service = ({ strapi }) => ({
611
655
  return this.clearTopLevelField(contentType, documentId, fieldName);
612
656
  }
613
657
  if (deepNestedField) {
614
- return this.clearDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices);
658
+ return this.clearDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices, middleIndices);
615
659
  }
616
660
  return this.clearNestedField(contentType, documentId, fieldName, nestedField, indices);
617
661
  },
@@ -694,7 +738,7 @@ const service = ({ strapi }) => ({
694
738
  };
695
739
  let document;
696
740
  try {
697
- document = await this.fetchDocumentWithPublishedFallback(contentType, documentId, populate);
741
+ document = await this.fetchDocument(contentType, documentId, populate);
698
742
  } catch (error) {
699
743
  try {
700
744
  document = await this.fetchDocument(contentType, documentId, {
@@ -723,14 +767,16 @@ const service = ({ strapi }) => ({
723
767
  }
724
768
  const componentArray = Array.isArray(components) ? components : [components];
725
769
  const isRepeatable = Array.isArray(components);
770
+ let convertedIndices = null;
726
771
  if (indices) {
727
- for (const idx of indices) {
772
+ convertedIndices = indices.map((idx) => idx - 1);
773
+ for (const idx of convertedIndices) {
728
774
  if (idx < 0 || idx >= componentArray.length) {
729
- throw new Error(`Index ${idx} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 0-${componentArray.length - 1})`);
775
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 1-${componentArray.length})`);
730
776
  }
731
777
  }
732
778
  }
733
- const targetIndices = indices ? new Set(indices) : null;
779
+ const targetIndices = convertedIndices ? new Set(convertedIndices) : null;
734
780
  for (let i = 0; i < componentArray.length; i++) {
735
781
  const comp = componentArray[i];
736
782
  if (!comp || typeof comp !== "object") {
@@ -835,7 +881,7 @@ const service = ({ strapi }) => ({
835
881
  * (e.g., "blocks[0].items.subfield" - clear subfield inside items inside first block)
836
882
  * Works for dynamic zones and repeatable components with nested components
837
883
  */
838
- async clearDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null) {
884
+ async clearDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null, middleIndices = null) {
839
885
  if (!parentField || !componentField || !nestedField) {
840
886
  throw new Error("Invalid field path provided");
841
887
  }
@@ -852,7 +898,7 @@ const service = ({ strapi }) => ({
852
898
  };
853
899
  let document;
854
900
  try {
855
- document = await this.fetchDocumentWithPublishedFallback(contentType, documentId, populate);
901
+ document = await this.fetchDocument(contentType, documentId, populate);
856
902
  } catch (error) {
857
903
  try {
858
904
  document = await this.fetchDocument(contentType, documentId, {
@@ -873,7 +919,14 @@ const service = ({ strapi }) => ({
873
919
  throw new Error("Document not found");
874
920
  }
875
921
  const parentComponents = document[parentField];
876
- const displayPath = indices ? `${parentField}[${indices.join(",")}].${componentField}.${nestedField}` : `${parentField}.${componentField}.${nestedField}`;
922
+ let displayPath;
923
+ if (indices) {
924
+ displayPath = `${parentField}[${indices.join(",")}].${componentField}.${nestedField}`;
925
+ } else if (middleIndices) {
926
+ displayPath = `${parentField}.${componentField}[${middleIndices.join(",")}].${nestedField}`;
927
+ } else {
928
+ displayPath = `${parentField}.${componentField}.${nestedField}`;
929
+ }
877
930
  if (parentComponents === void 0) {
878
931
  throw new Error(`Field "${parentField}" does not exist on this content type`);
879
932
  }
@@ -882,24 +935,53 @@ const service = ({ strapi }) => ({
882
935
  }
883
936
  const parentArray = Array.isArray(parentComponents) ? parentComponents : [parentComponents];
884
937
  const isRepeatable = Array.isArray(parentComponents);
938
+ let targetParentIndices;
885
939
  if (indices) {
886
- for (const idx of indices) {
940
+ targetParentIndices = indices.map((idx) => idx - 1);
941
+ for (const idx of targetParentIndices) {
887
942
  if (idx < 0 || idx >= parentArray.length) {
888
- throw new Error(`Index ${idx} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 0-${parentArray.length - 1})`);
943
+ throw new Error(`Index ${idx + 1} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 1-${parentArray.length})`);
944
+ }
945
+ }
946
+ } else {
947
+ targetParentIndices = parentArray.map((_, i) => i);
948
+ }
949
+ const targetParentSet = new Set(targetParentIndices);
950
+ const targetMiddleIndices = middleIndices ? middleIndices.map((idx) => idx - 1) : null;
951
+ let maxSubArrayLength = 0;
952
+ let subFieldFound = false;
953
+ for (const i of targetParentIndices) {
954
+ const parentComp = parentArray[i];
955
+ if (!parentComp) continue;
956
+ const subComponent = parentComp[componentField];
957
+ if (subComponent === void 0) continue;
958
+ subFieldFound = true;
959
+ const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
960
+ if (subArray.length > maxSubArrayLength) {
961
+ maxSubArrayLength = subArray.length;
962
+ }
963
+ }
964
+ if (!subFieldFound) {
965
+ throw new Error(`Field "${componentField}" does not exist inside "${parentField}"`);
966
+ }
967
+ if (targetMiddleIndices) {
968
+ for (const idx of targetMiddleIndices) {
969
+ if (idx < 0 || idx >= maxSubArrayLength) {
970
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${maxSubArrayLength} item${maxSubArrayLength !== 1 ? "s" : ""} (indices 1-${maxSubArrayLength})`);
889
971
  }
890
972
  }
891
973
  }
892
- const targetIndices = indices ? new Set(indices) : null;
893
974
  let totalCleared = 0;
894
975
  let fieldExists = false;
895
- for (let i = 0; i < parentArray.length; i++) {
896
- if (targetIndices && !targetIndices.has(i)) continue;
976
+ for (const i of targetParentIndices) {
897
977
  const parentComp = parentArray[i];
898
978
  if (!parentComp) continue;
899
979
  const subComponent = parentComp[componentField];
900
980
  if (subComponent === void 0) continue;
901
981
  const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
902
- for (const sub of subArray) {
982
+ for (let j = 0; j < subArray.length; j++) {
983
+ if (targetMiddleIndices && !targetMiddleIndices.includes(j)) continue;
984
+ const sub = subArray[j];
903
985
  if (sub && sub[nestedField] !== void 0) {
904
986
  fieldExists = true;
905
987
  totalCleared += this.countFieldItems(sub[nestedField]);
@@ -930,8 +1012,8 @@ const service = ({ strapi }) => ({
930
1012
  }
931
1013
  }
932
1014
  const subComponent = parentComp[componentField];
933
- const isTargeted = !targetIndices || targetIndices.has(idx);
934
- if (!isTargeted || subComponent === void 0 || subComponent === null) {
1015
+ const isParentTargeted = targetParentSet.has(idx);
1016
+ if (!isParentTargeted || subComponent === void 0 || subComponent === null) {
935
1017
  if (subComponent === void 0 || subComponent === null) {
936
1018
  updated[componentField] = subComponent;
937
1019
  } else if (Array.isArray(subComponent)) {
@@ -949,7 +1031,7 @@ const service = ({ strapi }) => ({
949
1031
  } else {
950
1032
  const subArray = Array.isArray(subComponent) ? subComponent : [subComponent];
951
1033
  const isSubRepeatable = Array.isArray(subComponent);
952
- const updatedSubs = subArray.map((sub) => {
1034
+ const updatedSubs = subArray.map((sub, subIdx) => {
953
1035
  if (!sub || typeof sub !== "object") return sub;
954
1036
  const updatedSub = { id: sub.id };
955
1037
  if (sub.__component) {
@@ -962,8 +1044,25 @@ const service = ({ strapi }) => ({
962
1044
  updatedSub[key] = value;
963
1045
  }
964
1046
  }
1047
+ const isSubTargeted = !targetMiddleIndices || targetMiddleIndices.includes(subIdx);
965
1048
  if (sub[nestedField] !== void 0) {
966
- updatedSub[nestedField] = this.getEmptyValue(sub[nestedField]);
1049
+ if (isSubTargeted) {
1050
+ updatedSub[nestedField] = this.getEmptyValue(sub[nestedField]);
1051
+ } else {
1052
+ const originalValue = sub[nestedField];
1053
+ if (Array.isArray(originalValue)) {
1054
+ updatedSub[nestedField] = originalValue.map((item) => {
1055
+ if (item && typeof item === "object") {
1056
+ return item.documentId ? { documentId: item.documentId } : { id: item.id };
1057
+ }
1058
+ return item;
1059
+ });
1060
+ } else if (originalValue && typeof originalValue === "object") {
1061
+ updatedSub[nestedField] = originalValue.documentId ? { documentId: originalValue.documentId } : { id: originalValue.id };
1062
+ } else {
1063
+ updatedSub[nestedField] = originalValue;
1064
+ }
1065
+ }
967
1066
  }
968
1067
  return updatedSub;
969
1068
  });
@@ -29,7 +29,7 @@ const config = {
29
29
  };
30
30
  const contentTypes = {};
31
31
  const PLUGIN_ID = "field-clearer";
32
- const VALID_FIELD_PATH_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?(\.[a-zA-Z_][a-zA-Z0-9_]*){0,2}$/;
32
+ const VALID_FIELD_PATH_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?(\.[a-zA-Z_][a-zA-Z0-9_]*(\[\d+(,\d+)*\])?){0,2}$/;
33
33
  const MAX_FIELD_PATH_LENGTH = 100;
34
34
  const MAX_DOCUMENT_ID_LENGTH = 50;
35
35
  const getAllowedContentTypes = (strapi) => {
@@ -187,17 +187,23 @@ const parseFieldPath = (path) => {
187
187
  if (bracketMatch) {
188
188
  const [, fieldName, indicesStr, nestedField, deepNestedField] = bracketMatch;
189
189
  const indices = indicesStr.split(",").map(Number);
190
- return { fieldName, nestedField, deepNestedField, indices };
190
+ return { fieldName, nestedField, deepNestedField, indices, middleIndices: null };
191
+ }
192
+ const middleBracketMatch = path.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)\[(\d+(?:,\d+)*)\]\.([a-zA-Z_][a-zA-Z0-9_]*)$/);
193
+ if (middleBracketMatch) {
194
+ const [, fieldName, nestedField, indicesStr, deepNestedField] = middleBracketMatch;
195
+ const middleIndices = indicesStr.split(",").map(Number);
196
+ return { fieldName, nestedField, deepNestedField, indices: null, middleIndices };
191
197
  }
192
198
  const parts = path.split(".");
193
199
  if (parts.length === 1) {
194
- return { fieldName: parts[0], indices: null };
200
+ return { fieldName: parts[0], indices: null, middleIndices: null };
195
201
  }
196
202
  if (parts.length === 2) {
197
- return { fieldName: parts[0], nestedField: parts[1], indices: null };
203
+ return { fieldName: parts[0], nestedField: parts[1], indices: null, middleIndices: null };
198
204
  }
199
205
  if (parts.length === 3) {
200
- return { fieldName: parts[0], nestedField: parts[1], deepNestedField: parts[2], indices: null };
206
+ return { fieldName: parts[0], nestedField: parts[1], deepNestedField: parts[2], indices: null, middleIndices: null };
201
207
  }
202
208
  throw new Error(`Invalid path format: "${path}"`);
203
209
  };
@@ -251,7 +257,7 @@ const service = ({ strapi }) => ({
251
257
  if (!trimmedPath) {
252
258
  throw new Error("Field path cannot be empty");
253
259
  }
254
- const { fieldName, nestedField, deepNestedField, indices } = parseFieldPath(trimmedPath);
260
+ const { fieldName, nestedField, deepNestedField, indices, middleIndices } = parseFieldPath(trimmedPath);
255
261
  if (!fieldName) {
256
262
  throw new Error("Field path cannot be empty");
257
263
  }
@@ -259,7 +265,7 @@ const service = ({ strapi }) => ({
259
265
  return this.previewTopLevelField(contentType, documentId, fieldName);
260
266
  }
261
267
  if (deepNestedField) {
262
- return this.previewDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices);
268
+ return this.previewDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices, middleIndices);
263
269
  }
264
270
  return this.previewNestedField(contentType, documentId, fieldName, nestedField, indices);
265
271
  },
@@ -349,14 +355,17 @@ const service = ({ strapi }) => ({
349
355
  };
350
356
  }
351
357
  const componentArray = Array.isArray(components) ? components : [components];
358
+ let targetIndices;
352
359
  if (indices) {
353
- for (const idx of indices) {
360
+ targetIndices = indices.map((idx) => idx - 1);
361
+ for (const idx of targetIndices) {
354
362
  if (idx < 0 || idx >= componentArray.length) {
355
- throw new Error(`Index ${idx} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 0-${componentArray.length - 1})`);
363
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 1-${componentArray.length})`);
356
364
  }
357
365
  }
366
+ } else {
367
+ targetIndices = componentArray.map((_, i) => i);
358
368
  }
359
- const targetIndices = indices || componentArray.map((_, i) => i);
360
369
  let totalCount = 0;
361
370
  let nestedFieldExists = false;
362
371
  const allItems = [];
@@ -400,7 +409,7 @@ const service = ({ strapi }) => ({
400
409
  * Preview a deep nested field deletion (3 levels: e.g., "blocks[0].items.subfield")
401
410
  * Used for fields inside components within dynamic zones or repeatable components
402
411
  */
403
- async previewDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null) {
412
+ async previewDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null, middleIndices = null) {
404
413
  const populate = {
405
414
  [parentField]: {
406
415
  populate: {
@@ -435,7 +444,14 @@ const service = ({ strapi }) => ({
435
444
  throw new Error("Document not found");
436
445
  }
437
446
  const parentComponents = document[parentField];
438
- const displayPath = indices ? `${parentField}[${indices.join(",")}].${componentField}.${nestedField}` : `${parentField}.${componentField}.${nestedField}`;
447
+ let displayPath;
448
+ if (indices) {
449
+ displayPath = `${parentField}[${indices.join(",")}].${componentField}.${nestedField}`;
450
+ } else if (middleIndices) {
451
+ displayPath = `${parentField}.${componentField}[${middleIndices.join(",")}].${nestedField}`;
452
+ } else {
453
+ displayPath = `${parentField}.${componentField}.${nestedField}`;
454
+ }
439
455
  if (parentComponents === void 0) {
440
456
  throw new Error(`Field "${parentField}" does not exist on this content type`);
441
457
  }
@@ -450,25 +466,53 @@ const service = ({ strapi }) => ({
450
466
  };
451
467
  }
452
468
  const parentArray = Array.isArray(parentComponents) ? parentComponents : [parentComponents];
469
+ let targetParentIndices;
453
470
  if (indices) {
454
- for (const idx of indices) {
471
+ targetParentIndices = indices.map((idx) => idx - 1);
472
+ for (const idx of targetParentIndices) {
455
473
  if (idx < 0 || idx >= parentArray.length) {
456
- throw new Error(`Index ${idx} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 0-${parentArray.length - 1})`);
474
+ throw new Error(`Index ${idx + 1} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 1-${parentArray.length})`);
457
475
  }
458
476
  }
477
+ } else {
478
+ targetParentIndices = parentArray.map((_, i) => i);
459
479
  }
460
- const targetIndices = indices || parentArray.map((_, i) => i);
480
+ const targetMiddleIndices = middleIndices ? middleIndices.map((idx) => idx - 1) : null;
461
481
  let totalCount = 0;
462
482
  let fieldExists = false;
463
483
  const allItems = [];
464
484
  let fieldType = "unknown";
465
- for (const i of targetIndices) {
485
+ let maxSubArrayLength = 0;
486
+ let subFieldFound = false;
487
+ for (const i of targetParentIndices) {
488
+ const parentComp = parentArray[i];
489
+ if (!parentComp) continue;
490
+ const subComponent = parentComp[componentField];
491
+ if (subComponent === void 0) continue;
492
+ subFieldFound = true;
493
+ const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
494
+ if (subArray.length > maxSubArrayLength) {
495
+ maxSubArrayLength = subArray.length;
496
+ }
497
+ }
498
+ if (!subFieldFound) {
499
+ throw new Error(`Field "${componentField}" does not exist inside "${parentField}"`);
500
+ }
501
+ if (targetMiddleIndices) {
502
+ for (const idx of targetMiddleIndices) {
503
+ if (idx < 0 || idx >= maxSubArrayLength) {
504
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${maxSubArrayLength} item${maxSubArrayLength !== 1 ? "s" : ""} (indices 1-${maxSubArrayLength})`);
505
+ }
506
+ }
507
+ }
508
+ for (const i of targetParentIndices) {
466
509
  const parentComp = parentArray[i];
467
510
  if (!parentComp) continue;
468
511
  const subComponent = parentComp[componentField];
469
512
  if (subComponent === void 0) continue;
470
513
  const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
471
514
  for (let j = 0; j < subArray.length; j++) {
515
+ if (targetMiddleIndices && !targetMiddleIndices.includes(j)) continue;
472
516
  const sub = subArray[j];
473
517
  if (sub && sub[nestedField] !== void 0) {
474
518
  fieldExists = true;
@@ -491,15 +535,15 @@ const service = ({ strapi }) => ({
491
535
  throw new Error(`Field "${nestedField}" does not exist inside "${parentField}.${componentField}"`);
492
536
  }
493
537
  const isEmpty = totalCount === 0;
494
- const targetDescription = indices ? `${targetIndices.length} selected "${parentField}"` : `${parentArray.length} "${parentField}"`;
538
+ const targetDescription = indices ? `${targetParentIndices.length} selected "${parentField}"` : `${parentArray.length} "${parentField}"`;
495
539
  return {
496
540
  fieldPath: displayPath,
497
541
  fieldType,
498
542
  isEmpty,
499
543
  itemCount: totalCount,
500
- componentCount: targetIndices.length,
544
+ componentCount: targetParentIndices.length,
501
545
  totalComponentCount: parentArray.length,
502
- targetIndices,
546
+ targetIndices: targetParentIndices,
503
547
  items: allItems,
504
548
  message: isEmpty ? `"${nestedField}" is already empty in ${targetDescription}` : `Will delete ${totalCount} item${totalCount !== 1 ? "s" : ""} from "${nestedField}" across ${targetDescription}`
505
549
  };
@@ -602,7 +646,7 @@ const service = ({ strapi }) => ({
602
646
  if (!trimmedPath) {
603
647
  throw new Error("Field path cannot be empty");
604
648
  }
605
- const { fieldName, nestedField, deepNestedField, indices } = parseFieldPath(trimmedPath);
649
+ const { fieldName, nestedField, deepNestedField, indices, middleIndices } = parseFieldPath(trimmedPath);
606
650
  if (!fieldName) {
607
651
  throw new Error("Field path cannot be empty");
608
652
  }
@@ -610,7 +654,7 @@ const service = ({ strapi }) => ({
610
654
  return this.clearTopLevelField(contentType, documentId, fieldName);
611
655
  }
612
656
  if (deepNestedField) {
613
- return this.clearDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices);
657
+ return this.clearDeepNestedField(contentType, documentId, fieldName, nestedField, deepNestedField, indices, middleIndices);
614
658
  }
615
659
  return this.clearNestedField(contentType, documentId, fieldName, nestedField, indices);
616
660
  },
@@ -693,7 +737,7 @@ const service = ({ strapi }) => ({
693
737
  };
694
738
  let document;
695
739
  try {
696
- document = await this.fetchDocumentWithPublishedFallback(contentType, documentId, populate);
740
+ document = await this.fetchDocument(contentType, documentId, populate);
697
741
  } catch (error) {
698
742
  try {
699
743
  document = await this.fetchDocument(contentType, documentId, {
@@ -722,14 +766,16 @@ const service = ({ strapi }) => ({
722
766
  }
723
767
  const componentArray = Array.isArray(components) ? components : [components];
724
768
  const isRepeatable = Array.isArray(components);
769
+ let convertedIndices = null;
725
770
  if (indices) {
726
- for (const idx of indices) {
771
+ convertedIndices = indices.map((idx) => idx - 1);
772
+ for (const idx of convertedIndices) {
727
773
  if (idx < 0 || idx >= componentArray.length) {
728
- throw new Error(`Index ${idx} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 0-${componentArray.length - 1})`);
774
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${componentArray.length} item${componentArray.length !== 1 ? "s" : ""} (indices 1-${componentArray.length})`);
729
775
  }
730
776
  }
731
777
  }
732
- const targetIndices = indices ? new Set(indices) : null;
778
+ const targetIndices = convertedIndices ? new Set(convertedIndices) : null;
733
779
  for (let i = 0; i < componentArray.length; i++) {
734
780
  const comp = componentArray[i];
735
781
  if (!comp || typeof comp !== "object") {
@@ -834,7 +880,7 @@ const service = ({ strapi }) => ({
834
880
  * (e.g., "blocks[0].items.subfield" - clear subfield inside items inside first block)
835
881
  * Works for dynamic zones and repeatable components with nested components
836
882
  */
837
- async clearDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null) {
883
+ async clearDeepNestedField(contentType, documentId, parentField, componentField, nestedField, indices = null, middleIndices = null) {
838
884
  if (!parentField || !componentField || !nestedField) {
839
885
  throw new Error("Invalid field path provided");
840
886
  }
@@ -851,7 +897,7 @@ const service = ({ strapi }) => ({
851
897
  };
852
898
  let document;
853
899
  try {
854
- document = await this.fetchDocumentWithPublishedFallback(contentType, documentId, populate);
900
+ document = await this.fetchDocument(contentType, documentId, populate);
855
901
  } catch (error) {
856
902
  try {
857
903
  document = await this.fetchDocument(contentType, documentId, {
@@ -872,7 +918,14 @@ const service = ({ strapi }) => ({
872
918
  throw new Error("Document not found");
873
919
  }
874
920
  const parentComponents = document[parentField];
875
- const displayPath = indices ? `${parentField}[${indices.join(",")}].${componentField}.${nestedField}` : `${parentField}.${componentField}.${nestedField}`;
921
+ let displayPath;
922
+ if (indices) {
923
+ displayPath = `${parentField}[${indices.join(",")}].${componentField}.${nestedField}`;
924
+ } else if (middleIndices) {
925
+ displayPath = `${parentField}.${componentField}[${middleIndices.join(",")}].${nestedField}`;
926
+ } else {
927
+ displayPath = `${parentField}.${componentField}.${nestedField}`;
928
+ }
876
929
  if (parentComponents === void 0) {
877
930
  throw new Error(`Field "${parentField}" does not exist on this content type`);
878
931
  }
@@ -881,24 +934,53 @@ const service = ({ strapi }) => ({
881
934
  }
882
935
  const parentArray = Array.isArray(parentComponents) ? parentComponents : [parentComponents];
883
936
  const isRepeatable = Array.isArray(parentComponents);
937
+ let targetParentIndices;
884
938
  if (indices) {
885
- for (const idx of indices) {
939
+ targetParentIndices = indices.map((idx) => idx - 1);
940
+ for (const idx of targetParentIndices) {
886
941
  if (idx < 0 || idx >= parentArray.length) {
887
- throw new Error(`Index ${idx} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 0-${parentArray.length - 1})`);
942
+ throw new Error(`Index ${idx + 1} is out of range. "${parentField}" has ${parentArray.length} item${parentArray.length !== 1 ? "s" : ""} (indices 1-${parentArray.length})`);
943
+ }
944
+ }
945
+ } else {
946
+ targetParentIndices = parentArray.map((_, i) => i);
947
+ }
948
+ const targetParentSet = new Set(targetParentIndices);
949
+ const targetMiddleIndices = middleIndices ? middleIndices.map((idx) => idx - 1) : null;
950
+ let maxSubArrayLength = 0;
951
+ let subFieldFound = false;
952
+ for (const i of targetParentIndices) {
953
+ const parentComp = parentArray[i];
954
+ if (!parentComp) continue;
955
+ const subComponent = parentComp[componentField];
956
+ if (subComponent === void 0) continue;
957
+ subFieldFound = true;
958
+ const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
959
+ if (subArray.length > maxSubArrayLength) {
960
+ maxSubArrayLength = subArray.length;
961
+ }
962
+ }
963
+ if (!subFieldFound) {
964
+ throw new Error(`Field "${componentField}" does not exist inside "${parentField}"`);
965
+ }
966
+ if (targetMiddleIndices) {
967
+ for (const idx of targetMiddleIndices) {
968
+ if (idx < 0 || idx >= maxSubArrayLength) {
969
+ throw new Error(`Index ${idx + 1} is out of range. "${componentField}" has ${maxSubArrayLength} item${maxSubArrayLength !== 1 ? "s" : ""} (indices 1-${maxSubArrayLength})`);
888
970
  }
889
971
  }
890
972
  }
891
- const targetIndices = indices ? new Set(indices) : null;
892
973
  let totalCleared = 0;
893
974
  let fieldExists = false;
894
- for (let i = 0; i < parentArray.length; i++) {
895
- if (targetIndices && !targetIndices.has(i)) continue;
975
+ for (const i of targetParentIndices) {
896
976
  const parentComp = parentArray[i];
897
977
  if (!parentComp) continue;
898
978
  const subComponent = parentComp[componentField];
899
979
  if (subComponent === void 0) continue;
900
980
  const subArray = Array.isArray(subComponent) ? subComponent : subComponent ? [subComponent] : [];
901
- for (const sub of subArray) {
981
+ for (let j = 0; j < subArray.length; j++) {
982
+ if (targetMiddleIndices && !targetMiddleIndices.includes(j)) continue;
983
+ const sub = subArray[j];
902
984
  if (sub && sub[nestedField] !== void 0) {
903
985
  fieldExists = true;
904
986
  totalCleared += this.countFieldItems(sub[nestedField]);
@@ -929,8 +1011,8 @@ const service = ({ strapi }) => ({
929
1011
  }
930
1012
  }
931
1013
  const subComponent = parentComp[componentField];
932
- const isTargeted = !targetIndices || targetIndices.has(idx);
933
- if (!isTargeted || subComponent === void 0 || subComponent === null) {
1014
+ const isParentTargeted = targetParentSet.has(idx);
1015
+ if (!isParentTargeted || subComponent === void 0 || subComponent === null) {
934
1016
  if (subComponent === void 0 || subComponent === null) {
935
1017
  updated[componentField] = subComponent;
936
1018
  } else if (Array.isArray(subComponent)) {
@@ -948,7 +1030,7 @@ const service = ({ strapi }) => ({
948
1030
  } else {
949
1031
  const subArray = Array.isArray(subComponent) ? subComponent : [subComponent];
950
1032
  const isSubRepeatable = Array.isArray(subComponent);
951
- const updatedSubs = subArray.map((sub) => {
1033
+ const updatedSubs = subArray.map((sub, subIdx) => {
952
1034
  if (!sub || typeof sub !== "object") return sub;
953
1035
  const updatedSub = { id: sub.id };
954
1036
  if (sub.__component) {
@@ -961,8 +1043,25 @@ const service = ({ strapi }) => ({
961
1043
  updatedSub[key] = value;
962
1044
  }
963
1045
  }
1046
+ const isSubTargeted = !targetMiddleIndices || targetMiddleIndices.includes(subIdx);
964
1047
  if (sub[nestedField] !== void 0) {
965
- updatedSub[nestedField] = this.getEmptyValue(sub[nestedField]);
1048
+ if (isSubTargeted) {
1049
+ updatedSub[nestedField] = this.getEmptyValue(sub[nestedField]);
1050
+ } else {
1051
+ const originalValue = sub[nestedField];
1052
+ if (Array.isArray(originalValue)) {
1053
+ updatedSub[nestedField] = originalValue.map((item) => {
1054
+ if (item && typeof item === "object") {
1055
+ return item.documentId ? { documentId: item.documentId } : { id: item.id };
1056
+ }
1057
+ return item;
1058
+ });
1059
+ } else if (originalValue && typeof originalValue === "object") {
1060
+ updatedSub[nestedField] = originalValue.documentId ? { documentId: originalValue.documentId } : { id: originalValue.id };
1061
+ } else {
1062
+ updatedSub[nestedField] = originalValue;
1063
+ }
1064
+ }
966
1065
  }
967
1066
  return updatedSub;
968
1067
  });
@@ -76,7 +76,7 @@ declare const _default: {
76
76
  items: any[];
77
77
  message: string;
78
78
  }>;
79
- previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[]): Promise<{
79
+ previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[], middleIndices?: number[]): Promise<{
80
80
  fieldPath: string;
81
81
  fieldType: string;
82
82
  isEmpty: boolean;
@@ -126,7 +126,7 @@ declare const _default: {
126
126
  clearedCount: number;
127
127
  path: string;
128
128
  }>;
129
- clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[]): Promise<{
129
+ clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[], middleIndices?: number[]): Promise<{
130
130
  message: string;
131
131
  clearedCount: number;
132
132
  path?: undefined;
@@ -41,7 +41,7 @@ declare const _default: {
41
41
  items: any[];
42
42
  message: string;
43
43
  }>;
44
- previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[]): Promise<{
44
+ previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[], middleIndices?: number[]): Promise<{
45
45
  fieldPath: string;
46
46
  fieldType: string;
47
47
  isEmpty: boolean;
@@ -91,7 +91,7 @@ declare const _default: {
91
91
  clearedCount: number;
92
92
  path: string;
93
93
  }>;
94
- clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[]): Promise<{
94
+ clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[], middleIndices?: number[]): Promise<{
95
95
  message: string;
96
96
  clearedCount: number;
97
97
  path?: undefined;
@@ -66,7 +66,7 @@ declare const service: ({ strapi }: {
66
66
  * Preview a deep nested field deletion (3 levels: e.g., "blocks[0].items.subfield")
67
67
  * Used for fields inside components within dynamic zones or repeatable components
68
68
  */
69
- previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[] | null): Promise<{
69
+ previewDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[] | null, middleIndices?: number[] | null): Promise<{
70
70
  fieldPath: string;
71
71
  fieldType: string;
72
72
  isEmpty: boolean;
@@ -157,7 +157,7 @@ declare const service: ({ strapi }: {
157
157
  * (e.g., "blocks[0].items.subfield" - clear subfield inside items inside first block)
158
158
  * Works for dynamic zones and repeatable components with nested components
159
159
  */
160
- clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[] | null): Promise<{
160
+ clearDeepNestedField(contentType: string, documentId: string, parentField: string, componentField: string, nestedField: string, indices?: number[] | null, middleIndices?: number[] | null): Promise<{
161
161
  message: string;
162
162
  clearedCount: number;
163
163
  path?: undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-field-clearer",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "A Strapi v5 plugin to clear/delete field data from content types. Supports nested fields, component arrays, relations, and more.",
5
5
  "keywords": [
6
6
  "strapi",
@@ -82,5 +82,6 @@
82
82
  "name": "field-clearer",
83
83
  "displayName": "Field Clearer",
84
84
  "description": "Clear/delete field data from content types with a simple UI"
85
- }
85
+ },
86
+ "dependencies": {}
86
87
  }