@payloadcms/plugin-import-export 3.84.1 → 3.85.0

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 (90) hide show
  1. package/dist/export/batchProcessor.d.ts +9 -1
  2. package/dist/export/batchProcessor.d.ts.map +1 -1
  3. package/dist/export/batchProcessor.js +57 -15
  4. package/dist/export/batchProcessor.js.map +1 -1
  5. package/dist/export/createExport.d.ts.map +1 -1
  6. package/dist/export/createExport.js +98 -20
  7. package/dist/export/createExport.js.map +1 -1
  8. package/dist/export/handlePreview.d.ts.map +1 -1
  9. package/dist/export/handlePreview.js +38 -13
  10. package/dist/export/handlePreview.js.map +1 -1
  11. package/dist/exports/types.d.ts +1 -1
  12. package/dist/exports/types.d.ts.map +1 -1
  13. package/dist/exports/types.js.map +1 -1
  14. package/dist/import/batchProcessor.d.ts +14 -2
  15. package/dist/import/batchProcessor.d.ts.map +1 -1
  16. package/dist/import/batchProcessor.js +49 -27
  17. package/dist/import/batchProcessor.js.map +1 -1
  18. package/dist/import/createImport.d.ts +1 -10
  19. package/dist/import/createImport.d.ts.map +1 -1
  20. package/dist/import/createImport.js +33 -52
  21. package/dist/import/createImport.js.map +1 -1
  22. package/dist/import/handlePreview.d.ts.map +1 -1
  23. package/dist/import/handlePreview.js +32 -6
  24. package/dist/import/handlePreview.js.map +1 -1
  25. package/dist/index.d.ts +58 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +18 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/types.d.ts +218 -39
  30. package/dist/types.d.ts.map +1 -1
  31. package/dist/types.js.map +1 -1
  32. package/dist/utilities/applyFieldHooks.d.ts +23 -0
  33. package/dist/utilities/applyFieldHooks.d.ts.map +1 -0
  34. package/dist/utilities/applyFieldHooks.js +118 -0
  35. package/dist/utilities/applyFieldHooks.js.map +1 -0
  36. package/dist/utilities/applyFieldHooks.spec.js +205 -0
  37. package/dist/utilities/applyFieldHooks.spec.js.map +1 -0
  38. package/dist/utilities/collectDisabledFieldPaths.d.ts.map +1 -1
  39. package/dist/utilities/collectDisabledFieldPaths.js +1 -1
  40. package/dist/utilities/collectDisabledFieldPaths.js.map +1 -1
  41. package/dist/utilities/flattenObject.d.ts +8 -6
  42. package/dist/utilities/flattenObject.d.ts.map +1 -1
  43. package/dist/utilities/flattenObject.js +95 -75
  44. package/dist/utilities/flattenObject.js.map +1 -1
  45. package/dist/utilities/flattenObject.spec.js +158 -0
  46. package/dist/utilities/flattenObject.spec.js.map +1 -0
  47. package/dist/utilities/flattenedFields.d.ts +21 -0
  48. package/dist/utilities/flattenedFields.d.ts.map +1 -0
  49. package/dist/utilities/flattenedFields.js +34 -0
  50. package/dist/utilities/flattenedFields.js.map +1 -0
  51. package/dist/utilities/getExportFieldFunctions.d.ts +5 -5
  52. package/dist/utilities/getExportFieldFunctions.d.ts.map +1 -1
  53. package/dist/utilities/getExportFieldFunctions.js +92 -98
  54. package/dist/utilities/getExportFieldFunctions.js.map +1 -1
  55. package/dist/utilities/getExportFieldFunctions.spec.js +50 -0
  56. package/dist/utilities/getExportFieldFunctions.spec.js.map +1 -0
  57. package/dist/utilities/getImportFieldFunctions.d.ts +5 -5
  58. package/dist/utilities/getImportFieldFunctions.d.ts.map +1 -1
  59. package/dist/utilities/getImportFieldFunctions.js +103 -103
  60. package/dist/utilities/getImportFieldFunctions.js.map +1 -1
  61. package/dist/utilities/getImportFieldFunctions.spec.js +167 -0
  62. package/dist/utilities/getImportFieldFunctions.spec.js.map +1 -0
  63. package/dist/utilities/isPlainObject.d.ts +2 -0
  64. package/dist/utilities/isPlainObject.d.ts.map +1 -0
  65. package/dist/utilities/isPlainObject.js +3 -0
  66. package/dist/utilities/isPlainObject.js.map +1 -0
  67. package/dist/utilities/legacyHookDispatch.spec.js +227 -0
  68. package/dist/utilities/legacyHookDispatch.spec.js.map +1 -0
  69. package/dist/utilities/polymorphicRel.d.ts +14 -0
  70. package/dist/utilities/polymorphicRel.d.ts.map +1 -0
  71. package/dist/utilities/polymorphicRel.js +17 -0
  72. package/dist/utilities/polymorphicRel.js.map +1 -0
  73. package/dist/utilities/processRichTextField.js.map +1 -1
  74. package/dist/utilities/removeDisabledFields.js.map +1 -1
  75. package/dist/utilities/setNestedValue.d.ts.map +1 -1
  76. package/dist/utilities/setNestedValue.js +10 -8
  77. package/dist/utilities/setNestedValue.js.map +1 -1
  78. package/dist/utilities/siblingDoc.spec.js +278 -0
  79. package/dist/utilities/siblingDoc.spec.js.map +1 -0
  80. package/dist/utilities/unflattenObject.d.ts +4 -3
  81. package/dist/utilities/unflattenObject.d.ts.map +1 -1
  82. package/dist/utilities/unflattenObject.js +57 -169
  83. package/dist/utilities/unflattenObject.js.map +1 -1
  84. package/dist/utilities/unflattenObject.spec.js +33 -0
  85. package/dist/utilities/unflattenObject.spec.js.map +1 -1
  86. package/dist/utilities/unflattenPostProcess.d.ts +11 -0
  87. package/dist/utilities/unflattenPostProcess.d.ts.map +1 -0
  88. package/dist/utilities/unflattenPostProcess.js +148 -0
  89. package/dist/utilities/unflattenPostProcess.js.map +1 -0
  90. package/package.json +7 -7
