make-mp-data 2.1.11 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +31 -0
  2. package/dungeons/adspend.js +35 -1
  3. package/dungeons/anon.js +25 -1
  4. package/dungeons/array-of-object-lookup.js +201 -0
  5. package/dungeons/benchmark-heavy.js +241 -0
  6. package/dungeons/benchmark-light.js +141 -0
  7. package/dungeons/big.js +10 -9
  8. package/dungeons/business.js +60 -12
  9. package/dungeons/complex.js +35 -1
  10. package/dungeons/copilot.js +383 -0
  11. package/dungeons/education.js +1005 -0
  12. package/dungeons/experiments.js +18 -4
  13. package/dungeons/fintech.js +976 -0
  14. package/dungeons/foobar.js +32 -0
  15. package/dungeons/food.js +988 -0
  16. package/dungeons/funnels.js +38 -1
  17. package/dungeons/gaming.js +26 -5
  18. package/dungeons/media.js +861 -270
  19. package/dungeons/mil.js +31 -3
  20. package/dungeons/mirror.js +33 -1
  21. package/dungeons/retention-cadence.js +211 -0
  22. package/dungeons/rpg.js +1178 -0
  23. package/dungeons/sanity.js +32 -2
  24. package/dungeons/sass.js +923 -0
  25. package/dungeons/scd.js +47 -1
  26. package/dungeons/simple.js +29 -14
  27. package/dungeons/social.js +928 -0
  28. package/dungeons/streaming.js +373 -0
  29. package/dungeons/strict-event-test.js +30 -0
  30. package/dungeons/student-teacher.js +19 -5
  31. package/dungeons/text-generation.js +120 -84
  32. package/dungeons/too-big-events.js +203 -0
  33. package/dungeons/{userAgent.js → user-agent.js} +23 -2
  34. package/entry.js +5 -4
  35. package/index.js +41 -54
  36. package/lib/core/config-validator.js +122 -7
  37. package/lib/core/context.js +7 -14
  38. package/lib/core/storage.js +57 -25
  39. package/lib/generators/adspend.js +12 -12
  40. package/lib/generators/events.js +6 -5
  41. package/lib/generators/funnels.js +32 -10
  42. package/lib/generators/product-lookup.js +262 -0
  43. package/lib/generators/product-names.js +195 -0
  44. package/lib/generators/profiles.js +3 -3
  45. package/lib/generators/scd.js +13 -3
  46. package/lib/generators/text.js +17 -4
  47. package/lib/orchestrators/mixpanel-sender.js +244 -204
  48. package/lib/orchestrators/user-loop.js +54 -16
  49. package/lib/templates/phrases.js +473 -16
  50. package/lib/templates/schema.d.ts +173 -0
  51. package/lib/templates/verbose-schema.js +140 -206
  52. package/lib/utils/chart.js +210 -0
  53. package/lib/utils/function-registry.js +285 -0
  54. package/lib/utils/json-evaluator.js +172 -0
  55. package/lib/utils/logger.js +34 -0
  56. package/lib/utils/utils.js +41 -4
  57. package/package.json +12 -21
  58. package/types.d.ts +15 -5
  59. package/dungeons/ai-chat-analytics-ed.js +0 -274
  60. package/dungeons/money2020-ed-also.js +0 -277
  61. package/dungeons/money2020-ed.js +0 -579
  62. package/lib/generators/text-bak-old.js +0 -1121
  63. package/lib/orchestrators/worker-manager.js +0 -203
  64. package/lib/templates/hooks-instructions.txt +0 -434
  65. package/lib/templates/phrases-bak.js +0 -925
  66. package/lib/templates/prompt (old).txt +0 -98
  67. package/lib/templates/schema-instructions.txt +0 -155
  68. package/lib/templates/scratch-dungeon-template.js +0 -116
  69. package/lib/templates/textQuickTest.js +0 -172
  70. package/lib/utils/ai.js +0 -120
  71. package/lib/utils/project.js +0 -166
