@ak--47/dungeon-master 1.5.0 → 1.5.1

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 (62) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/HOOKS.md +13 -0
  3. package/dungeons/technical/ad-spend.js +41 -49
  4. package/dungeons/technical/anonymous-users.js +38 -36
  5. package/dungeons/technical/array-of-object-lookup.js +136 -153
  6. package/dungeons/technical/datagen-v15-verify.js +24 -11
  7. package/dungeons/technical/experiments.js +42 -40
  8. package/dungeons/technical/foobar.js +114 -118
  9. package/dungeons/technical/group-analytics.js +42 -40
  10. package/dungeons/technical/hook-helpers-verify.js +69 -50
  11. package/dungeons/technical/identity-model-verify.js +22 -12
  12. package/dungeons/technical/mirror-strategies.js +37 -39
  13. package/dungeons/technical/nested-objects.js +119 -118
  14. package/dungeons/technical/pattern-aggregate-by-bin.js +21 -8
  15. package/dungeons/technical/pattern-attributed-by-source.js +23 -9
  16. package/dungeons/technical/pattern-frequency-by-frequency.js +21 -8
  17. package/dungeons/technical/pattern-funnel-frequency.js +30 -15
  18. package/dungeons/technical/pattern-ttc-by-segment.js +21 -8
  19. package/dungeons/technical/retention-cadence.js +115 -112
  20. package/dungeons/technical/sanity.js +86 -80
  21. package/dungeons/technical/scale-test.js +34 -38
  22. package/dungeons/technical/scd.js +111 -128
  23. package/dungeons/technical/simple.js +134 -141
  24. package/dungeons/technical/simplest.js +54 -62
  25. package/dungeons/technical/text-generation.js +110 -146
  26. package/dungeons/vertical/ai-platform.js +296 -333
  27. package/dungeons/vertical/community.js +284 -255
  28. package/dungeons/vertical/crypto.js +395 -391
  29. package/dungeons/vertical/dating.js +411 -378
  30. package/dungeons/vertical/devtools.js +336 -298
  31. package/dungeons/vertical/ecommerce.js +316 -394
  32. package/dungeons/vertical/education.js +369 -325
  33. package/dungeons/vertical/fintech.js +358 -325
  34. package/dungeons/vertical/fitness.js +335 -291
  35. package/dungeons/vertical/food-delivery.js +343 -307
  36. package/dungeons/vertical/gaming.js +480 -444
  37. package/dungeons/vertical/healthcare.js +306 -262
  38. package/dungeons/vertical/insurance-application.js +427 -409
  39. package/dungeons/vertical/logistics.js +271 -252
  40. package/dungeons/vertical/marketplace.js +333 -323
  41. package/dungeons/vertical/media.js +382 -335
  42. package/dungeons/vertical/real-estate.js +395 -346
  43. package/dungeons/vertical/sass.js +319 -333
  44. package/dungeons/vertical/social.js +368 -316
  45. package/dungeons/vertical/travel.js +297 -295
  46. package/index.js +46 -4
  47. package/lib/core/config-validator.js +126 -28
  48. package/lib/generators/funnels.js +4 -1
  49. package/lib/orchestrators/mixpanel-sender.js +7 -0
  50. package/lib/orchestrators/user-loop.js +132 -31
  51. package/lib/templates/defaults.js +59 -59
  52. package/lib/templates/macro-presets.js +14 -2
  53. package/lib/utils/dataset-context.js +103 -0
  54. package/lib/utils/retention-curve.js +140 -0
  55. package/lib/utils/utils.js +149 -38
  56. package/lib/verify/counting.js +40 -0
  57. package/lib/verify/emulate-breakdown.js +20 -1
  58. package/lib/verify/index.js +1 -0
  59. package/lib/verify/schema-validator.js +3 -1
  60. package/package.json +11 -2
  61. package/scripts/run-dungeon.mjs +12 -1
  62. package/types.d.ts +117 -1
@@ -1,96 +1,57 @@
1
- // ── TWEAK THESE ──
2
- const SEED = "SUPER DUPER DANGEROUS BROOO";
3
- const num_days = 92;
4
- const num_users = 8_000;
5
- const avg_events_per_user_per_day = 1.3;
6
- let token = "";
7
-
8
- // ── env overrides ──
9
- if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
10
-
1
+ // ── IMPORTS ──
11
2
  import dayjs from "dayjs";
12
3
  import utc from "dayjs/plugin/utc.js";
13
4
  import "dotenv/config";
