@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.
- package/dist/export/batchProcessor.d.ts +9 -1
- package/dist/export/batchProcessor.d.ts.map +1 -1
- package/dist/export/batchProcessor.js +57 -15
- package/dist/export/batchProcessor.js.map +1 -1
- package/dist/export/createExport.d.ts.map +1 -1
- package/dist/export/createExport.js +98 -20
- package/dist/export/createExport.js.map +1 -1
- package/dist/export/handlePreview.d.ts.map +1 -1
- package/dist/export/handlePreview.js +38 -13
- package/dist/export/handlePreview.js.map +1 -1
- package/dist/exports/types.d.ts +1 -1
- package/dist/exports/types.d.ts.map +1 -1
- package/dist/exports/types.js.map +1 -1
- package/dist/import/batchProcessor.d.ts +14 -2
- package/dist/import/batchProcessor.d.ts.map +1 -1
- package/dist/import/batchProcessor.js +49 -27
- package/dist/import/batchProcessor.js.map +1 -1
- package/dist/import/createImport.d.ts +1 -10
- package/dist/import/createImport.d.ts.map +1 -1
- package/dist/import/createImport.js +33 -52
- package/dist/import/createImport.js.map +1 -1
- package/dist/import/handlePreview.d.ts.map +1 -1
- package/dist/import/handlePreview.js +32 -6
- package/dist/import/handlePreview.js.map +1 -1
- package/dist/index.d.ts +58 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +218 -39
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/applyFieldHooks.d.ts +23 -0
- package/dist/utilities/applyFieldHooks.d.ts.map +1 -0
- package/dist/utilities/applyFieldHooks.js +118 -0
- package/dist/utilities/applyFieldHooks.js.map +1 -0
- package/dist/utilities/applyFieldHooks.spec.js +205 -0
- package/dist/utilities/applyFieldHooks.spec.js.map +1 -0
- package/dist/utilities/collectDisabledFieldPaths.d.ts.map +1 -1
- package/dist/utilities/collectDisabledFieldPaths.js +1 -1
- package/dist/utilities/collectDisabledFieldPaths.js.map +1 -1
- package/dist/utilities/flattenObject.d.ts +8 -6
- package/dist/utilities/flattenObject.d.ts.map +1 -1
- package/dist/utilities/flattenObject.js +95 -75
- package/dist/utilities/flattenObject.js.map +1 -1
- package/dist/utilities/flattenObject.spec.js +158 -0
- package/dist/utilities/flattenObject.spec.js.map +1 -0
- package/dist/utilities/flattenedFields.d.ts +21 -0
- package/dist/utilities/flattenedFields.d.ts.map +1 -0
- package/dist/utilities/flattenedFields.js +34 -0
- package/dist/utilities/flattenedFields.js.map +1 -0
- package/dist/utilities/getExportFieldFunctions.d.ts +5 -5
- package/dist/utilities/getExportFieldFunctions.d.ts.map +1 -1
- package/dist/utilities/getExportFieldFunctions.js +92 -98
- package/dist/utilities/getExportFieldFunctions.js.map +1 -1
- package/dist/utilities/getExportFieldFunctions.spec.js +50 -0
- package/dist/utilities/getExportFieldFunctions.spec.js.map +1 -0
- package/dist/utilities/getImportFieldFunctions.d.ts +5 -5
- package/dist/utilities/getImportFieldFunctions.d.ts.map +1 -1
- package/dist/utilities/getImportFieldFunctions.js +103 -103
- package/dist/utilities/getImportFieldFunctions.js.map +1 -1
- package/dist/utilities/getImportFieldFunctions.spec.js +167 -0
- package/dist/utilities/getImportFieldFunctions.spec.js.map +1 -0
- package/dist/utilities/isPlainObject.d.ts +2 -0
- package/dist/utilities/isPlainObject.d.ts.map +1 -0
- package/dist/utilities/isPlainObject.js +3 -0
- package/dist/utilities/isPlainObject.js.map +1 -0
- package/dist/utilities/legacyHookDispatch.spec.js +227 -0
- package/dist/utilities/legacyHookDispatch.spec.js.map +1 -0
- package/dist/utilities/polymorphicRel.d.ts +14 -0
- package/dist/utilities/polymorphicRel.d.ts.map +1 -0
- package/dist/utilities/polymorphicRel.js +17 -0
- package/dist/utilities/polymorphicRel.js.map +1 -0
- package/dist/utilities/processRichTextField.js.map +1 -1
- package/dist/utilities/removeDisabledFields.js.map +1 -1
- package/dist/utilities/setNestedValue.d.ts.map +1 -1
- package/dist/utilities/setNestedValue.js +10 -8
- package/dist/utilities/setNestedValue.js.map +1 -1
- package/dist/utilities/siblingDoc.spec.js +278 -0
- package/dist/utilities/siblingDoc.spec.js.map +1 -0
- package/dist/utilities/unflattenObject.d.ts +4 -3
- package/dist/utilities/unflattenObject.d.ts.map +1 -1
- package/dist/utilities/unflattenObject.js +57 -169
- package/dist/utilities/unflattenObject.js.map +1 -1
- package/dist/utilities/unflattenObject.spec.js +33 -0
- package/dist/utilities/unflattenObject.spec.js.map +1 -1
- package/dist/utilities/unflattenPostProcess.d.ts +11 -0
- package/dist/utilities/unflattenPostProcess.d.ts.map +1 -0
- package/dist/utilities/unflattenPostProcess.js +148 -0
- package/dist/utilities/unflattenPostProcess.js.map +1 -0
- 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;
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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(
|
|
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
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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(
|
|
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:
|
|
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;
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
129
|
-
|
|
129
|
+
findArgs,
|
|
130
|
+
msg: 'Find arguments:'
|
|
130
131
|
});
|
|
131
132
|
}
|
|
132
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
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){
|