strapi-plugin-bulk-updater 1.2.13 → 1.3.1

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.
@@ -10,7 +10,7 @@ const index = {
10
10
  id: `${PLUGIN_ID}.plugin.name`,
11
11
  defaultMessage: "Bulk Attribute Updater"
12
12
  },
13
- Component: () => Promise.resolve().then(() => require("../_chunks/BulkUpdater-CWJYmuGU.js")),
13
+ Component: () => Promise.resolve().then(() => require("../_chunks/BulkUpdater-CRvwX326.js")),
14
14
  permissions: [],
15
15
  position: 3
16
16
  });
@@ -9,7 +9,7 @@ const index = {
9
9
  id: `${PLUGIN_ID}.plugin.name`,
10
10
  defaultMessage: "Bulk Attribute Updater"
11
11
  },
12
- Component: () => import("../_chunks/BulkUpdater-Cfig2f7d.mjs"),
12
+ Component: () => import("../_chunks/BulkUpdater-DowSmA56.mjs"),
13
13
  permissions: [],
14
14
  position: 3
15
15
  });
@@ -328,12 +328,13 @@ const bulkUpdaterController = ({ strapi: strapi2 }) => ({
328
328
  async getCollectionEntries(ctx) {
329
329
  try {
330
330
  const { uid } = ctx.params;
331
- const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
331
+ const { page, pageSize, attribute, searchAttribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
332
332
  const service = strapi2.plugin("bulk-updater").service("bulkUpdater");
333
333
  const data = await service.getCollectionEntries(uid, {
334
334
  page: page ? Number(page) : 1,
335
335
  pageSize: pageSize ? Number(pageSize) : 10,
336
336
  attribute: attribute ? String(attribute) : void 0,
337
+ searchAttribute: searchAttribute ? String(searchAttribute) : void 0,
337
338
  filterOperator: filterOperator ? String(filterOperator) : void 0,
338
339
  filterQuery: filterQuery !== void 0 ? String(filterQuery) : void 0,
339
340
  search: search ? String(search) : void 0,
@@ -692,6 +693,132 @@ const modelsCache = /* @__PURE__ */ new Map();
692
693
  const MAX_QUERY_CACHE_SIZE = 500;
693
694
  const CACHE_TTL_MS = 60 * 1e3;
694
695
  let staticSchemaCache = null;
696
+ function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
697
+ if (depth2 > 5) return void 0;
698
+ const populate2 = {};
699
+ for (const [key, def] of Object.entries(attributes || {})) {
700
+ if (def.type === "component" && def.component) {
701
+ const comp = allComponents[def.component];
702
+ if (comp?.attributes) {
703
+ const subPopulate = buildDeepPopulate$1(comp.attributes, allComponents, depth2 + 1);
704
+ if (subPopulate && Object.keys(subPopulate).length > 0) {
705
+ populate2[key] = {
706
+ populate: subPopulate
707
+ };
708
+ } else {
709
+ populate2[key] = true;
710
+ }
711
+ } else {
712
+ populate2[key] = true;
713
+ }
714
+ } else if (def.type === "dynamiczone") {
715
+ populate2[key] = true;
716
+ } else if (def.type === "relation" || def.type === "media") {
717
+ populate2[key] = true;
718
+ }
719
+ }
720
+ return Object.keys(populate2).length > 0 ? populate2 : void 0;
721
+ }
722
+ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, maxDepth, systemFields, allContentTypes, relationsByAttr, allRelationsMeta, attributesMeta) {
723
+ if (currentDepth > maxDepth || !compUid) return [];
724
+ const compSchema = strapi2.components[compUid];
725
+ if (!compSchema?.attributes) return [];
726
+ const discoveredAttrs = [];
727
+ for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
728
+ if (systemFields.includes(subKey)) continue;
729
+ if (subAttr.type === "dynamiczone") continue;
730
+ const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
731
+ if (subAttr.type === "component") {
732
+ if (!subAttr.repeatable && subAttr.component) {
733
+ const deeper = discoverComponentAttributes(
734
+ strapi2,
735
+ subAttr.component,
736
+ fullPath,
737
+ currentDepth + 1,
738
+ maxDepth,
739
+ systemFields,
740
+ allContentTypes,
741
+ relationsByAttr,
742
+ allRelationsMeta,
743
+ attributesMeta
744
+ );
745
+ discoveredAttrs.push(...deeper);
746
+ }
747
+ } else if (subAttr.type === "relation") {
748
+ const targetUid = subAttr.target;
749
+ if (targetUid && allContentTypes[targetUid]) {
750
+ const targetInfo = allContentTypes[targetUid]?.info;
751
+ const isMulti = subAttr.relation === "oneToMany" || subAttr.relation === "manyToMany";
752
+ const relMeta = {
753
+ isRelation: true,
754
+ relationType: subAttr.relation,
755
+ targetUid,
756
+ targetDisplayName: targetInfo?.displayName || targetUid,
757
+ isMulti
758
+ };
759
+ relationsByAttr[fullPath] = relMeta;
760
+ allRelationsMeta[fullPath] = relMeta;
761
+ attributesMeta[fullPath] = { depth: currentDepth, type: "relation", component: compUid, isRelation: true };
762
+ discoveredAttrs.push(fullPath);
763
+ }
764
+ } else {
765
+ attributesMeta[fullPath] = { depth: currentDepth, type: subAttr.type, component: compUid };
766
+ discoveredAttrs.push(fullPath);
767
+ }
768
+ }
769
+ return discoveredAttrs;
770
+ }
771
+ function isDeepComponentField(contentType, allComponents, attribute) {
772
+ if (!attribute || !attribute.includes(".")) return false;
773
+ const parts = attribute.split(".");
774
+ let currentSchema = contentType;
775
+ for (let i = 0; i < parts.length - 1; i++) {
776
+ const part = parts[i];
777
+ const attrDef = currentSchema?.attributes?.[part];
778
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
779
+ currentSchema = allComponents[attrDef.component];
780
+ if (!currentSchema) return false;
781
+ }
782
+ const leafField = parts[parts.length - 1];
783
+ return Boolean(currentSchema?.attributes?.[leafField]);
784
+ }
785
+ function getLeafAttributeSchema(contentType, allComponents, attribute) {
786
+ if (!attribute) return null;
787
+ if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
788
+ const parts = attribute.split(".");
789
+ let currentSchema = contentType;
790
+ for (let i = 0; i < parts.length - 1; i++) {
791
+ const part = parts[i];
792
+ const attrDef = currentSchema?.attributes?.[part];
793
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
794
+ currentSchema = allComponents[attrDef.component];
795
+ if (!currentSchema) return null;
796
+ }
797
+ return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
798
+ }
799
+ function setDeepProperty(obj, pathParts, value) {
800
+ if (pathParts.length === 0) return value;
801
+ const current = { ...obj || {} };
802
+ const [head, ...tail] = pathParts;
803
+ if (tail.length === 0) {
804
+ current[head] = value;
805
+ } else {
806
+ current[head] = setDeepProperty(current[head] || {}, tail, value);
807
+ }
808
+ return current;
809
+ }
810
+ function getDeepProperty(obj, path2) {
811
+ if (!obj || !path2) return void 0;
812
+ if (!path2.includes(".")) return obj[path2];
813
+ return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
814
+ }
815
+ function buildDeepFilter(pathParts, condition) {
816
+ if (pathParts.length === 0) return condition;
817
+ const [head, ...tail] = pathParts;
818
+ return {
819
+ [head]: buildDeepFilter(tail, condition)
820
+ };
821
+ }
695
822
  function getSafeTimezone(timeZone) {
696
823
  if (!timeZone || typeof timeZone !== "string") {
697
824
  return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
@@ -900,43 +1027,30 @@ async function executeBatchInternal(strapi2, {
900
1027
  const currentEntry = await getDocumentDeduplicated(target.uid, target.documentId);
901
1028
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
902
1029
  const isNested = attribute.includes(".");
903
- let isComponentNested = false;
904
- let parentKey = "";
905
- let childKey = "";
906
- if (isNested) {
907
- const parts = attribute.split(".");
908
- parentKey = parts[0];
909
- childKey = parts[1];
910
- const parentAttrDef = contentType?.attributes?.[parentKey];
911
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
912
- const compSchema = strapi2.components[parentAttrDef.component];
913
- if (compSchema?.attributes?.[childKey]) {
914
- isComponentNested = true;
915
- }
916
- }
917
- }
1030
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1031
+ const parts = isNested ? attribute.split(".") : [];
1032
+ const rootKey = isNested ? parts[0] : "";
918
1033
  const attrDef = contentType?.attributes?.[attribute];
919
1034
  const hasAttribute = Boolean(attrDef) || isComponentNested;
920
1035
  if (hasAttribute) {
921
1036
  if (isComponentNested) {
922
- const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne({
923
- documentId: target.documentId,
924
- populate: [parentKey]
925
- });
926
- const existingParent = currentEntryWithPopulate?.[parentKey] || {};
927
- const currentVal2 = existingParent?.[childKey];
1037
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1038
+ const findOneArgs = { documentId: target.documentId };
1039
+ if (populateOption) {
1040
+ findOneArgs.populate = populateOption;
1041
+ }
1042
+ const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne(findOneArgs);
1043
+ const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
1044
+ const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
928
1045
  let newValue2 = value;
929
1046
  if (regexObj && typeof currentVal2 === "string") {
930
1047
  newValue2 = currentVal2.replace(regexObj, value);
931
1048
  }
932
- const updatedParent = {
933
- ...existingParent,
934
- [childKey]: newValue2
935
- };
1049
+ const updatedRoot = setDeepProperty(existingRoot, parts.slice(1), newValue2);
936
1050
  await strapi2.documents(target.uid).update({
937
1051
  documentId: target.documentId,
938
1052
  data: {
939
- [parentKey]: updatedParent
1053
+ [rootKey]: updatedRoot
940
1054
  }
941
1055
  });
942
1056
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
@@ -1371,12 +1485,14 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1371
1485
  }
1372
1486
  const metaByUid = {};
1373
1487
  const allRelationsMeta = {};
1488
+ const allAttributesMeta = {};
1374
1489
  for (const uid of targetUids) {
1375
1490
  const contentType = allContentTypes[uid];
1376
1491
  if (!contentType || contentType.kind !== "collectionType" && contentType.kind !== "singleType") continue;
1377
1492
  const isSingleType = contentType.kind === "singleType";
1378
1493
  const rawAttrs = contentType.attributes || {};
1379
1494
  const relationsByAttr = {};
1495
+ const colAttributesMeta = {};
1380
1496
  const nestedComponentAttrs = [];
1381
1497
  const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
1382
1498
  if (systemFields.includes(attrKey)) return false;
@@ -1384,14 +1500,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1384
1500
  if (rawAttrs[attrKey].type === "component") {
1385
1501
  const compDef = rawAttrs[attrKey];
1386
1502
  if (!compDef.repeatable && compDef.component) {
1387
- const componentSchema = strapi2.components[compDef.component];
1388
- if (componentSchema && componentSchema.attributes) {
1389
- for (const [subKey, subAttr] of Object.entries(componentSchema.attributes)) {
1390
- if (systemFields.includes(subKey)) continue;
1391
- if (["component", "dynamiczone", "relation"].includes(subAttr.type)) continue;
1392
- nestedComponentAttrs.push(`${attrKey}.${subKey}`);
1393
- }
1394
- }
1503
+ const compAttrs = discoverComponentAttributes(
1504
+ strapi2,
1505
+ compDef.component,
1506
+ attrKey,
1507
+ 1,
1508
+ 5,
1509
+ systemFields,
1510
+ allContentTypes,
1511
+ relationsByAttr,
1512
+ allRelationsMeta,
1513
+ colAttributesMeta
1514
+ );
1515
+ nestedComponentAttrs.push(...compAttrs);
1395
1516
  }
1396
1517
  return false;
1397
1518
  }
@@ -1410,12 +1531,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1410
1531
  };
1411
1532
  relationsByAttr[attrKey] = relMeta;
1412
1533
  allRelationsMeta[attrKey] = relMeta;
1534
+ colAttributesMeta[attrKey] = { depth: 0, type: "relation", isRelation: true };
1413
1535
  return true;
1414
1536
  }
1537
+ colAttributesMeta[attrKey] = { depth: 0, type: rawAttrs[attrKey].type };
1415
1538
  return true;
1416
1539
  });
1417
1540
  const allColAttrs = [...validAttrs, ...nestedComponentAttrs];
1418
- allColAttrs.forEach((attr) => allAttributesSet.add(attr));
1541
+ allColAttrs.forEach((attr) => {
1542
+ allAttributesSet.add(attr);
1543
+ if (colAttributesMeta[attr]) {
1544
+ allAttributesMeta[attr] = colAttributesMeta[attr];
1545
+ }
1546
+ });
1419
1547
  metaByUid[uid] = {
1420
1548
  uid,
1421
1549
  kind: contentType.kind,
@@ -1425,11 +1553,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1425
1553
  pluralName: contentType.info?.pluralName || uid,
1426
1554
  displayName: contentType.info?.displayName || uid,
1427
1555
  attributes: allColAttrs,
1556
+ attributesMeta: colAttributesMeta,
1428
1557
  relations: relationsByAttr
1429
1558
  };
1430
1559
  }
1431
1560
  staticSchemaCache = {
1432
1561
  attributes: Array.from(allAttributesSet),
1562
+ attributesMeta: allAttributesMeta,
1433
1563
  targetUids: Object.keys(metaByUid),
1434
1564
  metaByUid,
1435
1565
  relationsMeta: allRelationsMeta
@@ -1460,32 +1590,27 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1460
1590
  let entries = [];
1461
1591
  let totalCount = 0;
1462
1592
  try {
1463
- const ctAttrs = allContentTypes[uid]?.attributes || {};
1464
- const relKeys = Object.keys(meta.relations || {});
1465
- const compKeys = Object.keys(ctAttrs).filter((k) => ctAttrs[k].type === "component");
1466
- const populateOptionObj = {};
1467
- relKeys.forEach((k) => {
1468
- populateOptionObj[k] = true;
1469
- });
1470
- compKeys.forEach((k) => {
1471
- populateOptionObj[k] = true;
1472
- });
1473
- const populateOption = Object.keys(populateOptionObj).length > 0 ? populateOptionObj : void 0;
1593
+ const populateOption = buildDeepPopulate$1(allContentTypes[uid]?.attributes || {}, strapi2.components);
1474
1594
  if (meta.isSingleType) {
1475
- const singleDoc = await strapi2.documents(uid).findFirst({
1476
- populate: populateOption
1477
- });
1595
+ const singleArgs = {};
1596
+ if (populateOption) {
1597
+ singleArgs.populate = populateOption;
1598
+ }
1599
+ const singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
1478
1600
  if (singleDoc) {
1479
1601
  entries = [singleDoc];
1480
1602
  totalCount = 1;
1481
1603
  }
1482
1604
  } else {
1483
1605
  totalCount = await strapi2.documents(uid).count();
1484
- entries = await strapi2.documents(uid).findMany({
1606
+ const findArgs = {
1485
1607
  limit: 10,
1486
- sort: { createdAt: "desc" },
1487
- populate: populateOption
1488
- });
1608
+ sort: { createdAt: "desc" }
1609
+ };
1610
+ if (populateOption) {
1611
+ findArgs.populate = populateOption;
1612
+ }
1613
+ entries = await strapi2.documents(uid).findMany(findArgs);
1489
1614
  }
1490
1615
  } catch (err) {
1491
1616
  strapi2.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);
@@ -1529,8 +1654,10 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1529
1654
  return {
1530
1655
  success: true,
1531
1656
  attributes: staticSchemaCache.attributes,
1657
+ attributesMeta: staticSchemaCache.attributesMeta,
1532
1658
  collections,
1533
1659
  relationsMeta: staticSchemaCache.relationsMeta,
1660
+ maxDiscoveredDepth: 5,
1534
1661
  executionWindow: {
1535
1662
  ...executionConfig,
1536
1663
  ...windowStatus
@@ -1549,6 +1676,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1549
1676
  page = 1,
1550
1677
  pageSize = 10,
1551
1678
  attribute,
1679
+ searchAttribute,
1552
1680
  filterOperator,
1553
1681
  filterQuery,
1554
1682
  search,
@@ -1563,8 +1691,9 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1563
1691
  const currentPage = Math.max(Number(page) || 1, 1);
1564
1692
  const start = (currentPage - 1) * limit;
1565
1693
  const isSingleType = contentType.kind === "singleType";
1694
+ const targetFilterAttr = searchAttribute || attribute;
1566
1695
  const signature = await getCollectionSignature(strapi2, uid);
1567
- const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1696
+ const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${searchAttribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1568
1697
  const cached = queryCache.get(cacheKey);
1569
1698
  const etag = `W/"entry-${crypto__default.default.createHash("md5").update(`${cacheKey}:${signature}`).digest("hex")}"`;
1570
1699
  if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {
@@ -1579,19 +1708,15 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1579
1708
  const attrs = contentType.attributes || {};
1580
1709
  let colHasAttribute = false;
1581
1710
  let isNestedAttr = false;
1582
- let parentKey = "";
1583
- let childKey = "";
1584
- if (attribute) {
1585
- if (attribute.includes(".")) {
1586
- [parentKey, childKey] = attribute.split(".");
1587
- if (attrs[parentKey]?.type === "component" && !attrs[parentKey].repeatable && attrs[parentKey].component) {
1588
- const compSchema = strapi2.components[attrs[parentKey].component];
1589
- if (compSchema?.attributes?.[childKey]) {
1590
- colHasAttribute = true;
1591
- isNestedAttr = true;
1592
- }
1711
+ let parts = [];
1712
+ if (targetFilterAttr) {
1713
+ if (targetFilterAttr.includes(".")) {
1714
+ isNestedAttr = isDeepComponentField(contentType, strapi2.components, targetFilterAttr);
1715
+ if (isNestedAttr) {
1716
+ colHasAttribute = true;
1717
+ parts = targetFilterAttr.split(".");
1593
1718
  }
1594
- } else if (attrs[attribute]) {
1719
+ } else if (attrs[targetFilterAttr]) {
1595
1720
  colHasAttribute = true;
1596
1721
  }
1597
1722
  }
@@ -1616,7 +1741,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1616
1741
  }
1617
1742
  let condition = void 0;
1618
1743
  const q = (filterQuery || "").trim();
1619
- const targetAttrSchema = isNestedAttr ? strapi2.components[attrs[parentKey]?.component]?.attributes?.[childKey] : attrs[attribute];
1744
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi2.components, targetFilterAttr) : attrs[targetFilterAttr];
1620
1745
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
1621
1746
  if (normOp === "empty") {
1622
1747
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
@@ -1646,19 +1771,18 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1646
1771
  }
1647
1772
  if (condition !== void 0) {
1648
1773
  if (isNestedAttr) {
1649
- filters2[parentKey] = {
1650
- [childKey]: condition
1651
- };
1774
+ const deepFilter = buildDeepFilter(parts, condition);
1775
+ Object.assign(filters2, deepFilter);
1652
1776
  } else {
1653
1777
  if (normOp === "empty" && isStringLike) {
1654
1778
  filters2.$or = [
1655
- { [attribute]: { $null: true } },
1656
- { [attribute]: { $eq: "" } }
1779
+ { [targetFilterAttr]: { $null: true } },
1780
+ { [targetFilterAttr]: { $eq: "" } }
1657
1781
  ];
1658
1782
  } else if (normOp === "notempty" && isStringLike) {
1659
- filters2[attribute] = { $notNull: true, $ne: "" };
1783
+ filters2[targetFilterAttr] = { $notNull: true, $ne: "" };
1660
1784
  } else {
1661
- filters2[attribute] = condition;
1785
+ filters2[targetFilterAttr] = condition;
1662
1786
  }
1663
1787
  }
1664
1788
  }
@@ -1681,12 +1805,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1681
1805
  }
1682
1806
  }
1683
1807
  }
1808
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1684
1809
  if (isSingleType) {
1685
1810
  let singleDoc = null;
1686
1811
  try {
1687
- singleDoc = await strapi2.documents(uid).findFirst({
1812
+ const singleArgs = {
1688
1813
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1689
- });
1814
+ };
1815
+ if (populateOption) {
1816
+ singleArgs.populate = populateOption;
1817
+ }
1818
+ singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
1690
1819
  } catch {
1691
1820
  singleDoc = null;
1692
1821
  }
@@ -1704,11 +1833,11 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1704
1833
  }
1705
1834
  };
1706
1835
  } else {
1707
- const sortClause = {};
1836
+ let sortClause = {};
1708
1837
  if (sortField && sortField.includes(".")) {
1709
- const [parent, child] = sortField.split(".");
1710
- if (contentType.attributes?.[parent]?.type === "component") {
1711
- sortClause[parent] = { [child]: sortOrder };
1838
+ const sortParts = sortField.split(".");
1839
+ if (isDeepComponentField(contentType, strapi2.components, sortField)) {
1840
+ sortClause = buildDeepFilter(sortParts, sortOrder);
1712
1841
  } else {
1713
1842
  sortClause["createdAt"] = "desc";
1714
1843
  }
@@ -1717,18 +1846,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1717
1846
  } else {
1718
1847
  sortClause["createdAt"] = "desc";
1719
1848
  }
1720
- const populateAttrs = Object.keys(contentType.attributes || {}).filter(
1721
- (k) => contentType.attributes[k].type === "relation" || contentType.attributes[k].type === "component"
1722
- );
1723
- const populateOption = populateAttrs.length > 0 ? populateAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {}) : void 0;
1849
+ const findManyArgs = {
1850
+ start,
1851
+ limit,
1852
+ filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1853
+ sort: sortClause
1854
+ };
1855
+ if (populateOption) {
1856
+ findManyArgs.populate = populateOption;
1857
+ }
1724
1858
  const [entries, totalEntries] = await Promise.all([
1725
- strapi2.documents(uid).findMany({
1726
- start,
1727
- limit,
1728
- filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1729
- sort: sortClause,
1730
- populate: populateOption
1731
- }),
1859
+ strapi2.documents(uid).findMany(findManyArgs),
1732
1860
  strapi2.documents(uid).count({
1733
1861
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1734
1862
  })
@@ -1842,21 +1970,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1842
1970
  const displayName = contentType?.info?.displayName || target.uid;
1843
1971
  try {
1844
1972
  const isNested = attribute.includes(".");
1845
- let isComponentNested = false;
1846
- let parentKey = "";
1847
- let childKey = "";
1848
- if (isNested) {
1849
- const parts = attribute.split(".");
1850
- parentKey = parts[0];
1851
- childKey = parts[1];
1852
- const parentAttrDef = contentType?.attributes?.[parentKey];
1853
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
1854
- const compSchema = strapi2.components[parentAttrDef.component];
1855
- if (compSchema?.attributes?.[childKey]) {
1856
- isComponentNested = true;
1857
- }
1858
- }
1859
- }
1973
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1860
1974
  const attrDef = contentType?.attributes?.[attribute];
1861
1975
  const hasAttribute = Boolean(attrDef) || isComponentNested;
1862
1976
  if (!hasAttribute) {
@@ -1879,10 +1993,12 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1879
1993
  }
1880
1994
  let currentEntry;
1881
1995
  if (isComponentNested) {
1882
- currentEntry = await strapi2.documents(target.uid).findOne({
1883
- documentId: target.documentId,
1884
- populate: [parentKey]
1885
- });
1996
+ const findOneArgs = { documentId: target.documentId };
1997
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1998
+ if (populateOption) {
1999
+ findOneArgs.populate = populateOption;
2000
+ }
2001
+ currentEntry = await strapi2.documents(target.uid).findOne(findOneArgs);
1886
2002
  } else if (attrDef?.type === "relation") {
1887
2003
  currentEntry = await strapi2.documents(target.uid).findOne({
1888
2004
  documentId: target.documentId,
@@ -1895,8 +2011,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1895
2011
  }
1896
2012
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
1897
2013
  if (isComponentNested) {
1898
- const existingParent = currentEntry?.[parentKey] || {};
1899
- const currentVal2 = existingParent?.[childKey];
2014
+ const currentVal2 = getDeepProperty(currentEntry, attribute);
1900
2015
  let newValue2 = value;
1901
2016
  if (regexObj && typeof currentVal2 === "string") {
1902
2017
  newValue2 = currentVal2.replace(regexObj, value);
@@ -3632,15 +3747,20 @@ function discoverExportFields(ct, allComponents, maxDepth = 4) {
3632
3747
  return fields2;
3633
3748
  }
3634
3749
  function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
3635
- if (depth2 > 4) return true;
3750
+ if (depth2 > 5) return void 0;
3636
3751
  const populate2 = {};
3637
3752
  for (const [key, def] of Object.entries(attributes || {})) {
3638
3753
  if (def.type === "component" && def.component) {
3639
3754
  const comp = allComponents[def.component];
3640
3755
  if (comp?.attributes) {
3641
- populate2[key] = {
3642
- populate: buildDeepPopulate(comp.attributes, allComponents, depth2 + 1)
3643
- };
3756
+ const subPopulate = buildDeepPopulate(comp.attributes, allComponents, depth2 + 1);
3757
+ if (subPopulate && Object.keys(subPopulate).length > 0) {
3758
+ populate2[key] = {
3759
+ populate: subPopulate
3760
+ };
3761
+ } else {
3762
+ populate2[key] = true;
3763
+ }
3644
3764
  } else {
3645
3765
  populate2[key] = true;
3646
3766
  }
@@ -3650,7 +3770,7 @@ function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
3650
3770
  populate2[key] = true;
3651
3771
  }
3652
3772
  }
3653
- return Object.keys(populate2).length > 0 ? populate2 : true;
3773
+ return Object.keys(populate2).length > 0 ? populate2 : void 0;
3654
3774
  }
3655
3775
  function getNestedExportValue(entry, path2) {
3656
3776
  if (!entry || !path2) return "";
@@ -3817,17 +3937,22 @@ const csvService = ({ strapi: strapi2 }) => ({
3817
3937
  const deepPopulate = buildDeepPopulate(ct.attributes || {}, allComponents);
3818
3938
  let entries = [];
3819
3939
  if (ct.kind === "singleType") {
3820
- const doc = await strapi2.documents(uid).findFirst({
3821
- populate: deepPopulate
3822
- });
3940
+ const findFirstArgs = {};
3941
+ if (deepPopulate) {
3942
+ findFirstArgs.populate = deepPopulate;
3943
+ }
3944
+ const doc = await strapi2.documents(uid).findFirst(findFirstArgs);
3823
3945
  entries = doc ? [doc] : [];
3824
3946
  } else {
3825
- entries = await strapi2.documents(uid).findMany({
3947
+ const findManyArgs = {
3826
3948
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
3827
3949
  limit: options.limit || 5e3,
3828
- sort: { createdAt: "desc" },
3829
- populate: deepPopulate
3830
- });
3950
+ sort: { createdAt: "desc" }
3951
+ };
3952
+ if (deepPopulate) {
3953
+ findManyArgs.populate = deepPopulate;
3954
+ }
3955
+ entries = await strapi2.documents(uid).findMany(findManyArgs);
3831
3956
  }
3832
3957
  const lines = [];
3833
3958
  lines.push(fields2.map((f) => escapeCsvValue(f.key)).join(","));