make-mp-data 3.0.3 → 3.0.5

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 (70) hide show
  1. package/README.md +46 -0
  2. package/dungeons/array-of-object-lookup-schema.json +327 -0
  3. package/dungeons/array-of-object-lookup.js +29 -9
  4. package/dungeons/capstone/capstone-ic3.js +291 -0
  5. package/dungeons/capstone/capstone-ic4.js +598 -0
  6. package/dungeons/capstone/capstone-ic5.js +668 -0
  7. package/dungeons/capstone/generate-product-lookup.js +309 -0
  8. package/dungeons/ecommerce-schema.json +462 -0
  9. package/dungeons/{copilot.js → ecommerce.js} +79 -17
  10. package/dungeons/education-schema.json +2409 -0
  11. package/dungeons/education.js +226 -462
  12. package/dungeons/fintech-schema.json +14034 -0
  13. package/dungeons/fintech.js +134 -413
  14. package/dungeons/foobar-schema.json +403 -0
  15. package/dungeons/foobar.js +27 -4
  16. package/dungeons/food-delivery-schema.json +192 -0
  17. package/dungeons/food-delivery.js +602 -0
  18. package/dungeons/food-schema.json +1152 -0
  19. package/dungeons/food.js +173 -406
  20. package/dungeons/gaming-schema.json +1270 -0
  21. package/dungeons/gaming.js +182 -42
  22. package/dungeons/insurance-application-schema.json +204 -0
  23. package/dungeons/insurance-application.js +605 -0
  24. package/dungeons/media-schema.json +906 -0
  25. package/dungeons/media.js +250 -420
  26. package/dungeons/retention-cadence-schema.json +78 -0
  27. package/dungeons/retention-cadence.js +35 -1
  28. package/dungeons/rpg-schema.json +4526 -0
  29. package/dungeons/rpg.js +171 -429
  30. package/dungeons/sanity-schema.json +255 -0
  31. package/dungeons/sanity.js +21 -10
  32. package/dungeons/sass-schema.json +1291 -0
  33. package/dungeons/sass.js +241 -368
  34. package/dungeons/scd-schema.json +919 -0
  35. package/dungeons/scd.js +41 -13
  36. package/dungeons/simple-schema.json +608 -0
  37. package/dungeons/simple.js +52 -15
  38. package/dungeons/simplest-schema.json +1418 -0
  39. package/dungeons/simplest.js +392 -0
  40. package/dungeons/social-schema.json +1118 -0
  41. package/dungeons/social.js +150 -391
  42. package/dungeons/text-generation-schema.json +3096 -0
  43. package/dungeons/text-generation.js +71 -0
  44. package/index.js +8 -6
  45. package/lib/core/config-validator.js +28 -8
  46. package/lib/core/storage.js +5 -5
  47. package/lib/generators/events.js +4 -4
  48. package/lib/orchestrators/mixpanel-sender.js +16 -13
  49. package/lib/orchestrators/user-loop.js +14 -6
  50. package/lib/templates/soup-presets.js +188 -0
  51. package/lib/utils/utils.js +52 -6
  52. package/package.json +1 -1
  53. package/types.d.ts +20 -3
  54. package/dungeons/adspend.js +0 -130
  55. package/dungeons/anon.js +0 -128
  56. package/dungeons/benchmark-heavy.js +0 -240
  57. package/dungeons/benchmark-light.js +0 -140
  58. package/dungeons/big.js +0 -226
  59. package/dungeons/business.js +0 -391
  60. package/dungeons/complex.js +0 -428
  61. package/dungeons/experiments.js +0 -137
  62. package/dungeons/funnels.js +0 -309
  63. package/dungeons/mil.js +0 -323
  64. package/dungeons/mirror.js +0 -161
  65. package/dungeons/soup-test.js +0 -52
  66. package/dungeons/streaming.js +0 -372
  67. package/dungeons/strict-event-test.js +0 -30
  68. package/dungeons/student-teacher.js +0 -438
  69. package/dungeons/too-big-events.js +0 -203
  70. package/dungeons/user-agent.js +0 -209
