strapi-plugin-bulk-updater 1.3.9 → 1.3.10

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.
Files changed (29) hide show
  1. package/dist/_chunks/{BulkUpdater-DPEJQYYa.js → BulkUpdater-DRHQBZZV.js} +20 -4
  2. package/dist/_chunks/BulkUpdater-DRHQBZZV.js.map +1 -0
  3. package/dist/_chunks/{BulkUpdater-CtEymixf.mjs → BulkUpdater-J21ZgJWx.mjs} +20 -4
  4. package/dist/_chunks/BulkUpdater-J21ZgJWx.mjs.map +1 -0
  5. package/dist/_chunks/{ScopedQuotaSelectInput-CyGlYa9x.js → ScopedQuotaSelectInput-BsBpmwun.js} +79 -11
  6. package/dist/_chunks/ScopedQuotaSelectInput-BsBpmwun.js.map +1 -0
  7. package/dist/_chunks/{ScopedQuotaSelectInput-Cy3eEj9-.mjs → ScopedQuotaSelectInput-YKdQgrPt.mjs} +79 -11
  8. package/dist/_chunks/ScopedQuotaSelectInput-YKdQgrPt.mjs.map +1 -0
  9. package/dist/_chunks/{SettingsPage-DLCYx2s4.mjs → SettingsPage-BDObdCIY.mjs} +2 -2
  10. package/dist/_chunks/{SettingsPage-DLCYx2s4.mjs.map → SettingsPage-BDObdCIY.mjs.map} +1 -1
  11. package/dist/_chunks/{SettingsPage-BM8B-uUz.js → SettingsPage-M-HQ8Jtr.js} +2 -2
  12. package/dist/_chunks/{SettingsPage-BM8B-uUz.js.map → SettingsPage-M-HQ8Jtr.js.map} +1 -1
  13. package/dist/_chunks/{ValidationRulesTab-vr-y8ntX.js → ValidationRulesTab-DuvL-K4F.js} +3 -3
  14. package/dist/_chunks/ValidationRulesTab-DuvL-K4F.js.map +1 -0
  15. package/dist/_chunks/{ValidationRulesTab-tT4HIeey.mjs → ValidationRulesTab-VeRr-vxa.mjs} +3 -3
  16. package/dist/_chunks/ValidationRulesTab-VeRr-vxa.mjs.map +1 -0
  17. package/dist/admin/index.js +3 -3
  18. package/dist/admin/index.mjs +3 -3
  19. package/dist/server/index.js +204 -35
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +204 -35
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +1 -1
  24. package/dist/_chunks/BulkUpdater-CtEymixf.mjs.map +0 -1
  25. package/dist/_chunks/BulkUpdater-DPEJQYYa.js.map +0 -1
  26. package/dist/_chunks/ScopedQuotaSelectInput-Cy3eEj9-.mjs.map +0 -1
  27. package/dist/_chunks/ScopedQuotaSelectInput-CyGlYa9x.js.map +0 -1
  28. package/dist/_chunks/ValidationRulesTab-tT4HIeey.mjs.map +0 -1
  29. package/dist/_chunks/ValidationRulesTab-vr-y8ntX.js.map +0 -1
@@ -842,7 +842,9 @@ function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
842
842
  populate2[key] = true;
843
843
  }
844
844
  } else if (def.type === "dynamiczone") {
845
- populate2[key] = true;
845
+ populate2[key] = {
846
+ populate: "*"
847
+ };
846
848
  } else if (def.type === "relation" || def.type === "media") {
847
849
  populate2[key] = true;
848
850
  }
@@ -856,10 +858,28 @@ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, max
856
858
  const discoveredAttrs = [];
857
859
  for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
858
860
  if (systemFields.includes(subKey)) continue;
859
- if (subAttr.type === "dynamiczone") continue;
860
861
  const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
