@ak--47/dungeon-master 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +518 -0
- package/dungeons/array-of-object-lookup-schema.json +327 -0
- package/dungeons/array-of-object-lookup.js +220 -0
- package/dungeons/ecommerce-schema.json +462 -0
- package/dungeons/ecommerce.js +447 -0
- package/dungeons/education-schema.json +2409 -0
- package/dungeons/education.js +768 -0
- package/dungeons/fintech-schema.json +14034 -0
- package/dungeons/fintech.js +696 -0
- package/dungeons/foobar-schema.json +403 -0
- package/dungeons/foobar.js +296 -0
- package/dungeons/food-delivery-schema.json +192 -0
- package/dungeons/food-delivery.js +602 -0
- package/dungeons/food-schema.json +1152 -0
- package/dungeons/food.js +754 -0
- package/dungeons/gaming-schema.json +1270 -0
- package/dungeons/gaming.js +508 -0
- package/dungeons/insurance-application-schema.json +204 -0
- package/dungeons/insurance-application.js +605 -0
- package/dungeons/media-schema.json +906 -0
- package/dungeons/media.js +790 -0
- package/dungeons/retention-cadence-schema.json +78 -0
- package/dungeons/retention-cadence.js +244 -0
- package/dungeons/rpg-schema.json +4526 -0
- package/dungeons/rpg.js +919 -0
- package/dungeons/sanity-schema.json +255 -0
- package/dungeons/sanity.js +152 -0
- package/dungeons/sass-schema.json +1291 -0
- package/dungeons/sass.js +795 -0
- package/dungeons/scd-schema.json +919 -0
- package/dungeons/scd.js +277 -0
- package/dungeons/simple-schema.json +608 -0
- package/dungeons/simple.js +285 -0
- package/dungeons/simplest-schema.json +1418 -0
- package/dungeons/simplest.js +392 -0
- package/dungeons/social-schema.json +1118 -0
- package/dungeons/social.js +686 -0
- package/dungeons/text-generation-schema.json +3096 -0
- package/dungeons/text-generation.js +812 -0
- package/index.js +567 -0
- package/lib/core/config-validator.js +395 -0
- package/lib/core/context.js +204 -0
- package/lib/core/dungeon-loader.js +337 -0
- package/lib/core/storage.js +379 -0
- package/lib/generators/adspend.js +132 -0
- package/lib/generators/events.js +271 -0
- package/lib/generators/funnels.js +407 -0
- package/lib/generators/mirror.js +167 -0
- package/lib/generators/product-lookup.js +262 -0
- package/lib/generators/product-names.js +195 -0
- package/lib/generators/profiles.js +93 -0
- package/lib/generators/scd.js +124 -0
- package/lib/generators/text.js +1192 -0
- package/lib/orchestrators/mixpanel-sender.js +266 -0
- package/lib/orchestrators/user-loop.js +335 -0
- package/lib/templates/abbreviated.d.ts +169 -0
- package/lib/templates/defaults.js +1405 -0
- package/lib/templates/phrases.js +2526 -0
- package/lib/templates/schema.d.ts +173 -0
- package/lib/templates/soup-presets.js +188 -0
- package/lib/utils/function-registry.js +302 -0
- package/lib/utils/json-evaluator.js +172 -0
- package/lib/utils/logger.js +34 -0
- package/lib/utils/utils.js +1490 -0
- package/package.json +89 -0
- package/types.d.ts +865 -0
package/dungeons/sass.js
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc.js";
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import * as u from "../lib/utils/utils.js";
|
|
5
|
+
import * as v from "ak-tools";
|
|
6
|
+
|
|
7
|
+
const SEED = "harness-sass";
|
|
8
|
+
dayjs.extend(utc);
|
|
9
|
+
const chance = u.initChance(SEED);
|
|
10
|
+
const num_users = 5_000;
|
|
11
|
+
const days = 100;
|
|
12
|
+
|
|
13
|
+
/** @typedef {import("../types.js").Dungeon} Config */
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
* DATASET OVERVIEW
|
|
18
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
*
|
|
20
|
+
* CLOUDFORGE - B2B Cloud Infrastructure Monitoring & Deployment Platform
|
|
21
|
+
*
|
|
22
|
+
* CloudForge is a B2B SaaS platform that combines infrastructure monitoring (like Datadog)
|
|
23
|
+
* with deployment automation (like Terraform). It serves engineering teams across companies
|
|
24
|
+
* of all sizes - from startups deploying their first microservice to enterprises managing
|
|
25
|
+
* thousands of services across multi-cloud environments.
|
|
26
|
+
*
|
|
27
|
+
* - 5,000 users over 100 days
|
|
28
|
+
* - 600K events across 18 event types (+ 1 hook-created event type)
|
|
29
|
+
* - 8 funnels (onboarding, monitoring, incident response, deployment, infra, team, docs, billing)
|
|
30
|
+
* - Group analytics (companies)
|
|
31
|
+
* - Desktop/browser only (B2B SaaS - no mobile devices)
|
|
32
|
+
*
|
|
33
|
+
* CORE PLATFORM:
|
|
34
|
+
* Teams create workspaces, deploy services across AWS/GCP/Azure, and monitor everything
|
|
35
|
+
* from a unified dashboard. The platform tracks uptime, latency, error rates, CPU/memory
|
|
36
|
+
* usage, and costs. When things go wrong, CloudForge triggers alerts that route through
|
|
37
|
+
* PagerDuty/Slack integrations, and on-call engineers acknowledge and resolve incidents
|
|
38
|
+
* using automated runbooks.
|
|
39
|
+
*
|
|
40
|
+
* PRICING MODEL:
|
|
41
|
+
* Four tiers: Free, Team, Business, Enterprise. Enterprise customers get dedicated
|
|
42
|
+
* customer success managers and annual contracts. Pricing based on seat count and
|
|
43
|
+
* resource usage.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
48
|
+
* ANALYTICS HOOKS
|
|
49
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
50
|
+
*
|
|
51
|
+
* 8 deliberately architected patterns hidden in the data, simulating real-world
|
|
52
|
+
* B2B SaaS behavior. Several hooks use event removal (splice), event replacement,
|
|
53
|
+
* and module-level closure state tracking via Map objects.
|
|
54
|
+
*
|
|
55
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
* 1. END-OF-QUARTER SPIKE (event hook)
|
|
57
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
*
|
|
59
|
+
* Days 80-90: billing events shift toward plan upgrades 40% of the time, and team
|
|
60
|
+
* member invitations are duplicated 50% of the time. Tagged: quarter_end_push: true.
|
|
61
|
+
*
|
|
62
|
+
* Mixpanel Report — Plan Upgrades Over Time:
|
|
63
|
+
* • Insights line chart
|
|
64
|
+
* • Event: "billing event", filter "event_type" = "plan_upgraded"
|
|
65
|
+
* • Daily trend
|
|
66
|
+
* • Expected: Spike in plan upgrades during days 80-90 (4x normal volume)
|
|
67
|
+
*
|
|
68
|
+
* Mixpanel Report — Team Expansion Surge:
|
|
69
|
+
* • Insights line chart
|
|
70
|
+
* • Event: "team member invited", filter "quarter_end_push" = true
|
|
71
|
+
* • Daily trend
|
|
72
|
+
* • Expected: Clear volume spike in last 10 days with duplicate invites
|
|
73
|
+
*
|
|
74
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
* 2. CHURNED ACCOUNT SILENCING (everything hook)
|
|
76
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
77
|
+
*
|
|
78
|
+
* ~10% of users (hash of distinct_id, idHash % 5 === 0) go completely silent
|
|
79
|
+
* after day 30. ALL events after month 1 are removed via splice(). User profiles
|
|
80
|
+
* are tagged churned_account: true for discoverability.
|
|
81
|
+
*
|
|
82
|
+
* Mixpanel Report — Churned Account Retention:
|
|
83
|
+
* • Retention report
|
|
84
|
+
* • Event A/B: Any event
|
|
85
|
+
* • Breakdown: User profile "churned_account"
|
|
86
|
+
* • Expected: churned_account=true shows 0% retention after day 30
|
|
87
|
+
*
|
|
88
|
+
* Mixpanel Report — Churned Account Activity:
|
|
89
|
+
* • Insights line chart
|
|
90
|
+
* • Event: Any event, measure total per user
|
|
91
|
+
* • Breakdown: User profile "churned_account"
|
|
92
|
+
* • Weekly trend
|
|
93
|
+
* • Expected: churned_account=true flatlines after week 4
|
|
94
|
+
*
|
|
95
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
* 3. ALERT ESCALATION REPLACEMENT (event hook)
|
|
97
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
98
|
+
*
|
|
99
|
+
* 30% of critical/emergency "alert triggered" events are REPLACED with a new
|
|
100
|
+
* event type "incident created" (not in the events array — hook-only). Includes
|
|
101
|
+
* escalation_level (P1/P2), teams_paged, incident_id.
|
|
102
|
+
*
|
|
103
|
+
* Mixpanel Report — Incident Created Discovery:
|
|
104
|
+
* • Insights report
|
|
105
|
+
* • Event: "incident created"
|
|
106
|
+
* • Breakdown: "escalation_level"
|
|
107
|
+
* • Expected: P1 and P2 incidents, ~30% of critical/emergency alert volume
|
|
108
|
+
*
|
|
109
|
+
* Mixpanel Report — Alert vs Incident Ratio:
|
|
110
|
+
* • Insights report
|
|
111
|
+
* • Events: "alert triggered" AND "incident created"
|
|
112
|
+
* • Expected: incident created count ~ 30% of critical+emergency alerts
|
|
113
|
+
*
|
|
114
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
+
* 4. INTEGRATION USERS SUCCEED (everything hook)
|
|
116
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
*
|
|
118
|
+
* Users with BOTH Slack AND PagerDuty integrations resolve alerts faster:
|
|
119
|
+
* response_time_mins reduced 60%, resolution_time_mins reduced 50%.
|
|
120
|
+
* Tagged: integrated_team: true.
|
|
121
|
+
*
|
|
122
|
+
* Mixpanel Report — Integration Impact on Response Time:
|
|
123
|
+
* • Insights report
|
|
124
|
+
* • Event: "alert acknowledged", measure avg "response_time_mins"
|
|
125
|
+
* • Breakdown: "integrated_team"
|
|
126
|
+
* • Expected: integrated_team=true ~ 60% lower response time
|
|
127
|
+
*
|
|
128
|
+
* Mixpanel Report — Integration Impact on Resolution:
|
|
129
|
+
* • Insights report
|
|
130
|
+
* • Event: "alert resolved", measure avg "resolution_time_mins"
|
|
131
|
+
* • Breakdown: "integrated_team"
|
|
132
|
+
* • Expected: integrated_team=true ~ 50% faster resolution
|
|
133
|
+
*
|
|
134
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
* 5. DOCS READERS DEPLOY MORE (everything hook)
|
|
136
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
*
|
|
138
|
+
* Users with 3+ "best_practices" documentation views get 2-3 extra production
|
|
139
|
+
* deploys spliced into their event stream. Tagged: docs_informed: true.
|
|
140
|
+
*
|
|
141
|
+
* Mixpanel Report — Docs-Informed Deployments:
|
|
142
|
+
* • Insights report
|
|
143
|
+
* • Event: "service deployed", filter "environment" = "production"
|
|
144
|
+
* • Breakdown: "docs_informed"
|
|
145
|
+
* • Expected: docs_informed=true shows extra production deployments
|
|
146
|
+
*
|
|
147
|
+
* Mixpanel Report — Docs Readers vs Non-Readers:
|
|
148
|
+
* • Insights report
|
|
149
|
+
* • Event: "service deployed", measure total per user
|
|
150
|
+
* • Segment: Users with 3+ "documentation viewed" (doc_section = "best_practices")
|
|
151
|
+
* • Expected: ~1.8x more production deploys per user for docs readers
|
|
152
|
+
*
|
|
153
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
* 6. COST OVERRUN PATTERN (event hook — closure state)
|
|
155
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
156
|
+
*
|
|
157
|
+
* When cost_change_percent > 25 on a "cost report generated" event, the user
|
|
158
|
+
* is stored in a module-level Map. Their next "infrastructure scaled" event
|
|
159
|
+
* is forced to scale_direction: "down". Tagged: budget_exceeded, cost_reaction.
|
|
160
|
+
*
|
|
161
|
+
* Mixpanel Report — Cost Overrun to Scale Down:
|
|
162
|
+
* • Insights report
|
|
163
|
+
* • Event: "infrastructure scaled"
|
|
164
|
+
* • Breakdown: "cost_reaction"
|
|
165
|
+
* • Expected: cost_reaction=true events are 100% scale_direction="down"
|
|
166
|
+
*
|
|
167
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
168
|
+
* 7. FAILED DEPLOYMENT RECOVERY (event hook — closure state)
|
|
169
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
170
|
+
*
|
|
171
|
+
* After a failed pipeline run, the user's next successful deploy has
|
|
172
|
+
* duration_sec * 1.5 (recovery deploys are slower). Tagged: recovery_deployment.
|
|
173
|
+
* Uses module-level Map for cross-call state.
|
|
174
|
+
*
|
|
175
|
+
* Mixpanel Report — Recovery Deploy Duration:
|
|
176
|
+
* • Insights report
|
|
177
|
+
* • Event: "deployment pipeline run", measure avg "duration_sec"
|
|
178
|
+
* • Breakdown: "recovery_deployment"
|
|
179
|
+
* • Expected: recovery_deployment=true ~ 1.5x longer duration
|
|
180
|
+
*
|
|
181
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
182
|
+
* 8. ENTERPRISE VS STARTUP (user hook)
|
|
183
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
184
|
+
*
|
|
185
|
+
* Company size determines seat_count, annual_contract_value, and
|
|
186
|
+
* customer_success_manager (enterprise only). All users get customer_health_score.
|
|
187
|
+
*
|
|
188
|
+
* Mixpanel Report — ACV by Company Size:
|
|
189
|
+
* • Insights report
|
|
190
|
+
* • Event: Any, measure unique users
|
|
191
|
+
* • Breakdown: User profile "company_size"
|
|
192
|
+
* • Expected: startup ($0-3.6K), smb ($3.6K-12K), mid_market ($12K-50K),
|
|
193
|
+
* enterprise ($50K-500K)
|
|
194
|
+
*
|
|
195
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
196
|
+
* EXPECTED METRICS SUMMARY
|
|
197
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
198
|
+
*
|
|
199
|
+
* Hook | Metric | Baseline | Hook Effect | Ratio
|
|
200
|
+
* -------------------------|--------------------------|-----------|----------------|------
|
|
201
|
+
* End-of-Quarter Spike | Plan upgrades/day | ~2/day | ~8/day | 4x
|
|
202
|
+
* Churned Accounts | Users active month 2 | 100% | 90% | 0.9x
|
|
203
|
+
* Alert Escalation | Incidents from alerts | 0% | ~30% of crit | new
|
|
204
|
+
* Integration Users | MTTR (minutes) | ~300 | ~150 | 0.5x
|
|
205
|
+
* Docs Readers | Prod deploys/user | ~3 | ~5-6 | 1.8x
|
|
206
|
+
* Cost Overrun | Scale-down after overrun | 50% | 100% | 2x
|
|
207
|
+
* Failed Deploy Recovery | Deploy duration (sec) | ~500 | ~750 | 1.5x
|
|
208
|
+
* Enterprise vs Startup | ACV range | $0-3.6K | $50K-500K | 100x+
|
|
209
|
+
*
|
|
210
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
* ADVANCED ANALYSIS IDEAS
|
|
212
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
213
|
+
*
|
|
214
|
+
* CROSS-HOOK PATTERNS:
|
|
215
|
+
* - Churned + Enterprise: Do churned accounts skew toward startups or are
|
|
216
|
+
* enterprise accounts also silenced?
|
|
217
|
+
* - Integration + Cost: Do teams with full integrations manage costs better?
|
|
218
|
+
* - Docs + Deploys + Failures: Do docs readers have fewer failed deployments?
|
|
219
|
+
* - Quarter Spike + Churn: Are quarter-end upgrades correlated with later churn?
|
|
220
|
+
* - Enterprise Recovery: Do enterprise customers recover from failed deploys
|
|
221
|
+
* differently than startups?
|
|
222
|
+
*
|
|
223
|
+
* COHORT ANALYSIS:
|
|
224
|
+
* - By company_size: Compare all metrics across startup/smb/mid_market/enterprise
|
|
225
|
+
* - By plan_tier: Free vs. Team vs. Business vs. Enterprise engagement
|
|
226
|
+
* - By cloud_provider: AWS vs. GCP vs. Azure deployment and alert patterns
|
|
227
|
+
* - By primary_role: Engineer vs. SRE vs. DevOps vs. Manager behaviors
|
|
228
|
+
*
|
|
229
|
+
* KEY METRICS:
|
|
230
|
+
* - MTTR: alert triggered → alert resolved duration
|
|
231
|
+
* - Deployment Frequency: service deployed per user per week
|
|
232
|
+
* - Deployment Success Rate: pipeline success vs. failure ratio
|
|
233
|
+
* - Cost Efficiency: total_cost trend over time per company
|
|
234
|
+
* - Feature Adoption: integration configured events by type
|
|
235
|
+
* - Documentation Engagement: documentation viewed by section
|
|
236
|
+
*/
|
|
237
|
+
|
|
238
|
+
// Generate consistent IDs for lookup tables and event properties
|
|
239
|
+
const serviceIds = v.range(1, 201).map(() => `svc_${v.uid(8)}`);
|
|
240
|
+
const alertIds = v.range(1, 501).map(() => `alert_${v.uid(6)}`);
|
|
241
|
+
const pipelineIds = v.range(1, 101).map(() => `pipe_${v.uid(6)}`);
|
|
242
|
+
const runbookIds = v.range(1, 51).map(() => `rb_${v.uid(6)}`);
|
|
243
|
+
const companyIds = v.range(1, 301).map(() => `comp_${v.uid(8)}`);
|
|
244
|
+
|
|
245
|
+
// Module-level Maps for closure-based state tracking across hook calls
|
|
246
|
+
const costOverrunUsers = new Map();
|
|
247
|
+
const failedDeployUsers = new Map();
|
|
248
|
+
|
|
249
|
+
/** @type {Config} */
|
|
250
|
+
const config = {
|
|
251
|
+
token: "",
|
|
252
|
+
seed: SEED,
|
|
253
|
+
numDays: days,
|
|
254
|
+
numEvents: num_users * 120,
|
|
255
|
+
numUsers: num_users,
|
|
256
|
+
hasAnonIds: false,
|
|
257
|
+
hasSessionIds: true,
|
|
258
|
+
format: "json",
|
|
259
|
+
gzip: true,
|
|
260
|
+
alsoInferFunnels: false,
|
|
261
|
+
hasLocation: true,
|
|
262
|
+
hasAndroidDevices: false,
|
|
263
|
+
hasIOSDevices: false,
|
|
264
|
+
hasDesktopDevices: true,
|
|
265
|
+
hasBrowser: true,
|
|
266
|
+
hasCampaigns: false,
|
|
267
|
+
isAnonymous: false,
|
|
268
|
+
hasAdSpend: false,
|
|
269
|
+
percentUsersBornInDataset: 50,
|
|
270
|
+
hasAvatar: true,
|
|
271
|
+
batchSize: 2_500_000,
|
|
272
|
+
concurrency: 1,
|
|
273
|
+
writeToDisk: false,
|
|
274
|
+
scdProps: {},
|
|
275
|
+
|
|
276
|
+
funnels: [
|
|
277
|
+
{
|
|
278
|
+
sequence: ["workspace created", "service deployed", "dashboard viewed"],
|
|
279
|
+
isFirstFunnel: true,
|
|
280
|
+
conversionRate: 70,
|
|
281
|
+
timeToConvert: 2,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
// Daily monitoring: dashboards, queries, API calls (most common)
|
|
285
|
+
sequence: ["dashboard viewed", "query executed", "api call"],
|
|
286
|
+
conversionRate: 80,
|
|
287
|
+
timeToConvert: 0.5,
|
|
288
|
+
weight: 5,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
// Incident response pipeline
|
|
292
|
+
sequence: ["alert triggered", "alert acknowledged", "alert resolved"],
|
|
293
|
+
conversionRate: 55,
|
|
294
|
+
timeToConvert: 6,
|
|
295
|
+
weight: 4,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
// Deployment cycle
|
|
299
|
+
sequence: ["deployment pipeline run", "service deployed", "dashboard viewed"],
|
|
300
|
+
conversionRate: 65,
|
|
301
|
+
timeToConvert: 1,
|
|
302
|
+
weight: 3,
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
// Infrastructure management
|
|
306
|
+
sequence: ["cost report generated", "infrastructure scaled", "security scan"],
|
|
307
|
+
conversionRate: 50,
|
|
308
|
+
timeToConvert: 4,
|
|
309
|
+
weight: 2,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
// Team and config management
|
|
313
|
+
sequence: ["team member invited", "integration configured", "feature flag toggled"],
|
|
314
|
+
conversionRate: 40,
|
|
315
|
+
timeToConvert: 8,
|
|
316
|
+
weight: 2,
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
// Documentation and runbook usage
|
|
320
|
+
sequence: ["documentation viewed", "runbook executed", "service deployed"],
|
|
321
|
+
conversionRate: 45,
|
|
322
|
+
timeToConvert: 2,
|
|
323
|
+
weight: 2,
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
// Billing and account management
|
|
327
|
+
sequence: ["billing event", "dashboard viewed"],
|
|
328
|
+
conversionRate: 60,
|
|
329
|
+
timeToConvert: 1,
|
|
330
|
+
weight: 1,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
|
|
334
|
+
events: [
|
|
335
|
+
{
|
|
336
|
+
event: "workspace created",
|
|
337
|
+
weight: 1,
|
|
338
|
+
isFirstEvent: true,
|
|
339
|
+
properties: {
|
|
340
|
+
company_size: ["startup", "smb", "mid_market", "enterprise"],
|
|
341
|
+
industry: ["tech", "finance", "healthcare", "retail", "media"],
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
event: "service deployed",
|
|
346
|
+
weight: 10,
|
|
347
|
+
properties: {
|
|
348
|
+
service_id: u.pickAWinner(serviceIds),
|
|
349
|
+
service_type: ["web_app", "api", "database", "cache", "queue", "ml_model"],
|
|
350
|
+
environment: ["production", "staging", "dev"],
|
|
351
|
+
cloud_provider: ["aws", "gcp", "azure"],
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
event: "dashboard viewed",
|
|
356
|
+
weight: 20,
|
|
357
|
+
properties: {
|
|
358
|
+
dashboard_type: ["overview", "cost", "performance", "security", "custom"],
|
|
359
|
+
time_range: ["1h", "6h", "24h", "7d", "30d"],
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
event: "alert triggered",
|
|
364
|
+
weight: 12,
|
|
365
|
+
properties: {
|
|
366
|
+
alert_id: u.pickAWinner(alertIds),
|
|
367
|
+
severity: ["info", "warning", "critical", "emergency"],
|
|
368
|
+
alert_type: ["cpu", "memory", "latency", "error_rate", "disk", "network"],
|
|
369
|
+
service_id: u.pickAWinner(serviceIds),
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
event: "alert acknowledged",
|
|
374
|
+
weight: 8,
|
|
375
|
+
properties: {
|
|
376
|
+
alert_id: u.pickAWinner(alertIds),
|
|
377
|
+
response_time_mins: u.weighNumRange(1, 120),
|
|
378
|
+
acknowledged_by_role: ["engineer", "sre", "manager", "oncall"],
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
event: "alert resolved",
|
|
383
|
+
weight: 7,
|
|
384
|
+
properties: {
|
|
385
|
+
alert_id: u.pickAWinner(alertIds),
|
|
386
|
+
resolution_time_mins: u.weighNumRange(5, 1440),
|
|
387
|
+
root_cause: ["config_change", "capacity", "bug", "dependency", "network"],
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
event: "deployment pipeline run",
|
|
392
|
+
weight: 9,
|
|
393
|
+
properties: {
|
|
394
|
+
pipeline_id: u.pickAWinner(pipelineIds),
|
|
395
|
+
status: ["success", "failed", "cancelled"],
|
|
396
|
+
duration_sec: u.weighNumRange(30, 1800),
|
|
397
|
+
commit_count: u.weighNumRange(1, 20),
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
event: "infrastructure scaled",
|
|
402
|
+
weight: 5,
|
|
403
|
+
properties: {
|
|
404
|
+
service_id: u.pickAWinner(serviceIds),
|
|
405
|
+
scale_direction: u.pickAWinner(["up", "down"], 0.15),
|
|
406
|
+
previous_capacity: u.weighNumRange(1, 100),
|
|
407
|
+
new_capacity: u.weighNumRange(1, 100),
|
|
408
|
+
auto_scaled: u.pickAWinner([true, false], 0.15),
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
event: "cost report generated",
|
|
413
|
+
weight: 4,
|
|
414
|
+
properties: {
|
|
415
|
+
report_period: ["daily", "weekly", "monthly"],
|
|
416
|
+
total_cost: u.weighNumRange(100, 50000),
|
|
417
|
+
cost_change_percent: u.weighNumRange(-30, 50),
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
event: "team member invited",
|
|
422
|
+
weight: 3,
|
|
423
|
+
properties: {
|
|
424
|
+
role: ["admin", "editor", "viewer", "billing"],
|
|
425
|
+
invitation_method: ["email", "sso", "slack"],
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
event: "integration configured",
|
|
430
|
+
weight: 4,
|
|
431
|
+
properties: {
|
|
432
|
+
integration_type: ["slack", "pagerduty", "jira", "github", "datadog", "terraform"],
|
|
433
|
+
status: ["active", "paused", "error"],
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
event: "query executed",
|
|
438
|
+
weight: 15,
|
|
439
|
+
properties: {
|
|
440
|
+
query_type: ["metrics", "logs", "traces"],
|
|
441
|
+
time_range_hours: u.weighNumRange(1, 720),
|
|
442
|
+
result_count: u.weighNumRange(0, 10000),
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
event: "runbook executed",
|
|
447
|
+
weight: 3,
|
|
448
|
+
properties: {
|
|
449
|
+
runbook_id: u.pickAWinner(runbookIds),
|
|
450
|
+
trigger: ["manual", "automated", "alert_triggered"],
|
|
451
|
+
success: u.pickAWinner([true, false], 0.15),
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
event: "billing event",
|
|
456
|
+
weight: 3,
|
|
457
|
+
properties: {
|
|
458
|
+
event_type: ["invoice_generated", "payment_received", "payment_failed", "plan_upgraded", "plan_downgraded"],
|
|
459
|
+
amount: u.weighNumRange(99, 25000),
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
event: "security scan",
|
|
464
|
+
weight: 6,
|
|
465
|
+
properties: {
|
|
466
|
+
scan_type: ["vulnerability", "compliance", "access_audit"],
|
|
467
|
+
findings_count: u.weighNumRange(0, 50),
|
|
468
|
+
critical_findings: u.weighNumRange(0, 10),
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
event: "api call",
|
|
473
|
+
weight: 16,
|
|
474
|
+
properties: {
|
|
475
|
+
endpoint: ["/deploy", "/status", "/metrics", "/alerts", "/config", "/billing"],
|
|
476
|
+
method: ["GET", "POST", "PUT", "DELETE"],
|
|
477
|
+
response_time_ms: u.weighNumRange(10, 5000),
|
|
478
|
+
status_code: u.pickAWinner([200, 201, 400, 401, 403, 500, 503]),
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
event: "documentation viewed",
|
|
483
|
+
weight: 7,
|
|
484
|
+
properties: {
|
|
485
|
+
doc_section: ["getting_started", "api_reference", "best_practices", "troubleshooting", "changelog"],
|
|
486
|
+
time_on_page_sec: u.weighNumRange(5, 600),
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
event: "feature flag toggled",
|
|
491
|
+
weight: 4,
|
|
492
|
+
properties: {
|
|
493
|
+
flag_name: () => `flag_${chance.word()}`,
|
|
494
|
+
new_state: u.pickAWinner(["enabled", "disabled"], 0.15),
|
|
495
|
+
environment: ["production", "staging", "dev"],
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
|
|
500
|
+
superProps: {
|
|
501
|
+
plan_tier: u.pickAWinner(["free", "free", "team", "team", "business", "enterprise"]),
|
|
502
|
+
cloud_provider: ["aws", "gcp", "azure", "multi_cloud"],
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
userProps: {
|
|
506
|
+
company_size: u.pickAWinner(["startup", "startup", "smb", "mid_market", "enterprise"]),
|
|
507
|
+
primary_role: ["engineer", "sre", "devops", "manager", "executive"],
|
|
508
|
+
team_name: ["Platform", "Backend", "Frontend", "Data", "Security", "Infrastructure"],
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
groupKeys: [
|
|
512
|
+
["company_id", 300, ["workspace created", "service deployed", "billing event", "team member invited"]],
|
|
513
|
+
],
|
|
514
|
+
|
|
515
|
+
groupProps: {
|
|
516
|
+
company_id: {
|
|
517
|
+
name: () => `${chance.word({ capitalize: true })} ${chance.pickone(["Systems", "Technologies", "Labs", "Cloud", "Digital", "Networks", "Solutions"])}`,
|
|
518
|
+
industry: ["tech", "finance", "healthcare", "retail", "media", "manufacturing", "logistics"],
|
|
519
|
+
employee_count: ["1-10", "11-50", "51-200", "201-1000", "1001-5000", "5000+"],
|
|
520
|
+
arr_bucket: ["<10k", "10k-50k", "50k-200k", "200k-1M", "1M+"],
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
lookupTables: [],
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* ARCHITECTED ANALYTICS HOOKS
|
|
528
|
+
*
|
|
529
|
+
* This hook function creates 8 deliberate patterns in the data:
|
|
530
|
+
*
|
|
531
|
+
* 1. END-OF-QUARTER SPIKE: Days 80-90 drive plan upgrades and team expansion
|
|
532
|
+
* 2. CHURNED ACCOUNT SILENCING: ~10% of users go completely silent after month 1
|
|
533
|
+
* 3. ALERT ESCALATION REPLACEMENT: Critical alerts become "incident created" events
|
|
534
|
+
* 4. INTEGRATION USERS SUCCEED: Slack+PagerDuty users resolve incidents 50-60% faster
|
|
535
|
+
* 5. DOCS READERS DEPLOY MORE: Best practices readers get extra production deploys
|
|
536
|
+
* 6. COST OVERRUN PATTERN: Budget-exceeded users react by scaling down infrastructure
|
|
537
|
+
* 7. FAILED DEPLOYMENT RECOVERY: Recovery deploys take 1.5x longer, tracked across calls
|
|
538
|
+
* 8. ENTERPRISE VS STARTUP: Company size determines seat count, ACV, and health score
|
|
539
|
+
*/
|
|
540
|
+
hook: function (record, type, meta) {
|
|
541
|
+
const NOW = dayjs();
|
|
542
|
+
const DATASET_START = NOW.subtract(days, "days");
|
|
543
|
+
|
|
544
|
+
// ─────────────────────────────────────────────────────────────
|
|
545
|
+
// Hook #1: END-OF-QUARTER SPIKE (event)
|
|
546
|
+
// Days 80-90: billing upgrades and team expansion surge
|
|
547
|
+
// ─────────────────────────────────────────────────────────────
|
|
548
|
+
if (type === "event") {
|
|
549
|
+
const EVENT_TIME = dayjs(record.time);
|
|
550
|
+
const dayInDataset = EVENT_TIME.diff(DATASET_START, "days", true);
|
|
551
|
+
|
|
552
|
+
if (record.event === "billing event") {
|
|
553
|
+
if (dayInDataset >= 80 && dayInDataset <= 90 && chance.bool({ likelihood: 40 })) {
|
|
554
|
+
record.event_type = "plan_upgraded";
|
|
555
|
+
record.quarter_end_push = true;
|
|
556
|
+
} else {
|
|
557
|
+
record.quarter_end_push = false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (record.event === "team member invited") {
|
|
562
|
+
if (dayInDataset >= 80 && dayInDataset <= 90) {
|
|
563
|
+
record.quarter_end_push = true;
|
|
564
|
+
// 50% of the time duplicate the invite event (hiring push)
|
|
565
|
+
if (chance.bool({ likelihood: 50 })) {
|
|
566
|
+
return {
|
|
567
|
+
event: "team member invited",
|
|
568
|
+
time: EVENT_TIME.add(chance.integer({ min: 1, max: 60 }), "minutes").toISOString(),
|
|
569
|
+
user_id: record.user_id,
|
|
570
|
+
role: chance.pickone(["editor", "viewer"]),
|
|
571
|
+
invitation_method: chance.pickone(["email", "sso", "slack"]),
|
|
572
|
+
quarter_end_push: true,
|
|
573
|
+
duplicate_invite: true,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
record.quarter_end_push = false;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// ─────────────────────────────────────────────────────────────
|
|
583
|
+
// Hook #3: ALERT ESCALATION REPLACEMENT (event)
|
|
584
|
+
// Critical/emergency alerts sometimes become formal incidents
|
|
585
|
+
// ─────────────────────────────────────────────────────────────
|
|
586
|
+
if (type === "event") {
|
|
587
|
+
if (record.event === "alert triggered") {
|
|
588
|
+
const severity = record.severity;
|
|
589
|
+
if ((severity === "critical" || severity === "emergency") && chance.bool({ likelihood: 30 })) {
|
|
590
|
+
// REPLACE the event entirely with an "incident created" event
|
|
591
|
+
return {
|
|
592
|
+
event: "incident created",
|
|
593
|
+
time: record.time,
|
|
594
|
+
user_id: record.user_id,
|
|
595
|
+
escalation_level: chance.pickone(["P1", "P2"]),
|
|
596
|
+
teams_paged: chance.integer({ min: 1, max: 5 }),
|
|
597
|
+
incident_id: `inc_${v.uid(8)}`,
|
|
598
|
+
original_severity: severity,
|
|
599
|
+
original_alert_type: record.alert_type,
|
|
600
|
+
service_id: record.service_id,
|
|
601
|
+
auto_escalated: true,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ─────────────────────────────────────────────────────────────
|
|
608
|
+
// Hook #6: COST OVERRUN PATTERN (event)
|
|
609
|
+
// Budget-exceeded users react by scaling down infrastructure
|
|
610
|
+
// Uses module-level costOverrunUsers Map for cross-call state
|
|
611
|
+
// ─────────────────────────────────────────────────────────────
|
|
612
|
+
if (type === "event") {
|
|
613
|
+
if (record.event === "cost report generated") {
|
|
614
|
+
const costChange = record.cost_change_percent;
|
|
615
|
+
if (costChange > 25) {
|
|
616
|
+
record.cost_alert = true;
|
|
617
|
+
record.budget_exceeded = true;
|
|
618
|
+
costOverrunUsers.set(record.user_id, true);
|
|
619
|
+
} else {
|
|
620
|
+
record.cost_alert = false;
|
|
621
|
+
record.budget_exceeded = false;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (record.event === "infrastructure scaled") {
|
|
626
|
+
if (costOverrunUsers.has(record.user_id)) {
|
|
627
|
+
record.scale_direction = "down";
|
|
628
|
+
record.cost_reaction = true;
|
|
629
|
+
costOverrunUsers.delete(record.user_id);
|
|
630
|
+
} else {
|
|
631
|
+
record.cost_reaction = false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// ─────────────────────────────────────────────────────────────
|
|
637
|
+
// Hook #7: FAILED DEPLOYMENT RECOVERY (event)
|
|
638
|
+
// Recovery deploys take 1.5x longer after a failure
|
|
639
|
+
// Uses module-level failedDeployUsers Map for cross-call state
|
|
640
|
+
// ─────────────────────────────────────────────────────────────
|
|
641
|
+
if (type === "event") {
|
|
642
|
+
if (record.event === "deployment pipeline run") {
|
|
643
|
+
const status = record.status;
|
|
644
|
+
if (status === "failed") {
|
|
645
|
+
failedDeployUsers.set(record.user_id, true);
|
|
646
|
+
record.recovery_deployment = false;
|
|
647
|
+
} else if (status === "success" && failedDeployUsers.has(record.user_id)) {
|
|
648
|
+
record.duration_sec = Math.floor((record.duration_sec || 300) * 1.5);
|
|
649
|
+
record.recovery_deployment = true;
|
|
650
|
+
failedDeployUsers.delete(record.user_id);
|
|
651
|
+
} else {
|
|
652
|
+
record.recovery_deployment = false;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ─────────────────────────────────────────────────────────────
|
|
658
|
+
// Hook #2: CHURNED ACCOUNT SILENCING (everything)
|
|
659
|
+
// ~20% targeted (hash % 5), yielding ~10% visible after accounting for invisible churned users
|
|
660
|
+
// ─────────────────────────────────────────────────────────────
|
|
661
|
+
if (type === "everything") {
|
|
662
|
+
const userEvents = record;
|
|
663
|
+
if (userEvents && userEvents.length > 0) {
|
|
664
|
+
const firstEvent = userEvents[0];
|
|
665
|
+
const idHash = String(firstEvent.user_id || firstEvent.device_id).split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
666
|
+
const isChurnedAccount = (idHash % 5) === 0;
|
|
667
|
+
|
|
668
|
+
if (isChurnedAccount) {
|
|
669
|
+
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
670
|
+
const evt = userEvents[i];
|
|
671
|
+
const dayInDataset = dayjs(evt.time).diff(DATASET_START, "days", true);
|
|
672
|
+
if (dayInDataset > 30) {
|
|
673
|
+
userEvents.splice(i, 1);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ─────────────────────────────────────────────────────────────
|
|
681
|
+
// Hook #4: INTEGRATION USERS SUCCEED (everything)
|
|
682
|
+
// Users with both Slack AND PagerDuty integrations resolve faster
|
|
683
|
+
// ─────────────────────────────────────────────────────────────
|
|
684
|
+
if (type === "everything") {
|
|
685
|
+
const userEvents = record;
|
|
686
|
+
|
|
687
|
+
// First pass: check if user has both slack and pagerduty integrations
|
|
688
|
+
let hasSlack = false;
|
|
689
|
+
let hasPagerduty = false;
|
|
690
|
+
|
|
691
|
+
userEvents.forEach((event) => {
|
|
692
|
+
if (event.event === "integration configured") {
|
|
693
|
+
const integrationType = event.integration_type;
|
|
694
|
+
if (integrationType === "slack") hasSlack = true;
|
|
695
|
+
if (integrationType === "pagerduty") hasPagerduty = true;
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
const hasFullIntegration = hasSlack && hasPagerduty;
|
|
700
|
+
|
|
701
|
+
// Second pass: set integrated_team on all alert events, then boost for integrated users
|
|
702
|
+
userEvents.forEach((event) => {
|
|
703
|
+
if (event.event === "alert acknowledged") {
|
|
704
|
+
if (hasFullIntegration && event.response_time_mins) {
|
|
705
|
+
event.response_time_mins = Math.floor(event.response_time_mins * 0.4);
|
|
706
|
+
event.integrated_team = true;
|
|
707
|
+
} else {
|
|
708
|
+
event.integrated_team = false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (event.event === "alert resolved") {
|
|
712
|
+
if (hasFullIntegration && event.resolution_time_mins) {
|
|
713
|
+
event.resolution_time_mins = Math.floor(event.resolution_time_mins * 0.5);
|
|
714
|
+
event.integrated_team = true;
|
|
715
|
+
} else {
|
|
716
|
+
event.integrated_team = false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ─────────────────────────────────────────────────────────────
|
|
723
|
+
// Hook #5: DOCS READERS DEPLOY MORE (everything)
|
|
724
|
+
// Users who read best_practices 3+ times get extra production deploys
|
|
725
|
+
// ─────────────────────────────────────────────────────────────
|
|
726
|
+
if (type === "everything") {
|
|
727
|
+
const userEvents = record;
|
|
728
|
+
|
|
729
|
+
// First pass: count best_practices documentation views
|
|
730
|
+
let bestPracticesCount = 0;
|
|
731
|
+
userEvents.forEach((event) => {
|
|
732
|
+
if (event.event === "documentation viewed" && event.doc_section === "best_practices") {
|
|
733
|
+
bestPracticesCount++;
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// Second pass: if 3+ best practices views, add extra production deploys
|
|
738
|
+
if (bestPracticesCount >= 3) {
|
|
739
|
+
const extraDeploys = chance.integer({ min: 2, max: 3 });
|
|
740
|
+
const lastEvent = userEvents[userEvents.length - 1];
|
|
741
|
+
if (lastEvent) {
|
|
742
|
+
for (let i = 0; i < extraDeploys; i++) {
|
|
743
|
+
const deployEvent = {
|
|
744
|
+
event: "service deployed",
|
|
745
|
+
time: dayjs(lastEvent.time).add(chance.integer({ min: 1, max: 48 }), "hours").toISOString(),
|
|
746
|
+
user_id: lastEvent.user_id,
|
|
747
|
+
service_id: chance.pickone(serviceIds),
|
|
748
|
+
service_type: chance.pickone(["web_app", "api", "database", "cache", "queue", "ml_model"]),
|
|
749
|
+
environment: "production",
|
|
750
|
+
cloud_provider: chance.pickone(["aws", "gcp", "azure"]),
|
|
751
|
+
docs_informed: true,
|
|
752
|
+
};
|
|
753
|
+
userEvents.splice(userEvents.length, 0, deployEvent);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// ─────────────────────────────────────────────────────────────
|
|
760
|
+
// Hook #8: ENTERPRISE VS STARTUP (user)
|
|
761
|
+
// Company size determines seat count, ACV, and health score
|
|
762
|
+
// ─────────────────────────────────────────────────────────────
|
|
763
|
+
if (type === "user") {
|
|
764
|
+
// Hook #2 support: tag churned accounts on user profile for discoverability
|
|
765
|
+
const idHash = String(record.distinct_id || "").split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
766
|
+
record.churned_account = (idHash % 5) === 0;
|
|
767
|
+
|
|
768
|
+
const companySize = record.company_size;
|
|
769
|
+
|
|
770
|
+
if (companySize === "enterprise") {
|
|
771
|
+
record.seat_count = chance.integer({ min: 50, max: 500 });
|
|
772
|
+
record.annual_contract_value = chance.integer({ min: 50000, max: 500000 });
|
|
773
|
+
record.customer_success_manager = true;
|
|
774
|
+
} else if (companySize === "mid_market") {
|
|
775
|
+
record.seat_count = chance.integer({ min: 10, max: 50 });
|
|
776
|
+
record.annual_contract_value = chance.integer({ min: 12000, max: 50000 });
|
|
777
|
+
record.customer_success_manager = false;
|
|
778
|
+
} else if (companySize === "smb") {
|
|
779
|
+
record.seat_count = chance.integer({ min: 3, max: 10 });
|
|
780
|
+
record.annual_contract_value = chance.integer({ min: 3600, max: 12000 });
|
|
781
|
+
record.customer_success_manager = false;
|
|
782
|
+
} else if (companySize === "startup") {
|
|
783
|
+
record.seat_count = chance.integer({ min: 1, max: 5 });
|
|
784
|
+
record.annual_contract_value = chance.integer({ min: 0, max: 3600 });
|
|
785
|
+
record.customer_success_manager = false;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
record.customer_health_score = chance.integer({ min: 1, max: 100 });
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return record;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
export default config;
|