make-mp-data 3.0.1 → 3.0.2

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 (50) hide show
  1. package/dungeons/adspend.js +35 -1
  2. package/dungeons/anon.js +25 -1
  3. package/dungeons/{array-of-object-loopup.js → array-of-object-lookup.js} +28 -8
  4. package/dungeons/benchmark-heavy.js +2 -2
  5. package/dungeons/benchmark-light.js +2 -2
  6. package/dungeons/big.js +2 -2
  7. package/dungeons/business.js +59 -12
  8. package/dungeons/complex.js +34 -1
  9. package/dungeons/copilot.js +1 -1
  10. package/dungeons/{harness/harness-education.js → education.js} +29 -12
  11. package/dungeons/experiments.js +15 -2
  12. package/dungeons/{harness/harness-fintech.js → fintech.js} +8 -8
  13. package/dungeons/foobar.js +33 -1
  14. package/dungeons/{harness/harness-food.js → food.js} +7 -4
  15. package/dungeons/funnels.js +38 -1
  16. package/dungeons/gaming.js +25 -5
  17. package/dungeons/media.js +861 -271
  18. package/dungeons/mil.js +29 -2
  19. package/dungeons/mirror.js +33 -1
  20. package/dungeons/{kurby.js → retention-cadence.js} +1 -1
  21. package/dungeons/{harness/harness-gaming.js → rpg.js} +5 -5
  22. package/dungeons/sanity.js +31 -2
  23. package/dungeons/{harness/harness-sass.js → sass.js} +2 -2
  24. package/dungeons/scd.js +46 -1
  25. package/dungeons/simple.js +1 -1
  26. package/dungeons/{harness/harness-social.js → social.js} +2 -2
  27. package/dungeons/streaming.js +373 -0
  28. package/dungeons/strict-event-test.js +1 -1
  29. package/dungeons/student-teacher.js +18 -5
  30. package/dungeons/text-generation.js +38 -1
  31. package/dungeons/too-big-events.js +38 -1
  32. package/dungeons/{userAgent.js → user-agent.js} +21 -1
  33. package/entry.js +5 -4
  34. package/lib/utils/logger.js +0 -4
  35. package/package.json +1 -4
  36. package/dungeons/ai-chat-analytics-ed.js +0 -275
  37. package/dungeons/clinch-agi.js +0 -632
  38. package/dungeons/ecommerce-store.js +0 -0
  39. package/dungeons/harness/harness-media.js +0 -961
  40. package/dungeons/money2020-ed-also.js +0 -277
  41. package/dungeons/money2020-ed.js +0 -580
  42. package/dungeons/uday-schema.json +0 -220
  43. package/lib/templates/funnels-instructions.txt +0 -272
  44. package/lib/templates/hook-examples.json +0 -187
  45. package/lib/templates/hooks-instructions.txt +0 -721
  46. package/lib/templates/refine-instructions.txt +0 -485
  47. package/lib/templates/schema-instructions.txt +0 -285
  48. package/lib/utils/ai.js +0 -896
  49. package/lib/utils/mixpanel.js +0 -101
  50. package/lib/utils/project.js +0 -167
@@ -15,7 +15,7 @@ const days = 100;
15
15
 
16
16
  /** @type {Config} */
