@ak--47/dungeon-master 1.0.0 → 1.1.0
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 +7 -7
- package/dungeons/technical/ad-spend-schema.json +128 -0
- package/dungeons/technical/ad-spend.js +116 -0
- package/dungeons/technical/anonymous-users-schema.json +91 -0
- package/dungeons/technical/anonymous-users.js +102 -0
- package/dungeons/technical/array-of-object-lookup-schema.json +191 -0
- package/dungeons/{array-of-object-lookup.js → technical/array-of-object-lookup.js} +11 -4
- package/dungeons/technical/experiments-schema.json +197 -0
- package/dungeons/technical/experiments.js +168 -0
- package/dungeons/technical/foobar-schema.json +362 -0
- package/dungeons/{foobar.js → technical/foobar.js} +2 -2
- package/dungeons/technical/group-analytics-schema.json +149 -0
- package/dungeons/technical/group-analytics.js +151 -0
- package/dungeons/technical/mirror-strategies-schema.json +84 -0
- package/dungeons/technical/mirror-strategies.js +126 -0
- package/dungeons/technical/nested-objects-schema.json +145 -0
- package/dungeons/technical/nested-objects.js +207 -0
- package/dungeons/technical/retention-cadence-schema.json +37 -0
- package/dungeons/{retention-cadence.js → technical/retention-cadence.js} +1 -1
- package/dungeons/technical/sanity-schema.json +185 -0
- package/dungeons/{sanity.js → technical/sanity.js} +3 -2
- package/dungeons/technical/scale-test-schema.json +70 -0
- package/dungeons/technical/scale-test.js +90 -0
- package/dungeons/technical/scd-schema.json +270 -0
- package/dungeons/{scd.js → technical/scd.js} +19 -10
- package/dungeons/technical/simple-schema.json +278 -0
- package/dungeons/{simple.js → technical/simple.js} +6 -6
- package/dungeons/technical/simplest-schema.json +635 -0
- package/dungeons/{simplest.js → technical/simplest.js} +6 -6
- package/dungeons/technical/text-generation-schema.json +1059 -0
- package/dungeons/{text-generation.js → technical/text-generation.js} +23 -9
- package/dungeons/vertical/community-schema.json +557 -0
- package/dungeons/vertical/community.js +788 -0
- package/dungeons/vertical/devtools-schema.json +580 -0
- package/dungeons/vertical/devtools.js +837 -0
- package/dungeons/vertical/ecommerce-schema.json +586 -0
- package/dungeons/{ecommerce.js → vertical/ecommerce.js} +34 -3
- package/dungeons/vertical/education-schema.json +697 -0
- package/dungeons/{education.js → vertical/education.js} +54 -36
- package/dungeons/vertical/fintech-schema.json +563 -0
- package/dungeons/{fintech.js → vertical/fintech.js} +91 -53
- package/dungeons/vertical/fitness-schema.json +508 -0
- package/dungeons/vertical/fitness.js +802 -0
- package/dungeons/vertical/food-delivery-schema.json +678 -0
- package/dungeons/{food.js → vertical/food-delivery.js} +51 -23
- package/dungeons/vertical/gaming-schema.json +394 -0
- package/dungeons/{gaming.js → vertical/gaming.js} +9 -7
- package/dungeons/vertical/healthcare-schema.json +527 -0
- package/dungeons/vertical/healthcare.js +769 -0
- package/dungeons/vertical/insurance-application-schema.json +466 -0
- package/dungeons/{insurance-application.js → vertical/insurance-application.js} +33 -29
- package/dungeons/vertical/logistics-schema.json +552 -0
- package/dungeons/vertical/logistics.js +793 -0
- package/dungeons/vertical/marketplace-schema.json +511 -0
- package/dungeons/vertical/marketplace.js +844 -0
- package/dungeons/vertical/media-schema.json +787 -0
- package/dungeons/{media.js → vertical/media.js} +94 -16
- package/dungeons/vertical/rpg-schema.json +654 -0
- package/dungeons/{rpg.js → vertical/rpg.js} +78 -57
- package/dungeons/vertical/sass-schema.json +653 -0
- package/dungeons/{sass.js → vertical/sass.js} +49 -23
- package/dungeons/vertical/social-schema.json +552 -0
- package/dungeons/{social.js → vertical/social.js} +60 -14
- package/dungeons/vertical/travel-schema.json +558 -0
- package/dungeons/vertical/travel.js +736 -0
- package/index.js +19 -15
- package/lib/core/config-validator.js +286 -1
- package/lib/core/context.js +7 -5
- package/lib/generators/adspend.js +3 -3
- package/lib/generators/events.js +141 -14
- package/lib/generators/funnels.js +67 -7
- package/lib/generators/text.js +2 -2
- package/lib/orchestrators/user-loop.js +441 -22
- package/lib/templates/soup-presets.js +2 -2
- package/lib/utils/utils.js +4 -5
- package/package.json +4 -3
- package/scripts/dungeon-to-json.mjs +163 -0
- package/scripts/extract-dungeon-schema.mjs +297 -0
- package/scripts/json-to-dungeon.mjs +200 -0
- package/scripts/run-dungeon.mjs +76 -0
- package/scripts/run-many.mjs +118 -0
- package/scripts/verify-runner.mjs +42 -0
- package/types.d.ts +313 -1
- package/dungeons/array-of-object-lookup-schema.json +0 -327
- package/dungeons/ecommerce-schema.json +0 -462
- package/dungeons/education-schema.json +0 -2409
- package/dungeons/fintech-schema.json +0 -14034
- package/dungeons/foobar-schema.json +0 -403
- package/dungeons/food-delivery-schema.json +0 -192
- package/dungeons/food-delivery.js +0 -602
- package/dungeons/food-schema.json +0 -1152
- package/dungeons/gaming-schema.json +0 -1270
- package/dungeons/insurance-application-schema.json +0 -204
- package/dungeons/media-schema.json +0 -906
- package/dungeons/retention-cadence-schema.json +0 -78
- package/dungeons/rpg-schema.json +0 -4526
- package/dungeons/sanity-schema.json +0 -255
- package/dungeons/sass-schema.json +0 -1291
- package/dungeons/scd-schema.json +0 -919
- package/dungeons/simple-schema.json +0 -608
- package/dungeons/simplest-schema.json +0 -1418
- package/dungeons/social-schema.json +0 -1118
- package/dungeons/text-generation-schema.json +0 -3096
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"events": [
|
|
3
|
+
{
|
|
4
|
+
"event": "page view",
|
|
5
|
+
"weight": 10,
|
|
6
|
+
"properties": {
|
|
7
|
+
"page": [
|
|
8
|
+
"/account",
|
|
9
|
+
"/",
|
|
10
|
+
"/help",
|
|
11
|
+
"/product",
|
|
12
|
+
"/pricing"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"event": "checkout",
|
|
18
|
+
"weight": 3,
|
|
19
|
+
"properties": {
|
|
20
|
+
"amount": {
|
|
21
|
+
"$range": [
|
|
22
|
+
44,
|
|
23
|
+
154
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"currency": [
|
|
27
|
+
"USD",
|
|
28
|
+
"EUR",
|
|
29
|
+
"GBP"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"event": "profile update",
|
|
35
|
+
"weight": 4,
|
|
36
|
+
"properties": {
|
|
37
|
+
"field": [
|
|
38
|
+
"name",
|
|
39
|
+
"email",
|
|
40
|
+
"avatar",
|
|
41
|
+
"address",
|
|
42
|
+
"phone"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"event": "subscription change",
|
|
48
|
+
"weight": 2,
|
|
49
|
+
"properties": {
|
|
50
|
+
"plan": [
|
|
51
|
+
"free",
|
|
52
|
+
"starter",
|
|
53
|
+
"pro",
|
|
54
|
+
"enterprise"
|
|
55
|
+
],
|
|
56
|
+
"action": [
|
|
57
|
+
"upgrade",
|
|
58
|
+
"downgrade",
|
|
59
|
+
"cancel",
|
|
60
|
+
"renew"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"event": "login",
|
|
66
|
+
"weight": 8,
|
|
67
|
+
"properties": {
|
|
68
|
+
"method": [
|
|
69
|
+
"password",
|
|
70
|
+
"google",
|
|
71
|
+
"sso"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"userProps": {
|
|
77
|
+
"plan": [
|
|
78
|
+
"free",
|
|
79
|
+
"starter",
|
|
80
|
+
"pro",
|
|
81
|
+
"enterprise"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mirror Strategies — tests all 4 mirror data strategies.
|
|
3
|
+
*
|
|
4
|
+
* Exercises: create, update, fill, delete mirror transformations.
|
|
5
|
+
* Each mirror prop targets a different event and uses a different
|
|
6
|
+
* strategy so the output can be diffed against the original data.
|
|
7
|
+
*
|
|
8
|
+
* - 500 users, 30K events, 30 days
|
|
9
|
+
* - 5 simple events, no funnels, no hooks
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import Chance from 'chance';
|
|
13
|
+
let chance = new Chance();
|
|
14
|
+
import { weighNumRange, weighChoices } from "../../lib/utils/utils.js";
|
|
15
|
+
|
|
16
|
+
/** @type {import('../../types').Dungeon} */
|
|
17
|
+
const config = {
|
|
18
|
+
token: "",
|
|
19
|
+
seed: "mirror-strategies",
|
|
20
|
+
numDays: 30,
|
|
21
|
+
numEvents: 30_000,
|
|
22
|
+
numUsers: 500,
|
|
23
|
+
format: "json",
|
|
24
|
+
region: "US",
|
|
25
|
+
hasAnonIds: false,
|
|
26
|
+
hasSessionIds: false,
|
|
27
|
+
hasAdSpend: false,
|
|
28
|
+
hasLocation: false,
|
|
29
|
+
hasAndroidDevices: false,
|
|
30
|
+
hasIOSDevices: false,
|
|
31
|
+
hasDesktopDevices: false,
|
|
32
|
+
hasBrowser: false,
|
|
33
|
+
hasCampaigns: false,
|
|
34
|
+
isAnonymous: false,
|
|
35
|
+
alsoInferFunnels: false,
|
|
36
|
+
concurrency: 1,
|
|
37
|
+
batchSize: 2_500_000,
|
|
38
|
+
writeToDisk: false,
|
|
39
|
+
|
|
40
|
+
events: [
|
|
41
|
+
{
|
|
42
|
+
event: "page view",
|
|
43
|
+
weight: 10,
|
|
44
|
+
properties: {
|
|
45
|
+
page: ["/", "/help", "/account", "/pricing", "/product"],
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
event: "checkout",
|
|
50
|
+
weight: 3,
|
|
51
|
+
properties: {
|
|
52
|
+
amount: weighNumRange(5, 200, .25),
|
|
53
|
+
currency: ["USD", "EUR", "GBP"],
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
event: "profile update",
|
|
58
|
+
weight: 4,
|
|
59
|
+
properties: {
|
|
60
|
+
field: ["name", "email", "avatar", "address", "phone"],
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
event: "subscription change",
|
|
65
|
+
weight: 2,
|
|
66
|
+
properties: {
|
|
67
|
+
plan: ["free", "starter", "pro", "enterprise"],
|
|
68
|
+
action: ["upgrade", "downgrade", "cancel", "renew"],
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
event: "login",
|
|
73
|
+
weight: 8,
|
|
74
|
+
properties: {
|
|
75
|
+
method: ["password", "google", "sso"],
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
|
|
80
|
+
funnels: [],
|
|
81
|
+
|
|
82
|
+
superProps: {},
|
|
83
|
+
|
|
84
|
+
userProps: {
|
|
85
|
+
plan: ["free", "starter", "pro", "enterprise"],
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
scdProps: {},
|
|
89
|
+
|
|
90
|
+
mirrorProps: {
|
|
91
|
+
// "update" — adds a status property to profile update events; keeps existing values
|
|
92
|
+
user_status: {
|
|
93
|
+
events: ["profile update"],
|
|
94
|
+
strategy: "update",
|
|
95
|
+
values: ["active", "inactive", "suspended", "pending"],
|
|
96
|
+
},
|
|
97
|
+
// "create" — always creates a new tier property on subscription change events
|
|
98
|
+
subscription_tier: {
|
|
99
|
+
events: ["subscription change"],
|
|
100
|
+
strategy: "create",
|
|
101
|
+
values: ["bronze", "silver", "gold", "platinum"],
|
|
102
|
+
},
|
|
103
|
+
// "fill" — fills login_source on login events only if older than 10 days
|
|
104
|
+
login_source: {
|
|
105
|
+
events: ["login"],
|
|
106
|
+
strategy: "fill",
|
|
107
|
+
values: ["web", "mobile", "api", "desktop"],
|
|
108
|
+
daysUnfilled: 10,
|
|
109
|
+
},
|
|
110
|
+
// "delete" — removes the amount property from checkout events in the mirror
|
|
111
|
+
amount: {
|
|
112
|
+
events: ["checkout"],
|
|
113
|
+
strategy: "delete",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
groupKeys: [],
|
|
118
|
+
groupProps: {},
|
|
119
|
+
lookupTables: [],
|
|
120
|
+
|
|
121
|
+
hook: function (record, type, meta) {
|
|
122
|
+
return record;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default config;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{
|
|
2
|
+
"events": [
|
|
3
|
+
{
|
|
4
|
+
"event": "checkout",
|
|
5
|
+
"weight": 3,
|
|
6
|
+
"properties": {
|
|
7
|
+
"cart": {
|
|
8
|
+
"$type": "object[]",
|
|
9
|
+
"$keys": [
|
|
10
|
+
"product_id",
|
|
11
|
+
"name",
|
|
12
|
+
"price",
|
|
13
|
+
"quantity",
|
|
14
|
+
"category"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"total_amount": {
|
|
18
|
+
"$range": [
|
|
19
|
+
408,
|
|
20
|
+
1563
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"currency": [
|
|
24
|
+
"USD",
|
|
25
|
+
"EUR",
|
|
26
|
+
"GBP",
|
|
27
|
+
"JPY",
|
|
28
|
+
"CAD"
|
|
29
|
+
],
|
|
30
|
+
"session_id": {
|
|
31
|
+
"$type": "string"
|
|
32
|
+
},
|
|
33
|
+
"metadata": {
|
|
34
|
+
"$type": "object"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"event": "search performed",
|
|
40
|
+
"weight": 5,
|
|
41
|
+
"properties": {
|
|
42
|
+
"query": [
|
|
43
|
+
"laptop",
|
|
44
|
+
"shoes",
|
|
45
|
+
"headphones",
|
|
46
|
+
"jacket",
|
|
47
|
+
"phone case",
|
|
48
|
+
"coffee maker",
|
|
49
|
+
"backpack",
|
|
50
|
+
"monitor",
|
|
51
|
+
"keyboard",
|
|
52
|
+
"mouse"
|
|
53
|
+
],
|
|
54
|
+
"results": {
|
|
55
|
+
"$type": "object[]",
|
|
56
|
+
"$keys": [
|
|
57
|
+
"result_id",
|
|
58
|
+
"title",
|
|
59
|
+
"score",
|
|
60
|
+
"sponsored"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"result_count": {
|
|
64
|
+
"$range": [
|
|
65
|
+
6,
|
|
66
|
+
46
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"top_result_id": {
|
|
70
|
+
"$type": "string"
|
|
71
|
+
},
|
|
72
|
+
"session_id": {
|
|
73
|
+
"$type": "string"
|
|
74
|
+
},
|
|
75
|
+
"metadata": {
|
|
76
|
+
"$type": "object"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"event": "form submitted",
|
|
82
|
+
"weight": 4,
|
|
83
|
+
"properties": {
|
|
84
|
+
"form_name": [
|
|
85
|
+
"checkout",
|
|
86
|
+
"signup",
|
|
87
|
+
"contact",
|
|
88
|
+
"feedback",
|
|
89
|
+
"settings",
|
|
90
|
+
"profile"
|
|
91
|
+
],
|
|
92
|
+
"fields": {
|
|
93
|
+
"$type": "object[]",
|
|
94
|
+
"$keys": [
|
|
95
|
+
"field_name",
|
|
96
|
+
"value",
|
|
97
|
+
"valid"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"submission_valid": [
|
|
101
|
+
true,
|
|
102
|
+
false
|
|
103
|
+
],
|
|
104
|
+
"session_id": {
|
|
105
|
+
"$type": "string"
|
|
106
|
+
},
|
|
107
|
+
"metadata": {
|
|
108
|
+
"$type": "object"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"event": "page view",
|
|
114
|
+
"weight": 8,
|
|
115
|
+
"properties": {
|
|
116
|
+
"page": [
|
|
117
|
+
"/",
|
|
118
|
+
"/products",
|
|
119
|
+
"/cart",
|
|
120
|
+
"/search",
|
|
121
|
+
"/account",
|
|
122
|
+
"/help",
|
|
123
|
+
"/about"
|
|
124
|
+
],
|
|
125
|
+
"product_id": {
|
|
126
|
+
"$type": "string"
|
|
127
|
+
},
|
|
128
|
+
"session_id": {
|
|
129
|
+
"$type": "string"
|
|
130
|
+
},
|
|
131
|
+
"metadata": {
|
|
132
|
+
"$type": "object"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"userProps": {
|
|
138
|
+
"plan": [
|
|
139
|
+
"free",
|
|
140
|
+
"basic",
|
|
141
|
+
"pro",
|
|
142
|
+
"enterprise"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import Chance from 'chance';
|
|
2
|
+
let chance = new Chance();
|
|
3
|
+
import dayjs from "dayjs";
|
|
4
|
+
import utc from "dayjs/plugin/utc.js";
|
|
5
|
+
dayjs.extend(utc);
|
|
6
|
+
import { uid, comma } from 'ak-tools';
|
|
7
|
+
import { weighNumRange, date, integer, weighChoices } from "../../lib/utils/utils.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ═══════════════════════════════════════════════════════════════
|
|
11
|
+
* TECHNICAL TEST: Nested Objects & High Cardinality
|
|
12
|
+
* ═══════════════════════════════════════════════════════════════
|
|
13
|
+
*
|
|
14
|
+
* Tests Mixpanel's handling of:
|
|
15
|
+
* - Array-of-object event properties (cart items, search results, form fields)
|
|
16
|
+
* - Deeply nested object properties (metadata.browser.name, etc.)
|
|
17
|
+
* - High cardinality string properties (10K product IDs, unique session IDs)
|
|
18
|
+
*
|
|
19
|
+
* 500 users, 30K events, 30 days. No hooks.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const productCategories = ["electronics", "books", "clothing", "home", "garden", "toys", "sports", "automotive", "beauty", "health", "grocery", "jewelry", "shoes", "tools", "office"];
|
|
23
|
+
const browsers = ["Chrome", "Firefox", "Safari", "Edge", "Opera", "Brave"];
|
|
24
|
+
const browserVersions = ["120.0", "121.0", "119.0", "118.0", "117.0", "116.0", "115.0"];
|
|
25
|
+
const deviceTypes = ["desktop", "mobile", "tablet"];
|
|
26
|
+
const osList = ["Windows 11", "macOS 14", "iOS 17", "Android 14", "Linux", "ChromeOS"];
|
|
27
|
+
const cities = ["New York", "London", "Tokyo", "Berlin", "Sydney", "Toronto", "Paris", "Mumbai", "Seoul", "Mexico City", "Cairo", "Lagos", "Dubai", "Bangkok", "Istanbul"];
|
|
28
|
+
const countries = ["US", "UK", "JP", "DE", "AU", "CA", "FR", "IN", "KR", "MX", "EG", "NG", "AE", "TH", "TR"];
|
|
29
|
+
|
|
30
|
+
/** @type {import('../../types').Dungeon} */
|
|
31
|
+
const config = {
|
|
32
|
+
seed: "nested objects test",
|
|
33
|
+
name: "nested-objects",
|
|
34
|
+
numDays: 30,
|
|
35
|
+
numEvents: 30_000,
|
|
36
|
+
numUsers: 500,
|
|
37
|
+
format: 'json',
|
|
38
|
+
region: "US",
|
|
39
|
+
hasAnonIds: false,
|
|
40
|
+
hasSessionIds: false,
|
|
41
|
+
hasAdSpend: false,
|
|
42
|
+
hasLocation: false,
|
|
43
|
+
hasAndroidDevices: false,
|
|
44
|
+
hasIOSDevices: false,
|
|
45
|
+
hasDesktopDevices: true,
|
|
46
|
+
hasBrowser: false,
|
|
47
|
+
hasCampaigns: false,
|
|
48
|
+
isAnonymous: false,
|
|
49
|
+
alsoInferFunnels: false,
|
|
50
|
+
concurrency: 1,
|
|
51
|
+
batchSize: 500_000,
|
|
52
|
+
writeToDisk: false,
|
|
53
|
+
|
|
54
|
+
events: [
|
|
55
|
+
{
|
|
56
|
+
event: "checkout",
|
|
57
|
+
weight: 3,
|
|
58
|
+
properties: {
|
|
59
|
+
// Array of objects: cart items
|
|
60
|
+
cart: makeCartItems(),
|
|
61
|
+
total_amount: weighNumRange(10, 2000, .25),
|
|
62
|
+
currency: ["USD", "EUR", "GBP", "JPY", "CAD"],
|
|
63
|
+
// High cardinality: unique session per event
|
|
64
|
+
session_id: () => chance.guid(),
|
|
65
|
+
// Deeply nested metadata
|
|
66
|
+
metadata: makeMetadata(),
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
event: "search performed",
|
|
71
|
+
weight: 5,
|
|
72
|
+
properties: {
|
|
73
|
+
query: ["laptop", "shoes", "headphones", "jacket", "phone case", "coffee maker", "backpack", "monitor", "keyboard", "mouse"],
|
|
74
|
+
// Array of objects: search results
|
|
75
|
+
results: makeSearchResults(),
|
|
76
|
+
result_count: weighNumRange(0, 50),
|
|
77
|
+
// High cardinality product ID
|
|
78
|
+
top_result_id: () => `prod_${integer(1, 10000)}`,
|
|
79
|
+
session_id: () => chance.guid(),
|
|
80
|
+
metadata: makeMetadata(),
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
event: "form submitted",
|
|
85
|
+
weight: 4,
|
|
86
|
+
properties: {
|
|
87
|
+
form_name: ["checkout", "signup", "contact", "feedback", "settings", "profile"],
|
|
88
|
+
// Array of objects: form fields
|
|
89
|
+
fields: makeFormFields(),
|
|
90
|
+
submission_valid: [true, true, true, false],
|
|
91
|
+
session_id: () => chance.guid(),
|
|
92
|
+
metadata: makeMetadata(),
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
event: "page view",
|
|
97
|
+
weight: 8,
|
|
98
|
+
properties: {
|
|
99
|
+
page: ["/", "/products", "/cart", "/search", "/account", "/help", "/about"],
|
|
100
|
+
// High cardinality
|
|
101
|
+
product_id: () => `prod_${integer(1, 10000)}`,
|
|
102
|
+
session_id: () => chance.guid(),
|
|
103
|
+
metadata: makeMetadata(),
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
funnels: [],
|
|
108
|
+
superProps: {},
|
|
109
|
+
userProps: {
|
|
110
|
+
plan: ["free", "basic", "pro", "enterprise"],
|
|
111
|
+
},
|
|
112
|
+
scdProps: {},
|
|
113
|
+
mirrorProps: {},
|
|
114
|
+
groupKeys: [],
|
|
115
|
+
groupProps: {},
|
|
116
|
+
lookupTables: [],
|
|
117
|
+
hook: function (record, type, meta) {
|
|
118
|
+
return record;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Generates an array-of-objects property for cart items.
|
|
124
|
+
* Each cart has 1-5 items with product_id, name, price, quantity, category.
|
|
125
|
+
*/
|
|
126
|
+
function makeCartItems() {
|
|
127
|
+
return function () {
|
|
128
|
+
const items = [];
|
|
129
|
+
const count = integer(1, 5);
|
|
130
|
+
for (let i = 0; i < count; i++) {
|
|
131
|
+
items.push({
|
|
132
|
+
product_id: `prod_${integer(1, 10000)}`,
|
|
133
|
+
name: `${chance.pickone(["Premium", "Basic", "Ultra", "Eco", "Pro"])} ${chance.pickone(["Widget", "Gadget", "Device", "Tool", "Kit"])}`,
|
|
134
|
+
price: integer(5, 500),
|
|
135
|
+
quantity: integer(1, 4),
|
|
136
|
+
category: chance.pickone(productCategories),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return () => [items];
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generates an array-of-objects property for search results.
|
|
145
|
+
* Each search has 1-5 results with result_id, title, score, sponsored.
|
|
146
|
+
*/
|
|
147
|
+
function makeSearchResults() {
|
|
148
|
+
return function () {
|
|
149
|
+
const results = [];
|
|
150
|
+
const count = integer(1, 5);
|
|
151
|
+
for (let i = 0; i < count; i++) {
|
|
152
|
+
results.push({
|
|
153
|
+
result_id: `res_${integer(1, 50000)}`,
|
|
154
|
+
title: `${chance.pickone(["Best", "Top", "New", "Sale"])} ${chance.pickone(productCategories)} item`,
|
|
155
|
+
score: parseFloat((Math.random() * 100).toFixed(2)),
|
|
156
|
+
sponsored: chance.pickone([true, false, false, false]),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return () => [results];
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generates an array-of-objects property for form fields.
|
|
165
|
+
* Each form has 2-6 fields with field_name, value, valid.
|
|
166
|
+
*/
|
|
167
|
+
function makeFormFields() {
|
|
168
|
+
return function () {
|
|
169
|
+
const fieldNames = ["email", "name", "phone", "address", "city", "zip", "country", "card_number", "expiry", "cvv"];
|
|
170
|
+
const fields = [];
|
|
171
|
+
const count = integer(2, 6);
|
|
172
|
+
const picked = chance.pickset(fieldNames, count);
|
|
173
|
+
for (const name of picked) {
|
|
174
|
+
fields.push({
|
|
175
|
+
field_name: name,
|
|
176
|
+
value: name === "email" ? chance.email() : chance.word(),
|
|
177
|
+
valid: chance.pickone([true, true, true, false]),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return () => [fields];
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Generates a deeply nested metadata object:
|
|
186
|
+
* metadata: { browser: { name, version }, device: { type, os }, location: { city, country } }
|
|
187
|
+
*/
|
|
188
|
+
function makeMetadata() {
|
|
189
|
+
return function () {
|
|
190
|
+
return {
|
|
191
|
+
browser: {
|
|
192
|
+
name: chance.pickone(browsers),
|
|
193
|
+
version: chance.pickone(browserVersions),
|
|
194
|
+
},
|
|
195
|
+
device: {
|
|
196
|
+
type: chance.pickone(deviceTypes),
|
|
197
|
+
os: chance.pickone(osList),
|
|
198
|
+
},
|
|
199
|
+
location: {
|
|
200
|
+
city: chance.pickone(cities),
|
|
201
|
+
country: chance.pickone(countries),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default config;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"events": [
|
|
3
|
+
{
|
|
4
|
+
"event": "first behavior",
|
|
5
|
+
"isFirstEvent": true
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"event": "hourly behavior",
|
|
9
|
+
"weight": 40
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"event": "daily behavior",
|
|
13
|
+
"weight": 30
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"event": "weekly behavior",
|
|
17
|
+
"weight": 15
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"event": "monthly behavior",
|
|
21
|
+
"weight": 10
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"event": "placeholder",
|
|
25
|
+
"weight": 50
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"userProps": {
|
|
29
|
+
"favorite color": [
|
|
30
|
+
"red",
|
|
31
|
+
"blue",
|
|
32
|
+
"green",
|
|
33
|
+
"yellow",
|
|
34
|
+
"purple"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -2,7 +2,7 @@ const SEED = "kurby-retention";
|
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import utc from 'dayjs/plugin/utc.js';
|
|
4
4
|
dayjs.extend(utc);
|
|
5
|
-
import * as u from "
|
|
5
|
+
import * as u from "../../lib/utils/utils.js";
|
|
6
6
|
import * as v from 'ak-tools';
|
|
7
7
|
const chance = u.initChance(SEED);
|
|
8
8
|
|