@@ -0,0 +1,173 @@
1
+ /**
2
+ * A "ValueValid" can be:
3
+ * - A primitive value (string, number, boolean)
4
+ * - An array of primitives (the system picks one randomly)
5
+ * - A function call object: { "functionName": "...", "args": [...] }
6
+ * - An arrow function object: { "functionName": "arrow", "body": "..." }
7
+ *
8
+ * This is the building block for all property values in the dungeon.
9
+ */
10
+ type Primitives = string | number | boolean;
11
+ type FunctionCall = { functionName: string; args?: any[]; body?: string };
12
+ type ValueValid = Primitives | Primitives[] | FunctionCall;
13
+
14
+
15
+ /**
16
+ * The main configuration object for the entire data generation spec, known as a "Dungeon".
17
+ * This is the high-level object you will be constructing.
18
+ *
19
+ * REQUIRED fields: events, funnels, superProps, userProps
20
+ * OPTIONAL fields: scdProps, groupKeys, groupProps, groupEvents
21
+ */
22
+ export interface Dungeon {
23
+ /** REQUIRED: A list of all possible events that can occur in the simulation. */
24
+ events: EventConfig[];
25
+
26
+ /** REQUIRED: A list of event sequences that represent user journeys (e.g., sign-up, purchase). */
27
+ funnels: Funnel[];
28
+
29
+ /** REQUIRED: Properties that are attached to every event for all users. */
30
+ superProps: Record<string, ValueValid>;
31
+
32
+ /** REQUIRED: Properties that define the characteristics of individual users. */
33
+ userProps: Record<string, ValueValid>;
34
+
35
+ /** OPTIONAL: Properties that change for users or groups over time (Slowly Changing Dimensions). Only include when properties explicitly change over time. */
36
+ scdProps?: Record<string, SCDProp>;
37
+
38
+ /** OPTIONAL: Defines group entities (companies, teams). Format: [["group_key", count], ...]. ONLY for B2B/SaaS scenarios. */
39
+ groupKeys?: [string, number][];
40
+
41
+ /** OPTIONAL: Properties for groups defined in groupKeys. ONLY include if groupKeys is defined. */
42
+ groupProps?: Record<string, Record<string, ValueValid>>;
43
+
44
+ /** OPTIONAL: Events attributed to groups on a schedule (e.g., monthly billing). Rarely needed. */
45
+ groupEvents?: GroupEventConfig[];
46
+ }
47
+
48
+
49
+ /**
50
+ * Defines a single event, its properties, and its likelihood of occurring.
51
+ *
52
+ * The "weight" determines relative frequency - an event with weight 10 occurs
53
+ * roughly 10x more often than an event with weight 1.
54
+ */
55
+ interface EventConfig {
56
+ /** REQUIRED: The name of the event (e.g., "Page View", "Add to Cart", "checkout"). */
57
+ event: string;
58
+
59
+ /** OPTIONAL: The relative frequency of this event. Higher numbers = more frequent. Default: 1 */
60
+ weight?: number;
61
+
62
+ /** OPTIONAL: Properties associated with this event. Each property can be a value or array. */
63
+ properties?: Record<string, ValueValid>;
64
+
65
+ /** OPTIONAL: If true, this event will be the first event for a new user (e.g., "sign up"). Only one event should have this. */
66
+ isFirstEvent?: boolean;
67
+
68
+ /** OPTIONAL: If true, this event signifies that a user has churned (e.g., "account deleted"). */
69
+ isChurnEvent?: boolean;
70
+ }
71
+
72
+
73
+ /**
74
+ * Defines a sequence of events that represents a meaningful user journey or workflow.
75
+ *
76
+ * Funnels model how users progress through your product - from sign-up to purchase,
77
+ * from onboarding to activation, etc. The conversionRate determines what percentage
78
+ * of users who start the funnel will complete it.
79
+ */
80
+ interface Funnel {
81
+ /** REQUIRED: Event names that make up this journey. Must match event names in the events array. */
82
+ sequence: string[];
83
+
84
+ /** REQUIRED: Percentage (0-100) of users who complete the funnel. 15 means 15% conversion. */
85
+ conversionRate: number;
86
+
87
+ /** OPTIONAL: The name of the funnel (e.g., "Purchase Funnel", "Onboarding Flow"). */
88
+ name?: string;
89
+
90
+ /** OPTIONAL: The likelihood that a user will attempt this funnel vs others. Default: 1 */
91
+ weight?: number;
92
+
93
+ /** OPTIONAL: If true, this is an initial user experience funnel (e.g., onboarding). */
94
+ isFirstFunnel?: boolean;
95
+
96
+ /** OPTIONAL: Average hours to complete the funnel. Default: 1 */
97
+ timeToConvert?: number;
98
+
99
+ /**
100
+ * OPTIONAL: How events are ordered within the funnel.
101
+ * - "sequential" (default): Events happen in exact order
102
+ * - "random": Events can happen in any order
103
+ * - "first-fixed": First event is fixed, rest are random
104
+ * - "last-fixed": Last event is fixed, rest are random
105
+ * - "first-and-last-fixed": First and last are fixed, middle is random
106
+ */
107
+ order?: "sequential" | "random" | "first-fixed" | "last-fixed" | "first-and-last-fixed";
108
+
109
+ /** OPTIONAL: Properties attached to every event in this funnel (e.g., experiment_variant, traffic_source). */
110
+ props?: Record<string, ValueValid>;
111
+
112
+ /** OPTIONAL: User property conditions for eligibility. Only users matching these values run this funnel. */
113
+ conditions?: Record<string, ValueValid>;
114
+
115
+ /** OPTIONAL: If true, generates 3 variants with different conversion rates for A/B testing analysis. */
116
+ experiment?: boolean;
117
+ }
118
+
119
+
120
+ /**
121
+ * Defines a "Slowly Changing Dimension" - a property of a user or group
122
+ * that changes periodically over time (e.g., subscription plan, user role).
123
+ *
124
+ * ONLY include SCDs when properties explicitly need to change over time.
125
+ * For static properties, just use userProps or groupProps.
126
+ */
127
+ interface SCDProp {
128
+ /** OPTIONAL: The entity type - 'user' or a group key like 'company_id'. Default: 'user' */
129
+ type?: "user" | string;
130
+
131
+ /** REQUIRED: How often this property can change. */
132
+ frequency: "day" | "week" | "month" | "year";
133
+
134
+ /** REQUIRED: Possible values for this property. */
135
+ values: ValueValid;
136
+
137
+ /**
138
+ * REQUIRED: When changes occur.
139
+ * - "fixed": Changes occur exactly on the frequency interval
140
+ * - "fuzzy": Changes occur randomly around the interval
141
+ */
142
+ timing: "fixed" | "fuzzy";
143
+
144
+ /** OPTIONAL: Maximum number of times this property can change per entity. Default: 100 */
145
+ max?: number;
146
+ }
147
+
148
+
149
+ /**
150
+ * Defines an event attributed to a group on a regular schedule.
151
+ * Example: monthly subscription charges, weekly reports, etc.
152
+ *
153
+ * This is rarely needed - only use for B2B scenarios with recurring group-level events.
154
+ */
155
+ interface GroupEventConfig {
156
+ /** REQUIRED: The name of the event. */
157
+ event: string;
158
+
159
+ /** REQUIRED: How often the event occurs (in days). e.g., 30 for monthly. */
160
+ frequency: number;
161
+
162
+ /** REQUIRED: The group key this event belongs to (e.g., "company_id"). */
163
+ group_key: string;
164
+
165
+ /** OPTIONAL: If true, a random user in the group is also attributed to the event. */
166
+ attribute_to_user?: boolean;
167
+
168
+ /** OPTIONAL: Properties for this event. */
169
+ properties?: Record<string, ValueValid>;
170
+
171
+ /** OPTIONAL: Relative frequency of this event. */
172
+ weight?: number;
173
+ }
@@ -3,6 +3,8 @@
3
3
  * and how they might be implemented with extensive comments so an AI can understand it
