@ak--47/dungeon-master 1.0.0 → 1.2.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.
Files changed (104) hide show
  1. package/README.md +7 -7
  2. package/dungeons/technical/ad-spend-schema.json +128 -0
  3. package/dungeons/technical/ad-spend.js +116 -0
  4. package/dungeons/technical/anonymous-users-schema.json +92 -0
  5. package/dungeons/technical/anonymous-users.js +102 -0
  6. package/dungeons/technical/array-of-object-lookup-schema.json +191 -0
  7. package/dungeons/{array-of-object-lookup.js → technical/array-of-object-lookup.js} +12 -5
  8. package/dungeons/technical/experiments-schema.json +203 -0
  9. package/dungeons/technical/experiments.js +168 -0
  10. package/dungeons/technical/foobar-schema.json +362 -0
  11. package/dungeons/{foobar.js → technical/foobar.js} +3 -3
  12. package/dungeons/technical/group-analytics-schema.json +241 -0
  13. package/dungeons/technical/group-analytics.js +151 -0
  14. package/dungeons/technical/mirror-strategies-schema.json +84 -0
  15. package/dungeons/technical/mirror-strategies.js +126 -0
  16. package/dungeons/technical/nested-objects-schema.json +145 -0
  17. package/dungeons/technical/nested-objects.js +207 -0
  18. package/dungeons/technical/retention-cadence-schema.json +37 -0
  19. package/dungeons/{retention-cadence.js → technical/retention-cadence.js} +2 -3
  20. package/dungeons/technical/sanity-schema.json +185 -0
  21. package/dungeons/{sanity.js → technical/sanity.js} +4 -2
  22. package/dungeons/technical/scale-test-schema.json +70 -0
  23. package/dungeons/technical/scale-test.js +90 -0
  24. package/dungeons/technical/scd-schema.json +467 -0
  25. package/dungeons/{scd.js → technical/scd.js} +20 -10
  26. package/dungeons/technical/simple-schema.json +362 -0
  27. package/dungeons/{simple.js → technical/simple.js} +7 -7
  28. package/dungeons/technical/simplest-schema.json +724 -0
  29. package/dungeons/{simplest.js → technical/simplest.js} +7 -7
  30. package/dungeons/technical/text-generation-schema.json +1062 -0
  31. package/dungeons/{text-generation.js → technical/text-generation.js} +24 -11
  32. package/dungeons/vertical/community-schema.json +579 -0
  33. package/dungeons/vertical/community.js +794 -0
  34. package/dungeons/vertical/devtools-schema.json +601 -0
  35. package/dungeons/vertical/devtools.js +843 -0
  36. package/dungeons/vertical/ecommerce-schema.json +604 -0
  37. package/dungeons/{ecommerce.js → vertical/ecommerce.js} +43 -5
  38. package/dungeons/vertical/education-schema.json +5686 -0
  39. package/dungeons/{education.js → vertical/education.js} +70 -39
  40. package/dungeons/vertical/fintech-schema.json +630 -0
  41. package/dungeons/{fintech.js → vertical/fintech.js} +107 -56
  42. package/dungeons/vertical/fitness-schema.json +530 -0
  43. package/dungeons/vertical/fitness.js +808 -0
  44. package/dungeons/vertical/food-delivery-schema.json +36728 -0
  45. package/dungeons/{food.js → vertical/food-delivery.js} +67 -26
  46. package/dungeons/vertical/gaming-schema.json +438 -0
  47. package/dungeons/{gaming.js → vertical/gaming.js} +29 -16
  48. package/dungeons/vertical/healthcare-schema.json +549 -0
  49. package/dungeons/vertical/healthcare.js +775 -0
  50. package/dungeons/vertical/insurance-application-schema.json +485 -0
  51. package/dungeons/{insurance-application.js → vertical/insurance-application.js} +41 -31
  52. package/dungeons/vertical/logistics-schema.json +574 -0
  53. package/dungeons/vertical/logistics.js +799 -0
  54. package/dungeons/vertical/marketplace-schema.json +533 -0
  55. package/dungeons/vertical/marketplace.js +850 -0
  56. package/dungeons/vertical/media-schema.json +4749 -0
  57. package/dungeons/{media.js → vertical/media.js} +103 -19
  58. package/dungeons/vertical/rpg-schema.json +2491 -0
  59. package/dungeons/{rpg.js → vertical/rpg.js} +87 -60
  60. package/dungeons/vertical/sass-schema.json +3128 -0
  61. package/dungeons/{sass.js → vertical/sass.js} +65 -26
  62. package/dungeons/vertical/social-schema.json +620 -0
  63. package/dungeons/{social.js → vertical/social.js} +76 -17
  64. package/dungeons/vertical/travel-schema.json +580 -0
  65. package/dungeons/vertical/travel.js +742 -0
  66. package/index.js +20 -16
  67. package/lib/core/config-validator.js +286 -1
  68. package/lib/core/context.js +16 -5
  69. package/lib/core/storage.js +2 -0
  70. package/lib/generators/adspend.js +3 -3
  71. package/lib/generators/events.js +141 -14
  72. package/lib/generators/funnels.js +67 -7
  73. package/lib/generators/text.js +2 -2
  74. package/lib/orchestrators/user-loop.js +444 -22
  75. package/lib/templates/soup-presets.js +2 -2
  76. package/lib/utils/utils.js +4 -5
  77. package/package.json +4 -3
  78. package/scripts/dungeon-to-json.mjs +163 -0
  79. package/scripts/extract-dungeon-schema.mjs +343 -0
  80. package/scripts/json-to-dungeon.mjs +200 -0
  81. package/scripts/run-dungeon.mjs +76 -0
  82. package/scripts/run-many.mjs +118 -0
  83. package/scripts/verify-runner.mjs +42 -0
  84. package/types.d.ts +316 -1
  85. package/dungeons/array-of-object-lookup-schema.json +0 -327
  86. package/dungeons/ecommerce-schema.json +0 -462
  87. package/dungeons/education-schema.json +0 -2409
  88. package/dungeons/fintech-schema.json +0 -14034
  89. package/dungeons/foobar-schema.json +0 -403
  90. package/dungeons/food-delivery-schema.json +0 -192
  91. package/dungeons/food-delivery.js +0 -602
  92. package/dungeons/food-schema.json +0 -1152
  93. package/dungeons/gaming-schema.json +0 -1270
  94. package/dungeons/insurance-application-schema.json +0 -204
  95. package/dungeons/media-schema.json +0 -906
  96. package/dungeons/retention-cadence-schema.json +0 -78
  97. package/dungeons/rpg-schema.json +0 -4526
  98. package/dungeons/sanity-schema.json +0 -255
  99. package/dungeons/sass-schema.json +0 -1291
  100. package/dungeons/scd-schema.json +0 -919
  101. package/dungeons/simple-schema.json +0 -608
  102. package/dungeons/simplest-schema.json +0 -1418
  103. package/dungeons/social-schema.json +0 -1118
  104. package/dungeons/text-generation-schema.json +0 -3096
