@ak--47/dungeon-master 1.6.2 → 1.6.4
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/.claude/skills/create-project/provision.mjs +16 -3
- package/.claude/skills/headless-build/SKILL.md +324 -0
- package/.claude/skills/powertools/SKILL.md +21 -1
- package/CHANGELOG.md +170 -0
- package/HOOKS.md +68 -5
- package/README.md +59 -2
- package/index.js +7 -2
- package/lib/core/config-validator.js +63 -2
- package/lib/generators/funnels.js +4 -1
- package/lib/hook-helpers/_internal.js +45 -0
- package/lib/hook-helpers/inject.js +7 -7
- package/lib/hook-helpers/mutate.js +48 -12
- package/lib/hook-helpers/shape.js +4 -4
- package/lib/hook-patterns/frequency-by-frequency.js +2 -2
- package/lib/orchestrators/user-loop.js +27 -0
- package/lib/templates/abbreviated.d.ts +7 -25
- package/lib/templates/schema.d.ts +10 -31
- package/lib/utils/utils.js +0 -1
- package/package.json +2 -2
- package/types.d.ts +74 -25
|
@@ -37,15 +37,16 @@ export interface Dungeon {
|
|
|
37
37
|
/** Properties that change for users or groups over time (Slowly Changing Dimensions). */
|
|
38
38
|
scdProps?: Record<string, SCDProp>;
|
|
39
39
|
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Defines group entities, like companies or teams, and how many of each to create.
|
|
42
|
+
* Tuple form: `[key, numGroups]` or `[key, numGroups, [events]]`.
|
|
43
|
+
* Named form: `{ key, cardinality, events? }`. Both are accepted.
|
|
44
|
+
*/
|
|
45
|
+
groupKeys?: ([string, number, string[]?] | { key: string; cardinality: number; events?: string[] })[];
|
|
46
|
+
|
|
43
47
|
/** Properties that define the characteristics of the groups defined in groupKeys. */
|
|
44
48
|
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
45
49
|
|
|
46
|
-
/** Events that are attributed to a group entity rather than an individual user. */
|
|
47
|
-
groupEvents?: GroupEventConfig[];
|
|
48
|
-
|
|
49
50
|
/** Static data tables (e.g., product catalogs) that can be referenced in events. */
|
|
50
51
|
lookupTables?: LookupTableSchema[];
|
|
51
52
|
|
|
@@ -134,25 +135,6 @@ interface SCDProp {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
|
|
137
|
-
/**
|
|
138
|
-
* Defines an event that is attributed to a group and occurs on a regular schedule.
|
|
139
|
-
* (e.g., a monthly subscription charge for a company).
|
|
140
|
-
*/
|
|
141
|
-
interface GroupEventConfig extends EventConfig {
|
|
142
|
-
/** How often the event occurs (in days). */
|
|
143
|
-
frequency: number;
|
|
144
|
-
|
|
145
|
-
/** The group key this event is associated with (e.g., "company_id"). */
|
|
146
|
-
group_key: string;
|
|
147
|
-
|
|
148
|
-
/** If true, a random user within the group is also associated with the event. */
|
|
149
|
-
attribute_to_user: boolean;
|
|
150
|
-
|
|
151
|
-
/** The number of entities in this group. */
|
|
152
|
-
group_size: number;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
138
|
/**
|
|
157
139
|
* Defines the schema for a static lookup table, which can be used to enrich event data.
|
|
158
140
|
* For example, a "products" table could hold details about product IDs.
|
|
@@ -17,7 +17,7 @@ type ValueValid = Primitives | Primitives[] | FunctionCall;
|
|
|
17
17
|
* This is the high-level object you will be constructing.
|
|
18
18
|
*
|
|
19
19
|
* REQUIRED fields: events, funnels, superProps, userProps
|
|
20
|
-
* OPTIONAL fields: scdProps, groupKeys, groupProps
|
|
20
|
+
* OPTIONAL fields: scdProps, groupKeys, groupProps
|
|
21
21
|
*/
|
|
22
22
|
export interface Dungeon {
|
|
23
23
|
/** REQUIRED: A list of all possible events that can occur in the simulation. */
|
|
@@ -35,14 +35,15 @@ export interface Dungeon {
|
|
|
35
35
|
/** OPTIONAL: Properties that change for users or groups over time (Slowly Changing Dimensions). Only include when properties explicitly change over time. */
|
|
36
36
|
scdProps?: Record<string, SCDProp>;
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* OPTIONAL: Defines group entities (companies, teams). ONLY for B2B/SaaS scenarios.
|
|
40
|
+
* Tuple form: `[["group_key", count], ...]`.
|
|
41
|
+
* Named form: `[{ key: "group_key", cardinality: count, events: [...] }, ...]`.
|
|
42
|
+
*/
|
|
43
|
+
groupKeys?: ([string, number] | { key: string; cardinality: number; events?: string[] })[];
|
|
40
44
|
|
|
41
45
|
/** OPTIONAL: Properties for groups defined in groupKeys. ONLY include if groupKeys is defined. */
|
|
42
46
|
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
43
|
-
|
|
44
|
-
/** OPTIONAL: Events attributed to groups on a schedule (e.g., monthly billing). Rarely needed. */
|
|
45
|
-
groupEvents?: GroupEventConfig[];
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
|
|
@@ -146,28 +147,6 @@ interface SCDProp {
|
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
*
|
|
153
|
-
* This is rarely needed - only use for B2B scenarios with recurring group-level events.
|
|
154
|
-
*/
|
|
155
|
-
interface GroupEventConfig {
|
|
156
|
-
/** REQUIRED: The name of the event. */
|
|
157
|
-
event: string;
|
|
158
|
-
|
|
159
|
-
/** REQUIRED: How often the event occurs (in days). e.g., 30 for monthly. */
|
|
160
|
-
frequency: number;
|
|
161
|
-
|
|
162
|
-
/** REQUIRED: The group key this event belongs to (e.g., "company_id"). */
|
|
163
|
-
group_key: string;
|
|
164
|
-
|
|
165
|
-
/** OPTIONAL: If true, a random user in the group is also attributed to the event. */
|
|
166
|
-
attribute_to_user?: boolean;
|
|
167
|
-
|
|
168
|
-
/** OPTIONAL: Properties for this event. */
|
|
169
|
-
properties?: Record<string, ValueValid>;
|
|
170
|
-
|
|
171
|
-
/** OPTIONAL: Relative frequency of this event. */
|
|
172
|
-
weight?: number;
|
|
173
|
-
}
|
|
150
|
+
// v1.6.4 — `GroupEventConfig` removed. It was a declared-only stub that nothing in
|
|
151
|
+
// `lib/` ever read. To scope an event to a group, list its name in that group key's
|
|
152
|
+
// `events` array instead.
|
package/lib/utils/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ak--47/dungeon-master",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "generate fancy datasets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"dotenv": "^16.4.5",
|
|
88
88
|
"hyparquet-writer": "^0.6.1",
|
|
89
89
|
"mixpanel": "^0.18.0",
|
|
90
|
-
"mixpanel-import": "^3.
|
|
90
|
+
"mixpanel-import": "^3.6.1",
|
|
91
91
|
"p-limit": "^3.1.0",
|
|
92
92
|
"pino": "^9.0.0",
|
|
93
93
|
"pino-pretty": "^11.0.0",
|
package/types.d.ts
CHANGED
|
@@ -13,22 +13,56 @@ type Primitives = string | number | boolean | Date | Record<string, any>;
|
|
|
13
13
|
export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
* Mixpanel data residency region. Matches the set `mixpanel-import` accepts.
|
|
17
|
+
*/
|
|
18
|
+
export type Region = 'US' | 'EU' | 'IN';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* v1.6.4 — named-object form of a group key. Equivalent to the positional tuple
|
|
22
|
+
* `[key, cardinality]` / `[key, cardinality, events]`, which stays supported.
|
|
23
|
+
* The validator normalizes this to the tuple form.
|
|
24
|
+
*/
|
|
25
|
+
export interface GroupKeyObject {
|
|
26
|
+
/** The group key property name (e.g. `"company_id"`). */
|
|
27
|
+
key: string;
|
|
28
|
+
/** How many distinct group entities to generate. */
|
|
29
|
+
cardinality: number;
|
|
30
|
+
/** Event names that carry this group key. Omit or leave empty for all events. */
|
|
31
|
+
events?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The normalized positional form of a group key. Everything downstream of
|
|
36
|
+
* `validateDungeonConfig` sees this shape — the validator converts
|
|
37
|
+
* `GroupKeyObject` entries for you.
|
|
38
|
+
*/
|
|
39
|
+
export type GroupKeyTuple = [string, number] | [string, number, string[]];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A group analytics key as an author may write it: either the legacy tuple form
|
|
43
|
+
* or the named-object form. `result.validatedConfig.groupKeys` is always
|
|
44
|
+
* `GroupKeyTuple[]`.
|
|
45
|
+
*/
|
|
46
|
+
export type GroupKey = GroupKeyTuple | GroupKeyObject;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* v1.5.1 — credentials sub-object. **This is the canonical form.** Groups Mixpanel
|
|
50
|
+
* project credentials. Top-level `token` / `region` / etc. remain functional as a
|
|
51
|
+
* back-compat alias; when both are set, the top-level value wins with a verbose
|
|
52
|
+
* warning. Emit one form or the other, never both.
|
|
19
53
|
*/
|
|
20
54
|
export interface DungeonCredentials {
|
|
21
55
|
token?: string;
|
|
22
|
-
region?:
|
|
56
|
+
region?: Region;
|
|
23
57
|
serviceAccount?: string;
|
|
24
58
|
serviceSecret?: string;
|
|
25
59
|
projectId?: string;
|
|
26
60
|
}
|
|
27
61
|
|
|
28
62
|
/**
|
|
29
|
-
* v1.5.1 — switches sub-object. Groups data-shape
|
|
30
|
-
* remain functional as a back-compat alias; same
|
|
31
|
-
* `DungeonCredentials`.
|
|
63
|
+
* v1.5.1 — switches sub-object. **This is the canonical form.** Groups data-shape
|
|
64
|
+
* booleans. Top-level keys remain functional as a back-compat alias; same
|
|
65
|
+
* precedence rules as `DungeonCredentials`. Emit one form or the other, never both.
|
|
32
66
|
*/
|
|
33
67
|
export interface DungeonSwitches {
|
|
34
68
|
hasLocation?: boolean;
|
|
@@ -42,13 +76,13 @@ export interface DungeonSwitches {
|
|
|
42
76
|
hasBrowser?: boolean;
|
|
43
77
|
isAnonymous?: boolean;
|
|
44
78
|
alsoInferFunnels?: boolean;
|
|
45
|
-
hasAttributionFlags?: boolean;
|
|
46
79
|
}
|
|
47
80
|
|
|
48
81
|
/**
|
|
49
|
-
* v1.5.1 — identity sub-object.
|
|
50
|
-
* `avgDevicePerUser` / `sessionTimeout` remain
|
|
51
|
-
* alias
|
|
82
|
+
* v1.5.1 — identity sub-object. **This is the canonical form.** Groups
|
|
83
|
+
* identity-model knobs. Top-level `avgDevicePerUser` / `sessionTimeout` remain
|
|
84
|
+
* functional as a back-compat alias; when both are set, the top-level value wins
|
|
85
|
+
* with a verbose warning. Emit one form or the other, never both.
|
|
52
86
|
*
|
|
53
87
|
* `hasAnonIds` is DEPRECATED — when present here, it maps to
|
|
54
88
|
* `avgDevicePerUser: 1` with a verbose warning. Use `avgDevicePerUser` instead.
|
|
@@ -143,8 +177,8 @@ export interface Dungeon {
|
|
|
143
177
|
avgEventsPerUserPerDay?: number;
|
|
144
178
|
/** Output format for files written to disk. */
|
|
145
179
|
format?: "csv" | "json" | "parquet" | string;
|
|
146
|
-
/** Mixpanel data residency region. */
|
|
147
|
-
region?:
|
|
180
|
+
/** Mixpanel data residency region. Back-compat alias for `credentials.region`. */
|
|
181
|
+
region?: Region;
|
|
148
182
|
/** User generation concurrency. Default: 1. Values > 1 break seed reproducibility and provide no performance benefit (CPU-bound). */
|
|
149
183
|
concurrency?: number;
|
|
150
184
|
/**
|
|
@@ -186,6 +220,12 @@ export interface Dungeon {
|
|
|
186
220
|
* @see EventConfig.isAttributionEvent
|
|
187
221
|
*/
|
|
188
222
|
hasCampaigns?: boolean;
|
|
223
|
+
/**
|
|
224
|
+
* @internal Derived, not settable. The validator unconditionally sets this to
|
|
225
|
+
* `events.some(e => e.isAttributionEvent)`. Read it off `result.validatedConfig`;
|
|
226
|
+
* setting it on an input config has no effect.
|
|
227
|
+
*/
|
|
228
|
+
hasAttributionFlags?: boolean;
|
|
189
229
|
/** If true, generates ad spend data (impressions, clicks, cost). */
|
|
190
230
|
hasAdSpend?: boolean;
|
|
191
231
|
/** If true, device pool includes iOS devices. */
|
|
@@ -196,7 +236,7 @@ export interface Dungeon {
|
|
|
196
236
|
hasDesktopDevices?: boolean;
|
|
197
237
|
/** If true, events include browser properties. */
|
|
198
238
|
hasBrowser?: boolean;
|
|
199
|
-
/** If true
|
|
239
|
+
/** If true, writes output files to ./data/. Can also be a directory path string or gs:// URI. Default: `false` — data is returned in memory only. */
|
|
200
240
|
writeToDisk?: boolean | string;
|
|
201
241
|
/** If true, deletes all written files (local and GCS) at end of run regardless of import success/failure. Default: false. */
|
|
202
242
|
cleanup?: boolean;
|
|
@@ -289,12 +329,19 @@ export interface Dungeon {
|
|
|
289
329
|
scdProps?: Record<string, SCDProp>;
|
|
290
330
|
/** Mirror dataset definitions: create transformed copies of event data. */
|
|
291
331
|
mirrorProps?: Record<string, MirrorProps>;
|
|
292
|
-
/**
|
|
293
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Group analytics keys. Two interchangeable forms:
|
|
334
|
+
*
|
|
335
|
+
* - Tuple (legacy): `[key, numGroups]` or `[key, numGroups, [associatedEvents]]`
|
|
336
|
+
* - Named object (v1.6.4, preferred): `{ key, cardinality, events? }`
|
|
337
|
+
*
|
|
338
|
+
* The validator normalizes the named form to the tuple form, so hooks and the
|
|
339
|
+
* verifier always see tuples. Mixing both forms in one array is allowed.
|
|
340
|
+
* An empty or omitted `events` list means every event carries the group key.
|
|
341
|
+
*/
|
|
342
|
+
groupKeys?: GroupKey[];
|
|
294
343
|
/** Properties for each group key's entities. */
|
|
295
344
|
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
296
|
-
/** Group-level events (stub — not yet implemented). */
|
|
297
|
-
groupEvents?: GroupEventConfig[];
|
|
298
345
|
/** Lookup table definitions for dimension tables. */
|
|
299
346
|
lookupTables?: LookupTableSchema[];
|
|
300
347
|
/** TimeSoup configuration: shapes intra-week and intra-day rhythm (peaks, deviation, DOW/HOD weights). Pair with `macro` for big-picture trend control. */
|
|
@@ -370,7 +417,11 @@ export interface Dungeon {
|
|
|
370
417
|
* **Incompatibility with `engagementDecay`:** decay drops events from late picked days,
|
|
371
418
|
* eroding the effective active-day count below the configured target. Use one or the
|
|
372
419
|
* other; if you need both, write decay logic in an `everything` hook scoped to specific
|
|
373
|
-
* cohorts. See HOOKS.md §2.5.
|
|
420
|
+
* cohorts. See HOOKS.md §2.5. v1.6.4: the validator warns unconditionally (not gated
|
|
421
|
+
* behind `verbose`) when both are set.
|
|
422
|
+
*
|
|
423
|
+
* **Precedence with `retentionCurve`:** the curve WINS. When `retentionCurve` is set,
|
|
424
|
+
* the active-day plan is built from the curve and this value is ignored entirely.
|
|
374
425
|
*
|
|
375
426
|
* Safe range: `[1, numDays * 0.5]`. Above 50% of `numDays` defeats the concentrator
|
|
376
427
|
* purpose; the v1.5 validator strict-clamps to `floor(numDays * 0.5)` with a warning.
|
|
@@ -928,12 +979,10 @@ export interface EventConfig {
|
|
|
928
979
|
isAttributionEvent?: boolean;
|
|
929
980
|
}
|
|
930
981
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
group_size: number; //the number of users in the group
|
|
936
|
-
}
|
|
982
|
+
// v1.6.4 — `GroupEventConfig` and `Dungeon.groupEvents` were removed from the
|
|
983
|
+
// public types. They were a declared-only stub: nothing in `lib/` ever read them,
|
|
984
|
+
// and no shipped dungeon set them. Group-scoped events are modeled today by
|
|
985
|
+
// listing the event name in a `groupKeys` entry's `events` array.
|
|
937
986
|
|
|
938
987
|
/**
|
|
939
988
|
* the generated event data
|