4
4
  * it is not meant to be used as a template, but rather as a reference for how to create a dungeon
5
5
  * it is also used as a test for the AI to see if it can generate a dungeon with the same structure
6
+ *
7
+ * IMPORTANT: This file uses the NEW JSON format for function calls with "functionName" and "args"
6
8
  */
7
9
 
8
10
 
@@ -13,13 +15,22 @@ import dayjs from "dayjs";
13
15
  import utc from "dayjs/plugin/utc.js";
14
16
  dayjs.extend(utc);
15
17
  import "dotenv/config";
16
- import { weighNumRange, range, date, initChance, exhaust, choose, integer, decimal, odds } from "../../lib/utils/utils.js";
18
+ import { weighNumRange, range, date, initChance, exhaust, integer, decimal, odds } from "../utils/utils.js";
17
19
  const { NODE_ENV = "unknown" } = process.env;
18
20
 
19
21
 
20
22
  /** @type {import("../../types.js").Dungeon} */
21
23
  //SPLIT HERE
22
24
  const DUNGEON = {
25
+ /**
26
+ * ⚠️ IMPORTANT NOTE ABOUT THIS EXAMPLE:
27
+ * This is a comprehensive example showing ALL possible features.
28
+ * Most dungeons will NOT need all these features, especially:
29
+ * - Groups (groupKeys, groupProps) - ONLY for B2B/SaaS scenarios
30
+ * - SCDs - ONLY when properties change over time
31
+ *
32
+ * Focus on the REQUIRED fields: events, funnels, superProps, userProps
33
+ */
23
34
 
24
35
  /**
25
36
  * events are the core building blocks of the dungeon
@@ -29,29 +40,30 @@ const DUNGEON = {
29
40
  * they can be simple values or functions that return a value
30
41
  * we have a few built-in functions to help you generate data
31
42
  * you MUST create events for every dungeon
43
+ *
44
+ * IMPORTANT: All function calls use the new JSON format with functionName and args
32
45
  */
33
46
  events: [
34
47
  {
35
48
  event: "checkout",
36
49
  weight: 2,
37
50
  properties: {
38
- amount: weighNumRange(5, 500, .25), // this is ok if you need to pick a number from a range; params are min, max, skew (opt), and size (size of pool, also opt)
51
+ amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] }, // weighted random number in range
39
52
  currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
40
53
  coupon: ["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"],
41
- numItems: weighNumRange(1, 10),
42
-
54
+ numItems: { "functionName": "weighNumRange", "args": [1, 10] },
43
55
  }
44
56
  },
