@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.
Files changed (66) hide show
  1. package/README.md +518 -0
  2. package/dungeons/array-of-object-lookup-schema.json +327 -0
  3. package/dungeons/array-of-object-lookup.js +220 -0
  4. package/dungeons/ecommerce-schema.json +462 -0
  5. package/dungeons/ecommerce.js +447 -0
  6. package/dungeons/education-schema.json +2409 -0
  7. package/dungeons/education.js +768 -0
  8. package/dungeons/fintech-schema.json +14034 -0
  9. package/dungeons/fintech.js +696 -0
  10. package/dungeons/foobar-schema.json +403 -0
  11. package/dungeons/foobar.js +296 -0
  12. package/dungeons/food-delivery-schema.json +192 -0
  13. package/dungeons/food-delivery.js +602 -0
  14. package/dungeons/food-schema.json +1152 -0
  15. package/dungeons/food.js +754 -0
  16. package/dungeons/gaming-schema.json +1270 -0
  17. package/dungeons/gaming.js +508 -0
  18. package/dungeons/insurance-application-schema.json +204 -0
  19. package/dungeons/insurance-application.js +605 -0
  20. package/dungeons/media-schema.json +906 -0
  21. package/dungeons/media.js +790 -0
  22. package/dungeons/retention-cadence-schema.json +78 -0
  23. package/dungeons/retention-cadence.js +244 -0
  24. package/dungeons/rpg-schema.json +4526 -0
  25. package/dungeons/rpg.js +919 -0
  26. package/dungeons/sanity-schema.json +255 -0
  27. package/dungeons/sanity.js +152 -0
  28. package/dungeons/sass-schema.json +1291 -0
  29. package/dungeons/sass.js +795 -0
  30. package/dungeons/scd-schema.json +919 -0
  31. package/dungeons/scd.js +277 -0
  32. package/dungeons/simple-schema.json +608 -0
  33. package/dungeons/simple.js +285 -0
  34. package/dungeons/simplest-schema.json +1418 -0
  35. package/dungeons/simplest.js +392 -0
  36. package/dungeons/social-schema.json +1118 -0
  37. package/dungeons/social.js +686 -0
  38. package/dungeons/text-generation-schema.json +3096 -0
  39. package/dungeons/text-generation.js +812 -0
  40. package/index.js +567 -0
  41. package/lib/core/config-validator.js +395 -0
  42. package/lib/core/context.js +204 -0
  43. package/lib/core/dungeon-loader.js +337 -0
  44. package/lib/core/storage.js +379 -0
  45. package/lib/generators/adspend.js +132 -0
  46. package/lib/generators/events.js +271 -0
  47. package/lib/generators/funnels.js +407 -0
  48. package/lib/generators/mirror.js +167 -0
  49. package/lib/generators/product-lookup.js +262 -0
  50. package/lib/generators/product-names.js +195 -0
  51. package/lib/generators/profiles.js +93 -0
  52. package/lib/generators/scd.js +124 -0
  53. package/lib/generators/text.js +1192 -0
  54. package/lib/orchestrators/mixpanel-sender.js +266 -0
  55. package/lib/orchestrators/user-loop.js +335 -0
  56. package/lib/templates/abbreviated.d.ts +169 -0
  57. package/lib/templates/defaults.js +1405 -0
  58. package/lib/templates/phrases.js +2526 -0
  59. package/lib/templates/schema.d.ts +173 -0
  60. package/lib/templates/soup-presets.js +188 -0
  61. package/lib/utils/function-registry.js +302 -0
  62. package/lib/utils/json-evaluator.js +172 -0
  63. package/lib/utils/logger.js +34 -0
  64. package/lib/utils/utils.js +1490 -0
  65. package/package.json +89 -0
  66. package/types.d.ts +865 -0
