make-mp-data 2.1.6 → 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.
- package/README.md +31 -0
- package/dungeons/adspend.js +2 -2
- package/dungeons/ai-chat-analytics-ed.js +3 -2
- package/dungeons/anon.js +2 -2
- package/dungeons/array-of-object-loopup.js +181 -0
- package/dungeons/benchmark-heavy.js +241 -0
- package/dungeons/benchmark-light.js +141 -0
- package/dungeons/big.js +9 -8
- package/dungeons/business.js +2 -1
- package/dungeons/clinch-agi.js +632 -0
- package/dungeons/complex.js +3 -2
- package/dungeons/copilot.js +383 -0
- package/dungeons/ecommerce-store.js +0 -0
- package/dungeons/experiments.js +5 -4
- package/dungeons/foobar.js +101 -101
- package/dungeons/funnels.js +2 -2
- package/dungeons/gaming.js +3 -2
- package/dungeons/harness/harness-education.js +988 -0
- package/dungeons/harness/harness-fintech.js +976 -0
- package/dungeons/harness/harness-food.js +985 -0
- package/dungeons/harness/harness-gaming.js +1178 -0
- package/dungeons/harness/harness-media.js +961 -0
- package/dungeons/harness/harness-sass.js +923 -0
- package/dungeons/harness/harness-social.js +928 -0
- package/dungeons/kurby.js +211 -0
- package/dungeons/media.js +5 -4
- package/dungeons/mil.js +4 -3
- package/dungeons/mirror.js +2 -2
- package/dungeons/money2020-ed.js +8 -7
- package/dungeons/sanity.js +3 -2
- package/dungeons/scd.js +3 -2
- package/dungeons/simple.js +29 -14
- package/dungeons/strict-event-test.js +30 -0
- package/dungeons/student-teacher.js +3 -2
- package/dungeons/text-generation.js +84 -85
- package/dungeons/too-big-events.js +166 -0
- package/dungeons/uday-schema.json +220 -0
- package/dungeons/userAgent.js +4 -3
- package/index.js +41 -54
- package/lib/core/config-validator.js +122 -7
- package/lib/core/context.js +7 -14
- package/lib/core/storage.js +60 -30
- package/lib/generators/adspend.js +12 -27
- package/lib/generators/events.js +6 -7
- package/lib/generators/funnels.js +16 -5
- package/lib/generators/product-lookup.js +262 -0
- package/lib/generators/product-names.js +195 -0
- package/lib/generators/profiles.js +3 -3
- package/lib/generators/scd.js +13 -3
- package/lib/generators/text.js +17 -4
- package/lib/orchestrators/mixpanel-sender.js +251 -208
- package/lib/orchestrators/user-loop.js +57 -19
- package/lib/templates/funnels-instructions.txt +272 -0
- package/lib/templates/hook-examples.json +187 -0
- package/lib/templates/hooks-instructions.txt +295 -8
- package/lib/templates/phrases.js +473 -16
- package/lib/templates/refine-instructions.txt +485 -0
- package/lib/templates/schema-instructions.txt +239 -109
- package/lib/templates/schema.d.ts +173 -0
- package/lib/templates/verbose-schema.js +140 -206
- package/lib/utils/ai.js +853 -77
- package/lib/utils/chart.js +210 -0
- package/lib/utils/function-registry.js +285 -0
- package/lib/utils/json-evaluator.js +172 -0
- package/lib/utils/logger.js +38 -0
- package/lib/utils/mixpanel.js +101 -0
- package/lib/utils/project.js +3 -2
- package/lib/utils/utils.js +41 -4
- package/package.json +13 -19
- package/types.d.ts +15 -5
- package/lib/generators/text-bak-old.js +0 -1121
- package/lib/orchestrators/worker-manager.js +0 -203
- package/lib/templates/phrases-bak.js +0 -925
- package/lib/templates/prompt (old).txt +0 -98
- package/lib/templates/scratch-dungeon-template.js +0 -116
- 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
|
-
|
|
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
|
-
|
|
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);
|
package/lib/generators/scd.js
CHANGED
|
@@ -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 {
|
|
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
|
|
package/lib/generators/text.js
CHANGED
|
@@ -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
|
|
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
|