14
- import { weighNumRange, range, date, initChance, exhaust, choose, integer } from "../../lib/utils/utils.js";
5
+ import { weighNumRange, initChance, integer } from "../../lib/utils/utils.js";
15
6
  import { createTextGenerator, generateBatch } from "../../lib/generators/text.js";
16
-
17
7
  dayjs.extend(utc);
18
- const chance = initChance(SEED);
19
-
20
8
  /** @typedef {import("../../types").Dungeon} Dungeon */
21
9
 
10
+ // ── OVERVIEW ──
22
11
  /*
23
- * ============================================================================
24
- * DATASET OVERVIEW
25
- * ============================================================================
26
- *
27
- * App: Text Generation Demo — showcases dungeon-master's organic text generation
28
- * Scale: 8,000 users, ~960K events, 92 days
29
- *
30
- * A text-heavy SaaS analytics dungeon that exercises every text generation
31
- * style available in dungeon-master. Users interact through support tickets, product
32
- * reviews, forum posts, search queries, chat messages, social media posts
33
- * (Twitter, LinkedIn, Reddit), bug reports, feature requests, onboarding
34
- * feedback, charity/wedding comments, and webinar chat.
35
- *
36
- * Each event type uses a dedicated createTextGenerator() configured with its
37
- * own style, tone, formality, keyword banks, typo rates, and authenticity
38
- * levels. Text generator styles used: support, review, forum, search,
39
- * feedback, chat, email, tweet, comments (9 distinct styles).
40
- *
41
- * Events:
42
- * social_media_tweet (15) > chat_message (10) > charity_comment_posted (8)
43
- * > wedding_comment_posted (6) > company_announcement_tweet (3)
44
- * > all others (1 each): support ticket, review, forum post, search,
45
- * feedback, email, twitter, linkedin, reddit, bug report, feature
46
- * request, onboarding, tutorial comment, webinar chat, api thread
47
- * ============================================================================
12
+ * NAME: text-generation
13
+ * PURPOSE: text-heavy fixture exercising every createTextGenerator style — support, review, forum, search, feedback, chat, email, tweet, comments
14
+ * SCALE: 8,000 users, ~960K events, 92 days
15
+ * EVENTS (20): enterprise_support_ticket, casual_product_review, technical_forum_post, search_query, business_feedback, chat_message (10), email_correspondence, twitter_post, linkedin_post, reddit_comment, bug_report_submitted, feature_request_submitted, onboarding_feedback, tutorial_comment_posted, webinar_chat_message, charity_comment_posted (8), wedding_comment_posted (6), social_media_tweet (15), company_announcement_tweet (3), api_discussion_thread, satisfaction_survey_triggered (hook-injected)
16
+ * FUNNELS (0): none (alsoInferFunnels: true)
48
17
  */
49
18
 
19
+ // ── HOOK STORIES ──
50
20
  /*
51
- * ============================================================================
52
- * ANALYTICS HOOKS
53
- * ============================================================================
21
+ * Hook 1: Power User + Churn Risk Classification (user)
22
+ * Users with engagement_score > 70 get is_power_user = true.
23
+ * Users with last_active_days_ago > 20 get risk_level = "high_churn", else "healthy".
24
+ * Mixpanel: Insights — user profile breakdown by "is_power_user" / "risk_level"
25
+ * cross-referenced with "user_tier".
54
26
  *
55
- * Hook 1: Power User + Churn Risk Classification
56
- * Type: user
57
- * What: Users with engagement_score > 70 get is_power_user = true.
58
- * Users inactive > 20 days get risk_level = "high_churn", else "healthy".
59
- * Mixpanel report:
60
- * - Insights > user profile breakdown by "is_power_user"
61
- * - Insights > user profile breakdown by "risk_level", cross-reference
62
- * with "user_tier" to see churn risk by plan
27
+ * Hook 2: Critical Ticket Auto-Escalation (event)
28
+ * enterprise_support_ticket events with priority = "critical" get
29
+ * escalation_level bumped by 1 (max 3) and auto_escalated = true.
30
+ * Mixpanel: Insights "enterprise_support_ticket" total, breakdown by auto_escalated.
63
31
  *
64
- * Hook 2: Critical Ticket Auto-Escalation
65
- * Type: event
66
- * What: enterprise_support_ticket events with priority = "critical" get
67
- * escalation_level bumped by 1 (max 3) and auto_escalated = true.
68
- * Mixpanel report:
69
- * - Insights > "enterprise_support_ticket" total events, breakdown by
70
- * auto_escalated
71
- * - Expect: critical tickets show auto_escalated = true
32
+ * Hook 3: Critical Bug Flagging (event)
33
+ * bug_report_submitted with severity = "critical" AND is_reproducible = true
34
+ * get requires_immediate_review = true and random estimated_fix_hours (1-8).
35
+ * Mixpanel: Insights "bug_report_submitted" total, breakdown by requires_immediate_review.
72
36
  *
73
- * Hook 3: Critical Bug Flagging
74
- * Type: event
75
- * What: bug_report_submitted events with severity = "critical" AND
76
- * is_reproducible = true get requires_immediate_review = true and a
77
- * random estimated_fix_hours (1-8).
78
- * Mixpanel report:
79
- * - Insights > "bug_report_submitted" total events, breakdown by
80
- * requires_immediate_review
81
- * - Expect: only critical + reproducible bugs are flagged
82
- *
83
- * Hook 4: Enterprise Satisfaction Survey Injection
84
- * Type: everything
85
- * What: Enterprise-tier users with > 5 events get a
86
- * "satisfaction_survey_triggered" event appended with a 1-10 NPS score.
87
- * Mixpanel report:
88
- * - Insights > "satisfaction_survey_triggered" AVG(score), breakdown by
89
- * product_tier
90
- * - Expect: only enterprise users have survey events
91
- * ============================================================================
37
+ * Hook 4: Enterprise Satisfaction Survey Injection (everything)
38
+ * Enterprise-tier users with > 5 events get a "satisfaction_survey_triggered"
39
+ * event appended with a 1-10 NPS score.
40
+ * Mixpanel: Insights "satisfaction_survey_triggered" AVG(score), breakdown by product_tier.
92
41
  */
