@ak--47/dungeon-master 1.1.0 → 1.2.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/dungeons/technical/ad-spend-schema.json +2 -2
- package/dungeons/technical/ad-spend.js +16 -5
- package/dungeons/technical/anonymous-users-schema.json +4 -3
- package/dungeons/technical/anonymous-users.js +16 -6
- package/dungeons/technical/array-of-object-lookup-schema.json +1 -1
- package/dungeons/technical/array-of-object-lookup.js +22 -6
- package/dungeons/technical/experiments-schema.json +25 -19
- package/dungeons/technical/experiments.js +24 -6
- package/dungeons/technical/foobar.js +29 -13
- package/dungeons/technical/group-analytics-schema.json +92 -0
- package/dungeons/technical/group-analytics.js +16 -6
- package/dungeons/technical/mirror-strategies-schema.json +4 -4
- package/dungeons/technical/mirror-strategies.js +16 -6
- package/dungeons/technical/nested-objects.js +16 -5
- package/dungeons/technical/retention-cadence.js +14 -9
- package/dungeons/technical/sanity-schema.json +22 -22
- package/dungeons/technical/sanity.js +26 -6
- package/dungeons/technical/scale-test.js +16 -5
- package/dungeons/technical/scd-schema.json +230 -33
- package/dungeons/technical/scd.js +25 -5
- package/dungeons/technical/simple-schema.json +132 -48
- package/dungeons/technical/simple.js +21 -6
- package/dungeons/technical/simplest-schema.json +168 -79
- package/dungeons/technical/simplest.js +24 -7
- package/dungeons/technical/text-generation-schema.json +7 -4
- package/dungeons/technical/text-generation.js +23 -8
- package/dungeons/user/.gitkeep +0 -0
- package/dungeons/user/shalini.js +847 -0
- package/dungeons/vertical/community-schema.json +71 -49
- package/dungeons/vertical/community.js +57 -30
- package/dungeons/vertical/devtools-schema.json +69 -48
- package/dungeons/vertical/devtools.js +58 -31
- package/dungeons/vertical/ecommerce-schema.json +126 -108
- package/dungeons/vertical/ecommerce.js +32 -8
- package/dungeons/vertical/education-schema.json +5261 -272
- package/dungeons/vertical/education.js +45 -22
- package/dungeons/vertical/fintech-schema.json +165 -98
- package/dungeons/vertical/fintech.js +43 -11
- package/dungeons/vertical/fitness-schema.json +69 -47
- package/dungeons/vertical/fitness.js +56 -24
- package/dungeons/vertical/food-delivery-schema.json +36237 -187
- package/dungeons/vertical/food-delivery.js +67 -15
- package/dungeons/vertical/gaming-schema.json +104 -60
- package/dungeons/vertical/gaming.js +61 -16
- package/dungeons/vertical/healthcare-schema.json +70 -48
- package/dungeons/vertical/healthcare.js +77 -42
- package/dungeons/vertical/insurance-application-schema.json +66 -47
- package/dungeons/vertical/insurance-application.js +34 -10
- package/dungeons/vertical/logistics-schema.json +60 -38
- package/dungeons/vertical/logistics.js +77 -43
- package/dungeons/vertical/marketplace-schema.json +65 -43
- package/dungeons/vertical/marketplace.js +52 -21
- package/dungeons/vertical/media-schema.json +4085 -123
- package/dungeons/vertical/media.js +65 -18
- package/dungeons/vertical/rpg-schema.json +1984 -147
- package/dungeons/vertical/rpg.js +47 -11
- package/dungeons/vertical/sass-schema.json +2622 -147
- package/dungeons/vertical/sass.js +40 -10
- package/dungeons/vertical/social-schema.json +146 -78
- package/dungeons/vertical/social.js +39 -10
- package/dungeons/vertical/travel-schema.json +57 -35
- package/dungeons/vertical/travel.js +58 -21
- package/index.js +4 -1
- package/lib/core/config-validator.js +13 -2
- package/lib/core/context.js +9 -0
- package/lib/core/storage.js +2 -0
- package/lib/generators/events.js +3 -5
- package/lib/orchestrators/user-loop.js +24 -1
- package/lib/utils/utils.js +129 -9
- package/package.json +1 -1
- package/scripts/extract-dungeon-schema.mjs +64 -18
- package/types.d.ts +8 -3
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "ad spend test";
|
|
3
|
+
const num_days = 90;
|
|
4
|
+
const num_users = 1_000;
|
|
5
|
+
const avg_events_per_user = 50;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
import Chance from 'chance';
|
|
2
12
|
let chance = new Chance();
|
|
3
13
|
import dayjs from "dayjs";
|
|
@@ -6,6 +16,7 @@ dayjs.extend(utc);
|
|
|
6
16
|
import { uid, comma } from 'ak-tools';
|
|
7
17
|
import { weighNumRange, date, integer, weighChoices } from "../../lib/utils/utils.js";
|
|
8
18
|
|
|
19
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
9
20
|
/**
|
|
10
21
|
* ═══════════════════════════════════════════════════════════════
|
|
11
22
|
* TECHNICAL TEST: Ad Spend & Campaign Attribution
|
|
@@ -24,11 +35,12 @@ import { weighNumRange, date, integer, weighChoices } from "../../lib/utils/util
|
|
|
24
35
|
|
|
25
36
|
/** @type {import('../../types').Dungeon} */
|
|
26
37
|
const config = {
|
|
27
|
-
|
|
38
|
+
token,
|
|
39
|
+
seed: SEED,
|
|
28
40
|
name: "ad-spend",
|
|
29
|
-
numDays:
|
|
30
|
-
numEvents:
|
|
31
|
-
numUsers:
|
|
41
|
+
numDays: num_days,
|
|
42
|
+
numEvents: num_users * avg_events_per_user,
|
|
43
|
+
numUsers: num_users,
|
|
32
44
|
format: 'json',
|
|
33
45
|
region: "US",
|
|
34
46
|
hasAnonIds: true,
|
|
@@ -43,7 +55,6 @@ const config = {
|
|
|
43
55
|
isAnonymous: false,
|
|
44
56
|
alsoInferFunnels: false,
|
|
45
57
|
concurrency: 1,
|
|
46
|
-
batchSize: 500_000,
|
|
47
58
|
writeToDisk: false,
|
|
48
59
|
|
|
49
60
|
events: [
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
"weight": 10,
|
|
6
6
|
"properties": {
|
|
7
7
|
"page": [
|
|
8
|
+
"/",
|
|
8
9
|
"/features",
|
|
9
|
-
"/docs",
|
|
10
10
|
"/pricing",
|
|
11
|
+
"/docs",
|
|
11
12
|
"/blog"
|
|
12
13
|
],
|
|
13
14
|
"referrer": [
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
"properties": {
|
|
51
52
|
"amount": {
|
|
52
53
|
"$range": [
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
114,
|
|
55
|
+
397
|
|
55
56
|
]
|
|
56
57
|
},
|
|
57
58
|
"plan": [
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "anonymous-users";
|
|
3
|
+
const num_days = 180;
|
|
4
|
+
const num_users = 1_000;
|
|
5
|
+
const avg_events_per_user = 50;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Anonymous Users — tests anonymous user mode, anonIds, and identity merge.
|
|
3
13
|
*
|
|
@@ -14,13 +24,14 @@ import Chance from 'chance';
|
|
|
14
24
|
let chance = new Chance();
|
|
15
25
|
import { weighNumRange, weighChoices } from "../../lib/utils/utils.js";
|
|
16
26
|
|
|
27
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
17
28
|
/** @type {import('../../types').Dungeon} */
|
|
18
29
|
const config = {
|
|
19
|
-
token
|
|
20
|
-
seed:
|
|
21
|
-
numDays:
|
|
22
|
-
numEvents:
|
|
23
|
-
numUsers:
|
|
30
|
+
token,
|
|
31
|
+
seed: SEED,
|
|
32
|
+
numDays: num_days,
|
|
33
|
+
numEvents: num_users * avg_events_per_user,
|
|
34
|
+
numUsers: num_users,
|
|
24
35
|
format: "json",
|
|
25
36
|
region: "US",
|
|
26
37
|
isAnonymous: true,
|
|
@@ -35,7 +46,6 @@ const config = {
|
|
|
35
46
|
hasCampaigns: false,
|
|
36
47
|
alsoInferFunnels: false,
|
|
37
48
|
concurrency: 1,
|
|
38
|
-
batchSize: 2_500_000,
|
|
39
49
|
writeToDisk: false,
|
|
40
50
|
|
|
41
51
|
events: [
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "dm4-array-of-object-lookup";
|
|
3
|
+
const num_days = 60;
|
|
4
|
+
const num_users = 1_000;
|
|
5
|
+
const avg_events_per_user = 100;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
import Chance from 'chance';
|
|
2
12
|
let chance = new Chance();
|
|
3
13
|
import dayjs from "dayjs";
|
|
@@ -6,6 +16,7 @@ dayjs.extend(utc);
|
|
|
6
16
|
import { uid, comma } from 'ak-tools';
|
|
7
17
|
import { weighNumRange, date, integer, weighChoices } from "../../lib/utils/utils.js";
|
|
8
18
|
|
|
19
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
9
20
|
const videoCategories = ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"];
|
|
10
21
|
const spiritAnimals = ["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"];
|
|
11
22
|
|
|
@@ -39,12 +50,12 @@ const spiritAnimals = ["duck", "dog", "otter", "penguin", "cat", "elephant", "li
|
|
|
39
50
|
|
|
40
51
|
/** @type {import('../types.js').Dungeon} */
|
|
41
52
|
const config = {
|
|
42
|
-
|
|
43
|
-
seed:
|
|
53
|
+
token,
|
|
54
|
+
seed: SEED,
|
|
44
55
|
name: "array-of-object-lookup",
|
|
45
|
-
numDays:
|
|
46
|
-
numEvents:
|
|
47
|
-
numUsers:
|
|
56
|
+
numDays: num_days,
|
|
57
|
+
numEvents: num_users * avg_events_per_user,
|
|
58
|
+
numUsers: num_users,
|
|
48
59
|
format: 'json', //csv or json
|
|
49
60
|
region: "US",
|
|
50
61
|
hasAnonIds: true, //if true, anonymousIds are created for each user
|
|
@@ -59,7 +70,6 @@ const config = {
|
|
|
59
70
|
isAnonymous: false,
|
|
60
71
|
alsoInferFunnels: true,
|
|
61
72
|
concurrency: 1,
|
|
62
|
-
batchSize: 250_000,
|
|
63
73
|
writeToDisk: false,
|
|
64
74
|
events: [
|
|
65
75
|
{
|
|
@@ -109,6 +119,7 @@ const config = {
|
|
|
109
119
|
each key should be an array or function reference
|
|
110
120
|
*/
|
|
111
121
|
userProps: {
|
|
122
|
+
theme: ["light", "dark", "custom", "light", "dark"],
|
|
112
123
|
// title: chance.profession.bind(chance),
|
|
113
124
|
// luckyNumber: weighNumRange(1, 500, .3),
|
|
114
125
|
spiritAnimal: spiritAnimals
|
|
@@ -160,6 +171,11 @@ const config = {
|
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
if (type === "everything") {
|
|
174
|
+
const profile = meta.profile;
|
|
175
|
+
record.forEach(e => {
|
|
176
|
+
e.theme = profile.theme;
|
|
177
|
+
});
|
|
178
|
+
|
|
163
179
|
// Pattern 3: Users who view 5+ items but never checkout are tagged as window shoppers
|
|
164
180
|
const views = record.filter(e => e.event === "view item").length;
|
|
165
181
|
const checkouts = record.filter(e => e.event === "checkout").length;
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
"weight": 10,
|
|
6
6
|
"properties": {
|
|
7
7
|
"page": [
|
|
8
|
-
"/
|
|
8
|
+
"/",
|
|
9
9
|
"/features",
|
|
10
|
+
"/pricing",
|
|
10
11
|
"/docs",
|
|
11
12
|
"/about"
|
|
12
13
|
]
|
|
@@ -72,8 +73,8 @@
|
|
|
72
73
|
"properties": {
|
|
73
74
|
"amount": {
|
|
74
75
|
"$range": [
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
123,
|
|
77
|
+
389
|
|
77
78
|
]
|
|
78
79
|
},
|
|
79
80
|
"plan": [
|
|
@@ -120,21 +121,6 @@
|
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
],
|
|
123
|
-
"superProps": {
|
|
124
|
-
"platform": [
|
|
125
|
-
"ios",
|
|
126
|
-
"web",
|
|
127
|
-
"android"
|
|
128
|
-
]
|
|
129
|
-
},
|
|
130
|
-
"userProps": {
|
|
131
|
-
"plan": [
|
|
132
|
-
"free",
|
|
133
|
-
"starter",
|
|
134
|
-
"growth",
|
|
135
|
-
"enterprise"
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
124
|
"funnels": [
|
|
139
125
|
{
|
|
140
126
|
"sequence": [
|
|
@@ -143,6 +129,7 @@
|
|
|
143
129
|
"feature viewed",
|
|
144
130
|
"action taken"
|
|
145
131
|
],
|
|
132
|
+
"name": "Onboarding Flow",
|
|
146
133
|
"isFirstFunnel": true,
|
|
147
134
|
"conversionRate": 45,
|
|
148
135
|
"timeToConvert": 3,
|
|
@@ -156,6 +143,7 @@
|
|
|
156
143
|
"button click",
|
|
157
144
|
"checkout"
|
|
158
145
|
],
|
|
146
|
+
"name": "Purchase Funnel",
|
|
159
147
|
"conversionRate": 30,
|
|
160
148
|
"timeToConvert": 5,
|
|
161
149
|
"order": "first-and-last-fixed",
|
|
@@ -169,12 +157,14 @@
|
|
|
169
157
|
"help viewed",
|
|
170
158
|
"action taken"
|
|
171
159
|
],
|
|
160
|
+
"name": "Feature Adoption",
|
|
172
161
|
"conversionRate": 50,
|
|
173
162
|
"timeToConvert": 2,
|
|
174
163
|
"order": "random",
|
|
175
164
|
"weight": 4,
|
|
176
165
|
"requireRepeats": true,
|
|
177
166
|
"experiment": true,
|
|
167
|
+
"bindPropsIndex": 2,
|
|
178
168
|
"props": {
|
|
179
169
|
"experiment_cohort": [
|
|
180
170
|
"new_ui",
|
|
@@ -188,10 +178,26 @@
|
|
|
188
178
|
"button click",
|
|
189
179
|
"signup"
|
|
190
180
|
],
|
|
181
|
+
"name": "Quick Signup",
|
|
191
182
|
"isFirstFunnel": true,
|
|
192
183
|
"conversionRate": 40,
|
|
193
184
|
"timeToConvert": 1,
|
|
194
185
|
"weight": 6
|
|
195
186
|
}
|
|
196
|
-
]
|
|
187
|
+
],
|
|
188
|
+
"superProps": {
|
|
189
|
+
"platform": [
|
|
190
|
+
"web",
|
|
191
|
+
"ios",
|
|
192
|
+
"android"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"userProps": {
|
|
196
|
+
"plan": [
|
|
197
|
+
"free",
|
|
198
|
+
"starter",
|
|
199
|
+
"growth",
|
|
200
|
+
"enterprise"
|
|
201
|
+
]
|
|
202
|
+
}
|
|
197
203
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "experiments";
|
|
3
|
+
const num_days = 60;
|
|
4
|
+
const num_users = 2_000;
|
|
5
|
+
const avg_events_per_user = 50;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Experiments — tests funnel experiments with A/B/C variants.
|
|
3
13
|
*
|
|
@@ -14,13 +24,14 @@ import Chance from 'chance';
|
|
|
14
24
|
let chance = new Chance();
|
|
15
25
|
import { weighNumRange, weighChoices } from "../../lib/utils/utils.js";
|
|
16
26
|
|
|
27
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
17
28
|
/** @type {import('../../types').Dungeon} */
|
|
18
29
|
const config = {
|
|
19
|
-
token
|
|
20
|
-
seed:
|
|
21
|
-
numDays:
|
|
22
|
-
numEvents:
|
|
23
|
-
numUsers:
|
|
30
|
+
token,
|
|
31
|
+
seed: SEED,
|
|
32
|
+
numDays: num_days,
|
|
33
|
+
numEvents: num_users * avg_events_per_user,
|
|
34
|
+
numUsers: num_users,
|
|
24
35
|
format: "json",
|
|
25
36
|
region: "US",
|
|
26
37
|
hasAnonIds: false,
|
|
@@ -35,7 +46,6 @@ const config = {
|
|
|
35
46
|
isAnonymous: false,
|
|
36
47
|
alsoInferFunnels: false,
|
|
37
48
|
concurrency: 1,
|
|
38
|
-
batchSize: 2_500_000,
|
|
39
49
|
writeToDisk: false,
|
|
40
50
|
|
|
41
51
|
events: [
|
|
@@ -152,6 +162,7 @@ const config = {
|
|
|
152
162
|
|
|
153
163
|
userProps: {
|
|
154
164
|
plan: weighChoices(["free", "free", "free", "starter", "growth", "enterprise"]),
|
|
165
|
+
platform: ["web", "web", "ios", "android"],
|
|
155
166
|
},
|
|
156
167
|
|
|
157
168
|
scdProps: {},
|
|
@@ -161,6 +172,13 @@ const config = {
|
|
|
161
172
|
lookupTables: [],
|
|
162
173
|
|
|
163
174
|
hook: function (record, type, meta) {
|
|
175
|
+
if (type === "everything") {
|
|
176
|
+
const profile = meta.profile;
|
|
177
|
+
record.forEach(e => {
|
|
178
|
+
e.platform = profile.platform;
|
|
179
|
+
});
|
|
180
|
+
return record;
|
|
181
|
+
}
|
|
164
182
|
return record;
|
|
165
183
|
}
|
|
166
184
|
};
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "dm4-foobar";
|
|
3
|
+
const num_days = 30;
|
|
4
|
+
const num_users = 4_000_000;
|
|
5
|
+
const avg_events_per_user = 500;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* ═══════════════════════════════════════════════════════════════
|
|
3
13
|
* DATASET OVERVIEW
|
|
@@ -46,6 +56,7 @@ function integer(min = 1, max = 100) {
|
|
|
46
56
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
47
57
|
}
|
|
48
58
|
|
|
59
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
49
60
|
|
|
50
61
|
const dates = [
|
|
51
62
|
"2024-09-18T21:48:28.173Z",
|
|
@@ -150,25 +161,17 @@ const dates = [
|
|
|
150
161
|
"2024-06-03T06:52:21.652Z"
|
|
151
162
|
];
|
|
152
163
|
|
|
153
|
-
const billionsOfEvents = 2
|
|
154
|
-
|
|
155
|
-
const numEvents = billionsOfEvents * 1_000_000_000;
|
|
156
|
-
const eventPerUser = 1_000;
|
|
157
|
-
const numUsers = Math.floor(numEvents / eventPerUser);
|
|
158
|
-
const seed = Math.random().toString()
|
|
159
|
-
|
|
160
164
|
/** @type {import('../../types').Dungeon} */
|
|
161
165
|
const config = {
|
|
162
|
-
token
|
|
163
|
-
seed:
|
|
164
|
-
numDays:
|
|
165
|
-
numEvents:
|
|
166
|
-
numUsers:
|
|
166
|
+
token,
|
|
167
|
+
seed: SEED,
|
|
168
|
+
numDays: num_days,
|
|
169
|
+
numEvents: num_users * avg_events_per_user,
|
|
170
|
+
numUsers: num_users,
|
|
167
171
|
format: 'json', //csv or json
|
|
168
172
|
region: "US",
|
|
169
173
|
hasAnonIds: false, //if true, anonymousIds are created for each user
|
|
170
174
|
hasSessionIds: false, //if true, hasSessionIds are created for each user
|
|
171
|
-
batchSize: 2_500_000,
|
|
172
175
|
hasAdSpend: false,
|
|
173
176
|
hasAvatar: false,
|
|
174
177
|
hasBrowser: false,
|
|
@@ -244,6 +247,11 @@ const config = {
|
|
|
244
247
|
luckyNumber: integer,
|
|
245
248
|
spiritAnimal: ["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"],
|
|
246
249
|
created: dates,
|
|
250
|
+
string: ["red", "orange", "yellow", "green", "blue", "indigo", "violet"],
|
|
251
|
+
number: integer,
|
|
252
|
+
boolean: [true, false],
|
|
253
|
+
date: dates,
|
|
254
|
+
temperature: ["warm"],
|
|
247
255
|
},
|
|
248
256
|
|
|
249
257
|
scdProps: {},
|
|
@@ -270,6 +278,14 @@ const config = {
|
|
|
270
278
|
|
|
271
279
|
// everything hook: hash-based cohort — 10% of users (by distinct_id) get doubled events
|
|
272
280
|
if (type === "everything") {
|
|
281
|
+
const profile = meta.profile;
|
|
282
|
+
record.forEach(e => {
|
|
283
|
+
e.string = profile.string;
|
|
284
|
+
e.number = profile.number;
|
|
285
|
+
e.boolean = profile.boolean;
|
|
286
|
+
e.date = profile.date;
|
|
287
|
+
e.temperature = profile.temperature;
|
|
288
|
+
});
|
|
273
289
|
if (record.length > 0) {
|
|
274
290
|
const userId = record[0].user_id || record[0].distinct_id || "";
|
|
275
291
|
if (userId && userId.charCodeAt(0) % 10 === 0) {
|
|
@@ -145,5 +145,97 @@
|
|
|
145
145
|
"manager",
|
|
146
146
|
"exec"
|
|
147
147
|
]
|
|
148
|
+
},
|
|
149
|
+
"groupKeys": [
|
|
150
|
+
{
|
|
151
|
+
"key": "company_id",
|
|
152
|
+
"count": 50,
|
|
153
|
+
"affectsEvents": [
|
|
154
|
+
"feature used",
|
|
155
|
+
"subscription change",
|
|
156
|
+
"support ticket"
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"key": "team_id",
|
|
161
|
+
"count": 200,
|
|
162
|
+
"affectsEvents": [
|
|
163
|
+
"feature used",
|
|
164
|
+
"task completed",
|
|
165
|
+
"meeting scheduled"
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"key": "project_id",
|
|
170
|
+
"count": 500,
|
|
171
|
+
"affectsEvents": [
|
|
172
|
+
"task completed",
|
|
173
|
+
"code committed",
|
|
174
|
+
"deployment run"
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
"groupProps": {
|
|
179
|
+
"company_id": {
|
|
180
|
+
"name": {
|
|
181
|
+
"$type": "string"
|
|
182
|
+
},
|
|
183
|
+
"industry": [
|
|
184
|
+
"technology",
|
|
185
|
+
"finance",
|
|
186
|
+
"healthcare",
|
|
187
|
+
"retail",
|
|
188
|
+
"education",
|
|
189
|
+
"manufacturing"
|
|
190
|
+
],
|
|
191
|
+
"plan": [
|
|
192
|
+
"free",
|
|
193
|
+
"starter",
|
|
194
|
+
"business",
|
|
195
|
+
"enterprise"
|
|
196
|
+
],
|
|
197
|
+
"employee_count": {
|
|
198
|
+
"$range": [
|
|
199
|
+
964,
|
|
200
|
+
3784
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"team_id": {
|
|
205
|
+
"name": {
|
|
206
|
+
"$type": "string"
|
|
207
|
+
},
|
|
208
|
+
"department": [
|
|
209
|
+
"engineering",
|
|
210
|
+
"design",
|
|
211
|
+
"product",
|
|
212
|
+
"marketing",
|
|
213
|
+
"sales",
|
|
214
|
+
"support"
|
|
215
|
+
],
|
|
216
|
+
"size": {
|
|
217
|
+
"$range": [
|
|
218
|
+
3,
|
|
219
|
+
20
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"project_id": {
|
|
224
|
+
"name": {
|
|
225
|
+
"$type": "string"
|
|
226
|
+
},
|
|
227
|
+
"status": [
|
|
228
|
+
"active",
|
|
229
|
+
"paused",
|
|
230
|
+
"completed",
|
|
231
|
+
"archived"
|
|
232
|
+
],
|
|
233
|
+
"priority": [
|
|
234
|
+
"low",
|
|
235
|
+
"medium",
|
|
236
|
+
"high",
|
|
237
|
+
"critical"
|
|
238
|
+
]
|
|
239
|
+
}
|
|
148
240
|
}
|
|
149
241
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "group-analytics";
|
|
3
|
+
const num_days = 60;
|
|
4
|
+
const num_users = 500;
|
|
5
|
+
const avg_events_per_user = 60;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Group Analytics — tests multiple group keys with varied entity counts.
|
|
3
13
|
*
|
|
@@ -14,13 +24,14 @@ import Chance from 'chance';
|
|
|
14
24
|
let chance = new Chance();
|
|
15
25
|
import { weighNumRange, weighChoices } from "../../lib/utils/utils.js";
|
|
16
26
|
|
|
27
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
17
28
|
/** @type {import('../../types').Dungeon} */
|
|
18
29
|
const config = {
|
|
19
|
-
token
|
|
20
|
-
seed:
|
|
21
|
-
numDays:
|
|
22
|
-
numEvents:
|
|
23
|
-
numUsers:
|
|
30
|
+
token,
|
|
31
|
+
seed: SEED,
|
|
32
|
+
numDays: num_days,
|
|
33
|
+
numEvents: num_users * avg_events_per_user,
|
|
34
|
+
numUsers: num_users,
|
|
24
35
|
format: "json",
|
|
25
36
|
region: "US",
|
|
26
37
|
hasAnonIds: false,
|
|
@@ -35,7 +46,6 @@ const config = {
|
|
|
35
46
|
isAnonymous: false,
|
|
36
47
|
alsoInferFunnels: false,
|
|
37
48
|
concurrency: 1,
|
|
38
|
-
batchSize: 2_500_000,
|
|
39
49
|
writeToDisk: false,
|
|
40
50
|
|
|
41
51
|
events: [
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"weight": 10,
|
|
6
6
|
"properties": {
|
|
7
7
|
"page": [
|
|
8
|
-
"/account",
|
|
9
8
|
"/",
|
|
10
9
|
"/help",
|
|
11
|
-
"/
|
|
12
|
-
"/pricing"
|
|
10
|
+
"/account",
|
|
11
|
+
"/pricing",
|
|
12
|
+
"/product"
|
|
13
13
|
]
|
|
14
14
|
}
|
|
15
15
|
},
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"amount": {
|
|
21
21
|
"$range": [
|
|
22
22
|
44,
|
|
23
|
-
|
|
23
|
+
157
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
"currency": [
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "mirror-strategies";
|
|
3
|
+
const num_days = 30;
|
|
4
|
+
const num_users = 500;
|
|
5
|
+
const avg_events_per_user = 60;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Mirror Strategies — tests all 4 mirror data strategies.
|
|
3
13
|
*
|
|
@@ -13,13 +23,14 @@ import Chance from 'chance';
|
|
|
13
23
|
let chance = new Chance();
|
|
14
24
|
import { weighNumRange, weighChoices } from "../../lib/utils/utils.js";
|
|
15
25
|
|
|
26
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
16
27
|
/** @type {import('../../types').Dungeon} */
|
|
17
28
|
const config = {
|
|
18
|
-
token
|
|
19
|
-
seed:
|
|
20
|
-
numDays:
|
|
21
|
-
numEvents:
|
|
22
|
-
numUsers:
|
|
29
|
+
token,
|
|
30
|
+
seed: SEED,
|
|
31
|
+
numDays: num_days,
|
|
32
|
+
numEvents: num_users * avg_events_per_user,
|
|
33
|
+
numUsers: num_users,
|
|
23
34
|
format: "json",
|
|
24
35
|
region: "US",
|
|
25
36
|
hasAnonIds: false,
|
|
@@ -34,7 +45,6 @@ const config = {
|
|
|
34
45
|
isAnonymous: false,
|
|
35
46
|
alsoInferFunnels: false,
|
|
36
47
|
concurrency: 1,
|
|
37
|
-
batchSize: 2_500_000,
|
|
38
48
|
writeToDisk: false,
|
|
39
49
|
|
|
40
50
|
events: [
|