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,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Too Big Events" dungeon
|
|
3
|
+
* Tests Mixpanel row limit (1MB) and column limit (8KB for arrays of objects)
|
|
4
|
+
* Generates events with absurd numbers of properties and oversized array-of-object columns
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function integer(min = 1, max = 100) {
|
|
8
|
+
if (min === max) return min;
|
|
9
|
+
if (min > max) [min, max] = [max, min];
|
|
10
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function loremChunk(size = 200) {
|
|
14
|
+
const words = ["foo", "bar", "baz", "qux", "garply", "waldo", "fred", "plugh", "xyzzy", "thud", "corge", "grault", "flob", "zorp", "narf", "blip", "snork", "quux", "wobble", "crumn"];
|
|
15
|
+
let result = "";
|
|
16
|
+
while (result.length < size) {
|
|
17
|
+
result += words[Math.floor(Math.random() * words.length)] + " ";
|
|
18
|
+
}
|
|
19
|
+
return result.trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Generate a fat array of objects (targeting >8KB per column)
|
|
23
|
+
function bigArrayOfObjects(numItems = 50) {
|
|
24
|
+
numItems = numItems * 2
|
|
25
|
+
return () => {
|
|
26
|
+
const arr = [];
|
|
27
|
+
for (let i = 0; i < numItems; i++) {
|
|
28
|
+
arr.push({
|
|
29
|
+
id: `item_${i}_${Math.random().toString(36).slice(2, 10)}`,
|
|
30
|
+
name: loremChunk(80),
|
|
31
|
+
description: loremChunk(150),
|
|
32
|
+
category: ["foo", "bar", "baz", "qux"][Math.floor(Math.random() * 4)],
|
|
33
|
+
price: parseFloat((Math.random() * 999).toFixed(2)),
|
|
34
|
+
quantity: integer(1, 100),
|
|
35
|
+
tags: ["alpha", "beta", "gamma", "delta", "epsilon"].slice(0, integer(1, 5)),
|
|
36
|
+
metadata: {
|
|
37
|
+
source: loremChunk(40),
|
|
38
|
+
ref: Math.random().toString(36).slice(2, 14),
|
|
39
|
+
ts: new Date().toISOString()
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return arr;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate a truly massive array of objects (way over 8KB)
|
|
48
|
+
function hugeArrayOfObjects() {
|
|
49
|
+
return bigArrayOfObjects(150)();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build a ton of flat properties to bloat row size toward 1MB
|
|
53
|
+
function manyProperties(count = 200) {
|
|
54
|
+
count = count * 2
|
|
55
|
+
const props = {};
|
|
56
|
+
for (let i = 0; i < count; i++) {
|
|
57
|
+
const type = i % 4;
|
|
58
|
+
if (type === 0) props[`str_prop_${i}`] = () => loremChunk(300);
|
|
59
|
+
else if (type === 1) props[`num_prop_${i}`] = () => integer(1, 999999);
|
|
60
|
+
else if (type === 2) props[`bool_prop_${i}`] = [true, false];
|
|
61
|
+
else props[`list_prop_${i}`] = () => Array.from({ length: integer(5, 20) }, () => loremChunk(50));
|
|
62
|
+
}
|
|
63
|
+
return props;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const seed = "too-big-" + Math.random().toString(36).slice(2, 8);
|
|
67
|
+
|
|
68
|
+
/** @type {import('../types').Dungeon} */
|
|
69
|
+
const config = {
|
|
70
|
+
token: "",
|
|
71
|
+
seed: seed,
|
|
72
|
+
numDays: 30,
|
|
73
|
+
numEvents: 1000,
|
|
74
|
+
numUsers: 50,
|
|
75
|
+
format: 'json',
|
|
76
|
+
region: "US",
|
|
77
|
+
hasAnonIds: false,
|
|
78
|
+
hasSessionIds: false,
|
|
79
|
+
batchSize: 10000,
|
|
80
|
+
hasAdSpend: false,
|
|
81
|
+
hasAvatar: false,
|
|
82
|
+
hasBrowser: false,
|
|
83
|
+
hasCampaigns: false,
|
|
84
|
+
hasIOSDevices: false,
|
|
85
|
+
hasLocation: false,
|
|
86
|
+
isAnonymous: true,
|
|
87
|
+
hasAndroidDevices: false,
|
|
88
|
+
hasDesktopDevices: false,
|
|
89
|
+
writeToDisk: true,
|
|
90
|
+
concurrency: 1,
|
|
91
|
+
|
|
92
|
+
events: [
|
|
93
|
+
{
|
|
94
|
+
// ~200 string/number/bool/list properties = fat rows approaching 1MB
|
|
95
|
+
event: "mega_row",
|
|
96
|
+
weight: 5,
|
|
97
|
+
properties: {
|
|
98
|
+
...manyProperties(250),
|
|
99
|
+
// also throw in some big array-of-object columns
|
|
100
|
+
cart_items: bigArrayOfObjects(60),
|
|
101
|
+
order_history: bigArrayOfObjects(80),
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
// single event focused on huge array-of-object columns (well over 8KB each)
|
|
106
|
+
event: "array_bomb",
|
|
107
|
+
weight: 5,
|
|
108
|
+
properties: {
|
|
109
|
+
massive_list_a: bigArrayOfObjects(150),
|
|
110
|
+
massive_list_b: bigArrayOfObjects(150),
|
|
111
|
+
massive_list_c: bigArrayOfObjects(100),
|
|
112
|
+
some_prop: ["foo", "bar", "baz"],
|
|
113
|
+
another_prop: () => integer(1, 1000),
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
// moderate event with a mix of oversized columns
|
|
118
|
+
event: "chonky_boi",
|
|
119
|
+
weight: 3,
|
|
120
|
+
properties: {
|
|
121
|
+
...manyProperties(100),
|
|
122
|
+
nested_blob: () => {
|
|
123
|
+
const obj = {};
|
|
124
|
+
for (let i = 0; i < 50; i++) {
|
|
125
|
+
obj[`key_${i}`] = {
|
|
126
|
+
value: loremChunk(200),
|
|
127
|
+
items: Array.from({ length: 20 }, (_, j) => ({
|
|
128
|
+
id: j,
|
|
129
|
+
data: loremChunk(100),
|
|
130
|
+
flag: Math.random() > 0.5
|
|
131
|
+
}))
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return obj;
|
|
135
|
+
},
|
|
136
|
+
product_catalog: bigArrayOfObjects(120),
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
// normal-ish event for contrast
|
|
141
|
+
event: "smol_event",
|
|
142
|
+
weight: 2,
|
|
143
|
+
properties: {
|
|
144
|
+
color: ["red", "blue", "green"],
|
|
145
|
+
count: () => integer(1, 10),
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
|
|
150
|
+
superProps: {},
|
|
151
|
+
userProps: {
|
|
152
|
+
name: () => `user_${Math.random().toString(36).slice(2, 8)}`,
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
scdProps: {},
|
|
156
|
+
mirrorProps: {},
|
|
157
|
+
lookupTables: [],
|
|
158
|
+
groupKeys: [],
|
|
159
|
+
groupProps: {},
|
|
160
|
+
|
|
161
|
+
hook: function (record, type, meta) {
|
|
162
|
+
return record;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export default config;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
{
|
|
2
|
+
"events": [
|
|
3
|
+
{
|
|
4
|
+
"event": "account created",
|
|
5
|
+
"weight": 0,
|
|
6
|
+
"isFirstEvent": true,
|
|
7
|
+
"properties": {
|
|
8
|
+
"signup_source": ["organic", "referral", "demo_request", "product_hunt", "g2_review"],
|
|
9
|
+
"initial_plan": ["free_trial", "free_trial", "free_trial", "growth", "enterprise"]
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"event": "agent deployed",
|
|
14
|
+
"weight": 3,
|
|
15
|
+
"properties": {
|
|
16
|
+
"agent_type": ["pre_call_researcher", "meeting_assistant", "follow_up_writer", "crm_updater", "pipeline_monitor", "outreach_agent"],
|
|
17
|
+
"configuration_time_min": {"$range": [2, 45]}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"event": "meeting joined",
|
|
22
|
+
"weight": 5,
|
|
23
|
+
"properties": {
|
|
24
|
+
"meeting_platform": ["zoom", "zoom", "google_meet", "google_meet", "teams", "webex"],
|
|
25
|
+
"meeting_type": ["discovery", "demo", "negotiation", "check_in", "onboarding", "qbr"],
|
|
26
|
+
"participants": {"$range": [2, 12]},
|
|
27
|
+
"duration_min": {"$range": [10, 90]}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"event": "meeting summary generated",
|
|
32
|
+
"weight": 5,
|
|
33
|
+
"properties": {
|
|
34
|
+
"summary_length_words": {"$range": [100, 800]},
|
|
35
|
+
"action_items_count": {"$range": [0, 8]},
|
|
36
|
+
"quality_score": {"$range": [50, 95]}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"event": "crm auto-updated",
|
|
41
|
+
"weight": 8,
|
|
42
|
+
"properties": {
|
|
43
|
+
"crm_platform": ["hubspot", "hubspot", "salesforce", "salesforce", "pipedrive"],
|
|
44
|
+
"fields_updated": {"$range": [1, 6]},
|
|
45
|
+
"update_type": ["contact_info", "deal_stage", "activity_log", "meeting_notes", "next_steps"]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"event": "follow-up drafted",
|
|
50
|
+
"weight": 4,
|
|
51
|
+
"properties": {
|
|
52
|
+
"personalization_score": {"$range": [30, 98]},
|
|
53
|
+
"template_used": ["post_meeting", "deal_nudge", "re_engagement", "intro", "proposal"],
|
|
54
|
+
"word_count": {"$range": [50, 400]}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"event": "follow-up sent",
|
|
59
|
+
"weight": 3,
|
|
60
|
+
"properties": {
|
|
61
|
+
"channel": ["email", "email", "email", "linkedin", "slack"],
|
|
62
|
+
"personalization_score": {"$range": [30, 98]},
|
|
63
|
+
"time_to_send_hr": {"$range": [0, 48]},
|
|
64
|
+
"email_outcome": ["pending", "pending", "pending", "opened", "opened", "clicked", "bounced", "replied"]
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"event": "pipeline alert received",
|
|
69
|
+
"weight": 4,
|
|
70
|
+
"properties": {
|
|
71
|
+
"alert_type": ["deal_stalling", "champion_left", "competitor_mentioned", "no_activity", "budget_risk"],
|
|
72
|
+
"severity": ["low", "medium", "high", "critical"],
|
|
73
|
+
"deal_value": {"$range": [5000, 500000]}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"event": "deal stage changed",
|
|
78
|
+
"weight": 5,
|
|
79
|
+
"properties": {
|
|
80
|
+
"from_stage": ["lead", "qualified", "proposal", "negotiation", "verbal_commit"],
|
|
81
|
+
"to_stage": ["qualified", "proposal", "negotiation", "verbal_commit", "closed_won", "closed_lost"],
|
|
82
|
+
"days_in_previous_stage": {"$range": [1, 60]}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"event": "insight generated",
|
|
87
|
+
"weight": 6,
|
|
88
|
+
"properties": {
|
|
89
|
+
"insight_type": ["deal_risk", "buyer_intent", "competitive_intel", "engagement_trend", "revenue_forecast", "coaching_opportunity"],
|
|
90
|
+
"confidence_score": {"$range": [55, 98]},
|
|
91
|
+
"data_sources_used": {"$range": [1, 5]}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"event": "agent feedback",
|
|
96
|
+
"weight": 4,
|
|
97
|
+
"properties": {
|
|
98
|
+
"rating": [1, 2, 3, 3, 4, 4, 4, 5, 5, 5],
|
|
99
|
+
"feedback_type": ["accuracy", "relevance", "timeliness", "completeness", "tone"],
|
|
100
|
+
"agent_type": ["meeting_assistant", "follow_up_writer", "crm_updater", "pre_call_researcher"]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"event": "dashboard viewed",
|
|
105
|
+
"weight": 7,
|
|
106
|
+
"properties": {
|
|
107
|
+
"dashboard_type": ["pipeline_overview", "team_performance", "ai_agent_stats", "revenue_forecast", "activity_feed"],
|
|
108
|
+
"time_spent_sec": {"$range": [10, 300]}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"event": "integration connected",
|
|
113
|
+
"weight": 1,
|
|
114
|
+
"properties": {
|
|
115
|
+
"integration_name": ["hubspot", "salesforce", "slack", "google_calendar", "zoom", "linkedin", "gmail", "outlook"],
|
|
116
|
+
"setup_time_min": {"$range": [2, 30]}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"event": "search performed",
|
|
121
|
+
"weight": 3,
|
|
122
|
+
"properties": {
|
|
123
|
+
"search_type": ["deals", "contacts", "meetings", "insights", "activities"],
|
|
124
|
+
"results_count": {"$range": [0, 50]},
|
|
125
|
+
"clicked_result": [true, true, true, false]
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"event": "workflow created",
|
|
130
|
+
"weight": 2,
|
|
131
|
+
"properties": {
|
|
132
|
+
"workflow_type": ["meeting_prep", "post_meeting", "deal_update", "weekly_digest", "pipeline_review"],
|
|
133
|
+
"steps_count": {"$range": [2, 8]},
|
|
134
|
+
"trigger_type": ["time_based", "event_based", "manual"]
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"event": "contact enriched",
|
|
139
|
+
"weight": 5,
|
|
140
|
+
"properties": {
|
|
141
|
+
"enrichment_source": ["linkedin", "company_website", "crm_history", "email_threads", "meeting_notes"],
|
|
142
|
+
"fields_enriched": {"$range": [2, 12]},
|
|
143
|
+
"confidence_level": ["high", "high", "medium", "medium", "low"]
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"event": "deal created",
|
|
148
|
+
"weight": 2,
|
|
149
|
+
"properties": {
|
|
150
|
+
"deal_source": ["inbound", "outbound", "referral", "upsell", "expansion"],
|
|
151
|
+
"estimated_value": {"$range": [5000, 250000]},
|
|
152
|
+
"close_date_days": {"$range": [14, 120]}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"event": "coaching tip viewed",
|
|
157
|
+
"weight": 3,
|
|
158
|
+
"properties": {
|
|
159
|
+
"tip_category": ["objection_handling", "discovery_questions", "closing_techniques", "rapport_building", "negotiation"],
|
|
160
|
+
"tip_relevance_score": {"$range": [40, 98]},
|
|
161
|
+
"applied": [true, false, false, false]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"superProps": {
|
|
166
|
+
"subscription_tier": ["starter", "growth", "growth", "enterprise"],
|
|
167
|
+
"team_role": ["sales_rep", "sales_rep", "sales_rep", "sales_manager", "rev_ops", "marketing"],
|
|
168
|
+
"ai_agent_version": ["v1.0", "v1.0", "v1.5", "v1.5", "v1.5", "v2.0", "v2.0", "v2.0"]
|
|
169
|
+
},
|
|
170
|
+
"userProps": {
|
|
171
|
+
"company_size": ["SMB", "mid_market", "enterprise"],
|
|
172
|
+
"industry": ["SaaS", "fintech", "healthcare", "e_commerce", "consulting", "manufacturing"],
|
|
173
|
+
"deals_in_pipeline": {"$range": [2, 50]},
|
|
174
|
+
"onboarding_cohort": ["Q1_2025", "Q2_2025", "Q3_2025", "Q4_2025"],
|
|
175
|
+
"integrations_connected": {"$range": [1, 8]}
|
|
176
|
+
},
|
|
177
|
+
"funnels": [
|
|
178
|
+
{
|
|
179
|
+
"name": "Onboarding",
|
|
180
|
+
"sequence": ["account created", "integration connected", "agent deployed", "meeting joined"],
|
|
181
|
+
"isFirstFunnel": true,
|
|
182
|
+
"conversionRate": 65,
|
|
183
|
+
"timeToConvert": 48,
|
|
184
|
+
"order": "sequential",
|
|
185
|
+
"weight": 1
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "Meeting to Action",
|
|
189
|
+
"sequence": ["meeting joined", "meeting summary generated", "follow-up drafted", "follow-up sent"],
|
|
190
|
+
"conversionRate": 70,
|
|
191
|
+
"timeToConvert": 4,
|
|
192
|
+
"order": "sequential",
|
|
193
|
+
"weight": 8
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "Pipeline Management",
|
|
197
|
+
"sequence": ["pipeline alert received", "dashboard viewed", "deal stage changed"],
|
|
198
|
+
"conversionRate": 45,
|
|
199
|
+
"timeToConvert": 24,
|
|
200
|
+
"order": "sequential",
|
|
201
|
+
"weight": 5
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"name": "AI Adoption",
|
|
205
|
+
"sequence": ["agent deployed", "insight generated", "agent feedback"],
|
|
206
|
+
"conversionRate": 55,
|
|
207
|
+
"timeToConvert": 72,
|
|
208
|
+
"order": "sequential",
|
|
209
|
+
"weight": 4
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"name": "Value Realization",
|
|
213
|
+
"sequence": ["dashboard viewed", "workflow created", "agent deployed"],
|
|
214
|
+
"conversionRate": 40,
|
|
215
|
+
"timeToConvert": 96,
|
|
216
|
+
"order": "sequential",
|
|
217
|
+
"weight": 3
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
package/dungeons/userAgent.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
const SEED = "my-seed";
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
|
-
import utc from 'dayjs/plugin/utc';
|
|
4
|
+
import utc from 'dayjs/plugin/utc.js';
|
|
5
5
|
dayjs.extend(utc);
|
|
6
6
|
import 'dotenv/config';
|
|
7
|
-
import * as u from
|
|
7
|
+
import * as u from "../lib/utils/utils.js";
|
|
8
8
|
import * as v from 'ak-tools';
|
|
9
9
|
const chance = u.initChance(SEED);
|
|
10
10
|
const num_users = 1000;
|
|
@@ -14,7 +14,7 @@ const days = 30;
|
|
|
14
14
|
|
|
15
15
|
/** @type {Config} */
|
|
16
16
|
const config = {
|
|
17
|
-
token: "",
|
|
17
|
+
token: process.env.MASTER_PROJECT_TOKEN || "",
|
|
18
18
|
seed: SEED,
|
|
19
19
|
numDays: days,
|
|
20
20
|
numEvents: num_users * 100,
|
|
@@ -33,6 +33,7 @@ const config = {
|
|
|
33
33
|
hasAdSpend: true,
|
|
34
34
|
|
|
35
35
|
hasAvatar: true,
|
|
36
|
+
makeChart: false,
|
|
36
37
|
|
|
37
38
|
batchSize: 500_000,
|
|
38
39
|
concurrency: 500,
|
package/index.js
CHANGED
|
@@ -21,8 +21,6 @@ import { StorageManager } from './lib/core/storage.js';
|
|
|
21
21
|
// Orchestrators
|
|
22
22
|
import { userLoop } from './lib/orchestrators/user-loop.js';
|
|
23
23
|
import { sendToMixpanel } from './lib/orchestrators/mixpanel-sender.js';
|
|
24
|
-
import { handleCloudFunctionEntry } from './lib/orchestrators/worker-manager.js';
|
|
25
|
-
|
|
26
24
|
// Generators
|
|
27
25
|
import { makeAdSpend } from './lib/generators/adspend.js';
|
|
28
26
|
import { makeMirror } from './lib/generators/mirror.js';
|
|
@@ -33,9 +31,9 @@ import { makeGroupProfile, makeProfile } from './lib/generators/profiles.js';
|
|
|
33
31
|
// External dependencies
|
|
34
32
|
import dayjs from "dayjs";
|
|
35
33
|
import utc from "dayjs/plugin/utc.js";
|
|
36
|
-
import
|
|
37
|
-
import { timer, sLog } from 'ak-tools';
|
|
34
|
+
import { timer } from 'ak-tools';
|
|
38
35
|
import { existsSync } from 'fs';
|
|
36
|
+
import { dataLogger as logger } from './lib/utils/logger.js';
|
|
39
37
|
|
|
40
38
|
// Initialize dayjs and time constants
|
|
41
39
|
dayjs.extend(utc);
|
|
@@ -134,19 +132,23 @@ async function main(config) {
|
|
|
134
132
|
|
|
135
133
|
}
|
|
136
134
|
|
|
137
|
-
if (config.verbose)
|
|
135
|
+
if (config.verbose) logger.info({ seed: config.seed }, 'Configuring dungeon');
|
|
138
136
|
let validatedConfig;
|
|
139
137
|
try {
|
|
140
138
|
// Step 1: Validate and enrich configuration
|
|
141
139
|
validatedConfig = validateDungeonConfig(config);
|
|
142
|
-
|
|
140
|
+
|
|
141
|
+
// Update FIXED_BEGIN based on configured numDays
|
|
142
|
+
const configNumDays = validatedConfig.numDays || 30;
|
|
143
|
+
global.FIXED_BEGIN = dayjs.unix(FIXED_NOW).subtract(configNumDays, 'd').unix();
|
|
144
|
+
|
|
143
145
|
// Step 1.5: Display configuration summary (CLI mode only)
|
|
144
146
|
if (isCLI && validatedConfig.verbose) {
|
|
145
147
|
displayConfigurationSummary(validatedConfig);
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
// Step 2: Create context with validated config
|
|
149
|
-
const context = createContext(validatedConfig
|
|
151
|
+
const context = createContext(validatedConfig);
|
|
150
152
|
|
|
151
153
|
// Step 3: Initialize storage containers
|
|
152
154
|
const storageManager = new StorageManager(context);
|
|
@@ -160,7 +162,7 @@ async function main(config) {
|
|
|
160
162
|
await generateAdSpendData(context);
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
if (context.config.verbose)
|
|
165
|
+
if (context.config.verbose) logger.info('Starting user and event generation...');
|
|
164
166
|
// Step 5: Main user and event generation
|
|
165
167
|
await userLoop(context);
|
|
166
168
|
|
|
@@ -184,27 +186,27 @@ async function main(config) {
|
|
|
184
186
|
await makeMirror(context);
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
if (context.config.verbose)
|
|
189
|
+
if (context.config.verbose) logger.info('Data generation completed successfully');
|
|
188
190
|
|
|
189
191
|
// ! DATA GENERATION ENDS HERE
|
|
190
192
|
|
|
191
|
-
// Step 10:
|
|
192
|
-
// IMPORTANT: Must happen BEFORE flushing to disk, because flush() clears the arrays
|
|
193
|
-
let importResults;
|
|
194
|
-
if (validatedConfig.token) {
|
|
195
|
-
importResults = await sendToMixpanel(context);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Step 11: Flush lookup tables to disk (always as CSVs)
|
|
193
|
+
// Step 10: Flush lookup tables to disk (always as CSVs)
|
|
199
194
|
if (validatedConfig.writeToDisk) {
|
|
200
195
|
await flushLookupTablesToDisk(storage, validatedConfig);
|
|
201
196
|
}
|
|
202
197
|
|
|
203
|
-
// Step
|
|
198
|
+
// Step 11: Flush other storage containers to disk (if writeToDisk enabled)
|
|
204
199
|
if (validatedConfig.writeToDisk) {
|
|
205
200
|
await flushStorageToDisk(storage, validatedConfig);
|
|
206
201
|
}
|
|
207
202
|
|
|
203
|
+
// Step 12: Send to Mixpanel (if token provided)
|
|
204
|
+
// Now happens AFTER disk flush so batch files are available for import
|
|
205
|
+
let importResults;
|
|
206
|
+
if (validatedConfig.token) {
|
|
207
|
+
importResults = await sendToMixpanel(context);
|
|
208
|
+
}
|
|
209
|
+
|
|
208
210
|
// Step 13: Compile results
|
|
209
211
|
jobTimer.stop(false);
|
|
210
212
|
const { start, end, delta, human } = jobTimer.report(false);
|
|
@@ -222,12 +224,7 @@ async function main(config) {
|
|
|
222
224
|
};
|
|
223
225
|
|
|
224
226
|
} catch (error) {
|
|
225
|
-
|
|
226
|
-
console.error(`\nā Error: ${error.message}\n`);
|
|
227
|
-
console.error(error.stack);
|
|
228
|
-
} else {
|
|
229
|
-
sLog("Main execution error", { error: error.message, stack: error.stack }, "ERROR");
|
|
230
|
-
}
|
|
227
|
+
logger.error({ err: error }, `Error: ${error.message}`);
|
|
231
228
|
throw error;
|
|
232
229
|
}
|
|
233
230
|
}
|
|
@@ -260,8 +257,8 @@ async function generateGroupProfiles(context) {
|
|
|
260
257
|
const { config, storage } = context;
|
|
261
258
|
const { groupKeys, groupProps = {} } = config;
|
|
262
259
|
|
|
263
|
-
if (
|
|
264
|
-
|
|
260
|
+
if (config.verbose) {
|
|
261
|
+
logger.info('Generating group profiles...');
|
|
265
262
|
}
|
|
266
263
|
|
|
267
264
|
for (let i = 0; i < groupKeys.length; i++) {
|
|
@@ -273,8 +270,8 @@ async function generateGroupProfiles(context) {
|
|
|
273
270
|
continue;
|
|
274
271
|
}
|
|
275
272
|
|
|
276
|
-
if (
|
|
277
|
-
|
|
273
|
+
if (config.verbose) {
|
|
274
|
+
logger.info({ groupKey, groupCount }, `Creating ${groupCount.toLocaleString()} ${groupKey} profiles...`);
|
|
278
275
|
}
|
|
279
276
|
|
|
280
277
|
// Get group-specific props if available
|
|
@@ -282,15 +279,15 @@ async function generateGroupProfiles(context) {
|
|
|
282
279
|
|
|
283
280
|
for (let j = 0; j < groupCount; j++) {
|
|
284
281
|
const groupProfile = await makeGroupProfile(context, groupKey, specificGroupProps, {
|
|
285
|
-
[groupKey]:
|
|
282
|
+
[groupKey]: String(j + 1)
|
|
286
283
|
});
|
|
287
284
|
|
|
288
285
|
await groupContainer.hookPush(groupProfile);
|
|
289
286
|
}
|
|
290
287
|
}
|
|
291
288
|
|
|
292
|
-
if (
|
|
293
|
-
|
|
289
|
+
if (config.verbose) {
|
|
290
|
+
logger.info('Group profiles generated successfully');
|
|
294
291
|
}
|
|
295
292
|
}
|
|
296
293
|
|
|
@@ -302,8 +299,8 @@ async function generateLookupTables(context) {
|
|
|
302
299
|
const { config, storage } = context;
|
|
303
300
|
const { lookupTables } = config;
|
|
304
301
|
|
|
305
|
-
if (
|
|
306
|
-
|
|
302
|
+
if (config.verbose) {
|
|
303
|
+
logger.info('Generating lookup tables...');
|
|
307
304
|
}
|
|
308
305
|
|
|
309
306
|
for (let i = 0; i < lookupTables.length; i++) {
|
|
@@ -316,8 +313,8 @@ async function generateLookupTables(context) {
|
|
|
316
313
|
continue;
|
|
317
314
|
}
|
|
318
315
|
|
|
319
|
-
if (
|
|
320
|
-
|
|
316
|
+
if (config.verbose) {
|
|
317
|
+
logger.info({ key, entries }, `Creating ${entries.toLocaleString()} ${key} lookup entries...`);
|
|
321
318
|
}
|
|
322
319
|
|
|
323
320
|
for (let j = 0; j < entries; j++) {
|
|
@@ -330,8 +327,8 @@ async function generateLookupTables(context) {
|
|
|
330
327
|
}
|
|
331
328
|
}
|
|
332
329
|
|
|
333
|
-
if (
|
|
334
|
-
|
|
330
|
+
if (config.verbose) {
|
|
331
|
+
logger.info('Lookup tables generated successfully');
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
334
|
|
|
@@ -343,8 +340,8 @@ async function generateGroupSCDs(context) {
|
|
|
343
340
|
const { config, storage } = context;
|
|
344
341
|
const { scdProps, groupKeys } = config;
|
|
345
342
|
|
|
346
|
-
if (
|
|
347
|
-
|
|
343
|
+
if (config.verbose) {
|
|
344
|
+
logger.info('Generating group SCDs...');
|
|
348
345
|
}
|
|
349
346
|
|
|
350
347
|
// Import utilities and generators
|
|
@@ -366,13 +363,13 @@ async function generateGroupSCDs(context) {
|
|
|
366
363
|
continue; // No SCDs for this group type
|
|
367
364
|
}
|
|
368
365
|
|
|
369
|
-
if (
|
|
370
|
-
|
|
366
|
+
if (config.verbose) {
|
|
367
|
+
logger.info({ groupKey, groupCount }, `Generating SCDs for ${groupCount.toLocaleString()} ${groupKey} entities...`);
|
|
371
368
|
}
|
|
372
369
|
|
|
373
370
|
// Generate SCDs for each group entity
|
|
374
371
|
for (let i = 0; i < groupCount; i++) {
|
|
375
|
-
const groupId =
|
|
372
|
+
const groupId = String(i + 1);
|
|
376
373
|
|
|
377
374
|
// Generate SCDs for this group entity
|
|
378
375
|
for (const [scdKey, scdConfig] of Object.entries(groupSpecificSCDs)) {
|
|
@@ -408,8 +405,8 @@ async function generateGroupSCDs(context) {
|
|
|
408
405
|
}
|
|
409
406
|
}
|
|
410
407
|
|
|
411
|
-
if (
|
|
412
|
-
|
|
408
|
+
if (config.verbose) {
|
|
409
|
+
logger.info('Group SCDs generated successfully');
|
|
413
410
|
}
|
|
414
411
|
}
|
|
415
412
|
|
|
@@ -566,16 +563,6 @@ function extractStorageData(storage) {
|
|
|
566
563
|
};
|
|
567
564
|
}
|
|
568
565
|
|
|
569
|
-
// Cloud Functions setup
|
|
570
|
-
functions.http('entry', async (req, res) => {
|
|
571
|
-
await handleCloudFunctionEntry(req, res, main);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
566
|
// ES Module export
|
|
575
567
|
export default main;
|
|
576
568
|
|
|
577
|
-
// CommonJS compatibility
|
|
578
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
579
|
-
module.exports = main;
|
|
580
|
-
}
|
|
581
|
-
|