862
+ if (subAttr.type === "dynamiczone") {
863
+ const dzComps = Array.isArray(subAttr.components) ? subAttr.components : [];
864
+ for (const dzCompUid of dzComps) {
865
+ const deeper = discoverComponentAttributes(
866
+ strapi2,
867
+ dzCompUid,
868
+ fullPath,
869
+ currentDepth + 1,
870
+ maxDepth,
871
+ systemFields,
872
+ allContentTypes,
873
+ relationsByAttr,
874
+ allRelationsMeta,
875
+ attributesMeta
876
+ );
877
+ discoveredAttrs.push(...deeper);
878
+ }
879
+ continue;
880
+ }
861
881
  if (subAttr.type === "component") {
862
- if (!subAttr.repeatable && subAttr.component) {
882
+ if (subAttr.component) {
863
883
  const deeper = discoverComponentAttributes(
864
884
  strapi2,
865
885
  subAttr.component,
@@ -901,46 +921,110 @@ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, max
901
921
  function isDeepComponentField(contentType, allComponents, attribute) {
902
922
  if (!attribute || !attribute.includes(".")) return false;
903
923
  const parts = attribute.split(".");
904
- let currentSchema = contentType;
905
- for (let i = 0; i < parts.length - 1; i++) {
906
- const part = parts[i];
907
- const attrDef = currentSchema?.attributes?.[part];
908
- if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
909
- currentSchema = allComponents[attrDef.component];
910
- if (!currentSchema) return false;
924
+ function checkPath(currentSchema, pathParts) {
925
+ if (pathParts.length === 0) return true;
926
+ if (!currentSchema || !currentSchema.attributes) return false;
927
+ const [head, ...tail] = pathParts;
928
+ const attrDef = currentSchema.attributes[head];
929
+ if (!attrDef) return false;
930
+ if (tail.length === 0) {
931
+ return true;
932
+ }
933
+ if (attrDef.type === "component" && attrDef.component) {
934
+ const comp = allComponents[attrDef.component];
935
+ return checkPath(comp, tail);
936
+ }
937
+ if (attrDef.type === "dynamiczone") {
938
+ const compUids = Array.isArray(attrDef.components) ? attrDef.components : [];
939
+ return compUids.some((compUid) => {
940
+ const comp = allComponents[compUid];
941
+ return checkPath(comp, tail);
942
+ });
943
+ }
944
+ return false;
911
945
  }
912
- const leafField = parts[parts.length - 1];
913
- return Boolean(currentSchema?.attributes?.[leafField]);
946
+ return checkPath(contentType, parts);
914
947
  }
915
948
  function getLeafAttributeSchema(contentType, allComponents, attribute) {
916
949
  if (!attribute) return null;
917
950
  if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
918
951
  const parts = attribute.split(".");
919
- let currentSchema = contentType;
920
- for (let i = 0; i < parts.length - 1; i++) {
921
- const part = parts[i];
922
- const attrDef = currentSchema?.attributes?.[part];
923
- if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
924
- currentSchema = allComponents[attrDef.component];
925
- if (!currentSchema) return null;
952
+ function resolveLeaf(currentSchema, pathParts) {
953
+ if (!currentSchema || !currentSchema.attributes) return null;
954
+ const [head, ...tail] = pathParts;
955
+ const attrDef = currentSchema.attributes[head];
956
+ if (!attrDef) return null;
957
+ if (tail.length === 0) {
958
+ return attrDef;
959
+ }
960
+ if (attrDef.type === "component" && attrDef.component) {
961
+ return resolveLeaf(allComponents[attrDef.component], tail);
962
+ }
963
+ if (attrDef.type === "dynamiczone") {
964
+ const compUids = Array.isArray(attrDef.components) ? attrDef.components : [];
965
+ for (const compUid of compUids) {
966
+ const found = resolveLeaf(allComponents[compUid], tail);
967
+ if (found) return found;
968
+ }
969
+ }
970
+ return null;
926
971
  }
927
- return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
972
+ return resolveLeaf(contentType, parts);
928
973
  }
929
974
  function setDeepProperty(obj, pathParts, value) {
930
975
  if (pathParts.length === 0) return value;
931
- const current = { ...obj || {} };
932
976
  const [head, ...tail] = pathParts;
977
+ if (Array.isArray(obj)) {
978
+ if (/^\d+$/.test(head)) {
979
+ const idx = parseInt(head, 10);
980
+ const copy = [...obj];
981
+ copy[idx] = setDeepProperty(copy[idx] || {}, tail, value);
982
+ return copy;
983
+ }
984
+ return obj.map((item) => {
985
+ if (!item || typeof item !== "object") return item;
986
+ if (head in item || tail.length > 0 && typeof item[head] === "object") {
987
+ return {
988
+ ...item,
989
+ [head]: setDeepProperty(item[head], tail, value)
990
+ };
991
+ }
992
+ return item;
993
+ });
994
+ }
995
+ const current = { ...obj || {} };
933
996
  if (tail.length === 0) {
934
997
  current[head] = value;
935
998
  } else {
936
- current[head] = setDeepProperty(current[head] || {}, tail, value);
999
+ const subTarget = current[head];
1000
+ if (Array.isArray(subTarget)) {
1001
+ current[head] = setDeepProperty(subTarget, tail, value);
1002
+ } else {
1003
+ current[head] = setDeepProperty(subTarget || {}, tail, value);
1004
+ }
937
1005
  }
938
1006
  return current;
939
1007
  }
940
1008
  function getDeepProperty(obj, path2) {
941
1009
  if (!obj || !path2) return void 0;
942
- if (!path2.includes(".")) return obj[path2];
943
- return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
1010
+ const parts = path2.split(".");
1011
+ function extract(current, remainingParts) {
1012
+ if (current === void 0 || current === null) return void 0;
1013
+ if (remainingParts.length === 0) return current;
1014
+ const [head, ...tail] = remainingParts;
1015
+ if (Array.isArray(current)) {
1016
+ if (/^\d+$/.test(head)) {
1017
+ return extract(current[parseInt(head, 10)], tail);
1018
+ }
1019
+ for (const item of current) {
1020
+ const found = extract(item?.[head], tail);
1021
+ if (found !== void 0 && found !== null) return found;
1022
+ }
1023
+ return void 0;
1024
+ }
1025
+ return extract(current[head], tail);
1026
+ }
1027
+ return extract(obj, parts);
944
1028
  }
945
1029
  function buildDeepFilter(pathParts, condition) {
946
1030
  if (pathParts.length === 0) return condition;
@@ -1170,7 +1254,8 @@ async function executeBatchInternal(strapi2, {
1170
1254
  findOneArgs.populate = populateOption;
1171
1255
  }
1172
1256
  const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne(findOneArgs);
1173
- const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
1257
+ const isRootDz = contentType?.attributes?.[rootKey]?.type === "dynamiczone";
1258
+ const existingRoot = currentEntryWithPopulate?.[rootKey] || (isRootDz ? [] : {});
1174
1259
  const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
1175
1260
  let newValue2 = value;
1176
1261
  if (regexObj && typeof currentVal2 === "string") {
@@ -1650,10 +1735,29 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1650
1735
  const nestedComponentAttrs = [];
1651
1736
  const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
1652
1737
  if (systemFields.includes(attrKey)) return false;
1653
- if (rawAttrs[attrKey].type === "dynamiczone") return false;
1738
+ if (rawAttrs[attrKey].type === "dynamiczone") {
1739
+ const dzDef = rawAttrs[attrKey];
1740
+ const dzComponents = Array.isArray(dzDef.components) ? dzDef.components : [];
1741
+ for (const compUid of dzComponents) {
1742
+ const compAttrs = discoverComponentAttributes(
1743
+ strapi2,
1744
+ compUid,
1745
+ attrKey,
1746
+ 1,
1747
+ 5,
1748
+ systemFields,
1749
+ allContentTypes,
1750
+ relationsByAttr,
1751
+ allRelationsMeta,
1752
+ colAttributesMeta
1753
+ );
1754
+ nestedComponentAttrs.push(...compAttrs);
1755
+ }
1756
+ return false;
1757
+ }
1654
1758
  if (rawAttrs[attrKey].type === "component") {
1655
1759
  const compDef = rawAttrs[attrKey];
1656
- if (!compDef.repeatable && compDef.component) {
1760
+ if (compDef.component) {
1657
1761
  const compAttrs = discoverComponentAttributes(
1658
1762
  strapi2,
1659
1763
  compDef.component,
@@ -3625,10 +3729,62 @@ function matchesPath(normalizedPath, leafKey, targetField) {
3625
3729
  const pClean = cleanKey(normalizedPath);
3626
3730
  const kClean = cleanKey(leafKey);
3627
3731
  const tClean = cleanKey(targetField);
3628
- if (!tClean.includes(".")) {
3629
- return kClean === tClean || pClean === tClean || pClean.endsWith(`.${tClean}`);
3732
+ if (kClean === tClean || pClean === tClean) return true;
3733
+ const targetSegments = targetField.replace(/^api::/i, "").split(".").map(cleanKey).filter(Boolean);
3734
+ const pathSegments = normalizedPath.split(".").map(cleanKey).filter(Boolean);
3735
+ if (targetSegments.length === 0 || pathSegments.length === 0) return false;
3736
+ if (targetSegments.length === 1) {
3737
+ const single = targetSegments[0];
3738
+ return kClean === single || pathSegments[pathSegments.length - 1] === single;
3739
+ }
3740
+ let isSuffix = true;
3741
+ for (let i = 0; i < targetSegments.length; i++) {
3742
+ const tSeg = targetSegments[targetSegments.length - 1 - i];
3743
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
3744
+ if (tSeg !== "*" && tSeg !== pSeg) {
3745
+ isSuffix = false;
3746
+ break;
3747
+ }
3630
3748
  }
3631
- return pClean === tClean || pClean.endsWith(`.${tClean}`) || pClean.endsWith(tClean) || pClean.includes(`.${tClean}`);
3749
+ if (isSuffix) return true;
3750
+ for (let startIdx = 1; startIdx < targetSegments.length; startIdx++) {
3751
+ const subTarget = targetSegments.slice(startIdx);
3752
+ if (subTarget.length > pathSegments.length) continue;
3753
+ let subSuffix = true;
3754
+ for (let i = 0; i < subTarget.length; i++) {
3755
+ const tSeg = subTarget[subTarget.length - 1 - i];
3756
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
3757
+ if (tSeg !== "*" && tSeg !== pSeg) {
3758
+ subSuffix = false;
3759
+ break;
3760
+ }
3761
+ }
3762
+ if (subSuffix) return true;
3763
+ }
3764
+ let pIdx = 0;
3765
+ let matchesInOrder = true;
3766
+ let tStart = 0;
3767
+ if (targetSegments.length > 2 && targetSegments[0] !== pathSegments[0] && targetSegments[0] !== "*") {
3768
+ tStart = 1;
3769
+ }
3770
+ for (let tIdx = tStart; tIdx < targetSegments.length; tIdx++) {
3771
+ const tSeg = targetSegments[tIdx];
3772
+ if (tSeg === "*") continue;
3773
+ let found = false;
3774
+ while (pIdx < pathSegments.length) {
3775
+ if (pathSegments[pIdx] === tSeg) {
3776
+ found = true;
3777
+ pIdx++;
3778
+ break;
3779
+ }
3780
+ pIdx++;
3781
+ }
3782
+ if (!found) {
3783
+ matchesInOrder = false;
3784
+ break;
3785
+ }
3786
+ }
3787
+ return matchesInOrder;
3632
3788
  }
3633
3789
  const validationRulesService = ({ strapi: strapi2 }) => ({
3634
3790
  async getStore() {
@@ -3747,6 +3903,8 @@ const validationRulesService = ({ strapi: strapi2 }) => ({
3747
3903
  if (matchesPath(attrName, attrName, targetField2) || matchesPath(`${compUid}.${attrName}`, attrName, targetField2)) return true;
3748
3904
  if (attrDef.type === "component" && attrDef.component) {
3749
3905
  if (componentHasField(attrDef.component, targetField2)) return true;
3906
+ } else if (attrDef.type === "dynamiczone" && Array.isArray(attrDef.components)) {
3907
+ if (attrDef.components.some((c) => componentHasField(c, targetField2))) return true;
3750
3908
  }
3751
3909
  }
3752
3910
  return false;
@@ -3763,7 +3921,7 @@ const validationRulesService = ({ strapi: strapi2 }) => ({
3763
3921
  if (attrDef.type === "component" && attrDef.component) {
3764
3922
  if (componentHasField(attrDef.component, targetField)) {
3765
3923
  hasField = true;
3766
- populateAttrs[attrKey] = true;
3924
+ populateAttrs[attrKey] = { populate: "*" };
3767
3925
  }
3768
3926
  } else if (attrDef.type === "dynamiczone" && Array.isArray(attrDef.components)) {
3769
3927
  const matchingComp = attrDef.components.some(
@@ -3771,7 +3929,7 @@ const validationRulesService = ({ strapi: strapi2 }) => ({
3771
3929
  );
3772
3930
  if (matchingComp) {
3773
3931
  hasField = true;
3774
- populateAttrs[attrKey] = true;
3932
+ populateAttrs[attrKey] = { populate: "*" };
3775
3933
  }
3776
3934
  }
3777
3935
  }
@@ -3779,7 +3937,11 @@ const validationRulesService = ({ strapi: strapi2 }) => ({
3779
3937
  }
3780
3938
  const candidateUids = Object.keys(allContentTypes).filter((uid) => {
3781
3939
  if (!uid.startsWith("api::") || uid.includes("bulk-updater")) return false;
3782
- if (rule.collectionUid && rule.collectionUid !== "*" && rule.collectionUid !== uid) {
3940
+ if (rule.collectionUid && rule.collectionUid !== "*") {
3941
+ if (rule.collectionUid === uid) return true;
3942
+ const cleanRuleCol = cleanKey(rule.collectionUid);
3943
+ const cleanUid = cleanKey(uid);
3944
+ if (cleanUid.includes(cleanRuleCol) || cleanRuleCol.includes(cleanUid)) return true;
3783
3945
  return false;
3784
3946
  }
3785
3947
  return true;
@@ -3789,7 +3951,8 @@ const validationRulesService = ({ strapi: strapi2 }) => ({
3789
3951
  const ct = allContentTypes[uid];
3790
3952
  if (!ct) continue;
3791
3953
  const { hasField, populateAttrs } = schemaHasField(ct);
3792
- if (hasField || rule.collectionUid && rule.collectionUid === uid) {
3954
+ const isTargetCol = rule.collectionUid && (rule.collectionUid === uid || cleanKey(uid).includes(cleanKey(rule.collectionUid)));
3955
+ if (hasField || isTargetCol) {
3793
3956
  relevantCollections.push({
3794
3957
  uid,
3795
3958
  displayName: ct.info?.displayName || uid,
@@ -34200,7 +34363,13 @@ const index = {
34200
34363
  strapi2.log.warn("[bulk-updater] Failed to fetch active UI validation rules:", err);
34201
34364
  }
34202
34365
  const effectiveRules = [
34203
- ...activeUiRules.filter((r) => !r.collectionUid || r.collectionUid === "*" || r.collectionUid === context.uid).map((r) => ({
34366
+ ...activeUiRules.filter((r) => {
34367
+ if (!r.collectionUid || r.collectionUid === "*") return true;
34368
+ if (r.collectionUid === context.uid) return true;
34369
+ const cleanRuleCol = cleanKey(r.collectionUid);
34370
+ const cleanCtxCol = cleanKey(context.uid);
34371
+ return cleanCtxCol.includes(cleanRuleCol) || cleanRuleCol.includes(cleanCtxCol);
34372
+ }).map((r) => ({
34204
34373
  id: r.id || r.targetField,
34205
34374
  name: r.name || r.targetField,
34206
34375
  collectionUid: r.collectionUid || "*",