@@ -0,0 +1,277 @@
1
+ import Chance from 'chance';
2
+ const chance = new Chance();
3
+ import dayjs from "dayjs";
4
+ import utc from "dayjs/plugin/utc.js";
5
+ dayjs.extend(utc);
6
+ import { uid, comma } from 'ak-tools';
7
+ import { pickAWinner, weighNumRange, date, integer, weighChoices } from "../lib/utils/utils.js";
8
+
9
+ const itemCategories = ["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"];
10
+
11
+ const videoCategories = ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"];
12
+
13
+ /**
14
+ * ═══════════════════════════════════════════════════════════════
15
+ * DATASET OVERVIEW
16
+ * ═══════════════════════════════════════════════════════════════
17
+ *
18
+ * SCD Test — dungeon focused on Slowly Changing Dimensions.
19
+ * - 500 users over 30 days, ~50K events (CSV format)
20
+ * - E-commerce events: checkout, add to cart, page view, watch video, view/save item
21
+ * - User SCDs: role (weekly), NPS (daily)
22
+ * - Group SCDs: MRR (monthly), AccountHealthScore (weekly), plan (monthly)
23
+ * - 1,000 company groups with industry, segment, CSM assignments
24
+ *
25
+ * ═══════════════════════════════════════════════════════════════
26
+ * ANALYTICS HOOKS (3 patterns)
27
+ * ═══════════════════════════════════════════════════════════════
28
+ *
29
+ * 1. SPEND TIERS (user hook)
30
+ * Users with luckyNumber > 250 = "high_spender", else "budget".
31
+ *
32
+ * 2. COUPON DISCOUNTS + WEEKEND VIEWING (event hook)
33
+ * Checkouts with coupons get discounted amounts. Weekend video
34
+ * watchers get 1.5x watch time with is_weekend: true.
35
+ *
36
+ * Mixpanel Report:
37
+ * - Insights: "checkout", AVG(amount), breakdown by discount_applied
38
+ * Expected: discount_applied=true shows lower amounts
39
+ * - Insights: "watch video", AVG(watchTimeSec), breakdown by is_weekend
40
+ * Expected: weekend watch times ~1.5x higher
41
+ *
42
+ * 3. CART ABANDONMENT (everything hook)
43
+ * Users who add to cart but never checkout get a synthetic
44
+ * "cart_abandoned" event 30 min after last add-to-cart.
45
+ *
46
+ * Mixpanel Report:
47
+ * - Insights: "cart_abandoned" total events
48
+ * Expected: visible volume of abandoned cart events
49
+ */
50
+
51
+ /** @type {import('../types').Dungeon} */
52
+ const config = {
53
+ token: "",
54
+ seed: "simple is best",
55
+ numDays: 30, //how many days worth1 of data
56
+ numEvents: 50000, //how many events
57
+ numUsers: 500, //how many users
58
+ format: 'csv', //csv or json
59
+ region: "US",
60
+ hasAnonIds: false, //if true, anonymousIds are created for each user
61
+ hasSessionIds: false, //if true, hasSessionIds are created for each user
62
+ hasAdSpend: false,
63
+ hasLocation: true,
64
+ hasAndroidDevices: true,
65
+ hasIOSDevices: true,
66
+ hasDesktopDevices: true,
67
+ hasBrowser: true,
68
+ hasCampaigns: true,
69
+ isAnonymous: false,
70
+
71
+
72
+ events: [
73
+ {
74
+ event: "checkout",
75
+ weight: 2,
76
+ properties: {
77
+ amount: weighNumRange(5, 500, .25),
78
+ currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
79
+ coupon: weighChoices(["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"]),
80
+ numItems: weighNumRange(1, 10),
81
+
82
+ }
83
+ },
84
+ {
85
+ event: "add to cart",
86
+ weight: 4,
87
+ properties: {
88
+ amount: weighNumRange(5, 500, .25),
89
+ rating: weighNumRange(1, 5),
90
+ reviews: weighNumRange(0, 35),
91
+ isFeaturedItem: [true, false, false],
92
+ itemCategory: pickAWinner(itemCategories, integer(0, 27)),
93
+ dateItemListed: date(30, true, 'YYYY-MM-DD'),
94
+ }
95
+ },
96
+ {
97
+ event: "page view",
98
+ weight: 10,
99
+ properties: {
100
+ page: pickAWinner(["/", "/", "/help", "/account", "/watch", "/listen", "/product", "/people", "/peace"]),
101
+ utm_source: pickAWinner(["$organic", "$organic", "$organic", "$organic", "google", "google", "google", "facebook", "facebook", "twitter", "linkedin"]),
102
+ }
103
+ },
104
+ {
105
+ event: "watch video",
106
+ weight: 8,
107
+ properties: {
108
+ videoCategory: pickAWinner(videoCategories, integer(0, 9)),
109
+ isFeaturedItem: [true, false, false],
110
+ watchTimeSec: weighNumRange(10, 600, .25),
111
+ quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
112
+ format: ["mp4", "avi", "mov", "mpg"],
113
+ uploader_id: chance.guid.bind(chance)
114
+
115
+ }
116
+ },
117
+ {
118
+ event: "view item",
119
+ weight: 8,
120
+ properties: {
121
+ isFeaturedItem: [true, false, false],
122
+ itemCategory: pickAWinner(itemCategories, integer(0, 27)),
123
+ dateItemListed: date(30, true, 'YYYY-MM-DD'),
124
+ }
125
+ },
126
+ {
127
+ event: "save item",
128
+ weight: 5,
129
+ properties: {
130
+ isFeaturedItem: [true, false, false],
131
+ itemCategory: pickAWinner(itemCategories, integer(0, 27)),
132
+ dateItemListed: date(30, true, 'YYYY-MM-DD'),
133
+ }
134
+ },
135
+ {
136
+ event: "sign up",
137
+ isFirstEvent: true,
138
+ weight: 0,
139
+ properties: {
140
+ variants: ["A", "B", "C", "Control"],
141
+ flows: ["new", "existing", "loyal", "churned"],
142
+ flags: ["on", "off"],
143
+ experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
144
+ multiVariate: [true, false]
145
+ }
146
+ }
147
+ ],
148
+ superProps: {
149
+ platform: ["web", "mobile", "web", "mobile", "web", "web", "kiosk", "smartTV"],
150
+ currentTheme: weighChoices(["light", "dark", "custom", "light", "dark"]),
151
+ },
152
+ /*
153
+ user properties work the same as event properties
154
+ each key should be an array or function reference
155
+ */
156
+ userProps: {
157
+ title: chance.profession.bind(chance),
158
+ luckyNumber: weighNumRange(42, 420, .3),
159
+ spiritAnimal: pickAWinner(["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"])
160
+ },
161
+ scdProps: {
162
+ role: {
163
+ type: "user",
164
+ frequency: "week",
165
+ values: ["admin", "collaborator", "user", "view only", "no access"],
166
+ timing: 'fuzzy',
167
+ max: 10
168
+ },
169
+ NPS: {
170
+ type: "user",
171
+ frequency: "day",
172
+ values: weighNumRange(1, 10, 2, 150),
173
+ timing: 'fuzzy',
174
+ max: 10
175
+ },
176
+ MRR: {
177
+ type: "company_id",
178
+ frequency: "month",
179
+ values: weighNumRange(0, 10000, .15),
180
+ timing: 'fixed',
181
+ max: 10
182
+ },
183
+ AccountHealthScore: {
184
+ type: "company_id",
185
+ frequency: "week",
186
+ values: weighNumRange(1, 10, .15),
187
+ timing: 'fixed',
188
+ max: 40
189
+ },
190
+ plan: {
191
+ type: "company_id",
192
+ frequency: "month",
193
+ values: ["free", "basic", "premium", "enterprise"],
194
+ timing: 'fixed',
195
+ max: 10
196
+ }
197
+ },
198
+ groupKeys: [["company_id", 1_000]],
199
+ groupProps: {
200
+ company_id: {
201
+ name: () => { return chance.name(); },
202
+ email: () => { return `CSM: ${chance.pickone(["AK", "Neha", "Rajiv", "Deepak", "Justin", "Hans", "Katie", "Somya", "Tony", "Kaan"])}`; },
203
+ industry: [
204
+ "technology",
205
+ "education",
206
+ "finance",
207
+ "healthcare",
208
+ "retail",
209
+ "manufacturing",
210
+ "transportation",
211
+ "entertainment",
212
+ "media",
213
+ "real estate",
214
+ "construction",
215
+ "hospitality",
216
+ "energy",
217
+ "utilities",
218
+ "agriculture",
219
+ "other",
220
+ ],
221
+ segment: ["SMB", "SMB", "SMB", "Mid Market", "Mid Market", "Enterprise"],
222
+ "# active users": chance.integer({ min: 2, max: 20 })
223
+ }
224
+ },
225
+ hook: function (record, type, meta) {
226
+ // --- user hook: classify users into spending tiers ---
227
+ if (type === "user") {
228
+ record.spendTier = record.luckyNumber > 250 ? "high_spender" : "budget";
229
+ return record;
230
+ }
231
+
232
+ // --- event hook: coupon users get discounted checkout amounts ---
233
+ if (type === "event") {
234
+ if (record.event === "checkout" && record.coupon && record.coupon !== "none") {
235
+ const discountPct = parseInt(record.coupon) || 10;
236
+ record.amount = Math.round(record.amount * (1 - discountPct / 100));
237
+ record.discount_applied = true;
238
+ }
239
+ // weekend watchers get longer watch times
240
+ if (record.event === "watch video" && record.time) {
241
+ const day = dayjs(record.time).day();
242
+ if (day === 0 || day === 6) {
243
+ record.watchTimeSec = Math.round((record.watchTimeSec || 60) * 1.5);
244
+ record.is_weekend = true;
245
+ }
246
+ }
247
+ return record;
248
+ }
249
+
250
+ // --- everything hook: simulate cart abandonment ---
251
+ if (type === "everything") {
252
+ const hasAddToCart = record.some(e => e.event === "add to cart");
253
+ const hasCheckout = record.some(e => e.event === "checkout");
254
+ // users who added to cart but never checked out: remove checkout events (if any slipped through)
255
+ // and mark them as abandoned
256
+ if (hasAddToCart && !hasCheckout && record.length > 2) {
257
+ const lastAdd = record.filter(e => e.event === "add to cart").pop();
258
+ if (lastAdd) {
259
+ record.push({
260
+ event: "cart_abandoned",
261
+ time: dayjs(lastAdd.time).add(30, "minute").toISOString(),
262
+ user_id: lastAdd.user_id,
263
+ platform: lastAdd.platform,
264
+ amount: lastAdd.amount
265
+ });
266
+ }
267
+ }
268
+ return record;
269
+ }
270
+
271
+ return record;
272
+ }
273
+ };
274
+
275
+
276
+
277
+ export default config;