@@ -0,0 +1,1152 @@
1
+ {
2
+ "schema": {
3
+ "token": "",
4
+ "seed": "harness-food",
5
+ "numDays": 100,
6
+ "numEvents": 600000,
7
+ "numUsers": 5000,
8
+ "hasAnonIds": false,
9
+ "hasSessionIds": true,
10
+ "format": "json",
11
+ "gzip": true,
12
+ "alsoInferFunnels": false,
13
+ "hasLocation": true,
14
+ "hasAndroidDevices": true,
15
+ "hasIOSDevices": true,
16
+ "hasDesktopDevices": true,
17
+ "hasBrowser": false,
18
+ "hasCampaigns": false,
19
+ "isAnonymous": false,
20
+ "hasAdSpend": false,
21
+ "percentUsersBornInDataset": 50,
22
+ "hasAvatar": true,
23
+ "batchSize": 2500000,
24
+ "concurrency": 1,
25
+ "writeToDisk": false,
26
+ "scdProps": {},
27
+ "funnels": [
28
+ {
29
+ "sequence": [
30
+ "account created",
31
+ "restaurant browsed",
32
+ "restaurant viewed"
33
+ ],
34
+ "isFirstFunnel": true,
35
+ "conversionRate": 80,
36
+ "timeToConvert": 0.5
37
+ },
38
+ {
39
+ "sequence": [
40
+ "restaurant browsed",
41
+ "restaurant viewed",
42
+ "item added to cart"
43
+ ],
44
+ "conversionRate": 55,
45
+ "timeToConvert": 1,
46
+ "weight": 5,
47
+ "props": {
48
+ "restaurant_id": {
49
+ "functionName": "arrow",
50
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "sequence": [
56
+ "search performed",
57
+ "restaurant viewed",
58
+ "item added to cart",
59
+ "checkout started"
60
+ ],
61
+ "conversionRate": 45,
62
+ "timeToConvert": 2,
63
+ "weight": 3
64
+ },
65
+ {
66
+ "sequence": [
67
+ "checkout started",
68
+ "order placed",
69
+ "order tracked",
70
+ "order delivered"
71
+ ],
72
+ "conversionRate": 65,
73
+ "timeToConvert": 2,
74
+ "weight": 4,
75
+ "props": {
76
+ "order_id": {
77
+ "functionName": "arrow",
78
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
79
+ }
80
+ }
81
+ },
82
+ {
83
+ "sequence": [
84
+ "order delivered",
85
+ "order rated",
86
+ "reorder initiated"
87
+ ],
88
+ "conversionRate": 40,
89
+ "timeToConvert": 24,
90
+ "weight": 2
91
+ },
92
+ {
93
+ "sequence": [
94
+ "promotion viewed",
95
+ "coupon applied",
96
+ "checkout started"
97
+ ],
98
+ "conversionRate": 50,
99
+ "timeToConvert": 1,
100
+ "weight": 2
101
+ },
102
+ {
103
+ "sequence": [
104
+ "support ticket",
105
+ "order rated"
106
+ ],
107
+ "conversionRate": 45,
108
+ "timeToConvert": 6,
109
+ "weight": 1
110
+ },
111
+ {
112
+ "sequence": [
113
+ "subscription started",
114
+ "order placed",
115
+ "subscription cancelled"
116
+ ],
117
+ "conversionRate": 20,
118
+ "timeToConvert": 48,
119
+ "weight": 1
120
+ }
121
+ ],
122
+ "events": [
123
+ {
124
+ "event": "account created",
125
+ "weight": 1,
126
+ "isFirstEvent": true,
127
+ "properties": {
128
+ "signup_method": [
129
+ "email",
130
+ "google",
131
+ "apple",
132
+ "facebook"
133
+ ],
134
+ "referral_code": {
135
+ "functionName": "arrow",
136
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
137
+ }
138
+ }
139
+ },
140
+ {
141
+ "event": "restaurant browsed",
142
+ "weight": 18,
143
+ "properties": {
144
+ "cuisine_type": [
145
+ "American",
146
+ "Italian",
147
+ "Chinese",
148
+ "Japanese",
149
+ "Mexican",
150
+ "Indian",
151
+ "Thai",
152
+ "Mediterranean"
153
+ ],
154
+ "sort_by": [
155
+ "recommended",
156
+ "distance",
157
+ "rating",
158
+ "price"
159
+ ],
160
+ "filter_applied": {
161
+ "functionName": "arrow",
162
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
163
+ }
164
+ }
165
+ },
166
+ {
167
+ "event": "restaurant viewed",
168
+ "weight": 15,
169
+ "properties": {
170
+ "restaurant_id": {
171
+ "functionName": "arrow",
172
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
173
+ },
174
+ "cuisine_type": [
175
+ "American",
176
+ "Italian",
177
+ "Chinese",
178
+ "Japanese",
179
+ "Mexican",
180
+ "Indian",
181
+ "Thai",
182
+ "Mediterranean"
183
+ ],
184
+ "avg_rating": [
185
+ 2,
186
+ 2,
187
+ 2,
188
+ 2,
189
+ 2,
190
+ 4,
191
+ 4,
192
+ 2,
193
+ 3,
194
+ 2,
195
+ 3,
196
+ 2,
197
+ 3,
198
+ 2,
199
+ 4,
200
+ 2,
201
+ 3,
202
+ 5,
203
+ 3,
204
+ 2,
205
+ 3,
206
+ 2,
207
+ 3,
208
+ 2,
209
+ 2,
210
+ 2,
211
+ 2,
212
+ 2,
213
+ 2,
214
+ 2
215
+ ],
216
+ "delivery_time_est_mins": [
217
+ 51,
218
+ 67,
219
+ 36,
220
+ 41,
221
+ 49,
222
+ 47,
223
+ 43,
224
+ 62,
225
+ 52,
226
+ 33,
227
+ 49,
228
+ 53,
229
+ 59,
230
+ 44,
231
+ 77,
232
+ 39,
233
+ 48,
234
+ 73,
235
+ 39,
236
+ 31,
237
+ 53,
238
+ 50,
239
+ 82,
240
+ 66,
241
+ 80,
242
+ 34,
243
+ 53,
244
+ 31,
245
+ 66,
246
+ 39,
247
+ 82,
248
+ 29,
249
+ 73,
250
+ 73,
251
+ 64,
252
+ 36,
253
+ 70,
254
+ 40,
255
+ 56,
256
+ 84
257
+ ],
258
+ "price_tier": [
259
+ "$",
260
+ "$$",
261
+ "$$$",
262
+ "$$$$"
263
+ ]
264
+ }
265
+ },
266
+ {
267
+ "event": "item added to cart",
268
+ "weight": 14,
269
+ "properties": {
270
+ "item_id": {
271
+ "functionName": "arrow",
272
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
273
+ },
274
+ "item_category": [
275
+ "entree",
276
+ "appetizer",
277
+ "drink",
278
+ "dessert",
279
+ "side"
280
+ ],
281
+ "item_price": [
282
+ 54,
283
+ 23,
284
+ 16,
285
+ 33,
286
+ 21,
287
+ 46,
288
+ 20,
289
+ 33,
290
+ 22,
291
+ 33,
292
+ 23,
293
+ 16,
294
+ 12,
295
+ 48,
296
+ 41,
297
+ 40,
298
+ 31,
299
+ 25,
300
+ 23,
301
+ 26,
302
+ 36,
303
+ 8,
304
+ 35,
305
+ 16,
306
+ 35,
307
+ 18,
308
+ 29,
309
+ 20,
310
+ 12,
311
+ 43,
312
+ 19,
313
+ 11,
314
+ 16,
315
+ 29,
316
+ 38,
317
+ 20,
318
+ 42,
319
+ 19,
320
+ 15,
321
+ 17
322
+ ],
323
+ "customization_count": [
324
+ 2,
325
+ 2,
326
+ 1,
327
+ 2,
328
+ 2,
329
+ 1,
330
+ 1,
331
+ 2,
332
+ 2,
333
+ 3,
334
+ 3,
335
+ 1,
336
+ 2,
337
+ 2,
338
+ 2,
339
+ 2,
340
+ 2,
341
+ 0,
342
+ 1,
343
+ 4
344
+ ]
345
+ }
346
+ },
347
+ {
348
+ "event": "item removed from cart",
349
+ "weight": 5,
350
+ "properties": {
351
+ "item_id": {
352
+ "functionName": "arrow",
353
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
354
+ },
355
+ "removal_reason": [
356
+ "changed_mind",
357
+ "too_expensive",
358
+ "substitution"
359
+ ]
360
+ }
361
+ },
362
+ {
363
+ "event": "coupon applied",
364
+ "weight": 4,
365
+ "properties": {
366
+ "coupon_code": {
367
+ "functionName": "arrow",
368
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
369
+ },
370
+ "discount_type": [
371
+ "percent",
372
+ "flat",
373
+ "free_delivery"
374
+ ],
375
+ "discount_value": [
376
+ 20,
377
+ 26,
378
+ 18,
379
+ 26,
380
+ 34,
381
+ 21,
382
+ 16,
383
+ 21,
384
+ 21,
385
+ 15,
386
+ 34,
387
+ 30,
388
+ 35,
389
+ 34,
390
+ 14,
391
+ 30,
392
+ 34,
393
+ 27,
394
+ 33,
395
+ 19
396
+ ]
397
+ }
398
+ },
399
+ {
400
+ "event": "checkout started",
401
+ "weight": 12,
402
+ "properties": {
403
+ "cart_total": [
404
+ 52,
405
+ 42,
406
+ 65,
407
+ 77,
408
+ 79,
409
+ 31,
410
+ 28,
411
+ 39,
412
+ 138,
413
+ 40,
414
+ 47,
415
+ 44,
416
+ 44,
417
+ 41,
418
+ 34,
419
+ 82,
420
+ 51,
421
+ 123,
422
+ 93,
423
+ 56,
424
+ 56,
425
+ 66,
426
+ 65,
427
+ 43,
428
+ 102,
429
+ 97,
430
+ 108,
431
+ 36,
432
+ 39,
433
+ 63,
434
+ 132,
435
+ 128,
436
+ 51,
437
+ 33,
438
+ 41,
439
+ 46,
440
+ 100,
441
+ 52,
442
+ 48,
443
+ 97
444
+ ],
445
+ "items_count": [
446
+ 5,
447
+ 5,
448
+ 3,
449
+ 5,
450
+ 4,
451
+ 3,
452
+ 7,
453
+ 6,
454
+ 3,
455
+ 7,
456
+ 5,
457
+ 4,
458
+ 4,
459
+ 2,
460
+ 6,
461
+ 3,
462
+ 4,
463
+ 5,
464
+ 4,
465
+ 3
466
+ ],
467
+ "delivery_address_saved": {
468
+ "functionName": "arrow",
469
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
470
+ }
471
+ }
472
+ },
473
+ {
474
+ "event": "order placed",
475
+ "weight": 10,
476
+ "properties": {
477
+ "order_id": {
478
+ "functionName": "arrow",
479
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
480
+ },
481
+ "payment_method": [
482
+ "credit_card",
483
+ "apple_pay",
484
+ "google_pay",
485
+ "paypal",
486
+ "cash"
487
+ ],
488
+ "order_total": [
489
+ 157,
490
+ 143,
491
+ 77,
492
+ 108,
493
+ 167,
494
+ 43,
495
+ 56,
496
+ 91,
497
+ 95,
498
+ 143,
499
+ 51,
500
+ 76,
501
+ 136,
502
+ 14,
503
+ 136,
504
+ 121,
505
+ 89,
506
+ 66,
507
+ 54,
508
+ 48,
509
+ 187,
510
+ 107,
511
+ 59,
512
+ 40,
513
+ 167,
514
+ 60,
515
+ 132,
516
+ 82,
517
+ 54,
518
+ 173,
519
+ 51,
520
+ 71,
521
+ 131,
522
+ 60,
523
+ 146,
524
+ 59,
525
+ 132,
526
+ 79,
527
+ 48,
528
+ 88
529
+ ],
530
+ "tip_amount": [
531
+ 15,
532
+ 12,
533
+ 8,
534
+ 15,
535
+ 3,
536
+ 11,
537
+ 10,
538
+ 8,
539
+ 8,
540
+ 11,
541
+ 15,
542
+ 17,
543
+ 12,
544
+ 18,
545
+ 14,
546
+ 15,
547
+ 7,
548
+ 13,
549
+ 28,
550
+ 4
551
+ ],
552
+ "delivery_fee": [
553
+ 9,
554
+ 8,
555
+ 5,
556
+ 3,
557
+ 3,
558
+ 3,
559
+ 8,
560
+ 3,
561
+ 3,
562
+ 4,
563
+ 6,
564
+ 7,
565
+ 2,
566
+ 9,
567
+ 1,
568
+ 4,
569
+ 9,
570
+ 7,
571
+ 2,
572
+ 5
573
+ ]
574
+ }
575
+ },
576
+ {
577
+ "event": "order tracked",
578
+ "weight": 13,
579
+ "properties": {
580
+ "order_id": {
581
+ "functionName": "arrow",
582
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
583
+ },
584
+ "order_status": [
585
+ "confirmed",
586
+ "preparing",
587
+ "picked_up",
588
+ "en_route",
589
+ "delivered"
590
+ ],
591
+ "eta_mins": [
592
+ 46,
593
+ 18,
594
+ 26,
595
+ 41,
596
+ 29,
597
+ 24,
598
+ 33,
599
+ 27,
600
+ 22,
601
+ 27,
602
+ 40,
603
+ 41,
604
+ 13,
605
+ 9,
606
+ 20,
607
+ 52,
608
+ 23,
609
+ 19,
610
+ 27,
611
+ 19,
612
+ 14,
613
+ 25,
614
+ 16,
615
+ 37,
616
+ 18,
617
+ 20,
618
+ 25,
619
+ 28,
620
+ 23,
621
+ 41
622
+ ]
623
+ }
624
+ },
625
+ {
626
+ "event": "order delivered",
627
+ "weight": 9,
628
+ "properties": {
629
+ "order_id": {
630
+ "functionName": "arrow",
631
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
632
+ },
633
+ "actual_delivery_mins": [
634
+ 21,
635
+ 57,
636
+ 30,
637
+ 54,
638
+ 55,
639
+ 51,
640
+ 31,
641
+ 35,
642
+ 15,
643
+ 54,
644
+ 34,
645
+ 58,
646
+ 25,
647
+ 15,
648
+ 32,
649
+ 78,
650
+ 31,
651
+ 35,
652
+ 33,
653
+ 62,
654
+ 40,
655
+ 45,
656
+ 33,
657
+ 43,
658
+ 77,
659
+ 72,
660
+ 56,
661
+ 31,
662
+ 32,
663
+ 43,
664
+ 40,
665
+ 35,
666
+ 51,
667
+ 44,
668
+ 31,
669
+ 75,
670
+ 35,
671
+ 70,
672
+ 47,
673
+ 52
674
+ ],
675
+ "on_time": {
676
+ "functionName": "arrow",
677
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
678
+ }
679
+ }
680
+ },
681
+ {
682
+ "event": "order rated",
683
+ "weight": 7,
684
+ "properties": {
685
+ "order_id": {
686
+ "functionName": "arrow",
687
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
688
+ },
689
+ "food_rating": [
690
+ 3,
691
+ 2,
692
+ 3,
693
+ 2,
694
+ 1,
695
+ 3,
696
+ 4,
697
+ 4,
698
+ 2,
699
+ 2,
700
+ 2,
701
+ 2,
702
+ 2,
703
+ 3,
704
+ 2,
705
+ 2,
706
+ 2,
707
+ 3,
708
+ 2,
709
+ 2,
710
+ 3,
711
+ 1,
712
+ 3,
713
+ 3,
714
+ 3,
715
+ 2,
716
+ 2,
717
+ 4,
718
+ 4,
719
+ 2
720
+ ],
721
+ "delivery_rating": [
722
+ 1,
723
+ 2,
724
+ 1,
725
+ 2,
726
+ 3,
727
+ 2,
728
+ 2,
729
+ 3,
730
+ 2,
731
+ 5,
732
+ 4,
733
+ 3,
734
+ 4,
735
+ 2,
736
+ 4,
737
+ 2,
738
+ 3,
739
+ 2,
740
+ 4,
741
+ 2,
742
+ 2,
743
+ 2,
744
+ 3,
745
+ 2,
746
+ 2,
747
+ 2,
748
+ 4,
749
+ 2,
750
+ 4,
751
+ 2
752
+ ],
753
+ "would_reorder": {
754
+ "functionName": "arrow",
755
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
756
+ }
757
+ }
758
+ },
759
+ {
760
+ "event": "search performed",
761
+ "weight": 11,
762
+ "properties": {
763
+ "search_query": {
764
+ "functionName": "arrow",
765
+ "body": "() => chance.pickone([\n\t\t\t\t\t\"pizza\", \"sushi\", \"burger\", \"tacos\", \"pad thai\",\n\t\t\t\t\t\"chicken\", \"salad\", \"ramen\", \"pasta\", \"sandwich\",\n\t\t\t\t\t\"wings\", \"curry\", \"pho\", \"burritos\", \"steak\"\n\t\t\t\t])"
766
+ },
767
+ "results_count": [
768
+ 8,
769
+ 13,
770
+ 19,
771
+ 13,
772
+ 33,
773
+ 31,
774
+ 20,
775
+ 10,
776
+ 12,
777
+ 39,
778
+ 41,
779
+ 28,
780
+ 5,
781
+ 15,
782
+ 14,
783
+ 29,
784
+ 18,
785
+ 14,
786
+ 27,
787
+ 15,
788
+ 34,
789
+ 21,
790
+ 12,
791
+ 13,
792
+ 28,
793
+ 12,
794
+ 23,
795
+ 8,
796
+ 15,
797
+ 37
798
+ ],
799
+ "search_type": [
800
+ "restaurant",
801
+ "cuisine",
802
+ "dish"
803
+ ]
804
+ }
805
+ },
806
+ {
807
+ "event": "promotion viewed",
808
+ "weight": 8,
809
+ "properties": {
810
+ "promo_id": {
811
+ "functionName": "arrow",
812
+ "body": "() => `promo_${v.uid(5)}`"
813
+ },
814
+ "promo_type": [
815
+ "banner",
816
+ "push",
817
+ "in_feed"
818
+ ],
819
+ "promo_value": [
820
+ "10%",
821
+ "15%",
822
+ "20%",
823
+ "25%",
824
+ "30%",
825
+ "40%",
826
+ "50%"
827
+ ]
828
+ }
829
+ },
830
+ {
831
+ "event": "subscription started",
832
+ "weight": 2,
833
+ "properties": {
834
+ "plan": {
835
+ "functionName": "arrow",
836
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
837
+ },
838
+ "price": {
839
+ "functionName": "arrow",
840
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
841
+ },
842
+ "trial": {
843
+ "functionName": "arrow",
844
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
845
+ }
846
+ }
847
+ },
848
+ {
849
+ "event": "subscription cancelled",
850
+ "weight": 1,
851
+ "properties": {
852
+ "reason": [
853
+ "too_expensive",
854
+ "not_ordering_enough",
855
+ "found_alternative",
856
+ "bad_experience"
857
+ ],
858
+ "months_subscribed": [
859
+ 2,
860
+ 15,
861
+ 12,
862
+ 14,
863
+ 16,
864
+ 10,
865
+ 13,
866
+ 11,
867
+ 18,
868
+ 14,
869
+ 10,
870
+ 2,
871
+ 10,
872
+ 12,
873
+ 3
874
+ ]
875
+ }
876
+ },
877
+ {
878
+ "event": "support ticket",
879
+ "weight": 3,
880
+ "properties": {
881
+ "issue_type": [
882
+ "missing_item",
883
+ "wrong_order",
884
+ "late_delivery",
885
+ "quality_issue",
886
+ "refund_request"
887
+ ],
888
+ "order_id": {
889
+ "functionName": "arrow",
890
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
891
+ }
892
+ }
893
+ },
894
+ {
895
+ "event": "reorder initiated",
896
+ "weight": 6,
897
+ "properties": {
898
+ "order_id": {
899
+ "functionName": "arrow",
900
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
901
+ },
902
+ "original_order_age_days": [
903
+ 38,
904
+ 18,
905
+ 24,
906
+ 27,
907
+ 36,
908
+ 31,
909
+ 44,
910
+ 28,
911
+ 37,
912
+ 27,
913
+ 13,
914
+ 29,
915
+ 19,
916
+ 20,
917
+ 30,
918
+ 46,
919
+ 18,
920
+ 32,
921
+ 32,
922
+ 23,
923
+ 31,
924
+ 25,
925
+ 32,
926
+ 29,
927
+ 43,
928
+ 21,
929
+ 5,
930
+ 25,
931
+ 26,
932
+ 46
933
+ ]
934
+ }
935
+ }
936
+ ],
937
+ "superProps": {
938
+ "platform": [
939
+ "iOS",
940
+ "Android",
941
+ "Web"
942
+ ],
943
+ "subscription_tier": {
944
+ "functionName": "arrow",
945
+ "body": "function () {\n\t\tconst weighted = [];\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tconst rand = chance.d10(); // Random number between 1 and 10\n\n\t\t\t// 35% chance to favor the most chosen index\n\t\t\tif (chance.bool({ likelihood: 35 })) {\n\t\t\t\t// 50% chance to slightly alter the index\n\t\t\t\tif (chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tweighted.push(items[mostChosenIndex]);\n\t\t\t\t} else {\n\t\t\t\t\tconst addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;\n\t\t\t\t\tlet newIndex = mostChosenIndex + addOrSubtract;\n\n\t\t\t\t\t// Ensure newIndex is within bounds\n\t\t\t\t\tif (newIndex < 0) newIndex = 0;\n\t\t\t\t\tif (newIndex >= items.length) newIndex = items.length - 1;\n\t\t\t\t\tweighted.push(items[newIndex]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 25% chance to favor the second most chosen index\n\t\t\telse if (chance.bool({ likelihood: 25 })) {\n\t\t\t\tweighted.push(items[secondMostChosenIndex]);\n\t\t\t}\n\t\t\t// 15% chance to favor the third most chosen index\n\t\t\telse if (chance.bool({ likelihood: 15 })) {\n\t\t\t\tweighted.push(items[thirdMostChosenIndex]);\n\t\t\t}\n\t\t\t// Otherwise, pick a random item from the list\n\t\t\telse {\n\t\t\t\tweighted.push(chance.pickone(items));\n\t\t\t}\n\t\t}\n\t\treturn weighted;\n\t}"
946
+ },
947
+ "city": [
948
+ "New York",
949
+ "Los Angeles",
950
+ "Chicago",
951
+ "Houston",
952
+ "Phoenix",
953
+ "San Francisco"
954
+ ]
955
+ },
956
+ "userProps": {
957
+ "preferred_cuisine": [
958
+ "American",
959
+ "Italian",
960
+ "Chinese",
961
+ "Japanese",
962
+ "Mexican",
963
+ "Indian",
964
+ "Thai",
965
+ "Mediterranean"
966
+ ],
967
+ "avg_order_value": [
968
+ 57,
969
+ 56,
970
+ 40,
971
+ 46,
972
+ 68,
973
+ 52,
974
+ 30,
975
+ 43,
976
+ 49,
977
+ 40,
978
+ 47,
979
+ 32,
980
+ 32,
981
+ 52,
982
+ 46,
983
+ 40,
984
+ 38,
985
+ 29,
986
+ 35,
987
+ 22,
988
+ 71,
989
+ 41,
990
+ 35,
991
+ 51,
992
+ 52,
993
+ 48,
994
+ 27,
995
+ 52,
996
+ 30,
997
+ 62,
998
+ 29,
999
+ 65,
1000
+ 31,
1001
+ 37,
1002
+ 40,
1003
+ 58,
1004
+ 29,
1005
+ 60,
1006
+ 33,
1007
+ 41
1008
+ ],
1009
+ "orders_per_month": [
1010
+ 5,
1011
+ 12,
1012
+ 13,
1013
+ 10,
1014
+ 15,
1015
+ 9,
1016
+ 6,
1017
+ 14,
1018
+ 10,
1019
+ 6
1020
+ ],
1021
+ "favorite_restaurant_count": [
1022
+ 4,
1023
+ 6,
1024
+ 6,
1025
+ 9,
1026
+ 5,
1027
+ 1,
1028
+ 8,
1029
+ 4,
1030
+ 3,
1031
+ 5,
1032
+ 5,
1033
+ 6,
1034
+ 1,
1035
+ 3,
1036
+ 5,
1037
+ 5,
1038
+ 8,
1039
+ 6,
1040
+ 4,
1041
+ 8,
1042
+ 6,
1043
+ 6,
1044
+ 8,
1045
+ 4,
1046
+ 8,
1047
+ 3,
1048
+ 9,
1049
+ 4,
1050
+ 3,
1051
+ 5,
1052
+ 2,
1053
+ 3,
1054
+ 5,
1055
+ 5,
1056
+ 4,
1057
+ 3,
1058
+ 6,
1059
+ 6,
1060
+ 5,
1061
+ 3,
1062
+ 6,
1063
+ 7,
1064
+ 3,
1065
+ 7,
1066
+ 8,
1067
+ 7,
1068
+ 5,
1069
+ 2,
1070
+ 2,
1071
+ 6
1072
+ ]
1073
+ },
1074
+ "groupKeys": [
1075
+ [
1076
+ "restaurant_id",
1077
+ 200,
1078
+ [
1079
+ "restaurant viewed",
1080
+ "order placed",
1081
+ "order rated"
1082
+ ]
1083
+ ]
1084
+ ],
1085
+ "groupProps": {
1086
+ "restaurant_id": {
1087
+ "name": {
1088
+ "functionName": "arrow",
1089
+ "body": "() => `${chance.pickone([\"The\", \"Big\", \"Lucky\", \"Golden\", \"Fresh\", \"Urban\"])} ${chance.pickone([\"Kitchen\", \"Grill\", \"Bowl\", \"Wok\", \"Bistro\", \"Plate\", \"Table\", \"Fork\"])}`"
1090
+ },
1091
+ "cuisine": [
1092
+ "American",
1093
+ "Italian",
1094
+ "Chinese",
1095
+ "Japanese",
1096
+ "Mexican",
1097
+ "Indian",
1098
+ "Thai",
1099
+ "Mediterranean"
1100
+ ],
1101
+ "avg_rating": [
1102
+ 4,
1103
+ 2,
1104
+ 4,
1105
+ 3,
1106
+ 3,
1107
+ 4,
1108
+ 3,
1109
+ 2,
1110
+ 4,
1111
+ 3,
1112
+ 3,
1113
+ 4,
1114
+ 4,
1115
+ 4,
1116
+ 2,
1117
+ 2,
1118
+ 3,
1119
+ 3,
1120
+ 2,
1121
+ 5,
1122
+ 3,
1123
+ 2,
1124
+ 3,
1125
+ 2,
1126
+ 3,
1127
+ 3,
1128
+ 2,
1129
+ 2,
1130
+ 3,
1131
+ 2
1132
+ ],
1133
+ "delivery_radius_mi": [
1134
+ 6,
1135
+ 4,
1136
+ 4,
1137
+ 11,
1138
+ 8,
1139
+ 9,
1140
+ 7,
1141
+ 10,
1142
+ 5,
1143
+ 10
1144
+ ]
1145
+ }
1146
+ },
1147
+ "lookupTables": []
1148
+ },
1149
+ "hooks": "function (record, type, meta) {\n\t\tconst NOW = dayjs();\n\t\tconst DATASET_START = NOW.subtract(days, 'days');\n\t\tconst RAINY_WEEK_START = DATASET_START.add(20, 'days');\n\t\tconst RAINY_WEEK_END = DATASET_START.add(27, 'days');\n\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\t// HOOK 1: LUNCH RUSH CONVERSION (funnel-pre)\n\t\t// During lunch (11AM-1PM) and dinner (5PM-8PM), funnel conversion\n\t\t// rates are boosted to reflect real-world meal-time ordering surges.\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\tif (type === \"funnel-pre\") {\n\t\t\tif (meta && meta.firstEventTime) {\n\t\t\t\tconst hour = dayjs.unix(meta.firstEventTime).hour();\n\n\t\t\t\trecord.props = record.props || {};\n\n\t\t\t\t// Lunch rush: 11AM - 1PM\n\t\t\t\tif (hour >= 11 && hour <= 13) {\n\t\t\t\t\trecord.conversionRate = record.conversionRate * 1.4;\n\t\t\t\t\trecord.props.lunch_rush = true;\n\t\t\t\t\trecord.props.dinner_rush = false;\n\t\t\t\t}\n\t\t\t\t// Dinner rush: 5PM - 8PM\n\t\t\t\telse if (hour >= 17 && hour <= 20) {\n\t\t\t\t\trecord.conversionRate = record.conversionRate * 1.2;\n\t\t\t\t\trecord.props.lunch_rush = false;\n\t\t\t\t\trecord.props.dinner_rush = true;\n\t\t\t\t} else {\n\t\t\t\t\trecord.props.lunch_rush = false;\n\t\t\t\t\trecord.props.dinner_rush = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\t// HOOK 8: FIRST ORDER BONUS (funnel-pre)\n\t\t\t// New users (born in dataset) get a massive conversion boost on\n\t\t\t// the Order Completion funnel, simulating first-order promotions\n\t\t\t// and new-user incentives that drive initial purchases.\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\tif (record.sequence &&\n\t\t\t\trecord.sequence[0] === \"checkout started\" &&\n\t\t\t\trecord.sequence[1] === \"order placed\") {\n\t\t\t\trecord.props = record.props || {};\n\t\t\t\t// Use hash to deterministically assign ~50% of users as \"new\"\n\t\t\t\tconst userId = meta && meta.user && (meta.user.distinct_id || String(meta.user));\n\t\t\t\tconst isNewUser = userId && userId.charCodeAt(0) % 2 === 0;\n\t\t\t\tif (isNewUser) {\n\t\t\t\t\trecord.conversionRate = Math.min(98, record.conversionRate * 1.4);\n\t\t\t\t\trecord.props.first_order_bonus = true;\n\t\t\t\t} else {\n\t\t\t\t\trecord.props.first_order_bonus = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\t// HOOK 2: COUPON INJECTION (funnel-post)\n\t\t// Free-tier users get coupon_applied events spliced into their\n\t\t// funnel sequences 30% of the time, simulating promotional nudges\n\t\t// that push non-subscribers toward conversion.\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\tif (type === \"funnel-post\") {\n\t\t\tif (Array.isArray(record) && record.length >= 2) {\n\t\t\t\t// Check if user is Free tier from first event's super props\n\t\t\t\tconst firstEvent = record[0];\n\t\t\t\tconst isFreeUser = firstEvent && firstEvent.subscription_tier === \"Free\";\n\n\t\t\t\tif (isFreeUser && chance.bool({ likelihood: 30 })) {\n\t\t\t\t\t// Pick a random insertion point between funnel steps\n\t\t\t\t\tconst insertIdx = chance.integer({ min: 1, max: record.length - 1 });\n\t\t\t\t\tconst prevEvent = record[insertIdx - 1];\n\t\t\t\t\tconst nextEvent = record[insertIdx];\n\t\t\t\t\tconst midTime = dayjs(prevEvent.time).add(\n\t\t\t\t\t\tdayjs(nextEvent.time).diff(dayjs(prevEvent.time)) / 2,\n\t\t\t\t\t\t'milliseconds'\n\t\t\t\t\t).toISOString();\n\n\t\t\t\t\tconst couponEvent = {\n\t\t\t\t\t\tevent: \"coupon applied\",\n\t\t\t\t\t\ttime: midTime,\n\t\t\t\t\t\tuser_id: firstEvent.user_id,\n\t\t\t\t\t\tsubscription_tier: firstEvent.subscription_tier,\n\t\t\t\t\t\tplatform: firstEvent.platform,\n\t\t\t\t\t\tcity: firstEvent.city,\n\t\t\t\t\t\tcoupon_code: chance.pickone(couponCodes),\n\t\t\t\t\t\tdiscount_type: chance.pickone([\"percent\", \"flat\", \"free_delivery\"]),\n\t\t\t\t\t\tdiscount_value: chance.integer({ min: 10, max: 30 }),\n\t\t\t\t\t\tcoupon_injected: true,\n\t\t\t\t\t};\n\t\t\t\t\trecord.splice(insertIdx, 0, couponEvent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\t// HOOK 3: LATE NIGHT MUNCHIES (event)\n\t\t// Between 10PM and 2AM, restaurant views and cart additions skew\n\t\t// heavily toward fast food (American cuisine) with inflated prices,\n\t\t// modeling the real-world late-night ordering pattern.\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\tif (type === \"event\") {\n\t\t\tconst EVENT_TIME = dayjs(record.time);\n\t\t\tconst hour = EVENT_TIME.hour();\n\t\t\tconst isLateNight = hour >= 22 || hour <= 2;\n\n\t\t\tif (record.event === \"restaurant viewed\" || record.event === \"item added to cart\") {\n\t\t\t\tif (isLateNight) {\n\t\t\t\t\t// 70% of late-night browsing/ordering is fast food (American)\n\t\t\t\t\tif (chance.bool({ likelihood: 70 })) {\n\t\t\t\t\t\tif (record.cuisine_type !== undefined) {\n\t\t\t\t\t\t\trecord.cuisine_type = \"American\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Boost item price by 1.3x (late-night surcharge effect)\n\t\t\t\t\tif (record.item_price !== undefined) {\n\t\t\t\t\t\trecord.item_price = Math.round(record.item_price * 1.3 * 100) / 100;\n\t\t\t\t\t}\n\n\t\t\t\t\trecord.late_night_order = true;\n\t\t\t\t} else {\n\t\t\t\t\trecord.late_night_order = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\t// HOOK 4: RAINY WEEK SURGE (event)\n\t\t\t// During days 20-27, delivery fees double on order_placed events\n\t\t\t// and there is a 40% chance of duplicating the event, simulating\n\t\t\t// a weather-driven demand surge with surge pricing.\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\tif (record.event === \"order placed\") {\n\t\t\t\tif (EVENT_TIME.isAfter(RAINY_WEEK_START) && EVENT_TIME.isBefore(RAINY_WEEK_END)) {\n\t\t\t\t\trecord.delivery_fee = (record.delivery_fee || 5) * 2;\n\t\t\t\t\trecord.surge_pricing = true;\n\t\t\t\t\trecord.rainy_week = true;\n\t\t\t\t} else {\n\t\t\t\t\trecord.surge_pricing = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\t// HOOK 5: REFERRAL POWER USERS (everything)\n\t\t// Users who signed up with a referral code get 2x more reorder\n\t\t// events and boosted food ratings. Referred users are more loyal\n\t\t// and satisfied, mirroring real referral program outcomes.\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\tif (type === \"everything\") {\n\t\t\tconst userEvents = record;\n\n\t\t\t// First pass: identify user patterns\n\t\t\tlet isReferralUser = false;\n\t\t\tlet hasTrialSubscription = false;\n\t\t\tlet earlyOrderCount = 0;\n\t\t\tlet firstEventTime = userEvents.length > 0 ? dayjs(userEvents[0].time) : null;\n\n\t\t\tuserEvents.forEach((event) => {\n\t\t\t\tconst eventTime = dayjs(event.time);\n\t\t\t\tconst daysSinceStart = firstEventTime ? eventTime.diff(firstEventTime, 'days', true) : 0;\n\n\t\t\t\t// Hook #5: Track referral users\n\t\t\t\tif (event.event === \"account created\" && event.referral_code === true) {\n\t\t\t\t\tisReferralUser = true;\n\t\t\t\t}\n\n\t\t\t\t// Hook #6: Track trial subscribers and early orders\n\t\t\t\tif (event.event === \"subscription started\" && event.trial === true) {\n\t\t\t\t\thasTrialSubscription = true;\n\t\t\t\t}\n\t\t\t\tif (event.event === \"order placed\" && daysSinceStart <= 14) {\n\t\t\t\t\tearlyOrderCount++;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Second pass: apply referral power user effects\n\t\t\tuserEvents.forEach((event, idx) => {\n\t\t\t\tif (event.event === \"order rated\") {\n\t\t\t\t\tif (isReferralUser) {\n\t\t\t\t\t\tevent.food_rating = chance.integer({ min: 4, max: 5 });\n\t\t\t\t\t\tevent.referral_user = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tevent.referral_user = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isReferralUser && event.event === \"reorder initiated\" && chance.bool({ likelihood: 50 })) {\n\t\t\t\t\tconst eventTime = dayjs(event.time);\n\t\t\t\t\tconst extraReorder = {\n\t\t\t\t\t\tevent: \"reorder initiated\",\n\t\t\t\t\t\ttime: eventTime.add(chance.integer({ min: 1, max: 7 }), 'days').toISOString(),\n\t\t\t\t\t\tuser_id: event.user_id,\n\t\t\t\t\t\torder_id: chance.pickone(orderIds),\n\t\t\t\t\t\toriginal_order_age_days: chance.integer({ min: 3, max: 30 }),\n\t\t\t\t\t\treferral_user: true,\n\t\t\t\t\t};\n\t\t\t\t\tuserEvents.splice(idx + 1, 0, extraReorder);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\t// HOOK 6: TRIAL CONVERSION (everything)\n\t\t\t// Trial subscribers who place 3+ orders in their first 14 days\n\t\t\t// are retained. Those with fewer orders churn: 60% of their\n\t\t\t// events after day 14 are removed, simulating subscription\n\t\t\t// abandonment after a failed trial experience.\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\tif (hasTrialSubscription) {\n\t\t\t\tconst trialCutoff = firstEventTime ? firstEventTime.add(14, 'days') : null;\n\n\t\t\t\tif (earlyOrderCount >= 3) {\n\t\t\t\t\t// Retained: mark events as trial_retained\n\t\t\t\t\tuserEvents.forEach((event) => {\n\t\t\t\t\t\tevent.trial_retained = true;\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// Churned: remove 60% of events after day 14\n\t\t\t\t\tuserEvents.forEach((event) => {\n\t\t\t\t\t\tevent.trial_retained = false;\n\t\t\t\t\t});\n\t\t\t\t\tfor (let i = userEvents.length - 1; i >= 0; i--) {\n\t\t\t\t\t\tconst evt = userEvents[i];\n\t\t\t\t\t\tif (trialCutoff && dayjs(evt.time).isAfter(trialCutoff)) {\n\t\t\t\t\t\t\tif (chance.bool({ likelihood: 60 })) {\n\t\t\t\t\t\t\t\tuserEvents.splice(i, 1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\t// HOOK 4: RAINY WEEK SURGE - duplicate order events (everything)\n\t\t\t// Events tagged surge_pricing=true in the event hook get a 40%\n\t\t\t// chance of duplication here, creating visible demand spikes.\n\t\t\t// ═══════════════════════════════════════════════════════════════\n\t\t\tconst rainyDuplicates = [];\n\t\t\tuserEvents.forEach((event) => {\n\t\t\t\tif (event.surge_pricing === true && event.event === \"order placed\" && chance.bool({ likelihood: 40 })) {\n\t\t\t\t\tconst dup = JSON.parse(JSON.stringify(event));\n\t\t\t\t\tdup.time = dayjs(event.time).add(chance.integer({ min: 5, max: 60 }), 'minutes').toISOString();\n\t\t\t\t\tdup.rainy_week = true;\n\t\t\t\t\trainyDuplicates.push(dup);\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (rainyDuplicates.length > 0) {\n\t\t\t\tuserEvents.push(...rainyDuplicates);\n\t\t\t}\n\t\t}\n\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\t// HOOK 7: SUPPORT TICKET CHURN (user)\n\t\t// 15% of users are flagged as high-risk with elevated churn scores.\n\t\t// The remaining users get low churn scores. This creates a clear\n\t\t// segmentation opportunity for proactive retention campaigns.\n\t\t// ═══════════════════════════════════════════════════════════════════\n\t\tif (type === \"user\") {\n\t\t\tif (chance.bool({ likelihood: 15 })) {\n\t\t\t\trecord.is_high_risk = true;\n\t\t\t\trecord.churn_risk_score = chance.integer({ min: 70, max: 100 });\n\t\t\t} else {\n\t\t\t\trecord.is_high_risk = false;\n\t\t\t\trecord.churn_risk_score = chance.integer({ min: 0, max: 40 });\n\t\t\t}\n\t\t}\n\n\t\treturn record;\n\t}",
1150
+ "timestamp": "2026-04-10T01:39:07.015Z",
1151
+ "version": "4.0"
1152
+ }