make-mp-data 3.0.2 → 3.0.4

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 (47) hide show
  1. package/dungeons/adspend.js +13 -26
  2. package/dungeons/anon.js +1 -1
  3. package/dungeons/array-of-object-lookup.js +1 -2
  4. package/dungeons/benchmark-heavy.js +5 -6
  5. package/dungeons/benchmark-light.js +13 -28
  6. package/dungeons/big.js +3 -3
  7. package/dungeons/business.js +11 -12
  8. package/dungeons/complex.js +1 -2
  9. package/dungeons/copilot.js +8 -6
  10. package/dungeons/education.js +21 -22
  11. package/dungeons/experiments.js +4 -5
  12. package/dungeons/fintech.js +25 -26
  13. package/dungeons/foobar.js +1 -1
  14. package/dungeons/food.js +24 -25
  15. package/dungeons/funnels.js +2 -2
  16. package/dungeons/gaming.js +39 -40
  17. package/dungeons/media.js +30 -31
  18. package/dungeons/mil.js +17 -18
  19. package/dungeons/mirror.js +2 -3
  20. package/dungeons/retention-cadence.js +1 -2
  21. package/dungeons/rpg.js +42 -43
  22. package/dungeons/sanity.js +1 -2
  23. package/dungeons/sass.js +32 -33
  24. package/dungeons/scd.js +3 -4
  25. package/dungeons/simple.js +13 -14
  26. package/dungeons/social.js +27 -28
  27. package/dungeons/soup-test.js +52 -0
  28. package/dungeons/streaming.js +17 -18
  29. package/dungeons/student-teacher.js +0 -1
  30. package/dungeons/text-generation.js +0 -1
  31. package/dungeons/user-agent.js +1 -2
  32. package/index.js +18 -6
  33. package/lib/core/config-validator.js +22 -33
  34. package/lib/core/context.js +6 -3
  35. package/lib/generators/events.js +13 -10
  36. package/lib/generators/funnels.js +7 -4
  37. package/lib/generators/scd.js +29 -17
  38. package/lib/generators/text.js +18 -12
  39. package/lib/orchestrators/mixpanel-sender.js +26 -38
  40. package/lib/orchestrators/user-loop.js +68 -15
  41. package/lib/templates/phrases.js +8 -5
  42. package/lib/utils/function-registry.js +17 -0
  43. package/lib/utils/utils.js +15 -84
  44. package/package.json +3 -1
  45. package/types.d.ts +86 -19
  46. package/lib/templates/verbose-schema.js +0 -272
  47. package/lib/utils/chart.js +0 -210
package/types.d.ts CHANGED
@@ -12,75 +12,123 @@ export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
12
12
  * main config object for the entire data generation
13
13
  */
