make-mp-data 2.1.11 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +31 -0
  2. package/dungeons/adspend.js +2 -2
  3. package/dungeons/ai-chat-analytics-ed.js +3 -2
  4. package/dungeons/anon.js +2 -2
  5. package/dungeons/array-of-object-loopup.js +181 -0
  6. package/dungeons/benchmark-heavy.js +241 -0
  7. package/dungeons/benchmark-light.js +141 -0
  8. package/dungeons/big.js +9 -8
  9. package/dungeons/business.js +2 -1
  10. package/dungeons/clinch-agi.js +632 -0
  11. package/dungeons/complex.js +3 -2
  12. package/dungeons/copilot.js +383 -0
  13. package/dungeons/ecommerce-store.js +0 -0
  14. package/dungeons/experiments.js +5 -4
  15. package/dungeons/foobar.js +1 -1
  16. package/dungeons/funnels.js +2 -2
  17. package/dungeons/gaming.js +3 -2
  18. package/dungeons/harness/harness-education.js +988 -0
  19. package/dungeons/harness/harness-fintech.js +976 -0
  20. package/dungeons/harness/harness-food.js +985 -0
  21. package/dungeons/harness/harness-gaming.js +1178 -0
  22. package/dungeons/harness/harness-media.js +961 -0
  23. package/dungeons/harness/harness-sass.js +923 -0
  24. package/dungeons/harness/harness-social.js +928 -0
  25. package/dungeons/kurby.js +211 -0
  26. package/dungeons/media.js +5 -4
  27. package/dungeons/mil.js +4 -3
  28. package/dungeons/mirror.js +2 -2
  29. package/dungeons/money2020-ed.js +8 -7
  30. package/dungeons/sanity.js +3 -2
  31. package/dungeons/scd.js +3 -2
  32. package/dungeons/simple.js +30 -15
  33. package/dungeons/strict-event-test.js +30 -0
  34. package/dungeons/student-teacher.js +3 -2
  35. package/dungeons/text-generation.js +84 -85
  36. package/dungeons/too-big-events.js +166 -0
  37. package/dungeons/uday-schema.json +220 -0
  38. package/dungeons/userAgent.js +4 -3
  39. package/index.js +41 -54
  40. package/lib/core/config-validator.js +122 -7
  41. package/lib/core/context.js +7 -14
  42. package/lib/core/storage.js +57 -25
  43. package/lib/generators/adspend.js +12 -12
  44. package/lib/generators/events.js +6 -5
  45. package/lib/generators/funnels.js +32 -10
  46. package/lib/generators/product-lookup.js +262 -0
  47. package/lib/generators/product-names.js +195 -0
  48. package/lib/generators/profiles.js +3 -3
  49. package/lib/generators/scd.js +13 -3
  50. package/lib/generators/text.js +17 -4
  51. package/lib/orchestrators/mixpanel-sender.js +244 -204
  52. package/lib/orchestrators/user-loop.js +54 -16
  53. package/lib/templates/funnels-instructions.txt +272 -0
  54. package/lib/templates/hook-examples.json +187 -0
  55. package/lib/templates/hooks-instructions.txt +295 -8
  56. package/lib/templates/phrases.js +473 -16
  57. package/lib/templates/refine-instructions.txt +485 -0
  58. package/lib/templates/schema-instructions.txt +239 -109
  59. package/lib/templates/schema.d.ts +173 -0
  60. package/lib/templates/verbose-schema.js +140 -206
  61. package/lib/utils/ai.js +853 -77
  62. package/lib/utils/chart.js +210 -0
  63. package/lib/utils/function-registry.js +285 -0
  64. package/lib/utils/json-evaluator.js +172 -0
  65. package/lib/utils/logger.js +38 -0
  66. package/lib/utils/mixpanel.js +101 -0
  67. package/lib/utils/project.js +3 -2
  68. package/lib/utils/utils.js +41 -4
  69. package/package.json +15 -21
  70. package/types.d.ts +15 -5
  71. package/lib/generators/text-bak-old.js +0 -1121
  72. package/lib/orchestrators/worker-manager.js +0 -203
  73. package/lib/templates/phrases-bak.js +0 -925
  74. package/lib/templates/prompt (old).txt +0 -98
  75. package/lib/templates/scratch-dungeon-template.js +0 -116
  76. package/lib/templates/textQuickTest.js +0 -172
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Product Lookup Table Generator
3
+ * Creates a comprehensive product catalog with unique IDs
4
+ * @module product-lookup
5
+ */
6
+
7
+ import Chance from 'chance';
8
+ import { GENERATION_PATTERNS } from '../templates/phrases.js';
9
+
10
+ /**
11
+ * Generate a unique product lookup table
12
+ * @param {number} count - Number of products to generate
13
+ * @param {string} seed - Seed for reproducible results
14
+ * @returns {Map<string, Object>} Map of product_id to product details
15
+ */
16
+ export function generateProductLookup(count = 10000, seed = 'product-catalog') {
17
+ const chance = new Chance(seed);
18
+ const productMap = new Map();
19
+
20
+ // Get all product categories
21
+ const categories = GENERATION_PATTERNS.store_products;
22
+ const categoryNames = Object.keys(categories);
23
+
24
+ // Flatten all products with their categories
25
+ const allProducts = [];
26
+ for (const [categoryKey, products] of Object.entries(categories)) {
27
+ const categoryName = getCategoryDisplayName(categoryKey);
28
+ products.forEach(productName => {
29
+ allProducts.push({
30
+ name: productName,
31
+ category: categoryName,
32
+ categoryKey: categoryKey
33
+ });
34
+ });
35
+ }
36
+
37
+ // Brand names for variety
38
+ const brands = [
39
+ "TechPro", "HomeEssentials", "FitLife", "StyleCo", "BeautyPlus",
40
+ "OfficeMax", "EcoGoods", "PremiumChoice", "ValueBrand", "GenericCo",
41
+ "ProGear", "UltraLife", "SmartHome", "ActiveWear", "LuxeBeauty",
42
+ "WorkSpace", "GreenLiving", "EliteChoice", "BudgetBuy", "QualityFirst",
43
+ "TechnoEdge", "HomeComfort", "FitnessPro", "FashionForward", "GlowUp",
44
+ "OfficeElite", "EarthFriendly", "PremiumPro", "SmartBuy", "TopTier",
45
+ "InnoTech", "CozyHome", "PowerFit", "TrendSet", "PureBeauty",
46
+ "DeskMaster", "NaturalChoice", "LuxuryLine", "BestValue", "PrimeSelect"
47
+ ];
48
+
49
+ // Generate products
50
+ for (let i = 0; i < count; i++) {
51
+ const productId = `PROD-${String(i + 1).padStart(6, '0')}`;
52
+
53
+ // Select a base product (reuse and vary)
54
+ const baseProduct = chance.pickone(allProducts);
55
+
56
+ // Add variation to product name for uniqueness
57
+ let productName = baseProduct.name;
58
+ if (chance.bool({ likelihood: 30 })) {
59
+ // Add color/size/model variations
60
+ const variations = [
61
+ ` - ${chance.pickone(['Black', 'White', 'Blue', 'Red', 'Gray', 'Silver', 'Gold', 'Rose Gold', 'Navy', 'Green'])}`,
62
+ ` - ${chance.pickone(['Small', 'Medium', 'Large', 'X-Large', 'One Size'])}`,
63
+ ` - ${chance.pickone(['Pro', 'Plus', 'Lite', 'Max', 'Mini', 'Ultra', 'Premium', 'Basic', 'Deluxe'])}`,
64
+ ` (${chance.pickone(['2024 Model', '2025 Edition', 'Updated Version', 'Gen 2', 'v2.0', 'Series 2'])})`,
65
+ ` - ${chance.integer({ min: 1, max: 50 })} Pack`
66
+ ];
67
+
68
+ if (chance.bool({ likelihood: 60 })) {
69
+ productName += chance.pickone(variations);
70
+ }
71
+ }
72
+
73
+ // Generate price based on category
74
+ const priceRange = getPriceRange(baseProduct.categoryKey);
75
+ const basePrice = chance.floating({
76
+ min: priceRange.min,
77
+ max: priceRange.max,
78
+ fixed: 2
79
+ });
80
+
81
+ // Generate rating
82
+ const rating = chance.weighted(
83
+ [5, 4.5, 4, 3.5, 3, 2.5, 2, 1.5, 1],
84
+ [35, 25, 20, 10, 5, 3, 1, 0.5, 0.5]
85
+ );
86
+
87
+ // Stock levels
88
+ const stockLevel = chance.weighted(
89
+ [500, 250, 100, 50, 25, 0],
90
+ [40, 25, 20, 10, 4, 1]
91
+ );
92
+
93
+ const product = {
94
+ product_id: productId,
95
+ name: productName,
96
+ brand: chance.pickone(brands),
97
+ category: baseProduct.category,
98
+ subcategory: baseProduct.categoryKey,
99
+ price: basePrice,
100
+ msrp: chance.bool({ likelihood: 40 }) ? basePrice * chance.floating({ min: 1.1, max: 1.5, fixed: 2 }) : basePrice,
101
+ rating: rating,
102
+ review_count: chance.integer({ min: 0, max: 5000 }),
103
+ stock_level: stockLevel,
104
+ stock_status: getStockStatus(stockLevel),
105
+ sku: `SKU-${chance.string({ length: 8, pool: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' })}`,
106
+ weight_oz: chance.floating({ min: 0.5, max: 50, fixed: 2 }),
107
+ is_featured: chance.bool({ likelihood: 10 }),
108
+ is_bestseller: chance.bool({ likelihood: 15 }),
109
+ is_new_arrival: chance.bool({ likelihood: 20 }),
110
+ is_on_sale: chance.bool({ likelihood: 25 }),
111
+ discount_percent: chance.bool({ likelihood: 25 }) ? chance.integer({ min: 5, max: 50 }) : 0,
112
+ shipping_eligible: chance.bool({ likelihood: 90 }),
113
+ free_shipping: chance.bool({ likelihood: 40 }),
114
+ prime_eligible: chance.bool({ likelihood: 60 }),
115
+ returnable: chance.bool({ likelihood: 85 }),
116
+ warranty_months: chance.pickone([0, 3, 6, 12, 24, 36]),
117
+ release_date: chance.date({
118
+ year: chance.integer({ min: 2020, max: 2025 })
119
+ }).toISOString().split('T')[0],
120
+ supplier_id: `SUP-${chance.integer({ min: 1, max: 500 })}`,
121
+ tags: generateTags(baseProduct.categoryKey, chance)
122
+ };
123
+
124
+ productMap.set(productId, product);
125
+ }
126
+
127
+ return productMap;
128
+ }
129
+
130
+ /**
131
+ * Convert a product lookup Map to a plain object for export
132
+ */
133
+ export function productLookupToObject(productMap) {
134
+ const obj = {};
135
+ for (const [id, product] of productMap.entries()) {
136
+ obj[id] = product;
137
+ }
138
+ return obj;
139
+ }
140
+
141
+ /**
142
+ * Get a random product from the lookup
143
+ */
144
+ export function getRandomProduct(productMap, chance) {
145
+ const keys = Array.from(productMap.keys());
146
+ const randomKey = chance.pickone(keys);
147
+ return productMap.get(randomKey);
148
+ }
149
+
150
+ /**
151
+ * Get products by category
152
+ */
153
+ export function getProductsByCategory(productMap, category) {
154
+ return Array.from(productMap.values()).filter(p => p.category === category);
155
+ }
156
+
157
+ /**
158
+ * Get products by price range
159
+ */
160
+ export function getProductsByPriceRange(productMap, minPrice, maxPrice) {
161
+ return Array.from(productMap.values()).filter(p => p.price >= minPrice && p.price <= maxPrice);
162
+ }
163
+
164
+ /**
165
+ * Search products by name
166
+ */
167
+ export function searchProducts(productMap, query) {
168
+ const lowerQuery = query.toLowerCase();
169
+ return Array.from(productMap.values()).filter(p =>
170
+ p.name.toLowerCase().includes(lowerQuery) ||
171
+ p.brand.toLowerCase().includes(lowerQuery) ||
172
+ p.category.toLowerCase().includes(lowerQuery)
173
+ );
174
+ }
175
+
176
+ // Helper functions
177
+
178
+ function getCategoryDisplayName(categoryKey) {
179
+ const displayNames = {
180
+ electronics: 'Electronics',
181
+ home_kitchen: 'Home & Kitchen',
182
+ clothing: 'Clothing',
183
+ beauty: 'Beauty & Personal Care',
184
+ sports: 'Sports & Outdoors',
185
+ books: 'Books & Media',
186
+ toys: 'Toys & Games',
187
+ pet_supplies: 'Pet Supplies',
188
+ office: 'Office Supplies',
189
+ baby: 'Baby Products',
190
+ automotive: 'Automotive',
191
+ garden: 'Garden & Outdoor'
192
+ };
193
+ return displayNames[categoryKey] || categoryKey;
194
+ }
195
+
196
+ function getPriceRange(categoryKey) {
197
+ const ranges = {
198
+ electronics: { min: 9.99, max: 799.99 },
199
+ home_kitchen: { min: 4.99, max: 399.99 },
200
+ clothing: { min: 9.99, max: 199.99 },
201
+ beauty: { min: 3.99, max: 89.99 },
202
+ sports: { min: 7.99, max: 299.99 },
203
+ books: { min: 4.99, max: 49.99 },
204
+ toys: { min: 5.99, max: 149.99 },
205
+ pet_supplies: { min: 3.99, max: 99.99 },
206
+ office: { min: 1.99, max: 499.99 },
207
+ baby: { min: 4.99, max: 299.99 },
208
+ automotive: { min: 9.99, max: 199.99 },
209
+ garden: { min: 5.99, max: 249.99 }
210
+ };
211
+ return ranges[categoryKey] || { min: 9.99, max: 99.99 };
212
+ }
213
+
214
+ function getStockStatus(stockLevel) {
215
+ if (stockLevel === 0) return 'Out of Stock';
216
+ if (stockLevel < 10) return 'Low Stock';
217
+ if (stockLevel < 50) return 'Limited Stock';
218
+ return 'In Stock';
219
+ }
220
+
221
+ function generateTags(categoryKey, chance) {
222
+ const commonTags = ['popular', 'trending', 'recommended', 'verified', 'quality'];
223
+ const categoryTags = {
224
+ electronics: ['tech', 'gadget', 'smart', 'wireless', 'digital'],
225
+ home_kitchen: ['home', 'kitchen', 'cooking', 'storage', 'organization'],
226
+ clothing: ['fashion', 'apparel', 'style', 'comfort', 'casual'],
227
+ beauty: ['skincare', 'makeup', 'grooming', 'wellness', 'organic'],
228
+ sports: ['fitness', 'outdoor', 'active', 'training', 'exercise'],
229
+ books: ['reading', 'literature', 'educational', 'entertainment'],
230
+ toys: ['kids', 'fun', 'educational', 'creative', 'play'],
231
+ pet_supplies: ['pets', 'animals', 'care', 'feeding', 'play'],
232
+ office: ['work', 'productivity', 'organization', 'supplies'],
233
+ baby: ['infant', 'nursery', 'feeding', 'safety', 'care'],
234
+ automotive: ['car', 'vehicle', 'maintenance', 'accessories'],
235
+ garden: ['outdoor', 'plants', 'gardening', 'landscaping']
236
+ };
237
+
238
+ const tags = [];
239
+ const catTags = categoryTags[categoryKey] || [];
240
+
241
+ // Add 2-4 category tags
242
+ for (let i = 0; i < chance.integer({ min: 2, max: 4 }); i++) {
243
+ const tag = chance.pickone(catTags);
244
+ if (!tags.includes(tag)) tags.push(tag);
245
+ }
246
+
247
+ // Maybe add a common tag
248
+ if (chance.bool({ likelihood: 40 })) {
249
+ tags.push(chance.pickone(commonTags));
250
+ }
251
+
252
+ return tags;
253
+ }
254
+
255
+ export default {
256
+ generateProductLookup,
257
+ productLookupToObject,
258
+ getRandomProduct,
259
+ getProductsByCategory,
260
+ getProductsByPriceRange,
261
+ searchProducts
262
+ };
@@ -0,0 +1,195 @@
1
+ /**
2
+ * E-commerce Product Name Generator
3
+ * Generates realistic product names for store inventories, reviews, and search queries
4
+ * @module product-names
5
+ */
6
+
7
+ import { createTextGenerator } from './text.js';
8
+
9
+ // Product Review Generator - For customer reviews mentioning specific products
10
+ export const productReviewGen = createTextGenerator({
11
+ style: "review",
12
+ tone: "pos",
13
+ intensity: "medium",
14
+ formality: "casual",
15
+ keywords: {
16
+ products: [
17
+ // Electronics
18
+ "Wireless Bluetooth Headphones", "USB-C Charging Cable", "Portable Power Bank 20000mAh",
19
+ "Smart Watch Series 7", "Wireless Gaming Mouse", "4K Webcam Pro",
20
+ "Bluetooth Speaker Waterproof", "Ring Light for Streaming",
21
+
22
+ // Home & Kitchen
23
+ "Stainless Steel Cookware Set", "Air Fryer 6 Quart Digital", "Instant Pot 8 Quart",
24
+ "Coffee Maker Programmable", "Blender High Speed 1200W",
25
+
26
+ // Clothing
27
+ "Men's Cotton T-Shirt Basic", "Women's Yoga Pants High Waist", "Athletic Shorts",
28
+ "Sneakers Running Lightweight", "Fleece Jacket Zip Up"
29
+ ],
30
+ features: [
31
+ "battery life", "sound quality", "build quality", "comfort", "durability",
32
+ "ease of use", "design", "value for money", "performance", "fit",
33
+ "material quality", "charging speed", "connectivity", "portability",
34
+ "warranty", "customer service", "packaging", "instructions",
35
+ "compatibility", "functionality", "aesthetics", "weight",
36
+ "size options", "color accuracy", "temperature control", "noise level"
37
+ ],
38
+ positive_aspects: [
39
+ "works great", "perfect for my needs", "exceeded expectations",
40
+ "highly recommend", "best purchase ever", "worth every penny",
41
+ "can't live without it", "game changer", "solid quality",
42
+ "does exactly what it claims", "super reliable", "love it so far",
43
+ "impressed with quality", "fast shipping too", "great value"
44
+ ],
45
+ negative_aspects: [
46
+ "a bit pricey", "could be better", "takes getting used to",
47
+ "wish it had more features", "broke after a month", "not as advertised",
48
+ "quality could be improved", "too bulky", "battery drains quickly",
49
+ "doesn't fit well", "cheaply made", "stopped working"
50
+ ]
51
+ },
52
+ mixedSentiment: true,
53
+ authenticityLevel: 0.6,
54
+ typos: true,
55
+ typoRate: 0.03,
56
+ min: 50,
57
+ max: 200,
58
+ includeMetadata: false
59
+ });
60
+
61
+ // Product Search Query Generator - For realistic search terms
62
+ export const productSearchGen = createTextGenerator({
63
+ style: "search",
64
+ tone: "neu",
65
+ formality: "casual",
66
+ keywords: {
67
+ products: [
68
+ "headphones", "charging cable", "power bank", "smart watch", "gaming mouse",
69
+ "webcam", "speaker", "ring light", "cookware set", "air fryer",
70
+ "instant pot", "coffee maker", "blender", "yoga pants", "running shoes",
71
+ "fleece jacket", "facial cleanser", "moisturizer", "shampoo", "toothbrush",
72
+ "yoga mat", "dumbbells", "water bottle", "camping tent", "sleeping bag",
73
+ "notebook", "pens", "desk chair", "monitor stand", "baby monitor"
74
+ ],
75
+ attributes: [
76
+ "wireless", "bluetooth", "USB-C", "waterproof", "portable", "lightweight",
77
+ "rechargeable", "adjustable", "foldable", "compact", "ergonomic",
78
+ "stainless steel", "non-stick", "organic", "eco-friendly", "sustainable",
79
+ "durable", "heavy duty", "professional", "premium", "budget",
80
+ "best rated", "top seller", "on sale", "discounted", "clearance"
81
+ ],
82
+ modifiers: [
83
+ "cheap", "affordable", "best", "good quality", "reliable",
84
+ "for beginners", "professional grade", "commercial", "industrial",
85
+ "for small spaces", "travel size", "family size", "bulk",
86
+ "name brand", "generic", "off brand", "authentic", "genuine",
87
+ "refurbished", "open box", "like new", "gently used"
88
+ ]
89
+ },
90
+ typos: true,
91
+ typoRate: 0.05,
92
+ min: 2,
93
+ max: 40,
94
+ includeMetadata: false
95
+ });
96
+
97
+ // Product Comparison Generator - For side-by-side product comparisons
98
+ export const productComparisonGen = createTextGenerator({
99
+ style: "feedback",
100
+ tone: "neu",
101
+ formality: "business",
102
+ keywords: {
103
+ products: [
104
+ "Wireless Bluetooth Headphones", "Air Fryer 6 Quart Digital",
105
+ "Smart Watch Series 7", "Coffee Maker Programmable",
106
+ "Yoga Mat Non-Slip 6mm", "Instant Pot 8 Quart"
107
+ ],
108
+ comparison_terms: [
109
+ "compared to", "versus", "better than", "not as good as",
110
+ "similar to", "different from", "cheaper alternative to",
111
+ "premium version of", "budget option for", "upgraded from"
112
+ ],
113
+ metrics: [
114
+ "price point", "durability", "features", "warranty coverage",
115
+ "brand reputation", "user reviews", "availability",
116
+ "customer support", "build quality", "performance metrics"
117
+ ]
118
+ },
119
+ authenticityLevel: 0.7,
120
+ specificityLevel: 0.8,
121
+ min: 100,
122
+ max: 300,
123
+ includeMetadata: false
124
+ });
125
+
126
+ // Product Question Generator - For Q&A sections
127
+ export const productQuestionGen = createTextGenerator({
128
+ style: "forum",
129
+ tone: "neu",
130
+ formality: "casual",
131
+ keywords: {
132
+ products: [
133
+ "Smart Watch Series 7", "Air Fryer 6 Quart", "Wireless Headphones",
134
+ "Yoga Mat", "Power Bank", "Instant Pot", "Bluetooth Speaker"
135
+ ],
136
+ question_starters: [
137
+ "Does this work with", "Is this compatible with", "Can I use this for",
138
+ "Will this fit", "How long does", "What's the difference between",
139
+ "Is this better than", "Should I get", "Any recommendations for",
140
+ "Has anyone tried", "Is it worth", "Does anyone know if"
141
+ ],
142
+ concerns: [
143
+ "battery life", "warranty", "return policy", "shipping time",
144
+ "compatibility", "size", "color options", "durability",
145
+ "maintenance", "cleaning", "storage", "safety",
146
+ "noise level", "power consumption", "assembly required"
147
+ ]
148
+ },
149
+ typos: true,
150
+ typoRate: 0.04,
151
+ min: 20,
152
+ max: 100,
153
+ includeMetadata: false
154
+ });
155
+
156
+ // Product Description Generator - For store listings
157
+ export const productDescriptionGen = createTextGenerator({
158
+ style: "feedback",
159
+ tone: "pos",
160
+ formality: "business",
161
+ keywords: {
162
+ products: [
163
+ "Premium Wireless Headphones", "Professional Air Fryer", "Advanced Smart Watch",
164
+ "Deluxe Coffee Maker", "Ultra Comfort Yoga Mat", "Multi-Function Instant Pot"
165
+ ],
166
+ features: [
167
+ "state-of-the-art technology", "innovative design", "superior quality",
168
+ "advanced features", "user-friendly interface", "premium materials",
169
+ "long-lasting durability", "exceptional performance", "modern aesthetics",
170
+ "versatile functionality", "energy efficient", "space-saving design",
171
+ "easy maintenance", "comprehensive warranty", "fast setup",
172
+ "multiple settings", "customizable options", "safety certified"
173
+ ],
174
+ benefits: [
175
+ "saves time", "improves efficiency", "enhances experience",
176
+ "provides convenience", "offers flexibility", "ensures reliability",
177
+ "delivers results", "maximizes value", "simplifies tasks",
178
+ "reduces effort", "increases productivity", "optimizes performance"
179
+ ]
180
+ },
181
+ authenticityLevel: 0.5,
182
+ specificityLevel: 0.7,
183
+ min: 80,
184
+ max: 250,
185
+ includeMetadata: false
186
+ });
187
+
188
+ // Export all generators
189
+ export default {
190
+ productReviewGen,
191
+ productSearchGen,
192
+ productComparisonGen,
193
+ productQuestionGen,
194
+ productDescriptionGen
195
+ };
@@ -6,6 +6,7 @@
6
6
  /** @typedef {import('../../types').Context} Context */
