make-mp-data 1.5.56 → 2.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 (40) hide show
  1. package/.claude/settings.local.json +21 -0
  2. package/.gcloudignore +2 -1
  3. package/.vscode/launch.json +6 -17
  4. package/.vscode/settings.json +31 -2
  5. package/dungeons/media.js +371 -0
  6. package/index.js +353 -1766
  7. package/{components → lib/cli}/cli.js +25 -6
  8. package/lib/cloud-function.js +20 -0
  9. package/lib/core/config-validator.js +248 -0
  10. package/lib/core/context.js +180 -0
  11. package/lib/core/storage.js +268 -0
  12. package/{components → lib/data}/defaults.js +17 -14
  13. package/lib/generators/adspend.js +133 -0
  14. package/lib/generators/events.js +242 -0
  15. package/lib/generators/funnels.js +330 -0
  16. package/lib/generators/mirror.js +168 -0
  17. package/lib/generators/profiles.js +93 -0
  18. package/lib/generators/scd.js +102 -0
  19. package/lib/orchestrators/mixpanel-sender.js +222 -0
  20. package/lib/orchestrators/user-loop.js +194 -0
  21. package/lib/orchestrators/worker-manager.js +200 -0
  22. package/{components → lib/utils}/ai.js +8 -36
  23. package/{components → lib/utils}/chart.js +9 -9
  24. package/{components → lib/utils}/project.js +4 -4
  25. package/{components → lib/utils}/utils.js +35 -23
  26. package/package.json +15 -15
  27. package/scripts/dana.mjs +137 -0
  28. package/scripts/new-dungeon.sh +7 -6
  29. package/scripts/update-deps.sh +2 -1
  30. package/tests/cli.test.js +28 -25
  31. package/tests/e2e.test.js +38 -36
  32. package/tests/int.test.js +151 -56
  33. package/tests/testSoup.mjs +1 -1
  34. package/tests/unit.test.js +15 -14
  35. package/tsconfig.json +1 -1
  36. package/types.d.ts +68 -11
  37. package/vitest.config.js +47 -0
  38. package/log.json +0 -1678
  39. package/tests/jest.config.js +0 -47
  40. /package/{components → lib/utils}/prompt.txt +0 -0
@@ -0,0 +1,21 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node:*)",
5
+ "Bash(mkdir:*)",
6
+ "Bash(npm install)",
7
+ "Bash(npx vitest run:*)",
8
+ "Bash(npm test)",
9
+ "Bash(grep:*)",
10
+ "Bash(rg:*)",
11
+ "Bash(./scripts/new-dungeon.sh:*)",
12
+ "Bash(cp:*)",
13
+ "Bash(find:*)",
14
+ "Bash(npm test:*)",
15
+ "Bash(npm run test:*)",
16
+ "Bash(npx tsc:*)",
17
+ "Bash(npm start)"
18
+ ],
19
+ "deny": []
20
+ }
21
+ }
package/.gcloudignore CHANGED
@@ -14,4 +14,5 @@ env.yml
14
14
  .gitattributes
15
15
  README.md
16
16
  dungeons
17
- schemas
17
+ schemas
18
+ CLAUDE.md
@@ -13,23 +13,8 @@
13
13
  {
14
14
  "type": "node",
15
15
  "request": "launch",
16
- "name": "run dungeon (debug)",
17
- "runtimeExecutable": "nodemon",
18
- "runtimeArgs": ["--inspect", "--ignore", "./data"],
19
- "program": "${workspaceFolder}/index.js",
20
- "args": ["${file}"],
21
- "restart": true,
22
- "console": "integratedTerminal",
23
- "internalConsoleOptions": "neverOpen",
24
- "skipFiles": ["<node_internals>/**"],
25
- "preLaunchTask": "npm: prune",
26
-
27
- },
28
- {
29
- "type": "node",
30
- "request": "launch",
31
- "name": "run dungeon (one shot)",
32
- "runtimeExecutable": "node",
16
+ "name": "run dungeon",
17
+ "runtimeExecutable": "nodemon",
33
18
  "program": "${workspaceFolder}/index.js",
34
19
  "args": ["${file}"],
35
20
  "restart": true,
@@ -37,6 +22,10 @@
37
22
  "internalConsoleOptions": "neverOpen",
38
23
  "skipFiles": ["<node_internals>/**"],
39
24
  "preLaunchTask": "npm: prune",
25
+ "runtimeArgs": [
26
+ "--ignore",
27
+ "./data"
28
+ ]
40
29
 
41
30
  },
