procbay-schema 1.0.58 → 1.0.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "procbay-schema",
3
- "version": "1.0.58",
3
+ "version": "1.0.60",
4
4
  "description": "A set of utilities for managing Prisma database schemas, seeding, and maintenance operations for the Procure-to-Pay system",
5
5
  "main": "src/prisma/index.js",
6
6
  "type": "module",
@@ -0,0 +1,2 @@
1
+ -- AlterTable
2
+ ALTER TABLE "export_logs" ALTER COLUMN "expires_at" SET DEFAULT NOW() + INTERVAL '7 days';
@@ -23,7 +23,7 @@ export async function seedIndustries(prismaClient) {
23
23
  const prisma = prismaClient || new PrismaClient();
24
24
 
25
25
  try {
26
- // Load categories from JSON
26
+ // Load industries from JSON
27
27
  const industryData = loadContentData("industries");
28
28
 
29
29
  if (!industryData || industryData.length === 0) {
@@ -40,7 +40,7 @@ export async function seedIndustries(prismaClient) {
40
40
  enableMemoryMonitoring: true,
41
41
  batchUpsertMode: true,
42
42
  // Industry-specific unique fields for constraint mapping
43
- uniqueFields: ["name", "slug"],
43
+ uniqueFields: ["name"],
44
44
  // Medium batch size as specified (500-1000)
45
45
  batchSize: 750,
46
46
  // Memory monitoring configuration
@@ -60,29 +60,24 @@ export async function seedIndustries(prismaClient) {
60
60
 
61
61
  for (const industry of chunk) {
62
62
  try {
63
- // Ensure parent_id defaults to 0 if not provided
64
- const industryRecord = {
65
- ...industry,
66
- };
67
-
68
63
  await client.industry.upsert({
69
64
  where: {
70
- name: industryRecord.name,
65
+ name: industry.name,
71
66
  },
72
67
  update: {
73
- slug: industryRecord.slug,
74
- description: industryRecord.description,
75
- is_active: industryRecord.is_active,
76
- created_by: industryRecord.created_by,
77
- updated_by: industryRecord.updated_by,
68
+ slug: industry.slug,
69
+ description: industry.description,
70
+ is_active: industry.is_active,
71
+ created_by: industry.created_by,
72
+ updated_by: industry.updated_by,
78
73
  },
79
74
  create: {
80
- name: industryRecord.name,
81
- slug: industryRecord.slug,
82
- description: industryRecord.description,
83
- is_active: industryRecord.is_active,
84
- created_by: industryRecord.created_by,
85
- updated_by: industryRecord.updated_by,
75
+ name: industry.name,
76
+ slug: industry.slug,
77
+ description: industry.description,
78
+ is_active: industry.is_active,
79
+ created_by: industry.created_by,
80
+ updated_by: industry.updated_by,
86
81
  },
87
82
  });
88
83
  created++;
@@ -1,7 +1,11 @@
1
1
  import colors from "colors";
2
2
  import { connectionManager, withContentPool } from "./connection-manager.js";
3
3
  import { MemoryManager } from "./performance-helpers.js";
4
- import { logSeederStart, logSeederComplete, logSeederError } from "./progress-tracker.js";
4
+ import {
5
+ logSeederStart,
6
+ logSeederComplete,
7
+ logSeederError,
8
+ } from "./progress-tracker.js";
5
9
 
6
10
  /**
7
11
  * Enhanced content seeder for medium-volume content data (500-1000 records)
@@ -19,13 +23,14 @@ export class ContentSeeder {
19
23
  memoryCheckInterval: options.memoryCheckInterval || 100,
20
24
  memoryThreshold: options.memoryThreshold || 0.5,
21
25
  useConnectionPooling: options.useConnectionPooling !== false,
22
- enableUniqueConstraintMapping: options.enableUniqueConstraintMapping !== false,
26
+ enableUniqueConstraintMapping:
27
+ options.enableUniqueConstraintMapping !== false,
23
28
  batchUpsertMode: options.batchUpsertMode !== false,
24
- uniqueFields: options.uniqueFields || ['name'],
25
- dbName: options.dbName || 'default',
29
+ uniqueFields: options.uniqueFields || ["name"],
30
+ dbName: options.dbName || "default",
26
31
  dbUrl: options.dbUrl || process.env.DATABASE_URL,
27
32
  logProgress: options.logProgress !== false,
28
- ...options
33
+ ...options,
29
34
  };
30
35
 
31
36
  this.uniqueConstraintMaps = new Map();
@@ -33,7 +38,7 @@ export class ContentSeeder {
33
38
  maxHeapUsed: 0,
34
39
  startHeapUsed: 0,
35
40
  currentHeapUsed: 0,
36
- gcTriggered: 0
41
+ gcTriggered: 0,
37
42
  };
38
43
  }
39
44
 
@@ -61,11 +66,21 @@ export class ContentSeeder {
61
66
  this.options.dbName,
62
67
  this.options.dbUrl,
63
68
  async (pooledClient) => {
64
- return await this.performSeeding(entityName, data, processor, pooledClient);
69
+ return await this.performSeeding(
70
+ entityName,
71
+ data,
72
+ processor,
73
+ pooledClient
74
+ );
65
75
  }
66
76
  );
67
77
  } else {
68
- return await this.performSeeding(entityName, data, processor, prismaClient);
78
+ return await this.performSeeding(
79
+ entityName,
80
+ data,
81
+ processor,
82
+ prismaClient
83
+ );
69
84
  }
70
85
  } catch (error) {
71
86
  logSeederError(entityName, error.message);
@@ -83,9 +98,11 @@ export class ContentSeeder {
83
98
  const errors = [];
84
99
 
85
100
  if (this.options.logProgress) {
86
- console.log(colors.cyan(
87
- `Processing ${data.length.toLocaleString()} ${entityName} in ${chunks.length} batches of ${this.options.batchSize}...`
88
- ));
101
+ console.log(
102
+ colors.cyan(
103
+ `Processing ${data.length.toLocaleString()} ${entityName} in ${chunks.length} batches of ${this.options.batchSize}...`
104
+ )
105
+ );
89
106
  }
90
107
 
91
108
  // Build unique constraint map if enabled
@@ -95,14 +112,17 @@ export class ContentSeeder {
95
112
 
96
113
  // Process chunks in parallel batches
97
114
  for (let i = 0; i < chunks.length; i += this.options.maxConcurrentBatches) {
98
- const currentBatch = chunks.slice(i, i + this.options.maxConcurrentBatches);
99
-
115
+ const currentBatch = chunks.slice(
116
+ i,
117
+ i + this.options.maxConcurrentBatches
118
+ );
119
+
100
120
  const batchPromises = currentBatch.map(async (chunk, index) => {
101
121
  const chunkIndex = i + index;
102
122
  return await this.processChunkWithRetry(
103
- chunk,
104
- processor,
105
- chunkIndex,
123
+ chunk,
124
+ processor,
125
+ chunkIndex,
106
126
  entityName,
107
127
  prismaClient
108
128
  );
@@ -112,7 +132,7 @@ export class ContentSeeder {
112
132
 
113
133
  // Aggregate results
114
134
  batchResults.forEach((result, index) => {
115
- if (result.status === 'fulfilled') {
135
+ if (result.status === "fulfilled") {
116
136
  totalProcessed += result.value.processed;
117
137
  totalCreated += result.value.created;
118
138
  } else {
@@ -132,12 +152,18 @@ export class ContentSeeder {
132
152
 
133
153
  // Progress reporting
134
154
  if (this.options.logProgress) {
135
- this.reportProgress(i + this.options.maxConcurrentBatches, chunks.length, totalProcessed, totalCreated, data.length);
155
+ this.reportProgress(
156
+ i + this.options.maxConcurrentBatches,
157
+ chunks.length,
158
+ totalProcessed,
159
+ totalCreated,
160
+ data.length
161
+ );
136
162
  }
137
163
  }
138
164
 
139
165
  if (this.options.logProgress) {
140
- process.stdout.write('\n');
166
+ process.stdout.write("\n");
141
167
  }
142
168
 
143
169
  const duration = (Date.now() - Date.now()) / 1000;
@@ -147,15 +173,19 @@ export class ContentSeeder {
147
173
  errors,
148
174
  duration,
149
175
  rate: totalProcessed / duration,
150
- memoryStats: this.memoryStats
176
+ memoryStats: this.memoryStats,
151
177
  };
152
178
 
153
179
  if (errors.length === 0) {
154
- logSeederComplete(`${entityName} (${totalCreated.toLocaleString()} created)`);
180
+ logSeederComplete(
181
+ `${entityName} (${totalCreated.toLocaleString()} created)`
182
+ );
155
183
  } else {
156
- console.log(colors.yellow(
157
- `${entityName} completed with ${errors.length} errors. ${totalCreated.toLocaleString()} created.`
158
- ));
184
+ console.log(
185
+ colors.yellow(
186
+ `${entityName} completed with ${errors.length} errors. ${totalCreated.toLocaleString()} created.`
187
+ )
188
+ );
159
189
  }
160
190
 
161
191
  // Final memory cleanup
@@ -167,16 +197,26 @@ export class ContentSeeder {
167
197
  /**
168
198
  * Process chunk with retry logic and exponential backoff
169
199
  */
170
- async processChunkWithRetry(chunk, processor, chunkIndex, entityName, prismaClient) {
200
+ async processChunkWithRetry(
201
+ chunk,
202
+ processor,
203
+ chunkIndex,
204
+ entityName,
205
+ prismaClient
206
+ ) {
171
207
  let lastError;
172
-
208
+
173
209
  for (let attempt = 1; attempt <= this.options.retryAttempts; attempt++) {
174
210
  try {
175
211
  let result;
176
212
 
177
213
  if (this.options.batchUpsertMode) {
178
214
  // Use batch upsert if enabled
179
- result = await this.performBatchUpsert(chunk, entityName, prismaClient);
215
+ result = await this.performBatchUpsert(
216
+ chunk,
217
+ entityName,
218
+ prismaClient
219
+ );
180
220
  } else {
181
221
  // Use custom processor
182
222
  result = await processor(chunk, chunkIndex, prismaClient);
@@ -184,25 +224,27 @@ export class ContentSeeder {
184
224
 
185
225
  return {
186
226
  processed: chunk.length,
187
- created: result.created || result.count || chunk.length
227
+ created: result.created || result.count || chunk.length,
188
228
  };
189
229
  } catch (error) {
190
230
  lastError = error;
191
231
  if (attempt < this.options.retryAttempts) {
192
- const delay = this.options.exponentialBackoff
232
+ const delay = this.options.exponentialBackoff
193
233
  ? this.options.retryDelay * Math.pow(2, attempt - 1)
194
234
  : this.options.retryDelay;
195
-
235
+
196
236
  if (this.options.logProgress) {
197
- console.log(colors.yellow(
198
- `Chunk ${chunkIndex} attempt ${attempt} failed, retrying in ${delay}ms...`
199
- ));
237
+ console.log(
238
+ colors.yellow(
239
+ `Chunk ${chunkIndex} attempt ${attempt} failed, retrying in ${delay}ms...`
240
+ )
241
+ );
200
242
  }
201
243
  await this.sleep(delay);
202
244
  }
203
245
  }
204
246
  }
205
-
247
+
206
248
  throw new Error(
207
249
  `Chunk ${chunkIndex} failed after ${this.options.retryAttempts} attempts: ${lastError.message}`
208
250
  );
@@ -212,12 +254,12 @@ export class ContentSeeder {
212
254
  * Perform batch upsert with unique constraint handling
213
255
  */
214
256
  async performBatchUpsert(chunk, entityName, prismaClient) {
215
- const model = entityName.toLowerCase().replace(/s$/, ''); // Simple pluralization handling
216
-
257
+ const model = this.getPrismaModelName(entityName);
258
+
217
259
  if (this.options.enableUniqueConstraintMapping) {
218
260
  // Filter out records that would violate unique constraints
219
261
  const validRecords = this.filterUniqueConstraints(chunk, entityName);
220
-
262
+
221
263
  if (validRecords.length === 0) {
222
264
  return { created: 0 };
223
265
  }
@@ -229,7 +271,7 @@ export class ContentSeeder {
229
271
  // Use createMany for batch insert with skip duplicates
230
272
  const result = await prismaClient[model].createMany({
231
273
  data: chunk,
232
- skipDuplicates: true
274
+ skipDuplicates: true,
233
275
  });
234
276
 
235
277
  // Update unique constraint map
@@ -240,7 +282,11 @@ export class ContentSeeder {
240
282
  return result;
241
283
  } catch (error) {
242
284
  // Fallback to individual upserts if batch fails
243
- console.warn(colors.yellow(`Batch upsert failed for ${entityName}, falling back to individual upserts`));
285
+ console.warn(
286
+ colors.yellow(
287
+ `Batch upsert failed for ${entityName}, falling back to individual upserts`
288
+ )
289
+ );
244
290
  return await this.fallbackToIndividualUpserts(chunk, model, prismaClient);
245
291
  }
246
292
  }
@@ -250,18 +296,20 @@ export class ContentSeeder {
250
296
  */
251
297
  async fallbackToIndividualUpserts(chunk, model, prismaClient) {
252
298
  let created = 0;
253
-
299
+
254
300
  for (const record of chunk) {
255
301
  try {
256
302
  await prismaClient[model].upsert({
257
303
  where: this.buildWhereClause(record),
258
304
  update: record,
259
- create: record
305
+ create: record,
260
306
  });
261
307
  created++;
262
308
  } catch (error) {
263
309
  // Log individual errors but continue processing
264
- console.warn(colors.yellow(`Individual upsert failed: ${error.message}`));
310
+ console.warn(
311
+ colors.yellow(`Individual upsert failed: ${error.message}`)
312
+ );
265
313
  }
266
314
  }
267
315
 
@@ -272,17 +320,17 @@ export class ContentSeeder {
272
320
  * Build unique constraint map for efficient duplicate detection
273
321
  */
274
322
  async buildUniqueConstraintMap(entityName, prismaClient) {
275
- const model = entityName.toLowerCase().replace(/s$/, '');
276
-
323
+ const model = this.getPrismaModelName(entityName);
324
+
277
325
  try {
278
326
  const existingRecords = await prismaClient[model].findMany({
279
- select: this.buildSelectClause()
327
+ select: this.buildSelectClause(),
280
328
  });
281
329
 
282
330
  const constraintMap = new Map();
283
-
284
- existingRecords.forEach(record => {
285
- this.options.uniqueFields.forEach(field => {
331
+
332
+ existingRecords.forEach((record) => {
333
+ this.options.uniqueFields.forEach((field) => {
286
334
  if (record[field]) {
287
335
  constraintMap.set(`${field}:${record[field]}`, true);
288
336
  }
@@ -290,14 +338,20 @@ export class ContentSeeder {
290
338
  });
291
339
 
292
340
  this.uniqueConstraintMaps.set(entityName, constraintMap);
293
-
341
+
294
342
  if (this.options.logProgress) {
295
- console.log(colors.debug(
296
- `Built unique constraint map for ${entityName}: ${constraintMap.size} existing records`
297
- ));
343
+ console.log(
344
+ colors.debug(
345
+ `Built unique constraint map for ${entityName}: ${constraintMap.size} existing records`
346
+ )
347
+ );
298
348
  }
299
349
  } catch (error) {
300
- console.warn(colors.yellow(`Failed to build unique constraint map for ${entityName}: ${error.message}`));
350
+ console.warn(
351
+ colors.yellow(
352
+ `Failed to build unique constraint map for ${entityName}: ${error.message}`
353
+ )
354
+ );
301
355
  }
302
356
  }
303
357
 
@@ -308,7 +362,7 @@ export class ContentSeeder {
308
362
  const constraintMap = this.uniqueConstraintMaps.get(entityName);
309
363
  if (!constraintMap) return chunk;
310
364
 
311
- return chunk.filter(record => {
365
+ return chunk.filter((record) => {
312
366
  for (const field of this.options.uniqueFields) {
313
367
  if (record[field] && constraintMap.has(`${field}:${record[field]}`)) {
314
368
  return false; // Skip this record as it would violate unique constraint
@@ -325,8 +379,8 @@ export class ContentSeeder {
325
379
  const constraintMap = this.uniqueConstraintMaps.get(entityName);
326
380
  if (!constraintMap) return;
327
381
 
328
- chunk.forEach(record => {
329
- this.options.uniqueFields.forEach(field => {
382
+ chunk.forEach((record) => {
383
+ this.options.uniqueFields.forEach((field) => {
330
384
  if (record[field]) {
331
385
  constraintMap.set(`${field}:${record[field]}`, true);
332
386
  }
@@ -334,12 +388,73 @@ export class ContentSeeder {
334
388
  });
335
389
  }
336
390
 
391
+ /**
392
+ * Convert entity name (plural) to Prisma model name (singular)
393
+ * Handles common English pluralization patterns
394
+ */
395
+ getPrismaModelName(entityName) {
396
+ const lower = entityName.toLowerCase();
397
+
398
+ // Handle special cases
399
+ const specialCases = {
400
+ industries: "industry",
401
+ categories: "category",
402
+ countries: "country",
403
+ cities: "city",
404
+ states: "state",
405
+ currencies: "currency",
406
+ companies: "company",
407
+ activities: "activity",
408
+ properties: "property",
409
+ facilities: "facility",
410
+ authorities: "authority",
411
+ deliveries: "delivery",
412
+ templates: "template",
413
+ forms: "form",
414
+ attributes: "attribute",
415
+ roles: "role",
416
+ permissions: "permission",
417
+ users: "user",
418
+ departments: "department",
419
+ vendors: "vendor",
420
+ };
421
+
422
+ // Return special case if exists
423
+ if (specialCases[lower]) {
424
+ return specialCases[lower];
425
+ }
426
+
427
+ // Handle -ies ending (e.g., "stories" -> "story")
428
+ if (lower.endsWith("ies")) {
429
+ return lower.slice(0, -3) + "y";
430
+ }
431
+
432
+ // Handle -es ending after s, x, z, ch, sh (e.g., "boxes" -> "box")
433
+ if (
434
+ lower.endsWith("ses") ||
435
+ lower.endsWith("xes") ||
436
+ lower.endsWith("zes") ||
437
+ lower.endsWith("ches") ||
438
+ lower.endsWith("shes")
439
+ ) {
440
+ return lower.slice(0, -2);
441
+ }
442
+
443
+ // Handle regular -s ending
444
+ if (lower.endsWith("s")) {
445
+ return lower.slice(0, -1);
446
+ }
447
+
448
+ // Return as-is if no pluralization detected
449
+ return lower;
450
+ }
451
+
337
452
  /**
338
453
  * Build WHERE clause for upsert operations
339
454
  */
340
455
  buildWhereClause(record) {
341
456
  const whereClause = {};
342
-
457
+
343
458
  // Use the first unique field as the primary identifier
344
459
  const primaryField = this.options.uniqueFields[0];
345
460
  if (record[primaryField]) {
@@ -354,8 +469,8 @@ export class ContentSeeder {
354
469
  */
355
470
  buildSelectClause() {
356
471
  const selectClause = { id: true };
357
-
358
- this.options.uniqueFields.forEach(field => {
472
+
473
+ this.options.uniqueFields.forEach((field) => {
359
474
  selectClause[field] = true;
360
475
  });
361
476
 
@@ -370,7 +485,7 @@ export class ContentSeeder {
370
485
  this.memoryStats.startHeapUsed = memUsage.heapUsed;
371
486
  this.memoryStats.currentHeapUsed = memUsage.heapUsed;
372
487
  this.memoryStats.maxHeapUsed = memUsage.heapUsed;
373
-
488
+
374
489
  if (this.options.logProgress) {
375
490
  MemoryManager.logMemoryUsage(`${this.options.dbName} seeding start`);
376
491
  }
@@ -382,20 +497,26 @@ export class ContentSeeder {
382
497
  checkMemoryUsage(processedCount, entityName) {
383
498
  const memUsage = process.memoryUsage();
384
499
  this.memoryStats.currentHeapUsed = memUsage.heapUsed;
385
- this.memoryStats.maxHeapUsed = Math.max(this.memoryStats.maxHeapUsed, memUsage.heapUsed);
500
+ this.memoryStats.maxHeapUsed = Math.max(
501
+ this.memoryStats.maxHeapUsed,
502
+ memUsage.heapUsed
503
+ );
386
504
 
387
505
  // Check every N records based on interval
388
506
  if (processedCount % this.options.memoryCheckInterval === 0) {
389
- const memoryThresholdBytes = this.options.memoryThreshold * this.memoryStats.startHeapUsed * 10; // Allow 10x growth
390
-
507
+ const memoryThresholdBytes =
508
+ this.options.memoryThreshold * this.memoryStats.startHeapUsed * 10; // Allow 10x growth
509
+
391
510
  if (memUsage.heapUsed > memoryThresholdBytes) {
392
511
  if (this.options.logProgress) {
393
- console.log(colors.yellow(`Memory threshold exceeded, triggering GC...`));
512
+ console.log(
513
+ colors.yellow(`Memory threshold exceeded, triggering GC...`)
514
+ );
394
515
  }
395
-
516
+
396
517
  MemoryManager.forceGarbageCollectionWithDelay();
397
518
  this.memoryStats.gcTriggered++;
398
-
519
+
399
520
  if (this.options.logProgress) {
400
521
  MemoryManager.logMemoryUsage(`After GC (${entityName})`);
401
522
  }
@@ -408,12 +529,20 @@ export class ContentSeeder {
408
529
  */
409
530
  finalMemoryCleanup() {
410
531
  MemoryManager.forceGarbageCollectionWithDelay();
411
-
532
+
412
533
  if (this.options.logProgress) {
413
- MemoryManager.logMemoryUsage('Seeding complete');
414
-
415
- const heapGrowth = ((this.memoryStats.maxHeapUsed - this.memoryStats.startHeapUsed) / 1024 / 1024).toFixed(2);
416
- console.log(colors.gray(`Memory growth: ${heapGrowth}MB, GC triggered: ${this.memoryStats.gcTriggered} times`));
534
+ MemoryManager.logMemoryUsage("Seeding complete");
535
+
536
+ const heapGrowth = (
537
+ (this.memoryStats.maxHeapUsed - this.memoryStats.startHeapUsed) /
538
+ 1024 /
539
+ 1024
540
+ ).toFixed(2);
541
+ console.log(
542
+ colors.gray(
543
+ `Memory growth: ${heapGrowth}MB, GC triggered: ${this.memoryStats.gcTriggered} times`
544
+ )
545
+ );
417
546
  }
418
547
  }
419
548
 
@@ -422,11 +551,11 @@ export class ContentSeeder {
422
551
  */
423
552
  reportProgress(currentIndex, totalChunks, processed, created, total) {
424
553
  const progress = Math.min((currentIndex / totalChunks) * 100, 100);
425
-
554
+
426
555
  process.stdout.write(
427
- `\r${colors.green('')} Progress: ${progress.toFixed(1)}% | ` +
428
- `Processed: ${processed.toLocaleString()}/${total.toLocaleString()} | ` +
429
- `Created: ${created.toLocaleString()}`
556
+ `\r${colors.green("")} Progress: ${progress.toFixed(1)}% | ` +
557
+ `Processed: ${processed.toLocaleString()}/${total.toLocaleString()} | ` +
558
+ `Created: ${created.toLocaleString()}`
430
559
  );
431
560
  }
432
561
 
@@ -445,7 +574,7 @@ export class ContentSeeder {
445
574
  * Sleep utility
446
575
  */
447
576
  sleep(ms) {
448
- return new Promise(resolve => setTimeout(resolve, ms));
577
+ return new Promise((resolve) => setTimeout(resolve, ms));
449
578
  }
450
579
  }
451
580
 
@@ -453,20 +582,25 @@ export class ContentSeeder {
453
582
  * Factory function to create ContentSeeder with performance config
454
583
  */
455
584
  export async function createContentSeeder(entityName, options = {}) {
456
- const { createPerformanceConfig } = await import('./performance-config.js');
585
+ const { createPerformanceConfig } = await import("./performance-config.js");
457
586
  const performanceConfig = createPerformanceConfig();
458
- const config = performanceConfig.getConfig('content', entityName);
459
-
587
+ const config = performanceConfig.getConfig("content", entityName);
588
+
460
589
  return new ContentSeeder({
461
590
  ...config,
462
- ...options
591
+ ...options,
463
592
  });
464
593
  }
465
594
 
466
595
  /**
467
596
  * Convenience function for content seeding with optimized defaults
468
597
  */
469
- export async function seedContentData(entityName, data, processor = null, options = {}) {
598
+ export async function seedContentData(
599
+ entityName,
600
+ data,
601
+ processor = null,
602
+ options = {}
603
+ ) {
470
604
  const seeder = await createContentSeeder(entityName, options);
471
605
  return await seeder.seedContent(entityName, data, processor);
472
606
  }