@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/types.d.ts
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* most of the time, the value of a property is a primitive
|
|
3
|
+
*/
|
|
4
|
+
type Primitives = string | number | boolean | Date | Record<string, any>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* a "validValue" can be a primitive, an array of primitives, or a function that returns a primitive
|
|
8
|
+
*/
|
|
9
|
+
export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* main config object for the entire data generation
|
|
13
|
+
*/
|
|
14
|
+
export interface Dungeon {
|
|
15
|
+
// ── Core Parameters ──
|
|
16
|
+
/** Mixpanel project token. If provided, data will be imported to Mixpanel after generation. */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** RNG seed for reproducible output. Same seed + concurrency=1 = identical data. */
|
|
19
|
+
seed?: string;
|
|
20
|
+
/** Number of days the dataset spans (from "now" looking backward). Default: 30 */
|
|
21
|
+
numDays?: number;
|
|
22
|
+
/** Explicit start of dataset window (unix seconds). Alternative to numDays. */
|
|
23
|
+
epochStart?: number;
|
|
24
|
+
/** Explicit end of dataset window (unix seconds). Defaults to FIXED_NOW. */
|
|
25
|
+
epochEnd?: number;
|
|
26
|
+
/** Target total number of events to generate across all users. */
|
|
27
|
+
numEvents?: number;
|
|
28
|
+
/** Number of unique users to generate. */
|
|
29
|
+
numUsers?: number;
|
|
30
|
+
/** Output format for files written to disk. */
|
|
31
|
+
format?: "csv" | "json" | "parquet" | string;
|
|
32
|
+
/** Mixpanel data residency region. */
|
|
33
|
+
region?: "US" | "EU";
|
|
34
|
+
/** User generation concurrency. Default: 1. Values > 1 break seed reproducibility and provide no performance benefit (CPU-bound). */
|
|
35
|
+
concurrency?: number;
|
|
36
|
+
/** Number of records before auto-flushing to disk. Prevents OOM for large datasets. Default: 1,000,000 */
|
|
37
|
+
batchSize?: number;
|
|
38
|
+
|
|
39
|
+
// ── Mixpanel Import Credentials (for SCD import) ──
|
|
40
|
+
serviceAccount?: string;
|
|
41
|
+
serviceSecret?: string;
|
|
42
|
+
projectId?: string;
|
|
43
|
+
|
|
44
|
+
// ── Identifiers ──
|
|
45
|
+
/** Dataset name prefix for output files. Auto-generated if not set. */
|
|
46
|
+
name?: string;
|
|
47
|
+
|
|
48
|
+
// ── Feature Switches ──
|
|
49
|
+
/** If true, users have no distinct_id (anonymous-only tracking). */
|
|
50
|
+
isAnonymous?: boolean;
|
|
51
|
+
/** If true, user profiles include avatar URLs. */
|
|
52
|
+
hasAvatar?: boolean;
|
|
53
|
+
/** If true, events include geo properties (city, region, country, lat/lng). */
|
|
54
|
+
hasLocation?: boolean;
|
|
55
|
+
/** If true, events include UTM campaign properties. */
|
|
56
|
+
hasCampaigns?: boolean;
|
|
57
|
+
/** If true, generates ad spend data (impressions, clicks, cost). */
|
|
58
|
+
hasAdSpend?: boolean;
|
|
59
|
+
/** If true, device pool includes iOS devices. */
|
|
60
|
+
hasIOSDevices?: boolean;
|
|
61
|
+
/** If true, device pool includes Android devices. */
|
|
62
|
+
hasAndroidDevices?: boolean;
|
|
63
|
+
/** If true, device pool includes desktop devices. */
|
|
64
|
+
hasDesktopDevices?: boolean;
|
|
65
|
+
/** If true, events include browser properties. */
|
|
66
|
+
hasBrowser?: boolean;
|
|
67
|
+
/** If true (default), writes output files to ./data/. Can also be a directory path string. */
|
|
68
|
+
writeToDisk?: boolean | string;
|
|
69
|
+
/** If true, gzip-compresses output files. */
|
|
70
|
+
gzip?: boolean;
|
|
71
|
+
/** If true, prints progress to stdout during generation. */
|
|
72
|
+
verbose?: boolean;
|
|
73
|
+
/** If true, users get anonymous device IDs in addition to distinct_id. */
|
|
74
|
+
hasAnonIds?: boolean;
|
|
75
|
+
/** If true, users get session IDs attached to events. */
|
|
76
|
+
hasSessionIds?: boolean;
|
|
77
|
+
/** If true, auto-generates funnels from the events array in addition to any explicit funnels. */
|
|
78
|
+
alsoInferFunnels?: boolean;
|
|
79
|
+
/** Restrict all location data to a single country (e.g., "US", "GB"). */
|
|
80
|
+
singleCountry?: string;
|
|
81
|
+
/** If true, stops generation at exactly numEvents (forces concurrency=1). Without this, event count is approximate. */
|
|
82
|
+
strictEventCount?: boolean;
|
|
83
|
+
/** Internal flag for UI-triggered jobs (affects SCD credential handling). */
|
|
84
|
+
isUIJob?: boolean;
|
|
85
|
+
|
|
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). */
|
|
90
|
+
superProps?: Record<string, ValueValid>;
|
|
91
|
+
/** Funnel definitions: conversion sequences, rates, ordering strategies. */
|
|
92
|
+
funnels?: Funnel[];
|
|
93
|
+
/** User profile properties set once per user. */
|
|
94
|
+
userProps?: Record<string, ValueValid>;
|
|
95
|
+
/** Slowly Changing Dimension properties: time-series mutations of user/group attributes. */
|
|
96
|
+
scdProps?: Record<string, SCDProp>;
|
|
97
|
+
/** Mirror dataset definitions: create transformed copies of event data. */
|
|
98
|
+
mirrorProps?: Record<string, MirrorProps>;
|
|
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. */
|
|
102
|
+
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
103
|
+
/** Group-level events (stub — not yet implemented). */
|
|
104
|
+
groupEvents?: GroupEventConfig[];
|
|
105
|
+
/** Lookup table definitions for dimension tables. */
|
|
106
|
+
lookupTables?: LookupTableSchema[];
|
|
107
|
+
/** TimeSoup configuration: controls the temporal distribution of events (peaks, deviation, mean). */
|
|
108
|
+
soup?: soup;
|
|
109
|
+
/** Hook function called on every data point. The primary mechanism for engineering deliberate trends and patterns. */
|
|
110
|
+
hook?: Hook<any>;
|
|
111
|
+
|
|
112
|
+
/** Allow arbitrary additional properties on the config. */
|
|
113
|
+
[key: string]: any;
|
|
114
|
+
|
|
115
|
+
// ── Distribution Controls ──
|
|
116
|
+
/** Percentage of users whose account creation falls within the dataset window (vs. pre-existing). Default: 15 */
|
|
117
|
+
percentUsersBornInDataset?: number;
|
|
118
|
+
/** Bias toward recent birth dates for users born in dataset (0 = uniform, 1 = heavily recent). Default: 0.3 */
|
|
119
|
+
bornRecentBias?: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
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" */
|
|
124
|
+
type?: string | "user" | "company_id" | "team_id" | "department_id";
|
|
125
|
+
/** How often the property mutates. Default: "day" */
|
|
126
|
+
frequency?: "day" | "week" | "month" | "year";
|
|
127
|
+
/** Array of possible values, or a function that returns values. */
|
|
128
|
+
values: ValueValid;
|
|
129
|
+
/** "fixed" = mutations at clean boundaries (start of day/week/month/year). "fuzzy" = mutations at any time. Default: "fuzzy" */
|
|
130
|
+
timing?: "fixed" | "fuzzy";
|
|
131
|
+
/** Maximum number of mutations per entity. Default: 10 */
|
|
132
|
+
max?: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Soup preset names for common time distribution patterns
|
|
137
|
+
*/
|
|
138
|
+
export type SoupPreset = "steady" | "growth" | "spiky" | "seasonal" | "global" | "churny" | "chaotic";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Soup configuration object for fine-grained control
|
|
142
|
+
*/
|
|
143
|
+
export type SoupConfig = {
|
|
144
|
+
/** Use a named preset as base, then override individual fields */
|
|
145
|
+
preset?: SoupPreset;
|
|
146
|
+
/** Controls clustering tightness. Higher = tighter peaks. Default: 2 */
|
|
147
|
+
deviation?: number;
|
|
148
|
+
/** Number of time clusters to distribute events across. Default: numDays*2 */
|
|
149
|
+
peaks?: number;
|
|
150
|
+
/** Offset for the normal distribution center within each peak. Default: 0 */
|
|
151
|
+
mean?: number;
|
|
152
|
+
/** Day-of-week weights (7 elements, index 0=Sunday). Normalized max=1.0. Set null to disable. */
|
|
153
|
+
dayOfWeekWeights?: number[] | null;
|
|
154
|
+
/** Hour-of-day weights (24 elements, index 0=midnight UTC). Normalized max=1.0. Set null to disable. */
|
|
155
|
+
hourOfDayWeights?: number[] | null;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* the soup is a set of parameters that determine the distribution of events over time.
|
|
160
|
+
* Can be a preset name string, a config object, or a config object with a preset base.
|
|
161
|
+
*/
|
|
162
|
+
type soup = SoupPreset | SoupConfig;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Hook types and when they fire (in order per user):
|
|
166
|
+
* - "user" — user profile object (mutate in-place, return ignored)
|
|
167
|
+
* - "scd-pre" — array of SCD entries (mutate in-place OR return new array to replace)
|
|
168
|
+
* - "funnel-pre" — funnel config object (mutate conversionRate, timeToConvert, etc. in-place)
|
|
169
|
+
* - "event" — single event with FLAT properties (return value replaces event)
|
|
170
|
+
* - "funnel-post" — array of generated funnel events (mutate in-place, splice to inject)
|
|
171
|
+
* - "everything" — array of ALL events for one user (return array to replace; meta.profile available)
|
|
172
|
+
*
|
|
173
|
+
* Storage-only hooks (fire during hookPush, not in generators):
|
|
174
|
+
* - "ad-spend", "group", "mirror", "lookup"
|
|
175
|
+
*/
|
|
176
|
+
export type hookTypes =
|
|
177
|
+
| "event"
|
|
178
|
+
| "user"
|
|
179
|
+
| "group"
|
|
180
|
+
| "lookup"
|
|
181
|
+
| "scd"
|
|
182
|
+
| "scd-pre"
|
|
183
|
+
| "mirror"
|
|
184
|
+
| "funnel-pre"
|
|
185
|
+
| "funnel-post"
|
|
186
|
+
| "ad-spend"
|
|
187
|
+
| "churn"
|
|
188
|
+
| "group-event"
|
|
189
|
+
| "everything"
|
|
190
|
+
| "";
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* A hook function that receives every piece of data as it flows through the pipeline.
|
|
194
|
+
* @param record - The data being processed (event, profile, array of events, etc.)
|
|
195
|
+
* @param type - Which hook type is firing
|
|
196
|
+
* @param meta - Contextual metadata (varies by type; "everything" includes meta.profile and meta.scd)
|
|
197
|
+
*/
|
|
198
|
+
export type Hook<T> = (record: any, type: hookTypes, meta: any) => T;
|
|
199
|
+
|
|
200
|
+
export interface hookArrayOptions<T> {
|
|
201
|
+
hook?: Hook<T>;
|
|
202
|
+
type?: hookTypes;
|
|
203
|
+
filename?: string;
|
|
204
|
+
format?: "csv" | "json" | "parquet" | string;
|
|
205
|
+
concurrency?: number;
|
|
206
|
+
context?: Context;
|
|
207
|
+
[key: string]: any;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* an enriched array is an array that has a hookPush method that can be used to transform-then-push items into the array
|
|
212
|
+
*/
|
|
213
|
+
export interface HookedArray<T> extends Array<T> {
|
|
214
|
+
hookPush: (item: T | T[], ...meta: any[]) => any;
|
|
215
|
+
flush: () => void;
|
|
216
|
+
getWriteDir: () => string;
|
|
217
|
+
getWritePath: () => string;
|
|
218
|
+
[key: string]: any;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type AllData =
|
|
222
|
+
| HookedArray<EventSchema>
|
|
223
|
+
| HookedArray<UserProfile>
|
|
224
|
+
| HookedArray<GroupProfileSchema>
|
|
225
|
+
| HookedArray<LookupTableSchema>
|
|
226
|
+
| HookedArray<SCDSchema>
|
|
227
|
+
| any[];
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* the storage object is a key-value store that holds arrays of data
|
|
231
|
+
*/
|
|
232
|
+
export interface Storage {
|
|
233
|
+
eventData?: HookedArray<EventSchema>;
|
|
234
|
+
mirrorEventData?: HookedArray<EventSchema>;
|
|
235
|
+
userProfilesData?: HookedArray<UserProfile>;
|
|
236
|
+
adSpendData?: HookedArray<EventSchema>;
|
|
237
|
+
groupProfilesData?: HookedArray<GroupProfileSchema>[];
|
|
238
|
+
lookupTableData?: HookedArray<LookupTableSchema>[];
|
|
239
|
+
scdTableData?: HookedArray<SCDSchema>[];
|
|
240
|
+
groupEventData?: HookedArray<EventSchema>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Runtime state for tracking execution metrics and flags
|
|
245
|
+
*/
|
|
246
|
+
export interface RuntimeState {
|
|
247
|
+
operations: number;
|
|
248
|
+
eventCount: number;
|
|
249
|
+
userCount: number;
|
|
250
|
+
isBatchMode: boolean;
|
|
251
|
+
verbose: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Default data factories for generating realistic test data
|
|
256
|
+
*/
|
|
257
|
+
export interface Defaults {
|
|
258
|
+
locationsUsers: () => any[];
|
|
259
|
+
locationsEvents: () => any[];
|
|
260
|
+
iOSDevices: () => any[];
|
|
261
|
+
androidDevices: () => any[];
|
|
262
|
+
desktopDevices: () => any[];
|
|
263
|
+
browsers: () => any[];
|
|
264
|
+
campaigns: () => any[];
|
|
265
|
+
devicePools: { android: any[]; ios: any[]; desktop: any[] };
|
|
266
|
+
allDevices:any[];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Context object that replaces global variables with dependency injection
|
|
271
|
+
* Contains validated config, storage containers, defaults, and runtime state
|
|
272
|
+
*/
|
|
273
|
+
export interface Context {
|
|
274
|
+
config: Dungeon;
|
|
275
|
+
storage: Storage | null;
|
|
276
|
+
defaults: Defaults;
|
|
277
|
+
campaigns: any[];
|
|
278
|
+
runtime: RuntimeState;
|
|
279
|
+
FIXED_NOW: number;
|
|
280
|
+
FIXED_BEGIN?: number;
|
|
281
|
+
TIME_SHIFT_SECONDS: number;
|
|
282
|
+
MAX_TIME: number;
|
|
283
|
+
|
|
284
|
+
// State update methods
|
|
285
|
+
incrementOperations(): void;
|
|
286
|
+
incrementEvents(): void;
|
|
287
|
+
incrementUsers(): void;
|
|
288
|
+
setStorage(storage: Storage): void;
|
|
289
|
+
|
|
290
|
+
// State getter methods
|
|
291
|
+
getOperations(): number;
|
|
292
|
+
getEventCount(): number;
|
|
293
|
+
getUserCount(): number;
|
|
294
|
+
incrementUserCount(): void;
|
|
295
|
+
incrementEventCount(): void;
|
|
296
|
+
isBatchMode(): boolean;
|
|
297
|
+
|
|
298
|
+
// Time helper methods
|
|
299
|
+
getTimeShift(): number;
|
|
300
|
+
getDaysShift(): number;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* how we define events and their properties
|
|
305
|
+
*/
|
|
306
|
+
export interface EventConfig {
|
|
307
|
+
/** The event name (e.g., "page viewed", "purchase completed"). */
|
|
308
|
+
event?: string;
|
|
309
|
+
/** Relative frequency weight (1-10). Higher = more likely to be selected. Used for both standalone event selection and funnel sequence building. Default: 1 */
|
|
310
|
+
weight?: number;
|
|
311
|
+
/** Properties to attach to this event type. Values can be arrays (random pick), functions, or primitives. */
|
|
312
|
+
properties?: Record<string, ValueValid>;
|
|
313
|
+
/** If true, this is the user's first-ever event (e.g., "sign up"). Used to create onboarding funnels. */
|
|
314
|
+
isFirstEvent?: boolean;
|
|
315
|
+
/** If true, generating this event signals the user has churned. The user stops producing further events unless returnLikelihood allows them to come back. */
|
|
316
|
+
isChurnEvent?: boolean;
|
|
317
|
+
/** 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 */
|
|
318
|
+
returnLikelihood?: number;
|
|
319
|
+
/** If true, this event is automatically prepended 15 seconds before each funnel sequence (e.g., "$session_started"). */
|
|
320
|
+
isSessionStartEvent?: boolean;
|
|
321
|
+
/** Internal: timing offset in milliseconds (set by funnel system, not user-configured). */
|
|
322
|
+
relativeTimeMs?: number;
|
|
323
|
+
/** 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. */
|
|
324
|
+
isStrictEvent?: boolean;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface GroupEventConfig extends EventConfig {
|
|
328
|
+
frequency: number; //how often the event occurs (in days)
|
|
329
|
+
group_key: string; //the key that the group is based on
|
|
330
|
+
attribute_to_user: boolean; //if true, the event also goes to a user
|
|
331
|
+
group_size: number; //the number of users in the group
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* the generated event data
|
|
336
|
+
*/
|
|
337
|
+
export interface EventSchema {
|
|
338
|
+
event: string;
|
|
339
|
+
time: string;
|
|
340
|
+
source: string;
|
|
341
|
+
insert_id: string;
|
|
342
|
+
device_id?: string;
|
|
343
|
+
session_id?: string;
|
|
344
|
+
user_id?: string;
|
|
345
|
+
[key: string]: ValueValid;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* how we define funnels and their properties
|
|
350
|
+
*/
|
|
351
|
+
export interface Funnel {
|
|
352
|
+
/**
|
|
353
|
+
* the name of the funnel
|
|
354
|
+
*/
|
|
355
|
+
name?: string;
|
|
356
|
+
/**
|
|
357
|
+
* the description of the funnel
|
|
358
|
+
*/
|
|
359
|
+
description?: string;
|
|
360
|
+
/**
|
|
361
|
+
* the sequence of events that define the funnel
|
|
362
|
+
*/
|
|
363
|
+
sequence: string[];
|
|
364
|
+
/**
|
|
365
|
+
* how likely the funnel is to be selected
|
|
366
|
+
*/
|
|
367
|
+
weight?: number;
|
|
368
|
+
/**
|
|
369
|
+
* If true, the funnel will be the first thing the user does
|
|
370
|
+
*/
|
|
371
|
+
isFirstFunnel?: boolean;
|
|
372
|
+
/**
|
|
373
|
+
* If true, the funnel will require the user to repeat the sequence of events in order to convert
|
|
374
|
+
* If false, the user does not need to repeat the sequence of events in order to convert
|
|
375
|
+
* ^ when false, users who repeat the repetitive steps are more likely to convert
|
|
376
|
+
*/
|
|
377
|
+
requireRepeats?: boolean;
|
|
378
|
+
/**
|
|
379
|
+
* how the events in the funnel are ordered for each user
|
|
380
|
+
*/
|
|
381
|
+
order?:
|
|
382
|
+
| "sequential"
|
|
383
|
+
| "first-fixed"
|
|
384
|
+
| "last-fixed"
|
|
385
|
+
| "random" //totally shuffled
|
|
386
|
+
| "first-and-last-fixed"
|
|
387
|
+
| "middle-fixed"
|
|
388
|
+
| "interrupted"
|
|
389
|
+
| string;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* the likelihood that a user will convert (0-100%)
|
|
393
|
+
*/
|
|
394
|
+
conversionRate?: number;
|
|
395
|
+
/**
|
|
396
|
+
* the time it takes (on average) to convert in hours
|
|
397
|
+
*/
|
|
398
|
+
timeToConvert?: number;
|
|
399
|
+
/**
|
|
400
|
+
* funnel properties go onto each event in the funnel and are held constant
|
|
401
|
+
*/
|
|
402
|
+
props?: Record<string, ValueValid>;
|
|
403
|
+
/**
|
|
404
|
+
* funnel conditions (user properties) are used to filter users who are eligible for the funnel
|
|
405
|
+
* these conditions must match the current user's profile for the user to be eligible for the funnel
|
|
406
|
+
*/
|
|
407
|
+
conditions?: Record<string, ValueValid>;
|
|
408
|
+
/**
|
|
409
|
+
* If true, the funnel will be part of an experiment where we generate 3 variants of the funnel with different conversion rates
|
|
410
|
+
*
|
|
411
|
+
*/
|
|
412
|
+
experiment?: boolean;
|
|
413
|
+
/**
|
|
414
|
+
* optional: if set, in sequential funnels, this will determine WHEN the property is bound to the rest of the events in the funnel
|
|
415
|
+
*/
|
|
416
|
+
bindPropsIndex?: number;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* mirror props are used to show mutations of event data over time
|
|
421
|
+
* there are different strategies for how to mutate the data
|
|
422
|
+
*/
|
|
423
|
+
export interface MirrorProps {
|
|
424
|
+
/**
|
|
425
|
+
* the event that will be mutated in the new version
|
|
426
|
+
*/
|
|
427
|
+
events?: string[] | "*";
|
|
428
|
+
/**
|
|
429
|
+
* "create" - create this key in the new version; value are chosen
|
|
430
|
+
* "update" - update this key in the new version; values are chosen
|
|
431
|
+
* "fill" - update this key in the new version, but only if the existing key is null or unset
|
|
432
|
+
* "delete" - delete this key in the new version; values are ignored
|
|
433
|
+
*/
|
|
434
|
+
strategy?: "create" | "update" | "fill" | "delete" | "";
|
|
435
|
+
values?: ValueValid[];
|
|
436
|
+
/**
|
|
437
|
+
* optional: for 'fill' mode, daysUnfilled will dictate where the cutoff is in the unfilled data
|
|
438
|
+
*/
|
|
439
|
+
daysUnfilled?: number;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export interface UserProfile {
|
|
443
|
+
name?: string;
|
|
444
|
+
email?: string;
|
|
445
|
+
avatar?: string;
|
|
446
|
+
created: string | undefined;
|
|
447
|
+
distinct_id: string;
|
|
448
|
+
[key: string]: ValueValid;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export interface Person {
|
|
452
|
+
name: string;
|
|
453
|
+
email?: string;
|
|
454
|
+
avatar?: string;
|
|
455
|
+
created: string | undefined;
|
|
456
|
+
anonymousIds: string[];
|
|
457
|
+
sessionIds: string[];
|
|
458
|
+
distinct_id?: string;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* the generated user data
|
|
463
|
+
*/
|
|
464
|
+
export interface LookupTableSchema {
|
|
465
|
+
key: string;
|
|
466
|
+
entries: number;
|
|
467
|
+
attributes: Record<string, ValueValid>;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export interface LookupTableData {
|
|
471
|
+
key: string;
|
|
472
|
+
data: any[];
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export interface SCDSchema {
|
|
476
|
+
distinct_id: string;
|
|
477
|
+
insertTime: string;
|
|
478
|
+
startTime: string;
|
|
479
|
+
[key: string]: ValueValid;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export interface GroupProfileSchema {
|
|
483
|
+
key: string;
|
|
484
|
+
data: any[];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* the end result of importing data into mixpanel
|
|
489
|
+
*/
|
|
490
|
+
export interface ImportResults {
|
|
491
|
+
events: ImportResult;
|
|
492
|
+
users: ImportResult;
|
|
493
|
+
groups: ImportResult[];
|
|
494
|
+
}
|
|
495
|
+
type ImportResult = import("mixpanel-import").ImportResults;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* the end result of the data generation
|
|
499
|
+
*/
|
|
500
|
+
export type Result = {
|
|
501
|
+
eventData: EventSchema[];
|
|
502
|
+
mirrorEventData: EventSchema[];
|
|
503
|
+
userProfilesData: any[];
|
|
504
|
+
scdTableData: any[][];
|
|
505
|
+
adSpendData: EventSchema[];
|
|
506
|
+
groupProfilesData: GroupProfileSchema[][];
|
|
507
|
+
lookupTableData: LookupTableData[][];
|
|
508
|
+
importResults?: ImportResults;
|
|
509
|
+
files?: string[];
|
|
510
|
+
time?: {
|
|
511
|
+
start: number;
|
|
512
|
+
end: number;
|
|
513
|
+
delta: number;
|
|
514
|
+
human: string;
|
|
515
|
+
};
|
|
516
|
+
operations?: number;
|
|
517
|
+
eventCount?: number;
|
|
518
|
+
userCount?: number;
|
|
519
|
+
groupCount?: number;
|
|
520
|
+
avgEPS?: number;
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* dungeon-master: generate realistic Mixpanel data at scale
|
|
525
|
+
*
|
|
526
|
+
* accepts multiple input formats:
|
|
527
|
+
* - config object: `DUNGEON_MASTER({ numUsers: 100, events: [...] })`
|
|
528
|
+
* - file path (.js/.mjs): `DUNGEON_MASTER('./dungeons/simple.js')`
|
|
529
|
+
* - file path (.json): `DUNGEON_MASTER('./dungeons/simple-schema.json')`
|
|
530
|
+
* - array of file paths: `DUNGEON_MASTER(['./dungeons/a.js', './dungeons/b.js'])`
|
|
531
|
+
* - raw JS string: `DUNGEON_MASTER('export default { numUsers: 50, ... }')`
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* import DUNGEON_MASTER from '@ak--47/dungeon-master';
|
|
535
|
+
* const data = await DUNGEON_MASTER({ numUsers: 100, numEvents: 10_000, numDays: 30 });
|
|
536
|
+
*
|
|
537
|
+
* @example
|
|
538
|
+
* const data = await DUNGEON_MASTER('./dungeons/simple.js', { writeToDisk: true });
|
|
539
|
+
*
|
|
540
|
+
* @example
|
|
541
|
+
* const results = await DUNGEON_MASTER(['./dungeons/gaming.js', './dungeons/media.js']);
|
|
542
|
+
*/
|
|
543
|
+
declare function DUNGEON_MASTER(input: Dungeon, overrides?: Partial<Dungeon>): Promise<Result>;
|
|
544
|
+
declare function DUNGEON_MASTER(input: string, overrides?: Partial<Dungeon>): Promise<Result>;
|
|
545
|
+
declare function DUNGEON_MASTER(input: string[], overrides?: Partial<Dungeon>): Promise<Result[]>;
|
|
546
|
+
|
|
547
|
+
export default DUNGEON_MASTER;
|
|
548
|
+
|
|
549
|
+
/** Load and validate a dungeon from a file path */
|
|
550
|
+
export declare function loadFromFile(filePath: string): Promise<Dungeon>;
|
|
551
|
+
/** Load and validate a dungeon from raw JavaScript text */
|
|
552
|
+
export declare function loadFromText(code: string): Promise<Dungeon>;
|
|
553
|
+
/** Parse a JSON dungeon (UI schema format) into a runnable config */
|
|
554
|
+
export declare function parseJSONDungeon(json: object): Dungeon;
|
|
555
|
+
/** Validate that an object has the minimum shape of a dungeon config */
|
|
556
|
+
export declare function validateDungeonShape(config: any): void;
|
|
557
|
+
|
|
558
|
+
// ============= Text Generator Types =============
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Sentiment tone of generated text
|
|
562
|
+
*/
|
|
563
|
+
export type TextTone = "pos" | "neg" | "neu";
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Style of text generation
|
|
567
|
+
*
|
|
568
|
+
* Supported styles:
|
|
569
|
+
* - "support": Customer support tickets and requests
|
|
570
|
+
* - "review": Product reviews and ratings
|
|
571
|
+
* - "search": Search queries and keywords
|
|
572
|
+
* - "feedback": User feedback and suggestions
|
|
573
|
+
* - "chat": Casual chat messages and conversations
|
|
574
|
+
* - "email": Formal email communications
|
|
575
|
+
* - "forum": Forum posts and discussions
|
|
576
|
+
* - "comments": Social media comments and reactions
|
|
577
|
+
* - "tweet": Twitter-style social media posts
|
|
578
|
+
*/
|
|
579
|
+
export type TextStyle = "support" | "review" | "search" | "feedback" | "chat" | "email" | "forum" | "comments" | "tweet";
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Emotional intensity level
|
|
583
|
+
*/
|
|
584
|
+
export type TextIntensity = "low" | "medium" | "high";
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Language formality level
|
|
588
|
+
*/
|
|
589
|
+
export type TextFormality = "casual" | "business" | "technical";
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Output format for batch generation
|
|
593
|
+
*/
|
|
594
|
+
export type TextReturnType = "strings" | "objects";
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Domain-specific keywords to inject into generated text
|
|
598
|
+
*
|
|
599
|
+
* Common predefined categories include:
|
|
600
|
+
* - features: Product features to mention
|
|
601
|
+
* - products: Product/company names
|
|
602
|
+
* - competitors: Competitor names for comparisons
|
|
603
|
+
* - technical: Technical terms and jargon
|
|
604
|
+
* - versions: Version numbers and releases
|
|
605
|
+
* - errors: Specific error messages or codes
|
|
606
|
+
* - metrics: Business metrics or KPIs
|
|
607
|
+
* - events: Event types (e.g., 'wedding', 'celebration', 'conference')
|
|
608
|
+
* - emotions: Emotional descriptors (e.g., 'inspiring', 'heartwarming')
|
|
609
|
+
* - issues: Common problems or issues
|
|
610
|
+
* - team: Team or role references
|
|
611
|
+
* - business_impact: Business impact phrases
|
|
612
|
+
* - comparisons: Comparison phrases
|
|
613
|
+
* - credibility: Credibility markers
|
|
614
|
+
* - user_actions: User action descriptions
|
|
615
|
+
* - specific_praise: Specific positive details
|
|
616
|
+
* - specific_issues: Specific negative details
|
|
617
|
+
* - error_messages: Error message text
|
|
618
|
+
* - categories: General categories
|
|
619
|
+
* - brands: Brand names
|
|
620
|
+
* - vendors: Vendor references
|
|
621
|
+
* - services: Service types
|
|
622
|
+
* - locations: Location references
|
|
623
|
+
*
|
|
624
|
+
* Custom categories can be added as needed.
|
|
625
|
+
*/
|
|
626
|
+
export interface TextKeywordSet {
|
|
627
|
+
/** Product features to mention */
|
|
628
|
+
features?: string[];
|
|
629
|
+
/** Product/company names */
|
|
630
|
+
products?: string[];
|
|
631
|
+
/** Competitor names for comparisons */
|
|
632
|
+
competitors?: string[];
|
|
633
|
+
/** Technical terms and jargon */
|
|
634
|
+
technical?: string[];
|
|
635
|
+
/** Version numbers and releases */
|
|
636
|
+
versions?: string[];
|
|
637
|
+
/** Specific error messages or codes */
|
|
638
|
+
errors?: string[];
|
|
639
|
+
/** Business metrics or KPIs */
|
|
640
|
+
metrics?: string[];
|
|
641
|
+
/** Event types (e.g., 'wedding', 'celebration', 'conference') */
|
|
642
|
+
events?: string[];
|
|
643
|
+
/** Emotional descriptors (e.g., 'inspiring', 'heartwarming') */
|
|
644
|
+
emotions?: string[];
|
|
645
|
+
/** Common problems or issues */
|
|
646
|
+
issues?: string[];
|
|
647
|
+
/** Team or role references */
|
|
648
|
+
team?: string[];
|
|
649
|
+
/** Business impact phrases */
|
|
650
|
+
business_impact?: string[];
|
|
651
|
+
/** Comparison phrases */
|
|
652
|
+
comparisons?: string[];
|
|
653
|
+
/** Credibility markers */
|
|
654
|
+
credibility?: string[];
|
|
655
|
+
/** User action descriptions */
|
|
656
|
+
user_actions?: string[];
|
|
657
|
+
/** Specific positive details */
|
|
658
|
+
specific_praise?: string[];
|
|
659
|
+
/** Specific negative details */
|
|
660
|
+
specific_issues?: string[];
|
|
661
|
+
/** Error message text */
|
|
662
|
+
error_messages?: string[];
|
|
663
|
+
/** General categories */
|
|
664
|
+
categories?: string[];
|
|
665
|
+
/** Brand names */
|
|
666
|
+
brands?: string[];
|
|
667
|
+
/** Vendor references */
|
|
668
|
+
vendors?: string[];
|
|
669
|
+
/** Service types */
|
|
670
|
+
services?: string[];
|
|
671
|
+
/** Location references */
|
|
672
|
+
locations?: string[];
|
|
673
|
+
/** Allow any custom keyword category */
|
|
674
|
+
[key: string]: string[] | undefined;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Configuration for text generator instance
|
|
679
|
+
*/
|
|
680
|
+
export interface TextGeneratorConfig {
|
|
681
|
+
/** Default sentiment tone */
|
|
682
|
+
tone?: TextTone;
|
|
683
|
+
/** Type of text to generate */
|
|
684
|
+
style?: TextStyle;
|
|
685
|
+
/** Emotional intensity */
|
|
686
|
+
intensity?: TextIntensity;
|
|
687
|
+
/** Language formality */
|
|
688
|
+
formality?: TextFormality;
|
|
689
|
+
/** Minimum text length in characters */
|
|
690
|
+
min?: number;
|
|
691
|
+
/** Maximum text length in characters */
|
|
692
|
+
max?: number;
|
|
693
|
+
/** RNG seed for reproducibility */
|
|
694
|
+
seed?: string;
|
|
695
|
+
/** Domain-specific keywords to inject */
|
|
696
|
+
keywords?: TextKeywordSet;
|
|
697
|
+
/** Probability of keyword injection (0-1) */
|
|
698
|
+
keywordDensity?: number;
|
|
699
|
+
/** Enable realistic typos */
|
|
700
|
+
typos?: boolean;
|
|
701
|
+
/** Base typo probability per word */
|
|
702
|
+
typoRate?: number;
|
|
703
|
+
/** Allow sentiment mixing for realism */
|
|
704
|
+
mixedSentiment?: boolean;
|
|
705
|
+
/** Amount of authentic markers (0-1) */
|
|
706
|
+
authenticityLevel?: number;
|
|
707
|
+
/** Add timestamps to some messages */
|
|
708
|
+
timestamps?: boolean;
|
|
709
|
+
/** Include user role/experience markers */
|
|
710
|
+
userPersona?: boolean;
|
|
711
|
+
/** Allow sentiment to drift during generation (0-1) */
|
|
712
|
+
sentimentDrift?: number;
|
|
713
|
+
/** Add metadata to generated text */
|
|
714
|
+
includeMetadata?: boolean;
|
|
715
|
+
/** How specific/detailed to make claims (0-1) */
|
|
716
|
+
specificityLevel?: number;
|
|
717
|
+
/** Filter near-duplicates */
|
|
718
|
+
enableDeduplication?: boolean;
|
|
719
|
+
/** Max generation attempts per item */
|
|
720
|
+
maxAttempts?: number;
|
|
721
|
+
// performanceMode removed - system is always optimized for speed + uniqueness
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Metadata for generated text
|
|
726
|
+
*/
|
|
727
|
+
export interface TextMetadata {
|
|
728
|
+
/** Timestamp if enabled */
|
|
729
|
+
timestamp?: string;
|
|
730
|
+
/** Sentiment analysis score */
|
|
731
|
+
sentimentScore?: number;
|
|
732
|
+
/** Keywords that were injected */
|
|
733
|
+
injectedKeywords?: string[];
|
|
734
|
+
/** User persona information */
|
|
735
|
+
persona?: Record<string, any>;
|
|
736
|
+
/** Flesch reading ease score */
|
|
737
|
+
readabilityScore?: number;
|
|
738
|
+
/** Text style used */
|
|
739
|
+
style?: TextStyle | string;
|
|
740
|
+
/** Intensity level used */
|
|
741
|
+
intensity?: TextIntensity | string;
|
|
742
|
+
/** Formality level used */
|
|
743
|
+
formality?: TextFormality | string;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Simple generated text object (without metadata)
|
|
748
|
+
*/
|
|
749
|
+
export interface SimpleGeneratedText {
|
|
750
|
+
/** The generated text */
|
|
751
|
+
text: string;
|
|
752
|
+
/** Actual tone of generated text */
|
|
753
|
+
tone: TextTone | string;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Generated text with metadata
|
|
758
|
+
*/
|
|
759
|
+
export interface GeneratedText {
|
|
760
|
+
/** The generated text */
|
|
761
|
+
text: string;
|
|
762
|
+
/** Actual tone of generated text */
|
|
763
|
+
tone: TextTone | string;
|
|
764
|
+
/** Additional metadata */
|
|
765
|
+
metadata?: TextMetadata;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Options for batch text generation
|
|
770
|
+
*/
|
|
771
|
+
export interface TextBatchOptions {
|
|
772
|
+
/** Number of items to generate */
|
|
773
|
+
n: number;
|
|
774
|
+
/** Output format */
|
|
775
|
+
returnType?: TextReturnType;
|
|
776
|
+
/** Override tone for this batch */
|
|
777
|
+
tone?: TextTone;
|
|
778
|
+
/** Generate related/coherent items */
|
|
779
|
+
related?: boolean;
|
|
780
|
+
/** Shared context/topic for related items */
|
|
781
|
+
sharedContext?: string;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Statistics for text generator performance
|
|
786
|
+
*/
|
|
787
|
+
export interface TextGeneratorStats {
|
|
788
|
+
/** Configuration used */
|
|
789
|
+
config: TextGeneratorConfig;
|
|
790
|
+
/** Total items generated */
|
|
791
|
+
generatedCount: number;
|
|
792
|
+
/** Items that were duplicates */
|
|
793
|
+
duplicateCount: number;
|
|
794
|
+
/** Items that failed generation */
|
|
795
|
+
failedCount: number;
|
|
796
|
+
/** Average generation time per item */
|
|
797
|
+
avgGenerationTime: number;
|
|
798
|
+
/** Total generation time */
|
|
799
|
+
totalGenerationTime: number;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Text generator instance interface
|
|
804
|
+
*/
|
|
805
|
+
export interface TextGenerator {
|
|
806
|
+
/** Generate a single text item */
|
|
807
|
+
generateOne(): string | GeneratedText | null;
|
|
808
|
+
/** Generate multiple text items in batch */
|
|
809
|
+
generateBatch(options: TextBatchOptions): (string | GeneratedText | SimpleGeneratedText)[];
|
|
810
|
+
/** Get generation statistics */
|
|
811
|
+
getStats(): TextGeneratorStats;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Creates a new text generator instance
|
|
816
|
+
* @param config - Configuration options for the generator
|
|
817
|
+
* @returns Text generator instance
|
|
818
|
+
*/
|
|
819
|
+
export declare function createTextGenerator(config?: TextGeneratorConfig): TextGenerator;
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Generate a batch of text items directly (standalone function)
|
|
823
|
+
* @param options - Combined generator config and batch options
|
|
824
|
+
* @returns Array of generated text items
|
|
825
|
+
*/
|
|
826
|
+
export declare function generateBatch(options: TextGeneratorConfig & TextBatchOptions): (string | GeneratedText | SimpleGeneratedText)[];
|
|
827
|
+
|
|
828
|
+
// ============= Additional Utility Types =============
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* File path configuration for data generation output
|
|
832
|
+
*/
|
|
833
|
+
export interface WritePaths {
|
|
834
|
+
eventFiles: string[];
|
|
835
|
+
userFiles: string[];
|
|
836
|
+
adSpendFiles: string[];
|
|
837
|
+
scdFiles: string[];
|
|
838
|
+
mirrorFiles: string[];
|
|
839
|
+
groupFiles: string[];
|
|
840
|
+
lookupFiles: string[];
|
|
841
|
+
folder: string;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Configuration for TimeSoup time distribution function
|
|
846
|
+
*/
|
|
847
|
+
export interface TimeSoupOptions {
|
|
848
|
+
earliestTime?: number;
|
|
849
|
+
latestTime?: number;
|
|
850
|
+
peaks?: number;
|
|
851
|
+
deviation?: number;
|
|
852
|
+
mean?: number;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Test context configuration for unit/integration tests
|
|
857
|
+
*/
|
|
858
|
+
export interface TestContext {
|
|
859
|
+
config: Dungeon;
|
|
860
|
+
storage: Storage | null;
|
|
861
|
+
defaults: Defaults;
|
|
862
|
+
campaigns: any[];
|
|
863
|
+
runtime: RuntimeState;
|
|
864
|
+
[key: string]: any;
|
|
865
|
+
}
|