45
57
  {
46
58
  event: "add to cart",
47
59
  weight: 4,
48
60
  properties: {
49
- amount: weighNumRange(5, 500, .25),
50
- rating: weighNumRange(1, 5),
51
- reviews: weighNumRange(0, 35),
61
+ amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] },
62
+ rating: { "functionName": "weighNumRange", "args": [1, 5] },
63
+ reviews: { "functionName": "weighNumRange", "args": [0, 35] },
52
64
  isFeaturedItem: [true, false, false],
53
65
  itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
54
- dateItemListed: date(30, true, "YYYY-MM-DD") // this is ok if you need to pick a date from a range; params are intheLastDays, isPast, format
66
+ dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] } // date in the last 30 days
55
67
  }
56
68
  },
57
69
  {
@@ -68,12 +80,10 @@ const DUNGEON = {
68
80
  properties: {
69
81
  videoCategory: ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"],
70
82
  isFeaturedItem: [true, false, false],
71
- watchTimeSec: weighNumRange(10, 600, .25),
83
+ watchTimeSec: { "functionName": "weighNumRange", "args": [10, 600, 0.25] },
72
84
  quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
73
85
  format: ["mp4", "avi", "mov", "mpg"],
74
- uploader_id: chance.guid.bind(chance), // You have access to the chance.js library for generating random data. You can assign a chance function directly to a property, like name: chance.company or uploader_id: chance.guid.
75
- video_id: range(1, 1000) // SEE LOOKUP TABLES BELOW
76
-
86
+ uploader_id: { "functionName": "chance.guid", "args": [] } // Using chance.js library with dot notation
77
87
  }
78
88
  },
79
89
  {
@@ -82,8 +92,7 @@ const DUNGEON = {
82
92
  properties: {
83
93
  isFeaturedItem: [true, false, false],
84
94
  itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
85
- dateItemListed: date(30, true, "YYYY-MM-DD"),
86
- product_id: range(1, 1000) // SEE LOOKUP TABLES BELOW
95
+ dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] }
87
96
  }
88
97
  },