@@ -1,7 +1,7 @@
1
1
  import dayjs from "dayjs";
2
2
  import utc from "dayjs/plugin/utc.js";
3
3
  import "dotenv/config";
4
- import * as u from "../lib/utils/utils.js";
4
+ import * as u from "../../lib/utils/utils.js";
5
5
 
6
6
  const SEED = "harness-fintech";
7
7
  dayjs.extend(utc);
@@ -9,7 +9,7 @@ const chance = u.initChance(SEED);
9
9
  const num_users = 5_000;
10
10
  const days = 100;
11
11
 
12
- /** @typedef {import("../types.d.ts").Dungeon} Config */
12
+ /** @typedef {import("../../types").Dungeon} Config */
13
13
 
14
14
  /**
15
15
  * ===================================================================
@@ -151,11 +151,24 @@ const config = {
151
151
 
152
152
  hasAvatar: true,
153
153
 
154
- batchSize: 2_500_000,
155
154
  concurrency: 1,
156
155
  writeToDisk: false,
157
156
 
158
- scdProps: {},
157
+ scdProps: {
158
+ account_tier: {
159
+ values: ["basic", "plus", "premium"],
160
+ frequency: "month",
161
+ timing: "fuzzy",
162
+ max: 6
163
+ },
164
+ risk_category: {
165
+ values: ["low", "medium", "high", "critical"],
166
+ frequency: "month",
167
+ timing: "fixed",
168
+ max: 8,
169
+ type: "household_id"
170
+ }
171
+ },
159
172
 
160
173
  funnels: [
161
174
  {
@@ -221,7 +234,7 @@ const config = {
221
234
  weight: 1,
222
235
  isFirstEvent: true,
223
236
  properties: {
224
- "account_type": u.pickAWinner(["personal", "business", "personal"]),
237
+ "account_type": ["personal", "business", "personal"],
225
238
  "signup_channel": ["app", "web", "referral", "branch"],
226
239
  }
227
240
  },
@@ -231,6 +244,7 @@ const config = {
231
244
  properties: {
232
245
  "session_duration_sec": u.weighNumRange(10, 600, 0.3, 60),
233
246
  "pages_viewed": u.weighNumRange(1, 15, 0.5, 3),
247
+ "month_end_anxiety": [false],
234
248
  }
235
249
  },
236
250
  {
@@ -239,6 +253,7 @@ const config = {
239
253
  properties: {
240
254
  "account_balance": u.weighNumRange(0, 50000, 0.8, 2500),
241
255
  "account_type": ["checking", "savings", "investment"],
256
+ "month_end_check": [false],
242
257
  }
243
258
  },
244
259
  {
@@ -249,6 +264,8 @@ const config = {
249
264
  "amount": u.weighNumRange(1, 5000, 0.3, 50),
250
265
  "merchant_category": ["grocery", "restaurant", "gas", "retail", "online", "subscription", "utilities"],
251
266
  "payment_method": ["debit", "credit", "contactless", "online"],
267
+ "payday": [false],
268
+ "fraud_sequence": [false],
252
269
  }
253
270
  },
254
271
  {
@@ -258,6 +275,7 @@ const config = {
258
275
  "transfer_type": ["internal", "external", "p2p", "wire"],
259
276
  "amount": u.weighNumRange(10, 10000, 0.3, 200),
260
277
  "recipient_type": ["friend", "family", "business", "self"],
278
+ "post_payday_spending": [false],
261
279
  }
262
280
  },
263
281
  {
@@ -266,7 +284,9 @@ const config = {
266
284
  properties: {
267
285
  "bill_type": ["rent", "utilities", "phone", "insurance", "subscription", "loan_payment"],
268
286
  "amount": u.weighNumRange(20, 3000, 0.5, 150),
269
- "auto_pay": u.pickAWinner([true, false], 0.4),
287
+ "auto_pay": [false, false, false, true, true],
288
+ "missed_payment": [false],
289
+ "manual_payment": [false],
270
290
  }
271
291
  },
272
292
  {
@@ -292,6 +312,7 @@ const config = {
292
312
  "goal_type": ["emergency", "vacation", "car", "home", "education", "retirement"],
293
313
  "target_amount": u.weighNumRange(500, 50000, 0.3, 5000),
294
314
  "monthly_contribution": u.weighNumRange(25, 2000, 0.5, 200),
315
+ "budget_discipline": [false],
295
316
  }
296
317
  },
297
318
  {
@@ -300,7 +321,9 @@ const config = {
300
321
  properties: {
301
322
  "investment_type": ["stocks", "etf", "crypto", "bonds", "mutual_fund"],
302
323
  "amount": u.weighNumRange(10, 10000, 0.3, 250),
303
- "action": u.pickAWinner(["buy", "sell", "buy"]),
324
+ "action": ["buy", "sell", "buy"],
325
+ "premium_returns": [false],
326
+ "budget_discipline": [false],
304
327
  }
305
328
  },
306
329
  {
@@ -308,6 +331,7 @@ const config = {
308
331
  weight: 1,
309
332
  properties: {
310
333
  "reason": ["lost", "stolen", "suspicious_activity", "travel"],
334
+ "fraud_sequence": [false],
311
335
  }
312
336
  },
313
337
  {
@@ -316,6 +340,7 @@ const config = {
316
340
  properties: {
317
341
  "dispute_amount": u.weighNumRange(10, 2000, 0.5, 100),
318
342
  "reason": ["unauthorized", "duplicate", "not_received", "damaged", "wrong_amount"],
343
+ "fraud_sequence": [false],
319
344
  }
320
345
  },
321
346
  {
@@ -340,8 +365,8 @@ const config = {
340
365
  weight: 2,
341
366
  properties: {
342
367
  "old_tier": ["basic", "plus", "premium"],
343
- "new_tier": u.pickAWinner(["plus", "premium", "premium"]),
344
- "monthly_fee": u.pickAWinner([4.99, 9.99, 14.99]),
368
+ "new_tier": ["plus", "premium", "premium"],
369
+ "monthly_fee": [4.99, 9.99, 14.99],
345
370
  }
346
371
  },
347
372
  {
@@ -350,7 +375,8 @@ const config = {
350
375
  properties: {
351
376
  "channel": ["chat", "phone", "email", "in_app"],
352
377
  "issue_type": ["transaction", "account", "card", "transfer", "technical"],
353
- "resolved": u.pickAWinner([true, false], 0.8),
378
+ "resolved": [false, true, true, true, true],
379
+ "fraud_sequence": [false],
354
380
  }
355
381
  },
356
382
  {
@@ -358,7 +384,7 @@ const config = {
358
384
  weight: 10,
359
385
  properties: {
360
386
  "notification_type": ["transaction", "low_balance", "bill_due", "reward", "security", "promo"],
361
- "action_taken": u.pickAWinner([true, false], 0.6),
387
+ "action_taken": [false, false, true, true, true],
362
388
  }
363
389
  },
364
390
  {
@@ -367,13 +393,15 @@ const config = {
367
393
  properties: {
368
394
  "reward_type": ["cashback", "points", "discount", "partner_offer"],
369
395
  "value": u.weighNumRange(1, 100, 0.5, 10),
396
+ "premium_reward": [false],
370
397
  }
371
398
  }
372
399
  ],
373
400
 
374
401
  superProps: {
375
- account_tier: u.pickAWinner(["basic", "basic", "basic", "plus", "plus", "premium"]),
402
+ account_tier: ["basic", "basic", "basic", "plus", "plus", "premium"],
376
403
  platform: ["ios", "android", "web"],
404
+ low_balance_churn: [false],
377
405
  },
378
406
 
379
407
  userProps: {
@@ -381,7 +409,13 @@ const config = {
381
409
  "income_bracket": ["under_30k", "30k_50k", "50k_75k", "75k_100k", "100k_150k", "over_150k"],
382
410
  "account_age_months": u.weighNumRange(1, 60, 0.5, 12),
383
411
  "total_balance": u.weighNumRange(0, 100000, 0.3, 5000),
384
- "has_direct_deposit": u.pickAWinner([true, false], 0.6),
412
+ "has_direct_deposit": [false, false, true, true, true],
413
+ "account_segment": ["personal"],
414
+ "employee_count": [0],
415
+ "annual_revenue": [0],
416
+ "industry": [""],
417
+ "age_range": [""],
418
+ "life_stage": [""],
385
419
  },
386
420
 
387
421
  groupKeys: [
@@ -393,7 +427,7 @@ const config = {
393
427
  "household_size": u.weighNumRange(1, 6),
394
428
  "combined_income": u.weighNumRange(20000, 300000, 0.3, 75000),
395
429
  "financial_health_score": u.weighNumRange(1, 100, 1, 65),
396
- "primary_bank": u.pickAWinner(["NexBank_only", "multi_bank", "NexBank_only"]),
430
+ "primary_bank": ["NexBank_only", "multi_bank", "NexBank_only"],
397
431
  }
398
432
  },
399
433
 
@@ -568,48 +602,62 @@ const config = {
568
602
  const burstCount = chance.integer({ min: 3, max: 5 });
569
603
  const fraudEvents = [];
570
604
 
605
+ // Find template events for cloning
606
+ const txnTemplate = userEvents.find(e => e.event === "transaction completed");
607
+ const cardTemplate = userEvents.find(e => e.event === "card locked");
608
+ const disputeTemplate = userEvents.find(e => e.event === "dispute filed");
609
+ const supportTemplate = userEvents.find(e => e.event === "support contacted");
610
+
571
611
  for (let i = 0; i < burstCount; i++) {
612
+ if (txnTemplate) {
613
+ fraudEvents.push({
614
+ ...txnTemplate,
615
+ time: midTime.add(i * 10, "minutes").toISOString(),
616
+ user_id: distinctId,
617
+ transaction_type: "purchase",
618
+ amount: chance.integer({ min: 500, max: 3000 }),
619
+ merchant_category: chance.pickone(["online", "retail"]),
620
+ payment_method: "credit",
621
+ fraud_sequence: true,
622
+ });
623
+ }
624
+ }
625
+
626
+ // Card locked after the burst
627
+ if (cardTemplate) {
572
628
  fraudEvents.push({
573
- event: "transaction completed",
574
- time: midTime.add(i * 10, "minutes").toISOString(),
629
+ ...cardTemplate,
630
+ time: midTime.add(burstCount * 10 + 5, "minutes").toISOString(),
575
631
  user_id: distinctId,
576
- transaction_type: "purchase",
577
- amount: chance.integer({ min: 500, max: 3000 }),
578
- merchant_category: chance.pickone(["online", "retail"]),
579
- payment_method: "credit",
632
+ reason: "suspicious_activity",
580
633
  fraud_sequence: true,
581
634
  });
582
635
  }
583
636
 
584
- // Card locked after the burst
585
- fraudEvents.push({
586
- event: "card locked",
587
- time: midTime.add(burstCount * 10 + 5, "minutes").toISOString(),
588
- user_id: distinctId,
589
- reason: "suspicious_activity",
590
- fraud_sequence: true,
591
- });
592
-
593
637
  // Dispute filed shortly after
594
- fraudEvents.push({
595
- event: "dispute filed",
596
- time: midTime.add(burstCount * 10 + 30, "minutes").toISOString(),
597
- user_id: distinctId,
598
- dispute_amount: chance.integer({ min: 500, max: 3000 }),
599
- reason: "unauthorized",
600
- fraud_sequence: true,
601
- });
638
+ if (disputeTemplate) {
639
+ fraudEvents.push({
640
+ ...disputeTemplate,
641
+ time: midTime.add(burstCount * 10 + 30, "minutes").toISOString(),
642
+ user_id: distinctId,
643
+ dispute_amount: chance.integer({ min: 500, max: 3000 }),
644
+ reason: "unauthorized",
645
+ fraud_sequence: true,
646
+ });
647
+ }
602
648
 
603
649
  // Support contacted
604
- fraudEvents.push({
605
- event: "support contacted",
606
- time: midTime.add(burstCount * 10 + 45, "minutes").toISOString(),
607
- user_id: distinctId,
608
- channel: "phone",
609
- issue_type: "card",
610
- resolved: true,
611
- fraud_sequence: true,
612
- });
650
+ if (supportTemplate) {
651
+ fraudEvents.push({
652
+ ...supportTemplate,
653
+ time: midTime.add(burstCount * 10 + 45, "minutes").toISOString(),
654
+ user_id: distinctId,
655
+ channel: "phone",
656
+ issue_type: "card",
657
+ resolved: true,
658
+ fraud_sequence: true,
659
+ });
660
+ }
613
661
 
614
662
  // Splice all fraud events into the user's timeline
615
663
  userEvents.splice(midIdx + 1, 0, ...fraudEvents);
@@ -674,16 +722,19 @@ const config = {
674
722
 
675
723
  // Splice extra savings goal events (budget-conscious users set more goals)
676
724
  if (event.event === "budget created" && chance.bool({ likelihood: 50 })) {
677
- const extraGoal = {
678
- event: "savings goal set",
679
- time: eventTime.add(chance.integer({ min: 1, max: 7 }), "days").toISOString(),
680
- user_id: event.user_id,
681
- goal_type: chance.pickone(["emergency", "vacation", "car", "home"]),
682
- target_amount: chance.integer({ min: 1000, max: 20000 }),
683
- monthly_contribution: chance.integer({ min: 100, max: 800 }),
684
- budget_discipline: true,
685
- };
686
- userEvents.splice(idx + 1, 0, extraGoal);
725
+ const savingsTemplate = userEvents.find(e => e.event === "savings goal set");
726
+ if (savingsTemplate) {
727
+ const extraGoal = {
728
+ ...savingsTemplate,
729
+ time: eventTime.add(chance.integer({ min: 1, max: 7 }), "days").toISOString(),
730
+ user_id: event.user_id,
731
+ goal_type: chance.pickone(["emergency", "vacation", "car", "home"]),
732
+ target_amount: chance.integer({ min: 1000, max: 20000 }),
733
+ monthly_contribution: chance.integer({ min: 100, max: 800 }),
734
+ budget_discipline: true,
735
+ };
736
+ userEvents.splice(idx + 1, 0, extraGoal);
737
+ }
687
738
  }
688
739
  });
689
740
  }