7
7
 
8
8
  import * as u from "../utils/utils.js";
9
+ import { dataLogger as logger } from "../utils/logger.js";
9
10
 
10
11
  /**
11
12
  * Creates a user or group profile by choosing from available property values
@@ -31,7 +32,7 @@ export async function makeProfile(context, props = {}, defaults = {}) {
31
32
  try {
32
33
  profile[key] = u.choose(profile[key]);
33
34
  } catch (e) {
34
- console.error(`Error processing default property ${key}:`, e);
35
+ logger.error({ err: e, key }, `Error processing default property ${key}`);
35
36
  // Keep original value on error
36
37
  }
37
38
  }
@@ -41,7 +42,7 @@ export async function makeProfile(context, props = {}, defaults = {}) {
41
42
  try {
42
43
  profile[key] = u.choose(props[key]);
43
44
  } catch (e) {
44
- console.error(`Error processing property ${key}:`, e);
45
+ logger.error({ err: e, key }, `Error processing property ${key}`);
45
46
  // Keep original value on error
46
47
  }
47
48
  }
@@ -86,7 +87,6 @@ export async function makeGroupProfile(context, groupKey, groupProps = {}, baseP
86
87
  const combinedProps = {
87
88
  ...configGroupProps,
88
89
  ...groupProps,
89
- groupKey // Always include the group key
90
90
  };
91
91
 
92
92
  return makeProfile(context, combinedProps, baseProfile);
@@ -32,7 +32,13 @@ export async function makeSCD(context, scdProp, scdKey, distinct_id, mutations,
32
32
  }
33
33
  if (typeof created === 'number') created = dayjs.unix(created).toISOString();
34
34
 
35
- const { frequency, max, timing, values, type = "user" } = scdProp;
35
+ const {
36
+ frequency = 'day', // Default to daily frequency
37
+ max = 10, // Default max mutations
38
+ timing = 'fuzzy', // Default to fuzzy timing
39
+ values,
40
+ type = "user"
41
+ } = scdProp;
36
42
 
37
43
  // Return empty array if no values provided
38
44
  if (JSON.stringify(values) === "{}" || JSON.stringify(values) === "[]") {
@@ -56,7 +62,8 @@ export async function makeSCD(context, scdProp, scdKey, distinct_id, mutations,
56
62
  ...scd,
57
63
  [uuidKeyName]: scd.distinct_id || distinct_id,
58
64
  startTime: null,
59
- insertTime: null
65
+ insertTime: null,
66
+ time: null // Required field for Mixpanel SCD import
60
67
  };
61
68
 
62
69
  // Set start time based on timing strategy
@@ -82,8 +89,11 @@ export async function makeSCD(context, scdProp, scdKey, distinct_id, mutations,
82
89
  const insertTime = lastInserted.add(u.integer(1, 9000), "seconds");
83
90
  scdEntry.insertTime = insertTime.toISOString();
84
91
 
92
+ // Set the time field for Mixpanel SCD import (uses startTime)
93
+ scdEntry.time = scdEntry.startTime;
94
+
85
95
  // Only add entry if all required properties are set
86
- if (scdEntry.hasOwnProperty('insertTime') && scdEntry.hasOwnProperty('startTime')) {
96
+ if (scdEntry.hasOwnProperty('insertTime') && scdEntry.hasOwnProperty('startTime') && scdEntry.hasOwnProperty('time')) {
87
97
  scdEntries.push(scdEntry);
88
98
  }
89
99
 
@@ -846,7 +846,17 @@ class OrganicTextGenerator {
846
846
  return this.enhance(simple);
847
847
  }
848
848
 
849
+ stripUnfilledPlaceholders(text) {
850
+ // Remove tracery's unfilled placeholders: ((pattern))
851
+ // These occur when a pattern like #TITLE# is used but not defined
852
+ return text.replace(/\(\([a-zA-Z_]+\)\)/g, '').replace(/\s{2,}/g, ' ').trim();
853
+ }
854
+
849
855
  enhance(text) {
856
+ // Layer 0: Remove unfilled placeholders (from tracery)
857
+ // Tracery wraps undefined patterns in (( )) - strip these out
858
+ text = this.stripUnfilledPlaceholders(text);
859
+
850
860
  // Layer 1: Mixed sentiment
851
861
  if (this.config.mixedSentiment && chance(0.3)) {
852
862
  text = this.addMixedSentiment(text);
@@ -1134,11 +1144,11 @@ class OrganicTextGenerator {
1134
1144
  * @returns {TextGeneratorStats} Performance statistics
1135
1145
  */