42
31
  {
@@ -7,6 +7,7 @@
7
7
  "Colour",
8
8
  "crumn",
9
9
  "darr",
10
+ "datagenerator",
10
11
  "dislikers",
11
12
  "Dont",
12
13
  "durtle",
@@ -34,7 +35,35 @@
34
35
  "weindgo",
35
36
  "whomacity"
36
37
  ],
37
- "jest.runMode": "on-demand",
38
- "jest.jestCommandLine": "npm run test --",
38
+ // Vitest settings
39
+ "vitest.enable": true,
40
+ "vitest.commandLine": "npx vitest",
41
+ "vitest.include": ["tests/**/*.test.js"],
42
+ "vitest.exclude": ["**/node_modules/**", "**/coverage/**", "**/data/**", "**/tmp/**"],
43
+
44
+ // Test Explorer settings
45
+ "testing.automaticallyOpenPeekView": "never",
46
+ "testing.openTesting": "neverOpen",
39
47
  "js/ts.implicitProjectConfig.checkJs": true,
48
+
49
+ // ESM and import settings
50
+ "typescript.preferences.includePackageJsonAutoImports": "auto",
51
+ "typescript.suggest.autoImports": true,
52
+ "javascript.suggest.autoImports": true,
53
+
54
+ // File associations
55
+ "files.associations": {
56
+ "*.js": "javascript",
57
+ "*.mjs": "javascript",
58
+ "*.cjs": "javascript"
59
+ },
60
+
61
+ // Search settings
62
+ "search.exclude": {
63
+ "**/node_modules": true,
64
+ "**/coverage": true,
65
+ "**/data": true,
66
+ "**/tmp": true
67
+ },
68
+ "testing.automaticallyOpenTestResults": "neverOpen"
40
69
  }