14
14
  export interface Dungeon {
15
- // constants
15
+ // ── Core Parameters ──
16
+ /** Mixpanel project token. If provided, data will be imported to Mixpanel after generation. */
16
17
  token?: string;
18
+ /** RNG seed for reproducible output. Same seed + concurrency=1 = identical data. */
17
19
  seed?: string;
20
+ /** Number of days the dataset spans (from "now" looking backward). Default: 30 */
18
21
  numDays?: number;
22
+ /** Explicit start of dataset window (unix seconds). Alternative to numDays. */
19
23
  epochStart?: number;
24
+ /** Explicit end of dataset window (unix seconds). Defaults to FIXED_NOW. */
20
25
  epochEnd?: number;
26
+ /** Target total number of events to generate across all users. */
21
27
  numEvents?: number;
28
+ /** Number of unique users to generate. */
22
29
  numUsers?: number;
30
+ /** Output format for files written to disk. */
23
31
  format?: "csv" | "json" | "parquet" | string;
32
+ /** Mixpanel data residency region. */
24
33
  region?: "US" | "EU";
34
+ /** User generation concurrency. Default: 1. Values > 1 break seed reproducibility and provide no performance benefit (CPU-bound). */
25
35
  concurrency?: number;
36
+ /** Number of records before auto-flushing to disk. Prevents OOM for large datasets. Default: 1,000,000 */
26
37
  batchSize?: number;
27
38
 
39
+ // ── Mixpanel Import Credentials (for SCD import) ──
28
40
  serviceAccount?: string;
29
41
  serviceSecret?: string;
30
42
  projectId?: string;
31
43
 
32
- // ids
44
+ // ── Identifiers ──
45
+ /** Dataset name prefix for output files. Auto-generated if not set. */
33
46
  name?: string;
34
47
 
35
- //switches
48
+ // ── Feature Switches ──
49
+ /** If true, users have no distinct_id (anonymous-only tracking). */
36
50
  isAnonymous?: boolean;
51
+ /** If true, user profiles include avatar URLs. */
37
52
  hasAvatar?: boolean;
53
+ /** If true, events include geo properties (city, region, country, lat/lng). */
38
54
  hasLocation?: boolean;
55
+ /** If true, events include UTM campaign properties. */
39
56
  hasCampaigns?: boolean;
57
+ /** If true, generates ad spend data (impressions, clicks, cost). */
40
58
  hasAdSpend?: boolean;
59
+ /** If true, device pool includes iOS devices. */
41
60
  hasIOSDevices?: boolean;
61
+ /** If true, device pool includes Android devices. */
42
62
  hasAndroidDevices?: boolean;
63
+ /** If true, device pool includes desktop devices. */
43
64
  hasDesktopDevices?: boolean;
65
+ /** If true, events include browser properties. */
44
66
  hasBrowser?: boolean;
67
+ /** If true (default), writes output files to ./data/. Can also be a directory path string. */
45
68
  writeToDisk?: boolean | string;
69
+ /** If true, gzip-compresses output files. */
46
70
  gzip?: boolean;
71
+ /** If true, prints progress to stdout during generation. */
47
72
  verbose?: boolean;
73
+ /** If true, users get anonymous device IDs in addition to distinct_id. */
48
74
  hasAnonIds?: boolean;
75
+ /** If true, users get session IDs attached to events. */
49
76
  hasSessionIds?: boolean;
77
+ /** If true, auto-generates funnels from the events array in addition to any explicit funnels. */
50
78
  alsoInferFunnels?: boolean;
51
- makeChart?: boolean | string;
79
+ /** Restrict all location data to a single country (e.g., "US", "GB"). */
52
80
  singleCountry?: string;
81
+ /** If true, stops generation at exactly numEvents (forces concurrency=1). Without this, event count is approximate. */
53
82
  strictEventCount?: boolean;
83
+ /** Internal flag for UI-triggered jobs (affects SCD credential handling). */
54
84
  isUIJob?: boolean;
55
85
 
56
- //models
57
- events?: EventConfig[]; //| string[]; //can also be a array of strings
86
+ // ── Data Models ──
87
+ /** Event definitions: names, weights, properties, and behavioral flags. */
88
+ events?: EventConfig[];
89
+ /** Properties that appear on EVERY event (e.g., platform, app_version). */
58
90
  superProps?: Record<string, ValueValid>;
91
+ /** Funnel definitions: conversion sequences, rates, ordering strategies. */
59
92
  funnels?: Funnel[];
93
+ /** User profile properties set once per user. */
60
94
  userProps?: Record<string, ValueValid>;
95
+ /** Slowly Changing Dimension properties: time-series mutations of user/group attributes. */
61
96
  scdProps?: Record<string, SCDProp>;
97
+ /** Mirror dataset definitions: create transformed copies of event data. */
62
98
  mirrorProps?: Record<string, MirrorProps>;
63
- groupKeys?: [string, number][] | [string, number, string[]][]; // [key, numGroups, [events]]
99
+ /** Group analytics keys. Format: [key, numGroups] or [key, numGroups, [associatedEvents]]. */
100
+ groupKeys?: [string, number][] | [string, number, string[]][];
101
+ /** Properties for each group key's entities. */
64
102
  groupProps?: Record<string, Record<string, ValueValid>>;
103
+ /** Group-level events (stub — not yet implemented). */
65
104
  groupEvents?: GroupEventConfig[];
105
+ /** Lookup table definitions for dimension tables. */
66
106
  lookupTables?: LookupTableSchema[];
107
+ /** TimeSoup configuration: controls the temporal distribution of events (peaks, deviation, mean). */
67
108
  soup?: soup;
109
+ /** Hook function called on every data point. The primary mechanism for engineering deliberate trends and patterns. */
68
110
  hook?: Hook<any>;
69
111
 
70
- //allow anything to be on the config
112
+ /** Allow arbitrary additional properties on the config. */
71
113
  [key: string]: any;
72
114
 
73
- //probabilities
115
+ // ── Distribution Controls ──
116
+ /** Percentage of users whose account creation falls within the dataset window (vs. pre-existing). Default: 15 */
74
117
  percentUsersBornInDataset?: number;
75
118
  /** Bias toward recent birth dates for users born in dataset (0 = uniform, 1 = heavily recent). Default: 0.3 */
76
119
  bornRecentBias?: number;
77
120
  }