1136
1146
  getStats() {
1137
- const avgTime = this.stats.generated > 0 ?
1147
+ const avgTime = this.stats.generated > 0 ?
1138
1148
  this.stats.totalTime / this.stats.generated : 0;
1139
-
1149
+
1140
1150
  return {
1141
- config: this.config,
1151
+ config: /** @type {import('../../types.d.ts').TextGeneratorConfig} */ (this.config),
1142
1152
  generatedCount: this.stats.generated,
1143
1153
  duplicateCount: this.stats.duplicates,
1144
1154
  failedCount: this.stats.failures,
@@ -1155,10 +1165,13 @@ class OrganicTextGenerator {
1155
1165
  * @param {TextGeneratorConfig} [config={}] - Configuration options for the generator
1156
1166
  * @returns {OrganicTextGenerator} Text generator instance
1157
1167
  */
1158
- export function createGenerator(config = {}) {
1168
+ export function createTextGenerator(config = {}) {
1159
1169
  return new OrganicTextGenerator(config);
1160
1170
  }
1161
1171
 
1172
+ // Alias for backwards compatibility
1173
+ export const createGenerator = createTextGenerator;
1174
+
1162
1175
  /**
1163
1176
  * Generate a batch of text items directly (standalone function)
1164
1177
  * @param {TextGeneratorConfig & TextBatchOptions} options - Combined generator config and batch options