89
98
  {
@@ -92,247 +101,172 @@ const DUNGEON = {
92
101
  properties: {
93
102
  isFeaturedItem: [true, false, false],
94
103
  itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
95
- dateItemListed: date(30, true, "YYYY-MM-DD"),
104
+ dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] },
96
105
  }
97
106
  },
98
107
  {
99
108
  event: "sign up",
100
109
  isFirstEvent: true,
101
- weight: 0,
110
+ weight: 1,
111
+ properties: {
112
+ wasReferred: [true, false, false, false],
113
+ }
114
+ },
115
+ {
116
+ event: "search",
117
+ weight: 6,
118
+ properties: {
119
+ query: { "functionName": "chance.word", "args": [] },
120
+ resultsFound: { "functionName": "weighNumRange", "args": [0, 100, 0.25] },
121
+ }
122
+ },
123
+ {
124
+ event: "remove from cart",
125
+ weight: 1,
102
126
  properties: {
103
- variants: ["A", "B", "C", "Control"],
104
- flows: ["new", "existing", "loyal", "churned"],
105
- flags: ["on", "off"],
106
- experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
107
- multiVariate: [true, false]
127
+ amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] },
128
+ reason: ["changed mind", "too expensive", "found better", "not needed"],
108
129
  }
130
+ },
131
+ {
132
+ event: "share item",
133
+ weight: 2,
134
+ properties: {
135
+ medium: ["email", "facebook", "twitter", "whatsapp", "sms", "slack"],
136
+ recipient_count: { "functionName": "weighNumRange", "args": [1, 10] },
137
+ }
138
+ },
139
+ {
140
+ event: "session end",
141
+ weight: 1
109
142
  }
110
143
  ],
144
+
111
145
  /**
112
- * superProps are properties that are attached to every event
113
- * they are selected randomly each time an event is generated
146
+ * superProps are properties that are added to EVERY event
114
147
  * they can be simple values or functions that return a value
115
- * you will LIKELY create superProps for every dungeon
148
+ * the random values will be chosen for each event
149
+ * you MUST create superProps for every dungeon
116
150
  */
117
151
  superProps: {
118
- theme: ["light", "dark", "custom", "light", "dark"],
152
+ plan: ["free", "free", "free", "plus", "plus", "pro"], // arrays are randomly sampled from
153
+ // using arrow functions with the new format
154
+ region: { "functionName": "arrow", "body": "chance.pickone(['North America', 'Europe', 'Asia', 'South America', 'Africa', 'Oceania'])" }
119
155
  },
156
+
120
157
  /**
121
- * user properties work the same as event properties during the generation phase
122
- * except each user has a single set of user properties
123
- * these are properties that are attached to the user profile
158
+ * userProps are properties that are associated with a user
124
159
  * they can be simple values or functions that return a value
160
+ * this is the $set property on the user profile
125
161
  * you MUST create userProps for every dungeon
126
162
  */
127
163
  userProps: {
128
- title: ["Mr.", "Ms.", "Mrs.", "Dr.", "Prof.", "Sir", "Madam", "Lord", "Lady", "Dame", "Baron", "Baroness", "Count", "Countess", "Viscount", "Viscountess", "Marquis", "Marchioness"],
129
- role: ["basic", "basic", "basic", "premium", "admin"], // role property that can be used in funnel conditions
130
- luckyNumber: weighNumRange(42, 420, .3),
131
- 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", "rhinoceus beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"]
164
+ favoriteBrand: ["nike", "adidas", "puma", "reebok", "new balance", "asics", "vans", "converse"],
165
+ favoriteProduct: ["shoes", "clothing", "sports", "equipment"],
166
+ // Example using chance.integer with options
167
+ age: { "functionName": "chance.integer", "args": [{"min": 18, "max": 65}] },
168
+ isSubscribed: [true, false],
169
+ plan: ["free", "free", "free", "plus", "plus", "pro"],
170
+ signupDate: { "functionName": "date", "args": [365, true, "YYYY-MM-DD"] },
171
+ // Example using arrow function for complex expression
172
+ user_id: { "functionName": "arrow", "body": "`user_${chance.guid()}`" },
173
+ company: { "functionName": "chance.company", "args": [] },
174
+ email: { "functionName": "chance.email", "args": [] },
175
+ name: { "functionName": "chance.name", "args": [] }
132
176
  },