@@ -3,6 +3,7 @@
3
3
  * Uses the generic batch processing utilities from useBatchProcessor.
4
4
  */
5
5
  import type { PayloadRequest, SelectType, Sort, TypedUser, Where } from 'payload';
6
+ import type { ExportAfterHook, ExportBeforeHook } from '../types.js';
6
7
  import { type BatchProcessorOptions } from '../utilities/useBatchProcessor.js';
7
8
  /**
8
9
  * Export-specific batch processor options
@@ -42,6 +43,11 @@ export interface ExportProcessOptions<TDoc = unknown> {
42
43
  * The export format - affects column tracking for CSV
43
44
  */
44
45
  format: 'csv' | 'json';
46
+ /** Lifecycle hooks for this export operation */
47
+ hooks?: {
48
+ after?: ExportAfterHook;
49
+ before?: ExportBeforeHook;
50
+ };
45
51
  /**
46
52
  * Maximum number of documents to export
47
53
  */
@@ -54,6 +60,8 @@ export interface ExportProcessOptions<TDoc = unknown> {
54
60
  * Starting page for pagination (default: 1)
55
61
  */
56
62
  startPage?: number;
63
+ /** Total number of docs available (used to compute totalBatches for hooks) */
64
+ totalDocs?: number;
57
65
  /**
58
66
  * Transform function to apply to each document
59
67
  */
@@ -92,7 +100,7 @@ export interface ExportResult {
92
100
  * format: 'csv',
93
101
  * maxDocs: 1000,
94
102
  * req,
95
- * transformDoc: (doc) => flattenObject({ doc }),
103
+ * transformDoc: (doc) => flattenObject({ data: doc }),
96
104
  * })
97
105
  * ```
98
106
  */
@@ -1 +1 @@
1
- {"version":3,"file":"batchProcessor.d.ts","sourceRoot":"","sources":["../../src/export/batchProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEjF,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAE9E;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,qBAAqB;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,OAAO,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,IAAI,GAAG,OAAO;IAClD;;OAEG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;IACxB;;OAEG;IACH,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,GAAG,EAAE,cAAc,CAAA;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC;sBAiJnD,IAAI,kBACjB,oBAAoB,CAAC,IAAI,CAAC,KACzC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAzIS,IAAI,kBACf,oBAAoB,CAAC,IAAI,CAAC,KACzC,OAAO,CAAC,YAAY,CAAC;mBAsEK,IAAI,kBACf,oBAAoB,CAAC,IAAI,CAAC,KACzC,cAAc,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC;EAwH1E"}
1
+ {"version":3,"file":"batchProcessor.d.ts","sourceRoot":"","sources":["../../src/export/batchProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEjF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAE9E;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,qBAAqB;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,OAAO,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,IAAI,GAAG,OAAO;IAClD;;OAEG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;IACxB;;OAEG;IACH,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,gDAAgD;IAChD,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,eAAe,CAAA;QACvB,MAAM,CAAC,EAAE,gBAAgB,CAAA;KAC1B,CAAA;IACD;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,GAAG,EAAE,cAAc,CAAA;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/B;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC;sBA6NnD,IAAI,kBACjB,oBAAoB,CAAC,IAAI,CAAC,KACzC,OAAO,CAAC,MAAM,EAAE,CAAC;oBA7MS,IAAI,kBACf,oBAAoB,CAAC,IAAI,CAAC,KACzC,OAAO,CAAC,YAAY,CAAC;mBA0GK,IAAI,kBACf,oBAAoB,CAAC,IAAI,CAAC,KACzC,cAAc,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC;EAwJ1E"}
@@ -17,19 +17,24 @@
17
17
  * format: 'csv',
18
18
  * maxDocs: 1000,
19
19
  * req,
20
- * transformDoc: (doc) => flattenObject({ doc }),
20
+ * transformDoc: (doc) => flattenObject({ data: doc }),
21
21
  * })
22
22
  * ```
