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.
@@ -315,12 +315,13 @@ const bulkUpdaterController = ({ strapi: strapi2 }) => ({
315
315
  async getCollectionEntries(ctx) {
316
316
  try {
317
317
  const { uid } = ctx.params;
318
- const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
318
+ const { page, pageSize, attribute, searchAttribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
319
319
  const service = strapi2.plugin("bulk-updater").service("bulkUpdater");
320
320
  const data = await service.getCollectionEntries(uid, {
321
321
  page: page ? Number(page) : 1,
322
322
  pageSize: pageSize ? Number(pageSize) : 10,
323
323
  attribute: attribute ? String(attribute) : void 0,
324
+ searchAttribute: searchAttribute ? String(searchAttribute) : void 0,
324
325
  filterOperator: filterOperator ? String(filterOperator) : void 0,
325
326
  filterQuery: filterQuery !== void 0 ? String(filterQuery) : void 0,
326
327
  search: search ? String(search) : void 0,
@@ -679,6 +680,132 @@ const modelsCache = /* @__PURE__ */ new Map();
679
680
  const MAX_QUERY_CACHE_SIZE = 500;
680
681
  const CACHE_TTL_MS = 60 * 1e3;
681
682
  let staticSchemaCache = null;
683
+ function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
684
+ if (depth2 > 5) return void 0;
685
+ const populate2 = {};
686
+ for (const [key, def] of Object.entries(attributes || {})) {
687
+ if (def.type === "component" && def.component) {
688
+ const comp = allComponents[def.component];
689
+ if (comp?.attributes) {
690
+ const subPopulate = buildDeepPopulate$1(comp.attributes, allComponents, depth2 + 1);
691
+ if (subPopulate && Object.keys(subPopulate).length > 0) {
692
+ populate2[key] = {
693
+ populate: subPopulate
694
+ };
695
+ } else {
696
+ populate2[key] = true;
697
+ }
698
+ } else {
699
+ populate2[key] = true;
700
+ }
701
+ } else if (def.type === "dynamiczone") {
702
+ populate2[key] = true;
703
+ } else if (def.type === "relation" || def.type === "media") {
704
+ populate2[key] = true;
705
+ }
706
+ }
707
+ return Object.keys(populate2).length > 0 ? populate2 : void 0;
708
+ }
709
+ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, maxDepth, systemFields, allContentTypes, relationsByAttr, allRelationsMeta, attributesMeta) {
710
+ if (currentDepth > maxDepth || !compUid) return [];
711
+ const compSchema = strapi2.components[compUid];
712
+ if (!compSchema?.attributes) return [];
713
+ const discoveredAttrs = [];
714
+ for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
715
+ if (systemFields.includes(subKey)) continue;
716
+ if (subAttr.type === "dynamiczone") continue;
717
+ const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
718
+ if (subAttr.type === "component") {
719
+ if (!subAttr.repeatable && subAttr.component) {
720
+ const deeper = discoverComponentAttributes(
721
+ strapi2,
722
+ subAttr.component,
723
+ fullPath,
724
+ currentDepth + 1,
725
+ maxDepth,
726
+ systemFields,
727
+ allContentTypes,
728
+ relationsByAttr,
729
+ allRelationsMeta,
730
+ attributesMeta
731
+ );
732
+ discoveredAttrs.push(...deeper);
733
+ }
734
+ } else if (subAttr.type === "relation") {
735
+ const targetUid = subAttr.target;
736
+ if (targetUid && allContentTypes[targetUid]) {
737
+ const targetInfo = allContentTypes[targetUid]?.info;
738
+ const isMulti = subAttr.relation === "oneToMany" || subAttr.relation === "manyToMany";
739
+ const relMeta = {
740
+ isRelation: true,
741
+ relationType: subAttr.relation,
742
+ targetUid,
743
+ targetDisplayName: targetInfo?.displayName || targetUid,
744
+ isMulti
745
+ };
746
+ relationsByAttr[fullPath] = relMeta;
747
+ allRelationsMeta[fullPath] = relMeta;
748
+ attributesMeta[fullPath] = { depth: currentDepth, type: "relation", component: compUid, isRelation: true };
749
+ discoveredAttrs.push(fullPath);
750
+ }
751
+ } else {
752
+ attributesMeta[fullPath] = { depth: currentDepth, type: subAttr.type, component: compUid };
753
+ discoveredAttrs.push(fullPath);
754
+ }
755
+ }
756
+ return discoveredAttrs;
757
+ }
758
+ function isDeepComponentField(contentType, allComponents, attribute) {
759
+ if (!attribute || !attribute.includes(".")) return false;
760
+ const parts = attribute.split(".");
761
+ let currentSchema = contentType;
762
+ for (let i = 0; i < parts.length - 1; i++) {
763
+ const part = parts[i];
764
+ const attrDef = currentSchema?.attributes?.[part];
765
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
766
+ currentSchema = allComponents[attrDef.component];
767
+ if (!currentSchema) return false;
768
+ }
769
+ const leafField = parts[parts.length - 1];
770
+ return Boolean(currentSchema?.attributes?.[leafField]);
771
+ }
772
+ function getLeafAttributeSchema(contentType, allComponents, attribute) {
773
+ if (!attribute) return null;
774
+ if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
775
+ const parts = attribute.split(".");
776
+ let currentSchema = contentType;
777
+ for (let i = 0; i < parts.length - 1; i++) {
778
+ const part = parts[i];
779
+ const attrDef = currentSchema?.attributes?.[part];
780
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
781
+ currentSchema = allComponents[attrDef.component];
782
+ if (!currentSchema) return null;
783
+ }
784
+ return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
785
+ }
786
+ function setDeepProperty(obj, pathParts, value) {
787
+ if (pathParts.length === 0) return value;
788
+ const current = { ...obj || {} };
789
+ const [head, ...tail] = pathParts;
790
+ if (tail.length === 0) {
791
+ current[head] = value;
792
+ } else {
793
+ current[head] = setDeepProperty(current[head] || {}, tail, value);
794
+ }
795
+ return current;
796
+ }
797
+ function getDeepProperty(obj, path2) {
798
+ if (!obj || !path2) return void 0;
799
+ if (!path2.includes(".")) return obj[path2];
800
+ return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
801
+ }
802
+ function buildDeepFilter(pathParts, condition) {
803
+ if (pathParts.length === 0) return condition;
804
+ const [head, ...tail] = pathParts;
805
+ return {
806
+ [head]: buildDeepFilter(tail, condition)
807
+ };
808
+ }
682
809
  function getSafeTimezone(timeZone) {
683
810
  if (!timeZone || typeof timeZone !== "string") {
684
811
  return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
@@ -887,43 +1014,30 @@ async function executeBatchInternal(strapi2, {
887
1014
  const currentEntry = await getDocumentDeduplicated(target.uid, target.documentId);
888
1015
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
889
1016
  const isNested = attribute.includes(".");
890
- let isComponentNested = false;
891
- let parentKey = "";
892
- let childKey = "";
893
- if (isNested) {
894
- const parts = attribute.split(".");
895
- parentKey = parts[0];
896
- childKey = parts[1];
897
- const parentAttrDef = contentType?.attributes?.[parentKey];
898
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
899
- const compSchema = strapi2.components[parentAttrDef.component];
900
- if (compSchema?.attributes?.[childKey]) {
901
- isComponentNested = true;
902
- }
903
- }
904
- }
1017
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1018
+ const parts = isNested ? attribute.split(".") : [];
1019
+ const rootKey = isNested ? parts[0] : "";
905
1020
  const attrDef = contentType?.attributes?.[attribute];
906
1021
  const hasAttribute = Boolean(attrDef) || isComponentNested;
907
1022
  if (hasAttribute) {
908
1023
  if (isComponentNested) {
909
- const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne({
910
- documentId: target.documentId,
911
- populate: [parentKey]
912
- });
913
- const existingParent = currentEntryWithPopulate?.[parentKey] || {};
914
- const currentVal2 = existingParent?.[childKey];
1024
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1025
+ const findOneArgs = { documentId: target.documentId };
1026
+ if (populateOption) {
1027
+ findOneArgs.populate = populateOption;
1028
+ }
1029
+ const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne(findOneArgs);
1030
+ const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
1031
+ const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
915
1032
  let newValue2 = value;
916
1033
  if (regexObj && typeof currentVal2 === "string") {
917
1034
  newValue2 = currentVal2.replace(regexObj, value);
918
1035
  }
919
- const updatedParent = {
920
- ...existingParent,
921
- [childKey]: newValue2
922
- };
1036
+ const updatedRoot = setDeepProperty(existingRoot, parts.slice(1), newValue2);
923
1037
  await strapi2.documents(target.uid).update({
924
1038
  documentId: target.documentId,
925
1039
  data: {
926
- [parentKey]: updatedParent
1040
+ [rootKey]: updatedRoot
927
1041
  }
928
1042
  });
929
1043
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
@@ -1358,12 +1472,14 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1358
1472
  }
1359
1473
  const metaByUid = {};
1360
1474
  const allRelationsMeta = {};
1475
+ const allAttributesMeta = {};
1361
1476
  for (const uid of targetUids) {
1362
1477
  const contentType = allContentTypes[uid];
1363
1478
  if (!contentType || contentType.kind !== "collectionType" && contentType.kind !== "singleType") continue;
1364
1479
  const isSingleType = contentType.kind === "singleType";
1365
1480
  const rawAttrs = contentType.attributes || {};
1366
1481
  const relationsByAttr = {};
1482
+ const colAttributesMeta = {};
1367
1483
  const nestedComponentAttrs = [];
1368
1484
  const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
1369
1485
  if (systemFields.includes(attrKey)) return false;
@@ -1371,14 +1487,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1371
1487
  if (rawAttrs[attrKey].type === "component") {
1372
1488
  const compDef = rawAttrs[attrKey];
1373
1489
  if (!compDef.repeatable && compDef.component) {
1374
- const componentSchema = strapi2.components[compDef.component];
1375
- if (componentSchema && componentSchema.attributes) {
1376
- for (const [subKey, subAttr] of Object.entries(componentSchema.attributes)) {
1377
- if (systemFields.includes(subKey)) continue;
1378
- if (["component", "dynamiczone", "relation"].includes(subAttr.type)) continue;
1379
- nestedComponentAttrs.push(`${attrKey}.${subKey}`);
1380
- }
1381
- }
1490
+ const compAttrs = discoverComponentAttributes(
1491
+ strapi2,
1492
+ compDef.component,
1493
+ attrKey,
1494
+ 1,
1495
+ 5,
1496
+ systemFields,
1497
+ allContentTypes,
1498
+ relationsByAttr,
1499
+ allRelationsMeta,
1500
+ colAttributesMeta
1501
+ );
1502
+ nestedComponentAttrs.push(...compAttrs);
1382
1503
  }
1383
1504
  return false;
1384
1505
  }
@@ -1397,12 +1518,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1397
1518
  };
1398
1519
  relationsByAttr[attrKey] = relMeta;
1399
1520
  allRelationsMeta[attrKey] = relMeta;
1521
+ colAttributesMeta[attrKey] = { depth: 0, type: "relation", isRelation: true };
1400
1522
  return true;
1401
1523
  }
1524
+ colAttributesMeta[attrKey] = { depth: 0, type: rawAttrs[attrKey].type };
1402
1525
  return true;
1403
1526
  });
1404
1527
  const allColAttrs = [...validAttrs, ...nestedComponentAttrs];
1405
- allColAttrs.forEach((attr) => allAttributesSet.add(attr));
1528
+ allColAttrs.forEach((attr) => {
1529
+ allAttributesSet.add(attr);
1530
+ if (colAttributesMeta[attr]) {
1531
+ allAttributesMeta[attr] = colAttributesMeta[attr];
1532
+ }
1533
+ });
1406
1534
  metaByUid[uid] = {
1407
1535
  uid,
1408
1536
  kind: contentType.kind,
@@ -1412,11 +1540,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1412
1540
  pluralName: contentType.info?.pluralName || uid,
1413
1541
  displayName: contentType.info?.displayName || uid,
1414
1542
  attributes: allColAttrs,
1543
+ attributesMeta: colAttributesMeta,
1415
1544
  relations: relationsByAttr
1416
1545
  };
1417
1546
  }
1418
1547
  staticSchemaCache = {
1419
1548
  attributes: Array.from(allAttributesSet),
1549
+ attributesMeta: allAttributesMeta,
1420
1550
  targetUids: Object.keys(metaByUid),
1421
1551
  metaByUid,
1422
1552
  relationsMeta: allRelationsMeta
@@ -1447,32 +1577,27 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1447
1577
  let entries = [];
1448
1578
  let totalCount = 0;
1449
1579
  try {
1450
- const ctAttrs = allContentTypes[uid]?.attributes || {};
1451
- const relKeys = Object.keys(meta.relations || {});
1452
- const compKeys = Object.keys(ctAttrs).filter((k) => ctAttrs[k].type === "component");
1453
- const populateOptionObj = {};
1454
- relKeys.forEach((k) => {
1455
- populateOptionObj[k] = true;
1456
- });
1457
- compKeys.forEach((k) => {
1458
- populateOptionObj[k] = true;
1459
- });
1460
- const populateOption = Object.keys(populateOptionObj).length > 0 ? populateOptionObj : void 0;
1580
+ const populateOption = buildDeepPopulate$1(allContentTypes[uid]?.attributes || {}, strapi2.components);
1461
1581
  if (meta.isSingleType) {
1462
- const singleDoc = await strapi2.documents(uid).findFirst({
1463
- populate: populateOption
1464
- });
1582
+ const singleArgs = {};
1583
+ if (populateOption) {
1584
+ singleArgs.populate = populateOption;
1585
+ }
1586
+ const singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
1465
1587
  if (singleDoc) {
1466
1588
  entries = [singleDoc];
1467
1589
  totalCount = 1;
1468
1590
  }
1469
1591
  } else {
1470
1592
  totalCount = await strapi2.documents(uid).count();
1471
- entries = await strapi2.documents(uid).findMany({
1593
+ const findArgs = {
1472
1594
  limit: 10,
1473
- sort: { createdAt: "desc" },
1474
- populate: populateOption
1475
- });
1595
+ sort: { createdAt: "desc" }
1596
+ };
1597
+ if (populateOption) {
1598
+ findArgs.populate = populateOption;
1599
+ }
1600
+ entries = await strapi2.documents(uid).findMany(findArgs);
1476
1601
  }
1477
1602
  } catch (err) {
1478
1603
  strapi2.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);
@@ -1516,8 +1641,10 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1516
1641
  return {
1517
1642
  success: true,
1518
1643
  attributes: staticSchemaCache.attributes,
1644
+ attributesMeta: staticSchemaCache.attributesMeta,
1519
1645
  collections,
1520
1646
  relationsMeta: staticSchemaCache.relationsMeta,
1647
+ maxDiscoveredDepth: 5,
1521
1648
  executionWindow: {
1522
1649
  ...executionConfig,
1523
1650
  ...windowStatus
@@ -1536,6 +1663,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1536
1663
  page = 1,
1537
1664
  pageSize = 10,
1538
1665
  attribute,
1666
+ searchAttribute,
1539
1667
  filterOperator,
1540
1668
  filterQuery,
1541
1669
  search,
@@ -1550,8 +1678,9 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1550
1678
  const currentPage = Math.max(Number(page) || 1, 1);
1551
1679
  const start = (currentPage - 1) * limit;
1552
1680
  const isSingleType = contentType.kind === "singleType";
1681
+ const targetFilterAttr = searchAttribute || attribute;
1553
1682
  const signature = await getCollectionSignature(strapi2, uid);
1554
- const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1683
+ const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${searchAttribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1555
1684
  const cached = queryCache.get(cacheKey);
1556
1685
  const etag = `W/"entry-${crypto.createHash("md5").update(`${cacheKey}:${signature}`).digest("hex")}"`;
1557
1686
  if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {
@@ -1566,19 +1695,15 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1566
1695
  const attrs = contentType.attributes || {};
1567
1696
  let colHasAttribute = false;
1568
1697
  let isNestedAttr = false;
1569
- let parentKey = "";
1570
- let childKey = "";
1571
- if (attribute) {
1572
- if (attribute.includes(".")) {
1573
- [parentKey, childKey] = attribute.split(".");
1574
- if (attrs[parentKey]?.type === "component" && !attrs[parentKey].repeatable && attrs[parentKey].component) {
1575
- const compSchema = strapi2.components[attrs[parentKey].component];
1576
- if (compSchema?.attributes?.[childKey]) {
1577
- colHasAttribute = true;
1578
- isNestedAttr = true;
1579
- }
1698
+ let parts = [];
1699
+ if (targetFilterAttr) {
1700
+ if (targetFilterAttr.includes(".")) {
1701
+ isNestedAttr = isDeepComponentField(contentType, strapi2.components, targetFilterAttr);
1702
+ if (isNestedAttr) {
1703
+ colHasAttribute = true;
1704
+ parts = targetFilterAttr.split(".");
1580
1705
  }
1581
- } else if (attrs[attribute]) {
1706
+ } else if (attrs[targetFilterAttr]) {
1582
1707
  colHasAttribute = true;
1583
1708
  }
1584
1709
  }
@@ -1603,7 +1728,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1603
1728
  }
1604
1729
  let condition = void 0;
1605
1730
  const q = (filterQuery || "").trim();
1606
- const targetAttrSchema = isNestedAttr ? strapi2.components[attrs[parentKey]?.component]?.attributes?.[childKey] : attrs[attribute];
1731
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi2.components, targetFilterAttr) : attrs[targetFilterAttr];
1607
1732
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
1608
1733
  if (normOp === "empty") {
1609
1734
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
@@ -1633,19 +1758,18 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1633
1758
  }
1634
1759
  if (condition !== void 0) {
1635
1760
  if (isNestedAttr) {
1636
- filters2[parentKey] = {
1637
- [childKey]: condition
1638
- };
1761
+ const deepFilter = buildDeepFilter(parts, condition);
1762
+ Object.assign(filters2, deepFilter);
1639
1763
  } else {
1640
1764
  if (normOp === "empty" && isStringLike) {
1641
1765
  filters2.$or = [
1642
- { [attribute]: { $null: true } },
1643
- { [attribute]: { $eq: "" } }
1766
+ { [targetFilterAttr]: { $null: true } },
1767
+ { [targetFilterAttr]: { $eq: "" } }
1644
1768
  ];
1645
1769
  } else if (normOp === "notempty" && isStringLike) {
1646
- filters2[attribute] = { $notNull: true, $ne: "" };
1770
+ filters2[targetFilterAttr] = { $notNull: true, $ne: "" };
1647
1771
  } else {
1648
- filters2[attribute] = condition;
1772
+ filters2[targetFilterAttr] = condition;
1649
1773
  }
1650
1774
  }
1651
1775
  }
@@ -1668,12 +1792,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1668
1792
  }
1669
1793
  }
1670
1794
  }
1795
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1671
1796
  if (isSingleType) {
1672
1797
  let singleDoc = null;
1673
1798
  try {
1674
- singleDoc = await strapi2.documents(uid).findFirst({
1799
+ const singleArgs = {
1675
1800
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1676
- });
1801
+ };
1802
+ if (populateOption) {
1803
+ singleArgs.populate = populateOption;
1804
+ }
1805
+ singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
1677
1806
  } catch {
1678
1807
  singleDoc = null;
1679
1808
  }
@@ -1691,11 +1820,11 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1691
1820
  }
1692
1821
  };
1693
1822
  } else {
1694
- const sortClause = {};
1823
+ let sortClause = {};
1695
1824
  if (sortField && sortField.includes(".")) {
1696
- const [parent, child] = sortField.split(".");
1697
- if (contentType.attributes?.[parent]?.type === "component") {
1698
- sortClause[parent] = { [child]: sortOrder };
1825
+ const sortParts = sortField.split(".");
1826
+ if (isDeepComponentField(contentType, strapi2.components, sortField)) {
1827
+ sortClause = buildDeepFilter(sortParts, sortOrder);
1699
1828
  } else {
1700
1829
  sortClause["createdAt"] = "desc";
1701
1830
  }
@@ -1704,18 +1833,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1704
1833
  } else {
1705
1834
  sortClause["createdAt"] = "desc";
1706
1835
  }
1707
- const populateAttrs = Object.keys(contentType.attributes || {}).filter(
1708
- (k) => contentType.attributes[k].type === "relation" || contentType.attributes[k].type === "component"
1709
- );
1710
- const populateOption = populateAttrs.length > 0 ? populateAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {}) : void 0;
1836
+ const findManyArgs = {
1837
+ start,
1838
+ limit,
1839
+ filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1840
+ sort: sortClause
1841
+ };
1842
+ if (populateOption) {
1843
+ findManyArgs.populate = populateOption;
1844
+ }
1711
1845
  const [entries, totalEntries] = await Promise.all([
1712
- strapi2.documents(uid).findMany({
1713
- start,
1714
- limit,
1715
- filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1716
- sort: sortClause,
1717
- populate: populateOption
1718
- }),
1846
+ strapi2.documents(uid).findMany(findManyArgs),
1719
1847
  strapi2.documents(uid).count({
1720
1848
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1721
1849
  })
@@ -1829,21 +1957,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1829
1957
  const displayName = contentType?.info?.displayName || target.uid;
1830
1958
  try {
1831
1959
  const isNested = attribute.includes(".");
1832
- let isComponentNested = false;
1833
- let parentKey = "";
1834
- let childKey = "";
1835
- if (isNested) {
1836
- const parts = attribute.split(".");
1837
- parentKey = parts[0];
1838
- childKey = parts[1];
1839
- const parentAttrDef = contentType?.attributes?.[parentKey];
1840
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
1841
- const compSchema = strapi2.components[parentAttrDef.component];
1842
- if (compSchema?.attributes?.[childKey]) {
1843
- isComponentNested = true;
1844
- }
1845
- }
1846
- }
1960
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1847
1961
  const attrDef = contentType?.attributes?.[attribute];
1848
1962
  const hasAttribute = Boolean(attrDef) || isComponentNested;
1849
1963
  if (!hasAttribute) {
@@ -1866,10 +1980,12 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1866
1980
  }
1867
1981
  let currentEntry;
1868
1982
  if (isComponentNested) {
1869
- currentEntry = await strapi2.documents(target.uid).findOne({
1870
- documentId: target.documentId,
1871
- populate: [parentKey]
1872
- });
1983
+ const findOneArgs = { documentId: target.documentId };
1984
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1985
+ if (populateOption) {
1986
+ findOneArgs.populate = populateOption;
1987
+ }
1988
+ currentEntry = await strapi2.documents(target.uid).findOne(findOneArgs);
1873
1989
  } else if (attrDef?.type === "relation") {
1874
1990
  currentEntry = await strapi2.documents(target.uid).findOne({
1875
1991
  documentId: target.documentId,
@@ -1882,8 +1998,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1882
1998
  }
1883
1999
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
1884
2000
  if (isComponentNested) {
1885
- const existingParent = currentEntry?.[parentKey] || {};
1886
- const currentVal2 = existingParent?.[childKey];
2001
+ const currentVal2 = getDeepProperty(currentEntry, attribute);
1887
2002
  let newValue2 = value;
1888
2003
  if (regexObj && typeof currentVal2 === "string") {
1889
2004
  newValue2 = currentVal2.replace(regexObj, value);
@@ -3619,15 +3734,20 @@ function discoverExportFields(ct, allComponents, maxDepth = 4) {
3619
3734
  return fields2;
3620
3735
  }
3621
3736
  function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
3622
- if (depth2 > 4) return true;
3737
+ if (depth2 > 5) return void 0;
3623
3738
  const populate2 = {};
3624
3739
  for (const [key, def] of Object.entries(attributes || {})) {
3625
3740
  if (def.type === "component" && def.component) {
3626
3741
  const comp = allComponents[def.component];
3627
3742
  if (comp?.attributes) {
3628
- populate2[key] = {
3629
- populate: buildDeepPopulate(comp.attributes, allComponents, depth2 + 1)
3630
- };
3743
+ const subPopulate = buildDeepPopulate(comp.attributes, allComponents, depth2 + 1);
3744
+ if (subPopulate && Object.keys(subPopulate).length > 0) {
3745
+ populate2[key] = {
3746
+ populate: subPopulate
3747
+ };
3748
+ } else {
3749
+ populate2[key] = true;
3750
+ }
3631
3751
  } else {
3632
3752
  populate2[key] = true;
3633
3753
  }
@@ -3637,7 +3757,7 @@ function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
3637
3757
  populate2[key] = true;
3638
3758
  }
3639
3759
  }
3640
- return Object.keys(populate2).length > 0 ? populate2 : true;
3760
+ return Object.keys(populate2).length > 0 ? populate2 : void 0;
3641
3761
  }
3642
3762
  function getNestedExportValue(entry, path2) {
3643
3763
  if (!entry || !path2) return "";
@@ -3804,17 +3924,22 @@ const csvService = ({ strapi: strapi2 }) => ({
3804
3924
  const deepPopulate = buildDeepPopulate(ct.attributes || {}, allComponents);
3805
3925
  let entries = [];
3806
3926
  if (ct.kind === "singleType") {
3807
- const doc = await strapi2.documents(uid).findFirst({
3808
- populate: deepPopulate
3809
- });
3927
+ const findFirstArgs = {};
3928
+ if (deepPopulate) {
3929
+ findFirstArgs.populate = deepPopulate;
3930
+ }
3931
+ const doc = await strapi2.documents(uid).findFirst(findFirstArgs);
3810
3932
  entries = doc ? [doc] : [];
3811
3933
  } else {
3812
- entries = await strapi2.documents(uid).findMany({
3934
+ const findManyArgs = {
3813
3935
  filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
3814
3936
  limit: options.limit || 5e3,
3815
- sort: { createdAt: "desc" },
3816
- populate: deepPopulate
3817
- });
3937
+ sort: { createdAt: "desc" }
3938
+ };
3939
+ if (deepPopulate) {
3940
+ findManyArgs.populate = deepPopulate;
3941
+ }
3942
+ entries = await strapi2.documents(uid).findMany(findManyArgs);
3818
3943
  }
3819
3944
  const lines = [];
3820
3945
  lines.push(fields2.map((f) => escapeCsvValue(f.key)).join(","));