78
121
 
79
122
  export type SCDProp = {
123
+ /** Entity type this SCD applies to. "user" for user profiles; use a group key (e.g., "company_id") for group SCDs. Default: "user" */
80
124
  type?: string | "user" | "company_id" | "team_id" | "department_id";
125
+ /** How often the property mutates. Default: "day" */
81
126
  frequency?: "day" | "week" | "month" | "year";
127
+ /** Array of possible values, or a function that returns values. */
82
128
  values: ValueValid;
129
+ /** "fixed" = mutations at clean boundaries (start of day/week/month/year). "fuzzy" = mutations at any time. Default: "fuzzy" */
83
130
  timing?: "fixed" | "fuzzy";
131
+ /** Maximum number of mutations per entity. Default: 10 */
84
132
  max?: number;
85
133
  };
86
134
 
@@ -88,13 +136,25 @@ export type SCDProp = {
88
136
  * the soup is a set of parameters that determine the distribution of events over time
89
137
  */
90
138
  type soup = {
139
+ /** Controls clustering tightness. Higher = tighter peaks. Default: 2 */
91
140
  deviation?: number;
141
+ /** Number of time clusters to distribute events across. Default: dynamic (numDays/7, minimum 5) */
92
142
  peaks?: number;
143
+ /** Offset for the normal distribution center within each peak. Default: 0 */
93
144
  mean?: number;
94
145
  };
95
146
 
96
147
  /**
97
- * the types of hooks that can be used
148
+ * Hook types and when they fire (in order per user):
149
+ * - "user" — user profile object (mutate in-place, return ignored)
150
+ * - "scd-pre" — array of SCD entries (mutate in-place OR return new array to replace)
151
+ * - "funnel-pre" — funnel config object (mutate conversionRate, timeToConvert, etc. in-place)
152
+ * - "event" — single event with FLAT properties (return value replaces event)
153
+ * - "funnel-post" — array of generated funnel events (mutate in-place, splice to inject)
154
+ * - "everything" — array of ALL events for one user (return array to replace; meta.profile available)
155
+ *
156
+ * Storage-only hooks (fire during hookPush, not in generators):
157
+ * - "ad-spend", "group", "mirror", "lookup"
98
158
  */
99
159
  export type hookTypes =
100
160
  | "event"
@@ -113,7 +173,10 @@ export type hookTypes =
113
173
  | "";
114
174
 
115
175
  /**
116
- * a hook is a function that can be called before each entity is created, and can be used to modify attributes
176
+ * A hook function that receives every piece of data as it flows through the pipeline.
177
+ * @param record - The data being processed (event, profile, array of events, etc.)
178
+ * @param type - Which hook type is firing
179
+ * @param meta - Contextual metadata (varies by type; "everything" includes meta.profile and meta.scd)
117
180
  */
118
181
  export type Hook<T> = (record: any, type: hookTypes, meta: any) => T;
119
182
 
@@ -199,6 +262,7 @@ export interface Context {
199
262
  FIXED_NOW: number;
200
263
  FIXED_BEGIN?: number;
201
264
  TIME_SHIFT_SECONDS: number;
265
+ MAX_TIME: number;
202
266
 
203
267
  // State update methods
204
268
  incrementOperations(): void;
@@ -223,14 +287,24 @@ export interface Context {
223
287
  * how we define events and their properties
224
288
  */
225
289
  export interface EventConfig {
290
+ /** The event name (e.g., "page viewed", "purchase completed"). */
226
291
  event?: string;
292
+ /** Relative frequency weight (1-10). Higher = more likely to be selected. Used for both standalone event selection and funnel sequence building. Default: 1 */
227
293
  weight?: number;
294
+ /** Properties to attach to this event type. Values can be arrays (random pick), functions, or primitives. */
228
295
  properties?: Record<string, ValueValid>;
296
+ /** If true, this is the user's first-ever event (e.g., "sign up"). Used to create onboarding funnels. */
229
297
  isFirstEvent?: boolean;
298
+ /** If true, generating this event signals the user has churned. The user stops producing further events unless returnLikelihood allows them to come back. */
230
299
  isChurnEvent?: boolean;
300
+ /** Probability (0-1) that a churned user returns and continues generating events. 0 = permanent churn, 1 = always returns. Only used when isChurnEvent is true. Default: 0 */
301
+ returnLikelihood?: number;
302
+ /** If true, this event is automatically prepended 15 seconds before each funnel sequence (e.g., "$session_started"). */
231
303
  isSessionStartEvent?: boolean;
304
+ /** Internal: timing offset in milliseconds (set by funnel system, not user-configured). */
232
305
  relativeTimeMs?: number;
233
- isStrictEvent?: boolean;
306
+ /** If true, this event is excluded from auto-generated funnels (inferFunnels and catch-all). Use for system events that shouldn't appear in conversion sequences. */
307
+ isStrictEvent?: boolean;
234
308
  }
235
309
 
236
310
  export interface GroupEventConfig extends EventConfig {
@@ -297,13 +371,6 @@ export interface Funnel {
297
371
  | "interrupted"
298
372
  | string;
299
373
 
300
- /**
301
- * todo: implement this
302
- * if set, the funnel might be the last thing the user does
303
- * ^ the numerical value is the likelihood that the user will churn
304
- * todo: allow for users to be resurrected
305
- */
306
- isChurnFunnel?: void | number;
307
374
  /**
308
375
  * the likelihood that a user will convert (0-100%)
309
376
  */
@@ -1,272 +0,0 @@
1
- /**
2
- * @fileoverview this is a highly verbose schema for a dungeon that shows all the options available
3
- * and how they might be implemented with extensive comments so an AI can understand it
4
- * it is not meant to be used as a template, but rather as a reference for how to create a dungeon
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"
8
- */
9
-
10
-
11
-
12
- import Chance from "chance";
13
- const chance = new Chance();
14
- import dayjs from "dayjs";
15
- import utc from "dayjs/plugin/utc.js";
16
- dayjs.extend(utc);
17
- import "dotenv/config";
18
- import { weighNumRange, range, date, initChance, exhaust, integer, decimal, odds } from "../utils/utils.js";
19
- const { NODE_ENV = "unknown" } = process.env;
20
-
21
-
22
- /** @type {import("../../types.js").Dungeon} */
23
- //SPLIT HERE
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
- */
34
-
35
- /**
36
- * events are the core building blocks of the dungeon
37
- * each event has a name, a weight, and properties
38
- * the weight determines how often the event occurs relative to other events
39
- * properties are the data associated with the event
40
- * they can be simple values or functions that return a value
41
- * we have a few built-in functions to help you generate data
42
- * you MUST create events for every dungeon
43
- *
44
- * IMPORTANT: All function calls use the new JSON format with functionName and args
45
- */
46
- events: [
47
- {
48
- event: "checkout",
49
- weight: 2,
50
- properties: {
51
- amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] }, // weighted random number in range
52
- currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
53
- coupon: ["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"],
54
- numItems: { "functionName": "weighNumRange", "args": [1, 10] },
55
- }
56
- },
57
- {
58
- event: "add to cart",
59
- weight: 4,
60
- properties: {
61
- amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] },
62
- rating: { "functionName": "weighNumRange", "args": [1, 5] },
63
- reviews: { "functionName": "weighNumRange", "args": [0, 35] },
64
- isFeaturedItem: [true, false, false],
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"],
66
- dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] } // date in the last 30 days
67
- }
68
- },
69
- {
70
- event: "page view",
71
- weight: 10,
72
- properties: {
73
- page: ["/", "/", "/help", "/account", "/watch", "/listen", "/product", "/people", "/peace"],
74
- utm_source: ["$organic", "$organic", "$organic", "$organic", "google", "google", "google", "facebook", "facebook", "twitter", "linkedin"],
75
- }
76
- },
77
- {
78
- event: "watch video",
79
- weight: 8,
80
- properties: {
81
- videoCategory: ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"],
82
- isFeaturedItem: [true, false, false],
83
- watchTimeSec: { "functionName": "weighNumRange", "args": [10, 600, 0.25] },
84
- quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
85
- format: ["mp4", "avi", "mov", "mpg"],
86
- uploader_id: { "functionName": "chance.guid", "args": [] } // Using chance.js library with dot notation
87
- }
88
- },
89
- {
90
- event: "view item",
91
- weight: 8,
92
- properties: {
93
- isFeaturedItem: [true, false, false],
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"],
95
- dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] }
96
- }
97
- },
98
- {
99
- event: "save item",
100
- weight: 5,
101
- properties: {
102
- isFeaturedItem: [true, false, false],
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"],
104
- dateItemListed: { "functionName": "date", "args": [30, true, "YYYY-MM-DD"] },
105
- }
106
- },
107
- {
108
- event: "sign up",
109
- isFirstEvent: true,
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,
126
- properties: {
127
- amount: { "functionName": "weighNumRange", "args": [5, 500, 0.25] },
128
- reason: ["changed mind", "too expensive", "found better", "not needed"],
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
142
- }
143
- ],
144
-
145
- /**
146
- * superProps are properties that are added to EVERY event
147
- * they can be simple values or functions that return a value
148
- * the random values will be chosen for each event
149
- * you MUST create superProps for every dungeon
150
- */
151
- superProps: {
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'])" }
155
- },
156
-
157
- /**
158
- * userProps are properties that are associated with a user
159
- * they can be simple values or functions that return a value
160
- * this is the $set property on the user profile
161
- * you MUST create userProps for every dungeon
162
- */
163
- userProps: {
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": [] }
176
- },
177
-
178
- /**
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
184
- */
185
- funnels: [
186
- {
187
- sequence: ["sign up", "page view", "view item", "add to cart", "checkout"],
188
- conversionRate: 15, // Integer 0-100 representing percentage
189
- },
190
- {
191
- sequence: ["page view", "watch video"],
192
- conversionRate: 65,
193
- },
194
- {
195
- sequence: ["page view", "view item", "save item"],
196
- conversionRate: 25,
197
- conditions: { plan: "free" } // Conditions must be an object, not a string expression
198
- },
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
206
- }
207
- ],
208
-
209
- /**
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
214
- */
215
- scdProps: {
216
- role: {
217
- type: "user",
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
222
- },
223
- subscription_tier: {
224
- type: "user",
225
- frequency: "month", // Changed from "quarter" - valid options are: day, week, month, year
226
- values: ["free", "basic", "premium", "enterprise"],
227
- timing: "fuzzy", // Changed from "random" - valid options are: fixed, fuzzy
228
- max: 3
229
- }
230
- },
231
-
232
- /**
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
244
- */
245
- groupKeys: [
246
- ["company_id", 1000], // 1000 companies
247
- ["team_id", 5000] // 5000 teams
248
- ],
249
-
250
- /**
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.)
256
- */
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"]
268
- }
269
- }
270
- };
271
-
272
- export default DUNGEON;