177
+
133
178
  /**
134
- * Funnels represent intentional user journeys (e.g., sign-up, checkout),
135
- * composed of a sequence of events.
136
- * You should design at least one first-time funnel (e.g., onboarding)
137
- * and one recurring funnel (e.g., purchases or content browsing)
138
- * unless otherwise specified in the prompt.
139
- * Funnels are the primary mechanism used to generate the example data, and it's critical sequences match events in the events array.
140
- * there are many different options for the funnels like:
141
- * isFirstFunnel, conversionRate, isChurnFunnel, order, props, requireRepeats, timeToConvert, weight, conditions
142
- *
143
- * isFirstFunnel are funnels a user will only go through once (like a sign up)
144
- * non isFirstFunnel are funnels a user will go through multiple times (like a purchase)
145
- *
146
- * conditions are used to filter which users are eligible for a specific funnel based on their user properties
147
- * this is useful when different user segments should have different behavioral patterns
148
- * for example: premium users might have access to advanced features, students vs teachers have different workflows
149
- *
179
+ * funnels are sequences of events that represent a user journey
180
+ * each funnel has a sequence of events and a conversion rate
181
+ * the conversion rate determines how many users complete the funnel
182
+ * you can also add conditions to funnels to filter users based on properties
183
+ * you MUST create funnels for every dungeon
150
184
  */
151
185
  funnels: [
152
186
  {
153
- name: "sign up funnel",
154
- description: "journey for users signing up",
155
- sequence: ["page view", "page view", "sign up"],
156
- isFirstFunnel: true,
157
- conversionRate: 50, // 50% of users will convert
158
- order: "sequential", // events must occur in order
159
- requireRepeats: false, // users can repeat events in the funnel
160
- props: {}, // you can add properties to the funnel
161
- timeToConvert: 1, // time to convert in hours
187
+ sequence: ["sign up", "page view", "view item", "add to cart", "checkout"],
188
+ conversionRate: 15, // Integer 0-100 representing percentage
162
189
  },
163
190
  {
164
- name: "purchase funnel",
165
- description: "how users purchase items",
166
- sequence: ["page view", "view item", "add to cart", "checkout"],
167
- isFirstFunnel: false,
168
- conversionRate: 10,
169
- timeToConvert: 24,
170
- requireRepeats: true, // users can repeat events in the funnel
171
- order: "first-and-last-fixed"
191
+ sequence: ["page view", "watch video"],
192
+ conversionRate: 65,
172
193
  },
173
194
  {
174
- name: "browsing funnel",
175
- description: "how users browse items",
176
- sequence: ["page view", "view item", "watch video", "save item"],
177
- isFirstFunnel: false,
178
- conversionRate: 65,
179
- timeToConvert: 2,
180
- requireRepeats: true, // users can repeat events in the funnel
181
- order: "random",
182
- props: {
183
- "browsing type": ["casual", "intentional", "exploratory"] // you can add properties to the funnel
184
- }
195
+ sequence: ["page view", "view item", "save item"],
196
+ conversionRate: 25,
197
+ conditions: { plan: "free" } // Conditions must be an object, not a string expression
185
198
  },
186
199
  {
187
- name: "premium user workflow",
188
- description: "advanced features only available to premium users",
189
- sequence: ["page view", "view item", "save item", "checkout"],
190
- isFirstFunnel: false,
191
- conversionRate: 85,
192
- timeToConvert: 3,
193
- conditions: {
194
- role: "premium" // only users with role "premium" are eligible for this funnel
195
- },
196
- order: "sequential",
197
- props: {
198
- "feature_tier": "premium"
199
- }
200
+ sequence: ["search", "view item", "add to cart"],
201
+ conversionRate: 35
202
+ },
203
+ {
204
+ sequence: ["page view", "view item", "share item"],
205
+ conversionRate: 10
200
206
  }
201
207
  ],