17
17
  const config = {
18
- token: process.env.MASTER_PROJECT_TOKEN || "",
18
+ token: "",
19
19
  seed: SEED,
20
20
  numDays: days,
21
21
  numEvents: num_users * 100,
@@ -397,17 +397,30 @@ const config = {
397
397
 
398
398
  if (type === "event") {
399
399
  const EVENT_TIME = dayjs(record.time);
400
+ // Pattern 1: Late submissions get lower scores
401
+ if (record.event === "student: submit assignment" && record.is_late === true) {
402
+ record.late_penalty_percent = chance.integer({ min: 5, max: 25 });
403
+ }
400
404
  }
401
405
 
402
406
  if (type === "user") {
403
-
407
+ // Pattern 2: Teachers get a department assignment
408
+ if (record.role === "teacher") {
409
+ record.department = chance.pickone(["Math", "Science", "English", "History", "Art", "PE"]);
410
+ record.years_experience = chance.integer({ min: 1, max: 30 });
411
+ }
404
412
  }
405
413
 
406
- if (type === "funnel-post") {
407
-
414
+ if (type === "funnel-pre") {
415
+ // Pattern 3: Students on chromebooks have lower test completion rates
416
+ if (meta && meta.profile && meta.profile.device_type === "chromebook") {
417
+ if (record.name === "Student Takes Test") {
418
+ record.conversionRate = Math.max(50, record.conversionRate * 0.7);
419
+ }
420
+ }
408
421
  }
409
422
 
410
- if (type === "funnel-pre") {
423
+ if (type === "funnel-post") {
411
424
 
412
425
  }
413
426
 
@@ -327,7 +327,7 @@ const webinarChatGen = createTextGenerator({
327
327
  /** @type {Dungeon} */
328
328
  const dungeon = {
329
329
  seed: SEED,
330
- token: process.env.MASTER_PROJECT_TOKEN || "",
330
+ token: "",
331
331
  numDays: days,
332
332
  numEvents: num_users * 120, // Increased for more variety
333
333
  numUsers: num_users,
@@ -698,6 +698,43 @@ const dungeon = {
698
698
  ],
699
699
 
700
700
  hook: function (record, type, meta) {
701
+ // --- user hook: classify users by engagement and tier ---
702
+ if (type === "user") {
703
+ record.is_power_user = record.engagement_score > 70;
704
+ record.risk_level = record.last_active_days_ago > 20 ? "high_churn" : "healthy";
705
+ return record;
706
+ }
707
+
708
+ // --- event hook: enterprise support tickets get auto-escalated if critical ---
709
+ if (type === "event") {
710
+ if (record.event === "enterprise_support_ticket" && record.priority === "critical") {
711
+ record.escalation_level = Math.min((record.escalation_level || 1) + 1, 3);
712
+ record.auto_escalated = true;
713
+ }
714
+ // bug reports with critical severity get flagged for immediate review
715
+ if (record.event === "bug_report_submitted" && record.severity === "critical" && record.is_reproducible === true) {
716
+ record.requires_immediate_review = true;
717
+ record.estimated_fix_hours = chance.integer({ min: 1, max: 8 });
718
+ }
719
+ return record;
720
+ }
721
+
722
+ // --- everything hook: enterprise users get a satisfaction survey event ---
723
+ if (type === "everything" && meta && meta.profile) {
724
+ if (meta.profile.user_tier === "enterprise" && record.length > 5) {
725
+ const lastEvent = record[record.length - 1];
726
+ record.push({
727
+ event: "satisfaction_survey_triggered",
728
+ time: lastEvent.time,
729
+ user_id: lastEvent.user_id,
730
+ product_tier: "enterprise",
731
+ survey_type: "quarterly_nps",
732
+ score: chance.integer({ min: 1, max: 10 })
733
+ });
734
+ }
735
+ return record;
736
+ }
737
+
701
738
  return record;
702
739
  }
703
740
  };
@@ -4,6 +4,10 @@
4
4
  * Generates events with absurd numbers of properties and oversized array-of-object columns
5
5
  */
6
6
 
7
+ import dayjs from "dayjs";
8
+ import utc from "dayjs/plugin/utc.js";
9
+ dayjs.extend(utc);
10
+
7
11
  function integer(min = 1, max = 100) {
8
12
  if (min === max) return min;
9
13
  if (min > max) [min, max] = [max, min];
@@ -86,7 +90,7 @@ const config = {
86
90
  isAnonymous: true,
87
91
  hasAndroidDevices: false,
88
92
  hasDesktopDevices: false,
89
- writeToDisk: true,
93
+ writeToDisk: false,
90
94
  concurrency: 1,
91
95
 
92
96
  events: [
@@ -159,6 +163,39 @@ const config = {
159
163
  groupProps: {},
160
164
 
161
165
  hook: function (record, type, meta) {
166
+ // --- event hook: tag events with estimated row size category ---
167
+ if (type === "event") {
168
+ const propCount = Object.keys(record).length;
169
+ if (propCount > 300) {
170
+ record.size_class = "mega";
171
+ } else if (propCount > 100) {
172
+ record.size_class = "large";
173
+ } else {
174
+ record.size_class = "normal";
175
+ }
176
+ return record;
177
+ }
178
+
179
+ // --- everything hook: append a summary event tallying the user's event types ---
180
+ if (type === "everything" && record.length > 0) {
181
+ const counts = {};
182
+ for (const e of record) {
183
+ counts[e.event] = (counts[e.event] || 0) + 1;
184
+ }
185
+ const lastEvent = record[record.length - 1];
186
+ record.push({
187
+ event: "user_event_summary",
188
+ time: dayjs(lastEvent.time).add(1, "second").toISOString(),
189
+ user_id: lastEvent.user_id,
190
+ mega_row_count: counts["mega_row"] || 0,
191
+ array_bomb_count: counts["array_bomb"] || 0,
192
+ chonky_boi_count: counts["chonky_boi"] || 0,
193
+ smol_event_count: counts["smol_event"] || 0,
194
+ total_events: record.length
195
+ });
196
+ return record;
197
+ }
198
+
162
199
  return record;
163
200
  }
164
201
  };
@@ -14,7 +14,7 @@ const days = 30;
14
14
 
15
15
  /** @type {Config} */
16
16
  const config = {
17
- token: process.env.MASTER_PROJECT_TOKEN || "",
17
+ token: "",
18
18
  seed: SEED,
19
19
  numDays: days,
20
20
  numEvents: num_users * 100,
@@ -165,6 +165,26 @@ const config = {
165
165
 
166
166
  if (type === "event") {
167
167
  const EVENT_TIME = dayjs(record.time);
168
+ // Pattern 1: Mobile user agents have higher error rates
169
+ const ua = record.user_agent || "";
170
+ if (ua.includes("iPhone") || ua.includes("Android") || ua.includes("Mobile")) {
171
+ record.is_mobile = true;
172
+ if (Math.random() < 0.15) {
173
+ record.had_error = true;
174
+ record.error_type = chance.pickone(["timeout", "network_fail", "crash"]);
175
+ }
176
+ } else {
177
+ record.is_mobile = false;
178
+ }
179
+
180
+ // Pattern 2: Bot user agents get flagged and have no session duration
181
+ if (ua.includes("bot") || ua.includes("Slurp") || ua.includes("Googlebot")) {
182
+ record.is_bot = true;
183
+ record.session_duration_sec = 0;
184
+ } else {
185
+ record.is_bot = false;
186
+ record.session_duration_sec = chance.integer({ min: 5, max: 600 });
187
+ }
168
188
  }
169
189
 
170
190
  if (type === "user") {
package/entry.js CHANGED
@@ -13,19 +13,20 @@ import getCliParams from './lib/cli/cli.js';
13
13
  const cliConfig = getCliParams();
14
14
 
15
15
  // Load dungeon config - default to simple mode if no mode specified
16
+ // CLI always writes to disk unless explicitly disabled
16
17
  let finalConfig = cliConfig;
17
18
  if (cliConfig.complex) {
18
19
  const complexConfig = await import('./dungeons/complex.js');
19
- finalConfig = { ...complexConfig.default, ...cliConfig };
20
- }
20
+ finalConfig = { ...complexConfig.default, writeToDisk: true, ...cliConfig };
21
+ }
21
22
  else if (cliConfig.sanity) {
22
23
  const sanityConfig = await import('./dungeons/sanity.js');
23
- finalConfig = { ...sanityConfig.default, ...cliConfig };
24
+ finalConfig = { ...sanityConfig.default, writeToDisk: true, ...cliConfig };
24
25
  }
25
26
  else if (cliConfig.simple || (!cliConfig.complex && !cliConfig.simple)) {
26
27
  // Default to simple mode when no flags or when --simple is explicitly set
27
28
  const simpleConfig = await import('./dungeons/simple.js');
28
- finalConfig = { ...simpleConfig.default, ...cliConfig };
29
+ finalConfig = { ...simpleConfig.default, writeToDisk: true, ...cliConfig };
29
30
  }
30
31
 
31
32
 
@@ -29,10 +29,6 @@ const logger = pino({
29
29
  : undefined // JSON output in production for Cloud Run
30
30
  });
31
31
 
32
- // Add child loggers for different components
33
- export const serverLogger = logger.child({ component: 'server' });
34
- export const aiLogger = logger.child({ component: 'ai' });
35
32
  export const dataLogger = logger.child({ component: 'data' });
36
- export const importLogger = logger.child({ component: 'import' });
37
33
 
38
34
  export default logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "builds all mixpanel primitives for a given project",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -22,7 +22,6 @@
22
22
  "dev": "nodemon scratch.mjs --ignore ./data/*",
23
23
  "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
24
24
  "deps": "./scripts/update-deps.sh",
25
- "dungeon:new": "node ./scripts/create-dungeon.mjs",
26
25
  "dungeon:run": "node ./scripts/run-dungeon.mjs",
27
26
  "dungeon:to-json": "node ./scripts/dungeon-to-json.mjs",
28
27
  "dungeon:from-json": "node ./scripts/json-to-dungeon.mjs"
@@ -54,8 +53,6 @@
54
53
  "homepage": "https://github.com/ak--47/make-mp-data#readme",
55
54
  "dependencies": {
56
55
  "@google-cloud/storage": "^7.14.0",
57
- "ak-fetch": "^2.0.15",
58
- "ak-gemini": "^1.1.13",
59
56
  "ak-tools": "^1.1.12",
60
57
  "chance": "^1.1.11",
61
58
  "dayjs": "^1.11.11",
@@ -1,275 +0,0 @@
1
-
2
-
3
- import dayjs from "dayjs";
4
- import utc from "dayjs/plugin/utc.js";
5
- import "dotenv/config";
6
- import { weighNumRange, range, date, initChance, exhaust, choose, integer } from "../lib/utils/utils.js";
7
-
8
- const SEED = "ai-generated-1759889544929";
9
- dayjs.extend(utc);
10
- const chance = initChance(SEED);
11
- const num_users = 1_000;
12
- const days = 100;
13
-
14
- /** @typedef {import("../types.js").Dungeon} Dungeon */
15
-
16
- /** @type {Dungeon} */
17
- const dungeon = {
18
- token: process.env.MASTER_PROJECT_TOKEN || "",
19
- seed: SEED,
20
- numDays: days,
21
- numEvents: num_users * 100,
22
- numUsers: num_users,
23
- hasAnonIds: false,
24
- hasSessionIds: false,
25
- format: "json",
26
- alsoInferFunnels: false,
27
- hasLocation: true,
28
- hasAndroidDevices: false,
29
- hasIOSDevices: false,
30
- hasDesktopDevices: true,
31
- hasBrowser: true,
32
- hasCampaigns: true,
33
- isAnonymous: false,
34
- hasAdSpend: true,
35
-
36
- hasAvatar: true,
37
- makeChart: false,
38
-
39
- batchSize: 2_500_000,
40
- concurrency: 50,
41
- writeToDisk: false,
42
-
43
- // AI-generated schema content:
44
- funnels: [
45
- {
46
- name: "Sign Up",
47
- sequence: [
48
- "Sign Up"
49
- ],
50
- isFirstFunnel: true,
51
- conversionRate: 20,
52
- timeToConvert: 10,
53
- order: "sequential"
54
- },
55
- {
56
- name: "AI Interaction",
57
- sequence: [
58
- "Launch AI",
59
- "AI Prompt Sent",
60
- "AI Response Sent",
61
- "User Feedback",
62
- "AI Dismissed"
63
- ],
64
- isFirstFunnel: false,
65
- conversionRate: 38,
66
- timeToConvert: 5,
67
- order: "sequential",
68
- weight: 7
69
- },
70
- {
71
- name: "AI Interaction Errors",
72
- sequence: [
73
- "Launch AI",
74
- "AI Prompt Sent",
75
- "AI Response Sent",
76
- "API Error",
77
- "User Feedback",
78
- "AI Dismissed"
79
- ],
80
- isFirstFunnel: false,
81
- conversionRate: 27,
82
- timeToConvert: 5,
83
- order: "sequential",
84
- weight: 3
85
- }
86
- ],
87
- events: [
88
- {
89
- event: "Sign Up",
90
- isFirstEvent: true,
91
- weight: 0,
92
- properties: {
93
- signup_method: [
94
- "email",
95
- "google",
96
- "github"
97
- ]
98
- }
99
- },
100
- {
101
- event: "Purchase",
102
- weight: 40,
103
- properties: {
104
- amount: weighNumRange(20, 500, 0.3),
105
- currency: [
106
- "USD",
107
- "EUR",
108
- "GBP"
109
- ],
110
- item_count: weighNumRange(1, 10)
111
- }
112
- },
113
- {
114
- event: "Launch AI",
115
- weight: 2,
116
- properties: {
117
- entry_point: [
118
- "dashboard_widget",
119
- "header_button",
120
- "in_app_prompt"
121
- ]
122
- }
123
- },
124
- {
125
- event: "AI Prompt Sent",
126
- weight: 10,
127
- properties: {
128
- prompt: [
129
- "how can I make a dashboard?",
130
- "what is a funnel?",
131
- "what drives new users?",
132
- "show me my top performing campaigns",
133
- "compare user retention by country"
134
- ],
135
- prompt_length: weighNumRange(15, 150)
136
- }
137
- },
138
- {
139
- event: "AI Response Sent",
140
- weight: 10,
141
- properties: {
142
- cost: weighNumRange(1, 10, 0.2),
143
- tokens: weighNumRange(100, 1000, 0.4),
144
- time_to_generate_ms: weighNumRange(1000, 10000, 0.2)
145
- }
146
- },
147
- {
148
- event: "User Feedback",
149
- weight: 4,
150
- properties: {
151
- feedback: [
152
- "I love it!",
153
- "meh...",
154
- "This sucks",
155
- "Fine"
156
- ],
157
- sentiment: [
158
- "thumbs up",
159
- "thumbs down"
160
- ]
161
- }
162
- },
163
- {
164
- event: "AI Dismissed",
165
- weight: 2,
166
- properties: {
167
- reason: [
168
- "finished",
169
- "clicked_away",
170
- "new_prompt",
171
- "error"
172
- ]
173
- }
174
- },
175
- {
176
- event: "API Error",
177
- weight: 2,
178
- properties: {
179
- error_code: [
180
- 400,
181
- 401,
182
- 429,
183
- 500,
184
- 503
185
- ],
186
- error_message: [
187
- "Bad Request",
188
- "Unauthorized",
189
- "Too Many Requests",
190
- "Internal Server Error",
191
- "Service Unavailable"
192
- ]
193
- }
194
- }
195
- ],
196
- superProps: {
197
- $os: [
198
- "Windows",
199
- "Mac OS X",
200
- "Linux",
201
- "Windows",
202
- "Mac OS X"
203
- ],
204
- $browser: [
205
- "Chrome",
206
- "Firefox",
207
- "Safari",
208
- "Edge",
209
- "Chrome"
210
- ],
211
- $device: [
212
- "Desktop",
213
- "Desktop",
214
- "Desktop",
215
- "Laptop"
216
- ],
217
- utm_source: [
218
- "$organic",
219
- "$organic",
220
- "google",
221
- "twitter",
222
- "linkedin",
223
- "product_hunt"
224
- ]
225
- },
226
- userProps: {
227
- plan_type: [
228
- "free",
229
- "pro",
230
- "pro",
231
- "enterprise",
232
- "free"
233
- ],
234
- company_size: [
235
- "1-10",
236
- "11-50",
237
- "51-200",
238
- "201-1000",
239
- "1000+"
240
- ],
241
- created_date: date(365, true, 'YYYY-MM-DD')
242
- },
243
-
244
- hook: function (record, type, meta) {
245
- const NOW = dayjs();
246
-
247
- if (type === "event") {
248
- const EVENT_TIME = dayjs(record.time);
249
- }
250
-
251
- if (type === "user") {
252
-
253
- }
254
-
255
- if (type === "funnel-post") {
256
-
257
- }
258
-
259
- if (type === "funnel-pre") {
260
-
261
- }
262
-
263
- if (type === "scd-pre") {
264
-
265
- }
266
-
267
- if (type === "everything") {
268
-
269
- }
270
-
271
- return record;
272
- }
273
- };
274
-
275
- export default dungeon;