93
42
 
43
+ // ── SCALE ──
44
+ const SEED = "SUPER DUPER DANGEROUS BROOO";
45
+ const NUM_DAYS = 92;
46
+ const NUM_USERS = 8_000;
47
+ const EVENTS_PER_DAY = 1.3;
48
+ const token = process.env.MP_TOKEN || "";
49
+
50
+ const chance = initChance(SEED);
51
+
52
+ // ── DATA ARRAYS ──
53
+ // (text generator instances — one per event style)
54
+
94
55
  // Enterprise support ticket generator with keywords and high authenticity
95
56
  const enterpriseSupportGen = createTextGenerator({
96
57
  style: "support",
@@ -402,32 +363,81 @@ const webinarChatGen = createTextGenerator({
402
363
  includeMetadata: false
403
364
  });
404
365
 
366
+ // ── HELPER FUNCTIONS ──
367
+ function handleUserHook(record) {
368
+ // H1: classify users by engagement and tier
369
+ record.is_power_user = record.engagement_score > 70;
370
+ record.risk_level = record.last_active_days_ago > 20 ? "high_churn" : "healthy";
371
+ return record;
372
+ }
373
+
374
+ function handleEventHook(record) {
375
+ // H2: enterprise support tickets get auto-escalated if critical
376
+ if (record.event === "enterprise_support_ticket" && record.priority === "critical") {
377
+ record.escalation_level = Math.min((record.escalation_level || 1) + 1, 3);
378
+ record.auto_escalated = true;
379
+ }
380
+ // H3: bug reports with critical severity get flagged for immediate review
381
+ if (record.event === "bug_report_submitted" && record.severity === "critical" && record.is_reproducible === true) {
382
+ record.requires_immediate_review = true;
383
+ record.estimated_fix_hours = chance.integer({ min: 1, max: 8 });
384
+ }
385
+ return record;
386
+ }
387
+
388
+ function handleEverythingHook(record, meta) {
389
+ if (!meta || !meta.profile) return record;
390
+ // stamp superProps from profile for consistency (skip feature_flags — intentionally per-event)
391
+ const profile = meta.profile;
392
+ record.forEach(e => {
393
+ e.product_tier = profile.product_tier;
394
+ e.data_center = profile.data_center;
395
+ });
396
+
397
+ // H4: enterprise users with > 5 events get a satisfaction_survey_triggered event appended
398
+ if (meta.profile.user_tier === "enterprise" && record.length > 5) {
399
+ const lastEvent = record[record.length - 1];
400
+ record.push({
401
+ ...lastEvent,
402
+ event: "satisfaction_survey_triggered",
403
+ time: lastEvent.time,
404
+ user_id: lastEvent.user_id,
405
+ survey_type: "quarterly_nps",
406
+ score: chance.integer({ min: 1, max: 10 }),
407
+ });
408
+ }
409
+ return record;
410
+ }
411
+
412
+ // ── CONFIG ──
405
413
  /** @type {Dungeon} */
406
414
  const dungeon = {
407
415
  seed: SEED,
408
- token,
409
- numDays: num_days,
410
- avgEventsPerUserPerDay: avg_events_per_user_per_day,
411
- numUsers: num_users,
412
- hasAnonIds: false,
413
- hasSessionIds: true, // Enable for chat flow tracking
416
+ numDays: NUM_DAYS,
417
+ avgEventsPerUserPerDay: EVENTS_PER_DAY,
418
+ numUsers: NUM_USERS,
414
419
  format: "json",
415
- alsoInferFunnels: true,
416
- hasLocation: true,
417
- hasAndroidDevices: false,
418
- hasIOSDevices: false,
419
- hasDesktopDevices: false,
420
- hasBrowser: false,
421
- hasCampaigns: false,
422
- isAnonymous: false,
423
- hasAdSpend: false,
424
- hasAvatar: false,
420
+ credentials: {
421
+ token,
422
+ },
423
+ switches: {
424
+ hasSessionIds: true, // Enable for chat flow tracking
425
+ alsoInferFunnels: true,
426
+ hasLocation: true,
427
+ hasAndroidDevices: false,
428
+ hasIOSDevices: false,
429
+ hasDesktopDevices: false,
430
+ hasBrowser: false,
431
+ hasCampaigns: false,
432
+ isAnonymous: false,
433
+ hasAdSpend: false,
434
+ hasAvatar: false,
435
+ },
425
436
 
426
437
  concurrency: 1,
427
438
  writeToDisk: false,
428
439
 
429
- // ============= Enhanced Events with New API =============
430
- events: [
440
+ events: [
431
441
  {
432
442
  event: "enterprise_support_ticket",
433
443
  weight: 1,
@@ -780,60 +790,14 @@ const dungeon = {
780
790
  product_tier: ["free", "basic", "pro", "enterprise"],
781
791
  data_center: ["us-east", "us-west", "eu-central", "asia-pacific"],
782
792
  },
783
-
784
- // ============= Slowly Changing Dimensions =============
793
+
785
794
  scdProps: {},
786
-
787
- // ============= Lookup Tables =============
788
- lookupTables: [
789
-
790
- ],
795
+ lookupTables: [],
791
796
 
792
797
  hook: function (record, type, meta) {
793
- // --- user hook: classify users by engagement and tier ---
794
- if (type === "user") {
795
- record.is_power_user = record.engagement_score > 70;
796
- record.risk_level = record.last_active_days_ago > 20 ? "high_churn" : "healthy";
797
- return record;
798
- }
799
-
800
- // --- event hook: enterprise support tickets get auto-escalated if critical ---
801
- if (type === "event") {
802
- if (record.event === "enterprise_support_ticket" && record.priority === "critical") {
803
- record.escalation_level = Math.min((record.escalation_level || 1) + 1, 3);
804
- record.auto_escalated = true;
805
- }
806
- // bug reports with critical severity get flagged for immediate review
807
- if (record.event === "bug_report_submitted" && record.severity === "critical" && record.is_reproducible === true) {
808
- record.requires_immediate_review = true;
809
- record.estimated_fix_hours = chance.integer({ min: 1, max: 8 });
810
- }
811
- return record;
812
- }
813
-
814
- // --- everything hook: enterprise users get a satisfaction survey event ---
815
- if (type === "everything" && meta && meta.profile) {
816
- // stamp superProps from profile for consistency (skip feature_flags — intentionally per-event)
817
- const profile = meta.profile;
818
- record.forEach(e => {
819
- e.product_tier = profile.product_tier;
820
- e.data_center = profile.data_center;
821
- });
822
-
823
- if (meta.profile.user_tier === "enterprise" && record.length > 5) {
824
- const lastEvent = record[record.length - 1];
825
- record.push({
826
- ...lastEvent,
827
- event: "satisfaction_survey_triggered",
828
- time: lastEvent.time,
829
- user_id: lastEvent.user_id,
830
- survey_type: "quarterly_nps",
831
- score: chance.integer({ min: 1, max: 10 }),
832
- });
833
- }
834
- return record;
835
- }
836
-
798
+ if (type === "user") return handleUserHook(record);
799
+ if (type === "event") return handleEventHook(record);
800
+ if (type === "everything") return handleEverythingHook(record, meta);
837
801
  return record;
838
802
  }
839
803
  };