208
+
202
209
  /**
203
- * scdProps are used for to represent slowly changing dimensions (at both a user and group level)
204
- * these are properties that change over time, but not frequently
205
- * and importantly we care about behaviors (events) during different epochs of SCD state
206
- * you do NOT need to make SCD properties for every dungeon; only the ones with a use case for them
210
+ * scdProps are Slowly Changing Dimensions properties
211
+ * these are properties that change over time for users or groups
212
+ * each SCD property has a type (user or group), frequency, values, timing, and max changes
213
+ * OPTIONAL: only include if your use case involves properties that change over time
207
214
  */
208
215
  scdProps: {
209
216
  role: {
210
217
  type: "user",
211
- frequency: "week",
212
- values: ["admin", "collaborator", "user", "view only", "no access"],
213
- timing: "fuzzy",
214
- max: 10
218
+ frequency: "month", // how often the property changes
219
+ values: ["admin", "user", "viewer", "editor"],
220
+ timing: "fixed", // fixed or fuzzy
221
+ max: 5 // maximum number of changes per entity
215
222
  },
216
- NPS: {
223
+ subscription_tier: {
217
224
  type: "user",
218
- frequency: "day",
219
- values: weighNumRange(1, 10, 2, 150),
220
- timing: "fuzzy",
221
- max: 10
222
- },
223
- MRR: {
224
- type: "company_id",
225
- frequency: "month",
226
- values: weighNumRange(0, 10000, .15),
227
- timing: "fixed",
228
- max: 10
229
- },
230
- AccountHealthScore: {
231
- type: "company_id",
232
- frequency: "week",
233
- values: weighNumRange(1, 10, .15),
234
- timing: "fixed",
235
- max: 40
236
- },
237
- plan: {
238
- type: "company_id",
239
- frequency: "month",
225
+ frequency: "month", // Changed from "quarter" - valid options are: day, week, month, year
240
226
  values: ["free", "basic", "premium", "enterprise"],
241
- timing: "fixed",
242
- max: 10
227
+ timing: "fuzzy", // Changed from "random" - valid options are: fixed, fuzzy
228
+ max: 3
243
229
  }
244
230
  },
245
231
 
246
-
247
232
  /**
248
- * groupKeys are used to represent entities for custom uniqueBy analysis
249
- * they are an array of arrays, where each inner array is a pair of group_key and the number of profiles for that key
250
- * this is useful for group analysis, where you want to analyze data by groups of users (by company, institution, school, etc...)
251
- * groups have props too (they can also have events) ... but there can be more than one "group entity"
252
- * you do NOT need to make groupKeys for every dungeon; only the ones with a use case for them
233
+ * groupKeys define the types of groups in your data
234
+ * Each group key is a tuple of [groupName, numberOfGroups]
235
+ *
236
+ * ⚠️ IMPORTANT: Groups are OPTIONAL and should ONLY be included when:
237
+ * - The use case explicitly involves B2B relationships
238
+ * - You have SaaS with company/team accounts
239
+ * - There's a clear one-to-many relationship (one company → many users)
240
+ * - Keywords mentioned: "company", "organization", "team", "workspace", "account"
241
+ *
242
+ * DO NOT include for B2C (business to consumer) scenarios
243
+ * DO NOT include unless explicitly needed for group analytics
253
244
  */
254
245
  groupKeys: [
255
- ["company_id", 500, []],
256
- ["room_id", 10000, ["save video", "comment", "watch video"]]
246
+ ["company_id", 1000], // 1000 companies
247
+ ["team_id", 5000] // 5000 teams
257
248
  ],
258
- groupProps: {
259
- company_id: {
260
- name: () => { return chance.company(); }, // YOU CAN USE CHANCE IT"S BUILT IN!
261
- email: () => { return `CSM: ${chance.pickone(["AK", "Jessica", "Michelle", "Dana", "Brian", "Dave"])}`; },
262
- "# of employees": weighNumRange(3, 10000),
263
- "industry": ["tech", "finance", "healthcare", "education", "government", "non-profit"],
264
- "segment": ["enterprise", "SMB", "mid-market"],
265
- "products": [["core"], ["core"], ["core", "add-ons"], ["core", "pro-serve"], ["core", "add-ons", "pro-serve"], ["core", "BAA", "enterprise"], ["free"], ["free"], ["free", "addons"]],
266
- },
267
- room_id: {
268
- name: () => { return `#${chance.word({ length: integer(4, 24), capitalize: true })}`; },
269
- email: ["public", "private"],
270
- "room provider": ["partner", "core", "core", "core"],
271
- "room capacity": weighNumRange(3, 1000000),
272
- "isPublic": [true, false, false, false, false],
273
- "country": chance.country.bind(chance),
274
- "isVerified": [true, true, false, false, false],
275
- }
276
- },
277
249
 
278
250
  /**
279
- * groupEvents are used to represent events that are associated with a group that occur
280
- * regularly (like a card charged, or a subscription renewed)
281
- * they are generally NOT attributed to a user, but rather to a group entity
282
- * you do NOT need to make groupEvents for every dungeon; only the ones with a use case for them
283
- */
284
- groupEvents: [
285
- {
286
- attribute_to_user: false,
287
- event: "card charged",
288
- weight: 1,
289
-
290
- frequency: 30,
291
- group_key: "company_id",
292
- group_size: 500,
293
- properties: {
294
- amount: weighNumRange(5, 500, .25),
295
- currency: ["USD", "USD", "USD", "CAD", "EUR", "EUR", "BTC", "BTC", "ETH", "JPY"],
296
- plan: ["basic", "premium", "enterprise"],
297
- "payment method": []
298
- }
299
- }],
300
-
301
- /**
302
- * lookupTables are used to represent static data that can be used in events
303
- * they are an array of objects, where each object is a key-value pair
304
- * this is useful for representing static data that can be used in events
305
- * like product information, user information, etc...
306
- * you do NOT need to make lookupTables for every dungeon; only the ones with a use case for them
307
- * if you DO make a lookupTable, you MUST provide a key in some events whose value will be numerical
251
+ * groupProps are properties associated with groups
252
+ * Similar to userProps but for group entities
253
+ *
254
+ * ⚠️ IMPORTANT: ONLY include if you have groupKeys defined
255
+ * These represent attributes of the group entities (companies, teams, etc.)
308
256
  */
309
- lookupTables: [{
310
- key: "product_id",
311
- entries: 1000,
312
- attributes: {
313
- category: [
314
- "Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden & Tools", "Pet Supplies", "Food & Grocery", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art"
315
- ],
316
- "demand": ["high", "medium", "medium", "low"],
317
- "supply": ["high", "medium", "medium", "low"],
318
- "manufacturer": chance.company.bind(chance),
319
- "price": weighNumRange(5, 500, .25),
320
- "rating": weighNumRange(1, 5),
321
- "reviews": weighNumRange(0, 35)
322
- }
323
-
324
- },
325
- {
326
- key: "video_id",
327
- entries: 50000,
328
- attributes: {
329
- isFlagged: [true, false, false, false, false],
330
- copyright: ["all rights reserved", "creative commons", "creative commons", "public domain", "fair use"],
331
- uploader_id: chance.guid.bind(chance),
332
- "uploader influence": ["low", "low", "low", "medium", "medium", "high"],
333
- thumbs: weighNumRange(0, 35),
334
- rating: ["G", "PG", "PG-13", "R", "NC-17", "PG-13", "R", "NC-17", "R", "PG", "PG"]
257
+ groupProps: {
258
+ company_id: {
259
+ name: { "functionName": "chance.company", "args": [] },
260
+ plan: ["startup", "growth", "enterprise"],
261
+ employees: { "functionName": "weighNumRange", "args": [1, 5000, 0.3] },
262
+ industry: { "functionName": "chance.pickone", "args": [["tech", "finance", "healthcare", "retail", "manufacturing"]] }
263
+ },
264
+ team_id: {
265
+ name: { "functionName": "arrow", "body": "`Team ${chance.word()}`" },
266
+ size: { "functionName": "weighNumRange", "args": [2, 50, 0.2] },
267
+ department: ["engineering", "sales", "marketing", "support", "product"]
335
268
  }
336
-
337
- }],
269
+ }
338
270
  };
271
+
272
+ export default DUNGEON;