make-mp-data 2.1.11 → 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 (71) hide show
  1. package/README.md +31 -0
  2. package/dungeons/adspend.js +35 -1
  3. package/dungeons/anon.js +25 -1
  4. package/dungeons/array-of-object-lookup.js +201 -0
  5. package/dungeons/benchmark-heavy.js +241 -0
  6. package/dungeons/benchmark-light.js +141 -0
  7. package/dungeons/big.js +10 -9
  8. package/dungeons/business.js +60 -12
  9. package/dungeons/complex.js +35 -1
  10. package/dungeons/copilot.js +383 -0
  11. package/dungeons/education.js +1005 -0
  12. package/dungeons/experiments.js +18 -4
  13. package/dungeons/fintech.js +976 -0
  14. package/dungeons/foobar.js +32 -0
  15. package/dungeons/food.js +988 -0
  16. package/dungeons/funnels.js +38 -1
  17. package/dungeons/gaming.js +26 -5
  18. package/dungeons/media.js +861 -270
  19. package/dungeons/mil.js +31 -3
  20. package/dungeons/mirror.js +33 -1
  21. package/dungeons/retention-cadence.js +211 -0
  22. package/dungeons/rpg.js +1178 -0
  23. package/dungeons/sanity.js +32 -2
  24. package/dungeons/sass.js +923 -0
  25. package/dungeons/scd.js +47 -1
  26. package/dungeons/simple.js +29 -14
  27. package/dungeons/social.js +928 -0
  28. package/dungeons/streaming.js +373 -0
  29. package/dungeons/strict-event-test.js +30 -0
  30. package/dungeons/student-teacher.js +19 -5
  31. package/dungeons/text-generation.js +120 -84
  32. package/dungeons/too-big-events.js +203 -0
  33. package/dungeons/{userAgent.js → user-agent.js} +23 -2
  34. package/entry.js +5 -4
  35. package/index.js +41 -54
  36. package/lib/core/config-validator.js +122 -7
  37. package/lib/core/context.js +7 -14
  38. package/lib/core/storage.js +57 -25
  39. package/lib/generators/adspend.js +12 -12
  40. package/lib/generators/events.js +6 -5
  41. package/lib/generators/funnels.js +32 -10
  42. package/lib/generators/product-lookup.js +262 -0
  43. package/lib/generators/product-names.js +195 -0
  44. package/lib/generators/profiles.js +3 -3
  45. package/lib/generators/scd.js +13 -3
  46. package/lib/generators/text.js +17 -4
  47. package/lib/orchestrators/mixpanel-sender.js +244 -204
  48. package/lib/orchestrators/user-loop.js +54 -16
  49. package/lib/templates/phrases.js +473 -16
  50. package/lib/templates/schema.d.ts +173 -0
  51. package/lib/templates/verbose-schema.js +140 -206
  52. package/lib/utils/chart.js +210 -0
  53. package/lib/utils/function-registry.js +285 -0
  54. package/lib/utils/json-evaluator.js +172 -0
  55. package/lib/utils/logger.js +34 -0
  56. package/lib/utils/utils.js +41 -4
  57. package/package.json +12 -21
  58. package/types.d.ts +15 -5
  59. package/dungeons/ai-chat-analytics-ed.js +0 -274
  60. package/dungeons/money2020-ed-also.js +0 -277
  61. package/dungeons/money2020-ed.js +0 -579
  62. package/lib/generators/text-bak-old.js +0 -1121
  63. package/lib/orchestrators/worker-manager.js +0 -203
  64. package/lib/templates/hooks-instructions.txt +0 -434
  65. package/lib/templates/phrases-bak.js +0 -925
  66. package/lib/templates/prompt (old).txt +0 -98
  67. package/lib/templates/schema-instructions.txt +0 -155
  68. package/lib/templates/scratch-dungeon-template.js +0 -116
  69. package/lib/templates/textQuickTest.js +0 -172
  70. package/lib/utils/ai.js +0 -120
  71. package/lib/utils/project.js +0 -166
@@ -208,10 +208,10 @@ function choose(value) {
208
208
 
209
209
  }
210
210
 