@@ -0,0 +1,371 @@
1
+
2
+ const SEED = "my-seed";
3
+ const dayjs = require("dayjs");
4
+ const utc = require("dayjs/plugin/utc");
5
+ dayjs.extend(utc);
6
+ require("dotenv").config();
7
+ const u = require("../components/utils");
8
+ const v = require("ak-tools");
9
+ const chance = u.initChance(SEED);
10
+ const num_users = 10_000;
11
+ const days = 125;
12
+
13
+ /** @typedef {import("../types").Dungeon} Config */
14
+
15
+ function genIds(numIds = 1000) {
16
+ const ids = [];
17
+ for (let i = 0; i < numIds; i++) {
18
+ ids.push(v.uid());
19
+ }
20
+ return ids;
21
+ }
22
+
23
+ const videoIds = genIds();
24
+ const channelIds = genIds(100);
25
+
26
+ /** @type {Config} */
27
+ const config = {
28
+ token: "32fd7149e2ac0cc030bda4c4b839b813",
29
+ seed: `LFG!`, //,
30
+ numDays: days,
31
+ numEvents: num_users * 69,
32
+ numUsers: num_users,
33
+ hasAnonIds: false,
34
+ hasSessionIds: false,
35
+ format: "json",
36
+ alsoInferFunnels: false,
37
+ hasLocation: false,
38
+ hasAndroidDevices: true,
39
+ hasIOSDevices: true,
40
+ hasDesktopDevices: true,
41
+ hasBrowser: false,
42
+ hasCampaigns: false,
43
+ isAnonymous: false,
44
+ hasAdSpend: false,
45
+
46
+ hasAvatar: false,
47
+ makeChart: false,
48
+
49
+ batchSize: 1_500_000,
50
+ concurrency: 1,
51
+ writeToDisk: false,
52
+
53
+ funnels: [],
54
+
55
+ events: [
56
+ {
57
+ event: "watch video",
58
+ weight: 55,
59
+ properties: {
60
+ video_id: u.pickAWinner(videoIds),
61
+ "watch percent": u.pickAWinner([
62
+ 25,
63
+ 50,
64
+ 75,
65
+ 100,
66
+ ]),
67
+ "watch time": u.weighNumRange(1, 65, .89, 100),
68
+
69
+ "category": u.pickAWinner([
70
+ "comedy",
71
+ "educational",
72
+ "music",
73
+ "sports",
74
+ "news",
75
+ "gaming",
76
+ "travel",
77
+ ]),
78
+ quality: u.pickAWinner([
79
+ "240p",
80
+ "360p",
81
+ "480p",
82
+ "720p",
83
+ "1080p",
84
+ "4k",
85
+ ], 4),
86
+ autoplay: [
87
+ true,
88
+ false,
89
+ ],
90
+ fullscreen: [
91
+ true,
92
+ false,
93
+ ],
94
+ "ads?": [
95
+ true, true,
96
+ false,
97
+ ],
98
+ },
99
+ },
100
+ {
101
+ event: "like",
102
+ weight: 10,
103
+ properties: {
104
+ video_id: u.pickAWinner(videoIds),
105
+ },
106
+ },
107
+ {
108
+ event: "comment",
109
+ weight: 5,
110
+ properties: {
111
+ video_id: u.pickAWinner(videoIds),
112
+ comment_length: [
113
+ "short",
114
+ "medium",
115
+ "long",
116
+ ],
117
+ },
118
+ },
119
+ {
120
+ event: "share",
121
+ weight: 3,
122
+ properties: {
123
+ video_id: u.pickAWinner(videoIds),
124
+ "share network": u.pickAWinner([
125
+ "facebook",
126
+ "twitter",
127
+ "reddit",
128
+ "email",
129
+ "whatsapp",
130
+ ]),
131
+ },
132
+ },
133
+ {
134
+ event: "search",
135
+ weight: 25,
136
+ properties: {
137
+ search_term: [
138
+ "cats",
139
+ "dogs",
140
+ "tutorial",
141
+ "news",
142
+ "music",
143
+ ],
144
+ "results count": u.pickAWinner([
145
+ 0,
146
+ 1,
147
+ 2,
148
+ 3,
149
+ 4,
150
+ 5,
151
+ 6, 7, 8, 9, 10
152
+ ], 5),
153
+ "search category": [
154
+ "all",
155
+ "channels",
156
+ "playlists",
157
+ ],
158
+ },
159
+ },
160
+ {
161
+ event: "subscribe",
162
+ weight: 7,
163
+ properties: {
164
+ channel_id: u.pickAWinner(genIds()),
165
+ },
166
+ },
167
+ {
168
+ event: "unsubscribe",
169
+ weight: 2,
170
+ properties: {
171
+ channel_id: u.pickAWinner(genIds()),
172
+ },
173
+ },
174
+ {
175
+ event: "create playlist",
176
+ weight: 4,
177
+ properties: {
178
+ "play list name": u.pickAWinner([
179
+ "favorites",
180
+ "watch later",
181
+ "my music",
182
+ "funny videos",
183
+ "educational",
184
+ ]),
185
+ privacy: u.pickAWinner([
186
+ "public",
187
+ "private",
188
+ "unlisted",
189
+ ]),
190
+ },
191
+ },
192
+ {
193
+ event: "account signup",
194
+ weight: 1,
195
+ isFirstEvent: true,
196
+ properties: {
197
+ "sign up method": [
198
+ "email",
199
+ "google",
200
+ "facebook",
201
+ ],
202
+ },
203
+ },
204
+ {
205
+ event: "account login",
206
+ weight: 9,
207
+ properties: {
208
+ "log in method": u.pickAWinner([
209
+ "email",
210
+ "google",
211
+ "facebook",
212
+ ]),
213
+ success: [
214
+ true,
215
+ false,
216
+ ],
217
+ error_message: [
218
+ "incorrect password",
219
+ "user not found",
220
+ "account locked",
221
+ ],
222
+ },
223
+ },
224
+ {
225
+ event: "$experiment_started",
226
+ weight: 5,
227
+ isSessionStartEvent: true,
228
+ properties: {
229
+ "$experiment_type": "ak_ad_hoc",
230
+ "Experiment name": "show results on empty search",
231
+ "Variant name": ["feature enabled", "feature disabled"],
232
+ }
233
+ }
234
+ ],
235
+ superProps: {
236
+ platform: u.pickAWinner([
237
+ "web",
238
+ "ios",
239
+ "android",
240
+ ]),
241
+ network_type: [
242
+ "wifi",
243
+ "cellular",
244
+ ],
245
+ },
246
+ userProps: {
247
+ subscription_status: [
248
+ "free",
249
+ "free",
250
+ "premium",
251
+ ],
252
+ age_range: [
253
+ "13-17",
254
+ "18-24",
255
+ "25-34",
256
+ "35-44",
257
+ "45-54",
258
+ "55+",
259
+ ],
260
+ preferred_genre: u.pickAWinner([
261
+ "comedy",
262
+ "action",
263
+ "drama",
264
+ "sci-fi",
265
+ "horror",
266
+ ]),
267
+ upload_count: [
268
+ 0,
269
+ 1,
270
+ 5,
271
+ 10,
272
+ 20,
273
+ ],
274
+ following_count: [
275
+ 0,
276
+ 10,
277
+ 50,
278
+ 100,
279
+ 500,
280
+ ],
281
+ dark_mode_enabled: [
282
+ true,
283
+ false,
284
+ ],
285
+ },
286
+
287
+ scdProps: {},
288
+ mirrorProps: {},
289
+ groupKeys: [],
290
+ groupProps: {},
291
+ lookupTables: [],
292
+ hook: function (record, type, meta) {
293
+ const NOW = dayjs();
294
+ const TIME_WHEN_SEARCH_GOT_BAD = NOW.subtract(21, 'days');
295
+ const TIME_WE_EXPERIMENTED = NOW.subtract(14, 'days');
296
+
297
+ if (type === "event") {
298
+ const EVENT_TIME = dayjs(record.time);
299
+ //when search got bad, people started searching less
300
+ //and got fewer results
301
+ if (EVENT_TIME.isAfter(TIME_WHEN_SEARCH_GOT_BAD)) {
302
+ if (chance.bool({ likelihood: 50 })) {
303
+ if (record.event === "search") {
304
+ record["results count"] = 0;
305
+ }
306
+ }
307
+
308
+ if (chance.bool({ likelihood: 18 })) {
309
+ return {};
310
+ }
311
+ }
312
+
313
+ if (EVENT_TIME.isBefore(TIME_WE_EXPERIMENTED)) {
314
+ if (record.event === "$experiment_started") {
315
+ return {};
316
+ }
317
+ }
318
+ }
319
+
320
+
321
+
322
+ if (type === "everything") {
323
+
324
+ const hadFeatureEnabled = record.some(event =>
325
+ event.event === "$experiment_started" &&
326
+ event["Variant name"] === "feature enabled"
327
+ );
328
+
329
+ const hadFeatureDisabled = record.some(event =>
330
+ event.event === "$experiment_started" &&
331
+ event["Variant name"] === "feature disabled"
332
+ );
333
+
334
+ record.forEach((event, idx) => {
335
+ const EVENT_TIME = dayjs(event.time);
336
+
337
+ if (EVENT_TIME.isAfter(TIME_WE_EXPERIMENTED)) {
338
+ if (hadFeatureEnabled) {
339
+ // Users with feature enabled variant have a higher likelihood of subscribing.
340
+ // Add an extra subscribe event 50% of the time immediately after watching a video.
341
+ if (event.event === "watch video" && chance.bool({ likelihood: 75 })) {
342
+ // watch time goes up
343
+ event["watch time"] = v.round(event["watch time"] * 1.7);
344
+ const subscribeEvent = {
345
+ event: "subscribe",
346
+ time: dayjs(event.time).add(1, 'minute').toISOString(),
347
+ user_id: event.user_id,
348
+ };
349
+ record.splice(idx + 1, 0, subscribeEvent);
350
+ }
351
+ } else if (hadFeatureDisabled) {
352
+ // Users with feature disabled variant have lower likelihood of subscribing.
353
+ // Drop subscribe events 50% of the time.
354
+ if (event.event === "subscribe" && chance.bool({ likelihood: 75 })) {
355
+ record.splice(idx, 1);
356
+ }
357
+
358
+ // watch time goes down
359
+ if (event.event === "watch video") {
360
+ event["watch time"] = v.round(event["watch time"] * 0.5);
361
+ }
362
+ }
363
+ }
364
+ });
365
+ }
366
+
367
+ return record;
368
+ }
369
+ };
370
+
371
+ module.exports = config;