23
23
  */ export function createExportBatchProcessor(options = {}) {
24
24
  const batchSize = options.batchSize ?? 100;
25
25
  const debug = options.debug ?? false;
26
+ const computeTotalBatches = (totalDocs, maxDocs)=>{
27
+ const effectiveDocs = totalDocs !== undefined ? Math.min(totalDocs, maxDocs === Number.POSITIVE_INFINITY ? totalDocs : maxDocs) : 0;
28
+ return effectiveDocs > 0 ? Math.ceil(effectiveDocs / batchSize) : 1;
29
+ };
26
30
  /**
27
31
  * Process an export operation by fetching and transforming documents in batches.
28
32
  *
29
33
  * @param processOptions - Options for the export operation
30
34
  * @returns The export result containing transformed documents and column info
31
35
  */ const processExport = async (processOptions)=>{
32
- const { findArgs, format, maxDocs, req, startPage = 1, transformDoc } = processOptions;
36
+ const { findArgs, format, hooks, maxDocs, req, startPage = 1, totalDocs, transformDoc } = processOptions;
37
+ const totalBatches = computeTotalBatches(totalDocs, maxDocs);
33
38
  const docs = [];
34
39
  const columnsSet = new Set();
35
40
  const columns = [];
@@ -49,12 +54,21 @@
49
54
  if (debug) {
50
55
  req.payload.logger.debug(`Processing export batch ${currentPage} with ${result.docs.length} documents`);
51
56
  }
52
- for (const doc of result.docs){
53
- const transformedDoc = transformDoc(doc);
54
- docs.push(transformedDoc);
55
- // Track columns for CSV format
57
+ const batchNumber = currentPage - startPage + 1;
58
+ const originalDocs = result.docs;
59
+ const batchData = result.docs.map((doc)=>transformDoc(doc));
60
+ const dataToWrite = hooks?.before && batchData.length > 0 ? await hooks.before({
61
+ batchNumber,
62
+ data: batchData,
63
+ format,
64
+ originalData: originalDocs,
65
+ req,
66
+ totalBatches
67
+ }) : batchData;
68
+ for (const row of dataToWrite){
69
+ docs.push(row);
56
70
  if (format === 'csv') {
57
- for (const key of Object.keys(transformedDoc)){
71
+ for (const key of Object.keys(row)){
58
72
  if (!columnsSet.has(key)) {
59
73
  columnsSet.add(key);
60
74
  columns.push(key);
@@ -62,6 +76,16 @@
62
76
  }
63
77
  }
64
78
  }
79
+ if (hooks?.after && dataToWrite.length > 0) {
80
+ await hooks.after({
81
+ batchNumber,
82
+ data: dataToWrite,
83
+ format,
84
+ originalData: originalDocs,
85
+ req,
86
+ totalBatches
87
+ });
88
+ }
65
89
  fetched += result.docs.length;
66
90
  hasNextPage = result.hasNextPage && fetched < maxDocs;
67
91
  currentPage++;
@@ -89,7 +113,8 @@
89
113
  * }
90
114
  * ```
91
115
  */ async function* streamExport(processOptions) {
92
- const { findArgs, format, maxDocs, req, startPage = 1, transformDoc } = processOptions;
116
+ const { findArgs, format, hooks, maxDocs, req, startPage = 1, totalDocs, transformDoc } = processOptions;
117
+ const totalBatches = computeTotalBatches(totalDocs, maxDocs);
93
118
  const columnsSet = new Set();
94
119
  const columns = [];
95
120
  let currentPage = startPage;
@@ -108,13 +133,20 @@
108
133
  if (debug) {
109
134
  req.payload.logger.debug(`Streaming export batch ${currentPage} with ${result.docs.length} documents`);
110
135
  }
111
- const batchDocs = [];
112
- for (const doc of result.docs){
113
- const transformedDoc = transformDoc(doc);
114
- batchDocs.push(transformedDoc);
115
- // Track columns for CSV format
136
+ const batchNumber = currentPage - startPage + 1;
137
+ const originalDocs = result.docs;
138
+ const batchData = result.docs.map((doc)=>transformDoc(doc));
139
+ const dataToWrite = hooks?.before && batchData.length > 0 ? await hooks.before({
140
+ batchNumber,
141
+ data: batchData,
142
+ format,
143
+ originalData: originalDocs,
144
+ req,
145
+ totalBatches
146
+ }) : batchData;
147
+ for (const row of dataToWrite){
116
148
  if (format === 'csv') {
117
- for (const key of Object.keys(transformedDoc)){
149
+ for (const key of Object.keys(row)){
118
150
  if (!columnsSet.has(key)) {
119
151
  columnsSet.add(key);
120
152
  columns.push(key);
@@ -126,8 +158,18 @@
126
158
  columns: [
127
159
  ...columns
128
160
  ],
129
- docs: batchDocs
161
+ docs: dataToWrite
130
162
  };
163
+ if (hooks?.after && dataToWrite.length > 0) {
164
+ await hooks.after({
165
+ batchNumber,
166
+ data: dataToWrite,
167
+ format,
168
+ originalData: originalDocs,
169
+ req,
170
+ totalBatches
171
+ });
172
+ }
131
173
  fetched += result.docs.length;
132
174
  hasNextPage = result.hasNextPage && fetched < maxDocs;
133
175
  currentPage++;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/export/batchProcessor.ts"],"sourcesContent":["/**\n * Export-specific batch processor for processing documents in batches during export.\n * Uses the generic batch processing utilities from useBatchProcessor.\n */\nimport type { PayloadRequest, SelectType, Sort, TypedUser, Where } from 'payload'\n\nimport { type BatchProcessorOptions } from '../utilities/useBatchProcessor.js'\n\n/**\n * Export-specific batch processor options\n */\nexport interface ExportBatchProcessorOptions extends BatchProcessorOptions {\n debug?: boolean\n}\n\n/**\n * Find arguments for querying documents during export\n */\nexport interface ExportFindArgs {\n collection: string\n depth: number\n draft: boolean\n limit: number\n locale?: string\n overrideAccess: boolean\n page?: number\n select?: SelectType\n sort?: Sort\n user?: TypedUser\n where?: Where\n}\n\n/**\n * Options for processing an export operation\n */\nexport interface ExportProcessOptions<TDoc = unknown> {\n /**\n * The slug of the collection to export\n */\n collectionSlug: string\n /**\n * Arguments to pass to payload.find()\n */\n findArgs: ExportFindArgs\n /**\n * The export format - affects column tracking for CSV\n */\n format: 'csv' | 'json'\n /**\n * Maximum number of documents to export\n */\n maxDocs: number\n /**\n * The Payload request object\n */\n req: PayloadRequest\n /**\n * Starting page for pagination (default: 1)\n */\n startPage?: number\n /**\n * Transform function to apply to each document\n */\n transformDoc: (doc: TDoc) => Record<string, unknown>\n}\n\n/**\n * Result from processing an export operation\n */\nexport interface ExportResult {\n /**\n * Discovered column names (for CSV exports)\n */\n columns: string[]\n /**\n * Transformed documents ready for output\n */\n docs: Record<string, unknown>[]\n /**\n * Total number of documents fetched\n */\n fetchedCount: number\n}\n\n/**\n * Creates an export batch processor with the specified options.\n *\n * @param options - Configuration options for the batch processor\n * @returns An object containing the processExport method\n *\n * @example\n * ```ts\n * const processor = createExportBatchProcessor({ batchSize: 100, debug: true })\n *\n * const result = await processor.processExport({\n * collectionSlug: 'posts',\n * findArgs: { collection: 'posts', depth: 1, draft: false, limit: 100, overrideAccess: false },\n * format: 'csv',\n * maxDocs: 1000,\n * req,\n * transformDoc: (doc) => flattenObject({ doc }),\n * })\n * ```\n */\nexport function createExportBatchProcessor(options: ExportBatchProcessorOptions = {}) {\n const batchSize = options.batchSize ?? 100\n const debug = options.debug ?? false\n\n /**\n * Process an export operation by fetching and transforming documents in batches.\n *\n * @param processOptions - Options for the export operation\n * @returns The export result containing transformed documents and column info\n */\n const processExport = async <TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): Promise<ExportResult> => {\n const { findArgs, format, maxDocs, req, startPage = 1, transformDoc } = processOptions\n\n const docs: Record<string, unknown>[] = []\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Processing export batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n for (const doc of result.docs) {\n const transformedDoc = transformDoc(doc as TDoc)\n docs.push(transformedDoc)\n\n // Track columns for CSV format\n if (format === 'csv') {\n for (const key of Object.keys(transformedDoc)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n\n return { columns, docs, fetchedCount: fetched }\n }\n\n /**\n * Async generator for streaming export - yields batches instead of collecting all.\n * Useful for streaming exports where you want to process batches as they're fetched.\n *\n * @param processOptions - Options for the export operation\n * @yields Batch results containing transformed documents and discovered columns\n *\n * @example\n * ```ts\n * const processor = createExportBatchProcessor({ batchSize: 100 })\n *\n * for await (const batch of processor.streamExport({ ... })) {\n * // Process each batch as it's yielded\n * console.log(`Got ${batch.docs.length} documents`)\n * }\n * ```\n */\n async function* streamExport<TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): AsyncGenerator<{ columns: string[]; docs: Record<string, unknown>[] }> {\n const { findArgs, format, maxDocs, req, startPage = 1, transformDoc } = processOptions\n\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Streaming export batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n const batchDocs: Record<string, unknown>[] = []\n\n for (const doc of result.docs) {\n const transformedDoc = transformDoc(doc as TDoc)\n batchDocs.push(transformedDoc)\n\n // Track columns for CSV format\n if (format === 'csv') {\n for (const key of Object.keys(transformedDoc)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n }\n\n yield { columns: [...columns], docs: batchDocs }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n }\n\n /**\n * Discover all columns from the dataset by scanning through all batches.\n * Useful for CSV exports where you need to know all columns before streaming.\n *\n * @param processOptions - Options for the export operation\n * @returns Array of discovered column names\n */\n const discoverColumns = async <TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): Promise<string[]> => {\n const { findArgs, maxDocs, req, startPage = 1, transformDoc } = processOptions\n\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Scanning columns from batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n for (const doc of result.docs) {\n const transformedDoc = transformDoc(doc as TDoc)\n\n for (const key of Object.keys(transformedDoc)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n\n if (debug) {\n req.payload.logger.debug(`Discovered ${columns.length} columns`)\n }\n\n return columns\n }\n\n return {\n discoverColumns,\n processExport,\n streamExport,\n }\n}\n"],"names":["createExportBatchProcessor","options","batchSize","debug","processExport","processOptions","findArgs","format","maxDocs","req","startPage","transformDoc","docs","columnsSet","Set","columns","currentPage","fetched","hasNextPage","remaining","Math","max","result","payload","find","limit","min","page","logger","length","doc","transformedDoc","push","key","Object","keys","has","add","fetchedCount","streamExport","batchDocs","discoverColumns"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/export/batchProcessor.ts"],"sourcesContent":["/**\n * Export-specific batch processor for processing documents in batches during export.\n * Uses the generic batch processing utilities from useBatchProcessor.\n */\nimport type { PayloadRequest, SelectType, Sort, TypedUser, Where } from 'payload'\n\nimport type { ExportAfterHook, ExportBeforeHook } from '../types.js'\n\nimport { type BatchProcessorOptions } from '../utilities/useBatchProcessor.js'\n\n/**\n * Export-specific batch processor options\n */\nexport interface ExportBatchProcessorOptions extends BatchProcessorOptions {\n debug?: boolean\n}\n\n/**\n * Find arguments for querying documents during export\n */\nexport interface ExportFindArgs {\n collection: string\n depth: number\n draft: boolean\n limit: number\n locale?: string\n overrideAccess: boolean\n page?: number\n select?: SelectType\n sort?: Sort\n user?: TypedUser\n where?: Where\n}\n\n/**\n * Options for processing an export operation\n */\nexport interface ExportProcessOptions<TDoc = unknown> {\n /**\n * The slug of the collection to export\n */\n collectionSlug: string\n /**\n * Arguments to pass to payload.find()\n */\n findArgs: ExportFindArgs\n /**\n * The export format - affects column tracking for CSV\n */\n format: 'csv' | 'json'\n /** Lifecycle hooks for this export operation */\n hooks?: {\n after?: ExportAfterHook\n before?: ExportBeforeHook\n }\n /**\n * Maximum number of documents to export\n */\n maxDocs: number\n /**\n * The Payload request object\n */\n req: PayloadRequest\n /**\n * Starting page for pagination (default: 1)\n */\n startPage?: number\n /** Total number of docs available (used to compute totalBatches for hooks) */\n totalDocs?: number\n /**\n * Transform function to apply to each document\n */\n transformDoc: (doc: TDoc) => Record<string, unknown>\n}\n\n/**\n * Result from processing an export operation\n */\nexport interface ExportResult {\n /**\n * Discovered column names (for CSV exports)\n */\n columns: string[]\n /**\n * Transformed documents ready for output\n */\n docs: Record<string, unknown>[]\n /**\n * Total number of documents fetched\n */\n fetchedCount: number\n}\n\n/**\n * Creates an export batch processor with the specified options.\n *\n * @param options - Configuration options for the batch processor\n * @returns An object containing the processExport method\n *\n * @example\n * ```ts\n * const processor = createExportBatchProcessor({ batchSize: 100, debug: true })\n *\n * const result = await processor.processExport({\n * collectionSlug: 'posts',\n * findArgs: { collection: 'posts', depth: 1, draft: false, limit: 100, overrideAccess: false },\n * format: 'csv',\n * maxDocs: 1000,\n * req,\n * transformDoc: (doc) => flattenObject({ data: doc }),\n * })\n * ```\n */\nexport function createExportBatchProcessor(options: ExportBatchProcessorOptions = {}) {\n const batchSize = options.batchSize ?? 100\n const debug = options.debug ?? false\n\n const computeTotalBatches = (totalDocs: number | undefined, maxDocs: number): number => {\n const effectiveDocs =\n totalDocs !== undefined\n ? Math.min(totalDocs, maxDocs === Number.POSITIVE_INFINITY ? totalDocs : maxDocs)\n : 0\n return effectiveDocs > 0 ? Math.ceil(effectiveDocs / batchSize) : 1\n }\n\n /**\n * Process an export operation by fetching and transforming documents in batches.\n *\n * @param processOptions - Options for the export operation\n * @returns The export result containing transformed documents and column info\n */\n const processExport = async <TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): Promise<ExportResult> => {\n const {\n findArgs,\n format,\n hooks,\n maxDocs,\n req,\n startPage = 1,\n totalDocs,\n transformDoc,\n } = processOptions\n\n const totalBatches = computeTotalBatches(totalDocs, maxDocs)\n\n const docs: Record<string, unknown>[] = []\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Processing export batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n const batchNumber = currentPage - startPage + 1\n const originalDocs = result.docs as Record<string, unknown>[]\n const batchData = result.docs.map((doc) => transformDoc(doc as TDoc))\n\n const dataToWrite =\n hooks?.before && batchData.length > 0\n ? await hooks.before({\n batchNumber,\n data: batchData,\n format,\n originalData: originalDocs,\n req,\n totalBatches,\n })\n : batchData\n\n for (const row of dataToWrite) {\n docs.push(row)\n\n if (format === 'csv') {\n for (const key of Object.keys(row)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n }\n\n if (hooks?.after && dataToWrite.length > 0) {\n await hooks.after({\n batchNumber,\n data: dataToWrite,\n format,\n originalData: originalDocs,\n req,\n totalBatches,\n })\n }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n\n return { columns, docs, fetchedCount: fetched }\n }\n\n /**\n * Async generator for streaming export - yields batches instead of collecting all.\n * Useful for streaming exports where you want to process batches as they're fetched.\n *\n * @param processOptions - Options for the export operation\n * @yields Batch results containing transformed documents and discovered columns\n *\n * @example\n * ```ts\n * const processor = createExportBatchProcessor({ batchSize: 100 })\n *\n * for await (const batch of processor.streamExport({ ... })) {\n * // Process each batch as it's yielded\n * console.log(`Got ${batch.docs.length} documents`)\n * }\n * ```\n */\n async function* streamExport<TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): AsyncGenerator<{ columns: string[]; docs: Record<string, unknown>[] }> {\n const {\n findArgs,\n format,\n hooks,\n maxDocs,\n req,\n startPage = 1,\n totalDocs,\n transformDoc,\n } = processOptions\n\n const totalBatches = computeTotalBatches(totalDocs, maxDocs)\n\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Streaming export batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n const batchNumber = currentPage - startPage + 1\n const originalDocs = result.docs as Record<string, unknown>[]\n const batchData = result.docs.map((doc) => transformDoc(doc as TDoc))\n\n const dataToWrite =\n hooks?.before && batchData.length > 0\n ? await hooks.before({\n batchNumber,\n data: batchData,\n format,\n originalData: originalDocs,\n req,\n totalBatches,\n })\n : batchData\n\n for (const row of dataToWrite) {\n if (format === 'csv') {\n for (const key of Object.keys(row)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n }\n\n yield { columns: [...columns], docs: dataToWrite }\n\n if (hooks?.after && dataToWrite.length > 0) {\n await hooks.after({\n batchNumber,\n data: dataToWrite,\n format,\n originalData: originalDocs,\n req,\n totalBatches,\n })\n }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n }\n\n /**\n * Discover all columns from the dataset by scanning through all batches.\n * Useful for CSV exports where you need to know all columns before streaming.\n *\n * @param processOptions - Options for the export operation\n * @returns Array of discovered column names\n */\n const discoverColumns = async <TDoc>(\n processOptions: ExportProcessOptions<TDoc>,\n ): Promise<string[]> => {\n const { findArgs, maxDocs, req, startPage = 1, transformDoc } = processOptions\n\n const columnsSet = new Set<string>()\n const columns: string[] = []\n\n let currentPage = startPage\n let fetched = 0\n let hasNextPage = true\n\n while (hasNextPage) {\n const remaining = Math.max(0, maxDocs - fetched)\n\n if (remaining === 0) {\n break\n }\n\n const result = await req.payload.find({\n ...findArgs,\n limit: Math.min(batchSize, remaining),\n page: currentPage,\n })\n\n if (debug) {\n req.payload.logger.debug(\n `Scanning columns from batch ${currentPage} with ${result.docs.length} documents`,\n )\n }\n\n for (const doc of result.docs) {\n const transformedDoc = transformDoc(doc as TDoc)\n\n for (const key of Object.keys(transformedDoc)) {\n if (!columnsSet.has(key)) {\n columnsSet.add(key)\n columns.push(key)\n }\n }\n }\n\n fetched += result.docs.length\n hasNextPage = result.hasNextPage && fetched < maxDocs\n currentPage++\n }\n\n if (debug) {\n req.payload.logger.debug(`Discovered ${columns.length} columns`)\n }\n\n return columns\n }\n\n return {\n discoverColumns,\n processExport,\n streamExport,\n }\n}\n"],"names":["createExportBatchProcessor","options","batchSize","debug","computeTotalBatches","totalDocs","maxDocs","effectiveDocs","undefined","Math","min","Number","POSITIVE_INFINITY","ceil","processExport","processOptions","findArgs","format","hooks","req","startPage","transformDoc","totalBatches","docs","columnsSet","Set","columns","currentPage","fetched","hasNextPage","remaining","max","result","payload","find","limit","page","logger","length","batchNumber","originalDocs","batchData","map","doc","dataToWrite","before","data","originalData","row","push","key","Object","keys","has","add","after","fetchedCount","streamExport","discoverColumns","transformedDoc"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"createExport.d.ts","sourceRoot":"","sources":["../../src/export/createExport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAa,KAAK,EAAE,MAAM,SAAS,CAAA;AAerE,MAAM,MAAM,MAAM,GAAG;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,GAAG,KAAK,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,EAAE,cAAc,CAAA;CACpB,GAAG,MAAM,CAAA;AAEV,eAAO,MAAM,YAAY,SAAgB,gBAAgB,kCA8fxD,CAAA"}
1
+ {"version":3,"file":"createExport.d.ts","sourceRoot":"","sources":["../../src/export/createExport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAa,KAAK,EAAE,MAAM,SAAS,CAAA;AAgBrE,MAAM,MAAM,MAAM,GAAG;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,GAAG,KAAK,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,EAAE,cAAc,CAAA;CACpB,GAAG,MAAM,CAAA;AAEV,eAAO,MAAM,YAAY,SAAgB,gBAAgB,kCAgmBxD,CAAA"}
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable perfectionist/sort-objects */ import { stringify } from 'csv-stringify/sync';
2
2
  import { APIError } from 'payload';
3
3
  import { Readable } from 'stream';
4
+ import { applyFieldHooks } from '../utilities/applyFieldHooks.js';
4
5
  import { buildDisabledFieldRegex } from '../utilities/buildDisabledFieldRegex.js';
5
6
  import { flattenObject } from '../utilities/flattenObject.js';
6
7
  import { getExportFieldFunctions } from '../utilities/getExportFieldFunctions.js';
@@ -63,10 +64,10 @@ export const createExport = async (args)=>{
63
64
  const select = Array.isArray(fields) && fields.length > 0 ? getSelect(fields) : undefined;
64
65
  if (debug) {
65
66
  req.payload.logger.debug({
66
- message: 'Export configuration:',
67
- name,
68
67
  isCSV,
69
- locale
68
+ locale,
69
+ msg: 'Export configuration:',
70
+ name
70
71
  });
71
72
  }
72
73
  // Determine maximum export documents:
@@ -102,8 +103,8 @@ export const createExport = async (args)=>{
102
103
  accessDenied = true;
103
104
  if (debug) {
104
105
  req.payload.logger.debug({
105
- message: 'Access denied for collection, creating empty export',
106
- collectionSlug
106
+ collectionSlug,
107
+ msg: 'Access denied for collection, creating empty export'
107
108
  });
108
109
  }
109
110
  }
@@ -125,13 +126,14 @@ export const createExport = async (args)=>{
125
126
  };
126
127
  if (debug) {
127
128
  req.payload.logger.debug({
128
- message: 'Find arguments:',
129
- findArgs
129
+ findArgs,
130
+ msg: 'Find arguments:'
130
131
  });
131
132
  }
132
- const toCSVFunctions = getExportFieldFunctions({
133
+ const exportFieldHooks = getExportFieldFunctions({
133
134
  fields: collectionConfig.flattenedFields
134
135
  });
136
+ const exportHooks = collectionConfig.custom?.['plugin-import-export']?.exportHooks;
135
137
  const disabledFields = collectionConfig.admin?.custom?.['plugin-import-export']?.disabledFields ?? [];
136
138
  const disabledMatchers = disabledFields.map(buildDisabledFieldRegex);
137
139
  const filterDisabledCSV = (row)=>{
@@ -193,6 +195,9 @@ export const createExport = async (args)=>{
193
195
  let currentBatchPage = adjustedPage;
194
196
  let fetched = 0;
195
197
  const maxDocs = typeof maxExportDocuments === 'number' ? maxExportDocuments : Number.POSITIVE_INFINITY;
198
+ const effectiveStreamDocs = typeof maxExportDocuments === 'number' ? Math.min(totalDocs, maxExportDocuments) : totalDocs;
199
+ const totalBatches = effectiveStreamDocs > 0 ? Math.ceil(effectiveStreamDocs / batchSize) : 1;
200
+ let streamBatchNumber = 0;
196
201
  const stream = new Readable({
197
202
  async read () {
198
203
  const remaining = Math.max(0, maxDocs - fetched);
@@ -221,18 +226,33 @@ export const createExport = async (args)=>{
221
226
  this.push(null);
222
227
  return;
223
228
  }
229
+ streamBatchNumber++;
224
230
  if (isCSV) {
225
231
  // --- CSV Streaming ---
226
232
  const batchRows = result.docs.map((doc)=>filterDisabledCSV(flattenObject({
227
- doc,
233
+ data: doc,
228
234
  fields,
229
- toCSVFunctions
235
+ format,
236
+ exportFieldHooks,
237
+ req
230
238
  })));
239
+ const originalDocs = result.docs;
240
+ let batchRowsToWrite = batchRows;
241
+ if (exportHooks?.before && batchRows.length > 0) {
242
+ batchRowsToWrite = await exportHooks.before({
243
+ batchNumber: streamBatchNumber,
244
+ data: batchRows,
245
+ format,
246
+ originalData: originalDocs,
247
+ req,
248
+ totalBatches
249
+ });
250
+ }
231
251
  // On first batch, discover additional columns from data and merge with schema
232
252
  if (!columnsFinalized) {
233
253
  const dataColumns = [];
234
254
  const seenCols = new Set();
235
- for (const row of batchRows){
255
+ for (const row of batchRowsToWrite){
236
256
  for (const key of Object.keys(row)){
237
257
  if (!seenCols.has(key)) {
238
258
  seenCols.add(key);
@@ -240,8 +260,11 @@ export const createExport = async (args)=>{
240
260
  }
241
261
  }
242
262
  }
243
- // Merge schema columns with data-discovered columns
244
- allColumns = mergeColumns(schemaColumns, dataColumns);
263
+ // Merge schema columns with data-discovered columns.
264
+ // When a before hook is active and rows exist, restrict to columns actually present
265
+ // in the data so that the hook can remove fields by not returning them.
266
+ const mergedColumns = mergeColumns(schemaColumns, dataColumns);
267
+ allColumns = Boolean(exportHooks?.before) && batchRowsToWrite.length > 0 ? mergedColumns.filter((col)=>dataColumns.includes(col)) : mergedColumns;
245
268
  columnsFinalized = true;
246
269
  if (debug) {
247
270
  req.payload.logger.debug({
@@ -251,7 +274,7 @@ export const createExport = async (args)=>{
251
274
  });
252
275
  }
253
276
  }
254
- const paddedRows = batchRows.map((row)=>{
277
+ const paddedRows = batchRowsToWrite.map((row)=>{
255
278
  const fullRow = {};
256
279
  for (const col of allColumns){
257
280
  fullRow[col] = row[col] ?? '';
@@ -264,16 +287,56 @@ export const createExport = async (args)=>{
264
287
  columns: allColumns
265
288
  });
266
289
  this.push(encoder.encode(csvString));
290
+ if (exportHooks?.after && batchRowsToWrite.length > 0) {
291
+ await exportHooks.after({
292
+ batchNumber: streamBatchNumber,
293
+ data: batchRowsToWrite,
294
+ format,
295
+ originalData: originalDocs,
296
+ req,
297
+ totalBatches
298
+ });
299
+ }
267
300
  } else {
268
301
  // --- JSON Streaming ---
269
- const batchRows = result.docs.map((doc)=>filterDisabledJSON(doc));
302
+ const batchRows = result.docs.map((doc)=>filterDisabledJSON(applyFieldHooks({
303
+ data: doc,
304
+ fieldHooks: exportFieldHooks,
305
+ fields: collectionConfig.flattenedFields,
306
+ format,
307
+ operation: 'export',
308
+ req,
309
+ type: 'beforeExport'
310
+ })));
311
+ const originalDocs = result.docs;
312
+ let batchRowsToWrite = batchRows;
313
+ if (exportHooks?.before && batchRows.length > 0) {
314
+ batchRowsToWrite = await exportHooks.before({
315
+ batchNumber: streamBatchNumber,
316
+ data: batchRows,
317
+ format,
318
+ originalData: originalDocs,
319
+ req,
320
+ totalBatches
321
+ });
322
+ }
270
323
  // Convert each filtered/flattened row into JSON string
271
- const batchJSON = batchRows.map((row)=>JSON.stringify(row)).join(',');
324
+ const batchJSON = batchRowsToWrite.map((row)=>JSON.stringify(row)).join(',');
272
325
  if (isFirstBatch) {
273
326
  this.push(encoder.encode('[' + batchJSON));
274
327
  } else {
275
328
  this.push(encoder.encode(',' + batchJSON));
276
329
  }
330
+ if (exportHooks?.after && batchRowsToWrite.length > 0) {
331
+ await exportHooks.after({
332
+ batchNumber: streamBatchNumber,
333
+ data: batchRowsToWrite,
334
+ format,
335
+ originalData: originalDocs,
336
+ req,
337
+ totalBatches
338
+ });
339
+ }
277
340
  }
278
341
  fetched += result.docs.length;
279
342
  isFirstBatch = false;
@@ -307,10 +370,20 @@ export const createExport = async (args)=>{
307
370
  });
308
371
  // Transform function based on format
309
372
  const transformDoc = (doc)=>isCSV ? filterDisabledCSV(flattenObject({
310
- doc,
373
+ data: doc,
311
374
  fields,
312
- toCSVFunctions
313
- })) : filterDisabledJSON(doc);
375
+ format,
376
+ exportFieldHooks,
377
+ req
378
+ })) : filterDisabledJSON(applyFieldHooks({
379
+ data: doc,
380
+ fieldHooks: exportFieldHooks,
381
+ fields: collectionConfig.flattenedFields,
382
+ format,
383
+ operation: 'export',
384
+ req,
385
+ type: 'beforeExport'
386
+ }));
314
387
  // Skip fetching if access was denied - we'll create an empty export
315
388
  let exportResult = {
316
389
  columns: [],
@@ -322,9 +395,11 @@ export const createExport = async (args)=>{
322
395
  collectionSlug,
323
396
  findArgs: findArgs,
324
397
  format,
398
+ hooks: exportHooks,
325
399
  maxDocs: typeof maxExportDocuments === 'number' ? maxExportDocuments : Number.POSITIVE_INFINITY,
326
400
  req,
327
401
  startPage: adjustedPage,
402
+ totalDocs,
328
403
  transformDoc
329
404
  });
330
405
  }
@@ -343,7 +418,10 @@ export const createExport = async (args)=>{
343
418
  });
344
419
  // Merge schema columns with data-discovered columns
345
420
  // Schema provides ordering, data provides additional columns (e.g., array indices > 0)
346
- const finalColumns = mergeColumns(schemaColumns, dataColumns);
421
+ // When a before hook is active and rows exist, restrict to columns actually present in the data
422
+ // so that the hook can remove fields by not returning them
423
+ const mergedColumns = mergeColumns(schemaColumns, dataColumns);
424
+ const finalColumns = Boolean(exportHooks?.before) && rows.length > 0 ? mergedColumns.filter((col)=>dataColumns.includes(col)) : mergedColumns;
347
425
  const paddedRows = rows.map((row)=>{
348
426
  const fullRow = {};
349
427
  for (const col of finalColumns){