211
- // if the thing has a .next() method, call that
211
+ // if the thing has a .next() method, call that (e.g., generators/iterators)
212
212
  try {
213
- if (value && typeof value.next === 'function') {
214
- return value.next();
213
+ if (value && typeof /** @type {any} */ (value).next === 'function') {
214
+ return /** @type {any} */ (value).next();
215
215
  }
216
216
  } catch (e) {
217
217
  console.error(`Error occurred while calling next(): ${e}`);
@@ -1070,7 +1070,42 @@ function buildFileNames(config) {
1070
1070
  }
1071
1071
 
1072
1072
  /**
1073
- * @param {[string, number][]} arrayOfArrays
1073
+ * Human-readable byte size
1074
+ * @param {number} bytes
1075
+ * @param {number} dp - decimal places
1076
+ * @param {boolean} si - use SI units
1077
+ * @returns {string}
1078
+ */
1079
+ function bytesHuman(bytes, dp = 2, si = true) {
1080
+ const thresh = si ? 1000 : 1024;
1081
+ if (Math.abs(bytes) < thresh) {
1082
+ return bytes + ' B';
1083
+ }
1084
+ const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
1085
+ let u = -1;
1086
+ const r = 10 ** dp;
1087
+ do {
1088
+ bytes /= thresh;
1089
+ ++u;
1090
+ } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
1091
+ return bytes.toFixed(dp) + ' ' + units[u];
1092
+ }
1093
+
1094
+ /**
1095
+ * Format milliseconds as HH:MM:SS
1096
+ * @param {number} ms - Milliseconds
1097
+ * @returns {string} Formatted duration string
1098
+ */
1099
+ function formatDuration(ms) {
1100
+ const seconds = Math.floor(ms / 1000);
1101
+ const hours = Math.floor(seconds / 3600);
1102
+ const minutes = Math.floor((seconds % 3600) / 60);
1103
+ const secs = seconds % 60;
1104
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
1105
+ }
1106
+
1107
+ /**
1108
+ * @param {[string, string | number][]} arrayOfArrays
1074
1109
  */
1075
1110
  function progress(arrayOfArrays) {
1076
1111
  const terminalWidth = process.stdout.columns || 120;
@@ -1503,4 +1538,6 @@ export {
1503
1538
  datesBetween,
1504
1539
  weighChoices,
1505
1540
  wrapFunc,
1541
+ bytesHuman,
1542
+ formatDuration,
1506
1543
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "2.1.11",
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",
7
7
  "types": "types.d.ts",
8
8
  "files": [
9
9
  "index.js",
10
- "cli.js",
10
+ "entry.js",
11
11
  "types.d.ts",
12
12
  "lib/",
13
13
  "dungeons/",
@@ -16,23 +16,15 @@
16
16
  "README.md"
17
17
  ],
18
18
  "scripts": {
19
- "new:dungeon": "node ./scripts/create-dungeon.mjs",
20
- "start": "node ./index.js",
21
- "dev": "nodemon scratch.mjs --ignore ./data/*",
22
- "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
23
19
  "post": "npm publish",
24
- "deps": "./scripts/update-deps.sh",
25
20
  "test": "NODE_ENV=test vitest run",
26
- "coverage": "vitest run --coverage && open ./coverage/index.html",
27
21
  "typecheck": "tsc --noEmit",
28
- "typecheck:build": "tsc --noEmit --project tsconfig.build.json",
29
- "typecheck:strict": "echo '⚠️ Running pedantic strict checks (many false positives expected)...' && tsc --noEmit --project tsconfig.build.json --strict",
30
- "typecheck:summary": "echo '🔍 Type-checking all shipped files...' && npm run typecheck:build && echo '✅ All TypeScript checks passed!'",
31
- "test:quick": "node ./tests/benchmark/quick-test.mjs",
32
- "exp:soup": "node ./tests/testSoup.mjs",
33
- "func:local": "functions-framework --target=entry",
34
- "func:deploy": "./scripts/deploy.sh",
35
- "textGen": "node ./lib/templates/textQuickTest.js"
22
+ "dev": "nodemon scratch.mjs --ignore ./data/*",
23
+ "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
24
+ "deps": "./scripts/update-deps.sh",
25
+ "dungeon:run": "node ./scripts/run-dungeon.mjs",
26
+ "dungeon:to-json": "node ./scripts/dungeon-to-json.mjs",
27
+ "dungeon:from-json": "node ./scripts/json-to-dungeon.mjs"
36
28
  },
37
29
  "repository": {
38
30
  "type": "git",
@@ -60,18 +52,17 @@
60
52
  },
61
53
  "homepage": "https://github.com/ak--47/make-mp-data#readme",
62
54
  "dependencies": {
63
- "@google-cloud/functions-framework": "^3.4.2",
64
55
  "@google-cloud/storage": "^7.14.0",
65
- "ak-fetch": "^2.0.12",
66
- "ak-gemini": "^1.0.12",
67
56
  "ak-tools": "^1.1.12",
68
57
  "chance": "^1.1.11",
69
58
  "dayjs": "^1.11.11",
70
59
  "dotenv": "^16.4.5",
71
- "google-auth-library": "^9.15.0",
72
60
  "hyparquet-writer": "^0.6.1",
73
- "mixpanel-import": "^3.0.0",
61
+ "mixpanel": "^0.18.0",
62
+ "mixpanel-import": "^3.2.8",
74
63
  "p-limit": "^3.1.0",
64
+ "pino": "^9.0.0",
65
+ "pino-pretty": "^11.0.0",
75
66
  "seedrandom": "^3.0.5",
76
67
  "sentiment": "^5.0.2",
77
68
  "tracery-grammar": "^2.8.4",
package/types.d.ts CHANGED
@@ -48,7 +48,10 @@ export interface Dungeon {
48
48
  hasAnonIds?: boolean;
49
49
  hasSessionIds?: boolean;
50
50
  alsoInferFunnels?: boolean;
51
+ makeChart?: boolean | string;
51
52
  singleCountry?: string;
53
+ strictEventCount?: boolean;
54
+ isUIJob?: boolean;
52
55
 
53
56
  //models
54
57
  events?: EventConfig[]; //| string[]; //can also be a array of strings
@@ -69,13 +72,15 @@ export interface Dungeon {
69
72
 
70
73
  //probabilities
71
74
  percentUsersBornInDataset?: number;
75
+ /** Bias toward recent birth dates for users born in dataset (0 = uniform, 1 = heavily recent). Default: 0.3 */
76
+ bornRecentBias?: number;
72
77
  }
73
78
 
74
79
  export type SCDProp = {
75
80
  type?: string | "user" | "company_id" | "team_id" | "department_id";
76
- frequency: "day" | "week" | "month" | "year";
81
+ frequency?: "day" | "week" | "month" | "year";
77
82
  values: ValueValid;
78
- timing: "fixed" | "fuzzy";
83
+ timing?: "fixed" | "fuzzy";
79
84
  max?: number;
80
85
  };
81
86
 
@@ -164,7 +169,6 @@ export interface RuntimeState {
164
169
  userCount: number;
165
170
  isBatchMode: boolean;
166
171
  verbose: boolean;
167
- isCLI: boolean;
168
172
  }
169
173
 
170
174
  /**
@@ -209,7 +213,6 @@ export interface Context {
209
213
  incrementUserCount(): void;
210
214
  incrementEventCount(): void;
211
215
  isBatchMode(): boolean;
212
- isCLI(): boolean;
213
216
 
214
217
  // Time helper methods
215
218
  getTimeShift(): number;
@@ -227,6 +230,7 @@ export interface EventConfig {
227
230
  isChurnEvent?: boolean;
228
231
  isSessionStartEvent?: boolean;
229
232
  relativeTimeMs?: number;
233
+ isStrictEvent?: boolean;
230
234
  }
231
235
 
232
236
  export interface GroupEventConfig extends EventConfig {
@@ -322,6 +326,10 @@ export interface Funnel {
322
326
  *
323
327
  */
324
328
  experiment?: boolean;
329
+ /**
330
+ * optional: if set, in sequential funnels, this will determine WHEN the property is bound to the rest of the events in the funnel
331
+ */
332
+ bindPropsIndex?: number;
325
333
  }
326
334
 
327
335
  /**
@@ -424,6 +432,8 @@ export type Result = {
424
432
  operations?: number;
425
433
  eventCount?: number;
426
434
  userCount?: number;
435
+ groupCount?: number;
436
+ avgEPS?: number;
427
437
  };
428
438
 
429
439
  /**
@@ -698,7 +708,7 @@ export interface TextGenerator {
698
708
  * @param config - Configuration options for the generator
699
709
  * @returns Text generator instance
700
710
  */
701
- export declare function createGenerator(config?: TextGeneratorConfig): TextGenerator;
711
+ export declare function createTextGenerator(config?: TextGeneratorConfig): TextGenerator;
702
712
 
703
713
  /**
704
714
  * Generate a batch of text items directly (standalone function)
@@ -1,274 +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: "",
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
-
38
- batchSize: 1_500_000,
39
- concurrency: 50,
40
- writeToDisk: false,
41
-
42
- // AI-generated schema content:
43
- funnels: [
44
- {
45
- name: "Sign Up",
46
- sequence: [
47
- "Sign Up"
48
- ],
49
- isFirstFunnel: true,
50
- conversionRate: 20,
51
- timeToConvert: 10,
52
- order: "sequential"
53
- },
54
- {
55
- name: "AI Interaction",
56
- sequence: [
57
- "Launch AI",
58
- "AI Prompt Sent",
59
- "AI Response Sent",
60
- "User Feedback",
61
- "AI Dismissed"
62
- ],
63
- isFirstFunnel: false,
64
- conversionRate: 38,
65
- timeToConvert: 5,
66
- order: "sequential",
67
- weight: 7
68
- },
69
- {
70
- name: "AI Interaction Errors",
71
- sequence: [
72
- "Launch AI",
73
- "AI Prompt Sent",
74
- "AI Response Sent",
75
- "API Error",
76
- "User Feedback",
77
- "AI Dismissed"
78
- ],
79
- isFirstFunnel: false,
80
- conversionRate: 27,
81
- timeToConvert: 5,
82
- order: "sequential",
83
- weight: 3
84
- }
85
- ],
86
- events: [
87
- {
88
- event: "Sign Up",
89
- isFirstEvent: true,
90
- weight: 0,
91
- properties: {
92
- signup_method: [
93
- "email",
94
- "google",
95
- "github"
96
- ]
97
- }
98
- },
99
- {
100
- event: "Purchase",
101
- weight: 40,
102
- properties: {
103
- amount: weighNumRange(20, 500, 0.3),
104
- currency: [
105
- "USD",
106
- "EUR",
107
- "GBP"
108
- ],
109
- item_count: weighNumRange(1, 10)
110
- }
111
- },
112
- {
113
- event: "Launch AI",
114
- weight: 2,
115
- properties: {
116
- entry_point: [
117
- "dashboard_widget",
118
- "header_button",
119
- "in_app_prompt"
120
- ]
121
- }
122
- },
123
- {
124
- event: "AI Prompt Sent",
125
- weight: 10,
126
- properties: {
127
- prompt: [
128
- "how can I make a dashboard?",
129
- "what is a funnel?",
130
- "what drives new users?",
131
- "show me my top performing campaigns",
132
- "compare user retention by country"
133
- ],
134
- prompt_length: weighNumRange(15, 150)
135
- }
136
- },
137
- {
138
- event: "AI Response Sent",
139
- weight: 10,
140
- properties: {
141
- cost: weighNumRange(1, 10, 0.2),
142
- tokens: weighNumRange(100, 1000, 0.4),
143
- time_to_generate_ms: weighNumRange(1000, 10000, 0.2)
144
- }
145
- },
146
- {
147
- event: "User Feedback",
148
- weight: 4,
149
- properties: {
150
- feedback: [
151
- "I love it!",
152
- "meh...",
153
- "This sucks",
154
- "Fine"
155
- ],
156
- sentiment: [
157
- "thumbs up",
158
- "thumbs down"
159
- ]
160
- }
161
- },
162
- {
163
- event: "AI Dismissed",
164
- weight: 2,
165
- properties: {
166
- reason: [
167
- "finished",
168
- "clicked_away",
169
- "new_prompt",
170
- "error"
171
- ]
172
- }
173
- },
174
- {
175
- event: "API Error",
176
- weight: 2,
177
- properties: {
178
- error_code: [
179
- 400,
180
- 401,
181
- 429,
182
- 500,
183
- 503
184
- ],
185
- error_message: [
186
- "Bad Request",
187
- "Unauthorized",
188
- "Too Many Requests",
189
- "Internal Server Error",
190
- "Service Unavailable"
191
- ]
192
- }
193
- }
194
- ],
195
- superProps: {
196
- $os: [
197
- "Windows",
198
- "Mac OS X",
199
- "Linux",
200
- "Windows",
201
- "Mac OS X"
202
- ],
203
- $browser: [
204
- "Chrome",
205
- "Firefox",
206
- "Safari",
207
- "Edge",
208
- "Chrome"
209
- ],
210
- $device: [
211
- "Desktop",
212
- "Desktop",
213
- "Desktop",
214
- "Laptop"
215
- ],
216
- utm_source: [
217
- "$organic",
218
- "$organic",
219
- "google",
220
- "twitter",
221
- "linkedin",
222
- "product_hunt"
223
- ]
224
- },
225
- userProps: {
226
- plan_type: [
227
- "free",
228
- "pro",
229
- "pro",
230
- "enterprise",
231
- "free"
232
- ],
233
- company_size: [
234
- "1-10",
235
- "11-50",
236
- "51-200",
237
- "201-1000",
238
- "1000+"
239
- ],
240
- created_date: date(365, true, 'YYYY-MM-DD')
241
- },
242
-
243
- hook: function (record, type, meta) {
244
- const NOW = dayjs();
245
-
246
- if (type === "event") {
247
- const EVENT_TIME = dayjs(record.time);
248
- }
249
-
250
- if (type === "user") {
251
-
252
- }
253
-
254
- if (type === "funnel-post") {
255
-
256
- }
257
-
258
- if (type === "funnel-pre") {
259
-
260
- }
261
-
262
- if (type === "scd-pre") {
263
-
264
- }
265
-
266
- if (type === "everything") {
267
-
268
- }
269
-
270
- return record;
271
- }
272
- };
273
-
274
- export default dungeon;