@ak--47/dungeon-master 1.3.1 → 1.4.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.
- package/CHANGELOG.md +58 -0
- package/dungeons/technical/hook-helpers-verify.js +89 -0
- package/dungeons/technical/identity-model-verify.js +47 -0
- package/dungeons/technical/pattern-aggregate-by-bin.js +41 -0
- package/dungeons/technical/pattern-attributed-by-source.js +42 -0
- package/dungeons/technical/pattern-frequency-by-frequency.js +40 -0
- package/dungeons/technical/pattern-funnel-frequency.js +54 -0
- package/dungeons/technical/pattern-ttc-by-segment.js +45 -0
- package/dungeons/vertical/ai-platform.js +45 -52
- package/dungeons/vertical/community.js +11 -8
- package/dungeons/vertical/crypto.js +25 -24
- package/dungeons/vertical/dating.js +56 -48
- package/dungeons/vertical/devtools.js +25 -18
- package/dungeons/vertical/ecommerce.js +42 -38
- package/dungeons/vertical/education.js +24 -9
- package/dungeons/vertical/fintech.js +13 -8
- package/dungeons/vertical/fitness.js +73 -122
- package/dungeons/vertical/food-delivery.js +18 -19
- package/dungeons/vertical/gaming.js +19 -20
- package/dungeons/vertical/healthcare.js +11 -8
- package/dungeons/vertical/insurance-application.js +6 -3
- package/dungeons/vertical/logistics.js +15 -9
- package/dungeons/vertical/marketplace.js +36 -27
- package/dungeons/vertical/media.js +27 -25
- package/dungeons/vertical/real-estate.js +18 -7
- package/dungeons/vertical/sass.js +84 -68
- package/dungeons/vertical/social.js +46 -47
- package/dungeons/vertical/travel.js +8 -5
- package/index.js +17 -71
- package/lib/core/config-validator.js +143 -164
- package/lib/core/storage.js +5 -1
- package/lib/generators/events.js +49 -93
- package/lib/generators/funnels.js +202 -91
- package/lib/hook-helpers/_internal.js +23 -0
- package/lib/hook-helpers/cohort.js +124 -0
- package/lib/hook-helpers/identity.js +56 -0
- package/lib/hook-helpers/index.js +44 -0
- package/lib/hook-helpers/inject.js +99 -0
- package/lib/hook-helpers/mutate.js +151 -0
- package/lib/hook-helpers/timing.js +99 -0
- package/lib/hook-patterns/aggregate-per-user-by-bin.js +38 -0
- package/lib/hook-patterns/attributed-by-source.js +72 -0
- package/lib/hook-patterns/frequency-by-frequency.js +46 -0
- package/lib/hook-patterns/funnel-frequency-breakdown.js +73 -0
- package/lib/hook-patterns/index.js +14 -0
- package/lib/hook-patterns/time-to-convert-by-segment.js +41 -0
- package/lib/orchestrators/mixpanel-sender.js +46 -51
- package/lib/orchestrators/user-loop.js +119 -269
- package/lib/utils/utils.js +39 -16
- package/lib/verify/emulate-breakdown.js +281 -0
- package/lib/verify/index.js +12 -0
- package/lib/verify/verify-dungeon.js +61 -0
- package/package.json +6 -4
- package/types.d.ts +404 -212
package/types.d.ts
CHANGED
|
@@ -17,11 +17,27 @@ export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
|
|
|
17
17
|
*/
|
|
18
18
|
export interface Dungeon {
|
|
19
19
|
// ── Core Parameters ──
|
|
20
|
+
/** Optional dungeon version. Not used by the engine — serves as metadata for tracking revisions when configs are saved/shared. */
|
|
21
|
+
version?: string | number;
|
|
22
|
+
/** Optional app or dataset name. Not used by the engine — available for logging and metadata when set. */
|
|
23
|
+
appName?: string;
|
|
20
24
|
/** Mixpanel project token. If provided, data will be imported to Mixpanel after generation. */
|
|
21
25
|
token?: string;
|
|
22
26
|
/** RNG seed for reproducible output. Same seed + concurrency=1 = identical data. */
|
|
23
27
|
seed?: string;
|
|
24
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Number of days the dataset spans. Default: 30.
|
|
30
|
+
*
|
|
31
|
+
* Three resolution modes:
|
|
32
|
+
* 1. **`numDays` alone (no datasetStart/End):** Window = `[today - numDays, today]`.
|
|
33
|
+
* Simplest API for ad-hoc dungeons. NOT deterministic across runs (today changes).
|
|
34
|
+
* 2. **`datasetStart` + `datasetEnd` (no numDays):** Window pinned exactly. `numDays`
|
|
35
|
+
* derived automatically. Fully deterministic — use for vertical/production dungeons.
|
|
36
|
+
* 3. **All three set:** `datasetStart`/`datasetEnd` win. `numDays` is recomputed from
|
|
37
|
+
* the window; user-supplied value is ignored (with a warning).
|
|
38
|
+
*
|
|
39
|
+
* Setting only one of `datasetStart`/`datasetEnd` throws.
|
|
40
|
+
*/
|
|
25
41
|
numDays?: number;
|
|
26
42
|
/**
|
|
27
43
|
* Explicit start of the dataset window. Pin BOTH `datasetStart` and `datasetEnd` for
|
|
@@ -69,7 +85,21 @@ export interface Dungeon {
|
|
|
69
85
|
hasAvatar?: boolean;
|
|
70
86
|
/** If true, events include geo properties (city, region, country, lat/lng). */
|
|
71
87
|
hasLocation?: boolean;
|
|
72
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* If true, events include UTM campaign properties (utm_source / utm_campaign / utm_medium /
|
|
90
|
+
* utm_content / utm_term).
|
|
91
|
+
*
|
|
92
|
+
* Default: false.
|
|
93
|
+
*
|
|
94
|
+
* Behavior:
|
|
95
|
+
* - false: no UTM stamping anywhere.
|
|
96
|
+
* - true + at least one event in `events[]` has `isAttributionEvent: true`: only the flagged
|
|
97
|
+
* events are eligible. Within those, ~25% are stamped with a randomly-picked campaign.
|
|
98
|
+
* - true + no event flagged: backwards-compat fallback — ~25% of ALL events are stamped
|
|
99
|
+
* with a randomly-picked campaign (legacy behavior).
|
|
100
|
+
*
|
|
101
|
+
* @see EventConfig.isAttributionEvent
|
|
102
|
+
*/
|
|
73
103
|
hasCampaigns?: boolean;
|
|
74
104
|
/** If true, generates ad spend data (impressions, clicks, cost). */
|
|
75
105
|
hasAdSpend?: boolean;
|
|
@@ -81,14 +111,55 @@ export interface Dungeon {
|
|
|
81
111
|
hasDesktopDevices?: boolean;
|
|
82
112
|
/** If true, events include browser properties. */
|
|
83
113
|
hasBrowser?: boolean;
|
|
84
|
-
/** If true (default), writes output files to ./data/. Can also be a directory path string. */
|
|
114
|
+
/** If true (default), writes output files to ./data/. Can also be a directory path string or gs:// URI. */
|
|
85
115
|
writeToDisk?: boolean | string;
|
|
116
|
+
/** If true, deletes all written files (local and GCS) at end of run regardless of import success/failure. Default: false. */
|
|
117
|
+
cleanup?: boolean;
|
|
86
118
|
/** If true, gzip-compresses output files. */
|
|
87
119
|
gzip?: boolean;
|
|
88
120
|
/** If true, prints progress to stdout during generation. */
|
|
89
121
|
verbose?: boolean;
|
|
90
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* @deprecated Prefer `avgDevicePerUser`. `true` is now an alias for `avgDevicePerUser: 1`
|
|
124
|
+
* (single sticky device per user, every event stamped with that `device_id`). `false`
|
|
125
|
+
* (default) leaves the engine in legacy "no device_id stamping" mode unless
|
|
126
|
+
* `avgDevicePerUser` is set.
|
|
127
|
+
*
|
|
128
|
+
* @see Dungeon.avgDevicePerUser
|
|
129
|
+
*/
|
|
91
130
|
hasAnonIds?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Number of distinct devices each user owns. Whole number ≥ 0. Default: 0 (legacy —
|
|
133
|
+
* no `device_id` stamping anywhere). `≤0` is coerced to `1` if `hasAnonIds: true` is
|
|
134
|
+
* also set; otherwise `0` keeps the engine in legacy mode for backwards compat.
|
|
135
|
+
*
|
|
136
|
+
* Behavior:
|
|
137
|
+
* - `0` (default): no `device_id` stamping. Every event gets `user_id` only. Same as
|
|
138
|
+
* pre-1.4 behavior when `hasAnonIds` is not set.
|
|
139
|
+
* - `1`: one device per user. All of that user's events that need a device share a
|
|
140
|
+
* single sticky `device_id`. `hasAnonIds: true` is an alias for this.
|
|
141
|
+
* - `>1`: per-user device pool sized via a normal distribution centered on this value
|
|
142
|
+
* (sd ≈ value/2, clamped ≥ 1, integer-rounded). Sessions are sticky to a single
|
|
143
|
+
* device drawn from the user's pool — every event in that session shares the same
|
|
144
|
+
* `device_id`. Cross-session events for the same user may differ.
|
|
145
|
+
*
|
|
146
|
+
* Identity stamping interactions (multi-device + auth + first funnel):
|
|
147
|
+
* - Pre-existing users (born before dataset window): every event gets both `user_id`
|
|
148
|
+
* and a per-session `device_id`.
|
|
149
|
+
* - Born-in-dataset users running their `isFirstFunnel`:
|
|
150
|
+
* * Pre-auth steps (steps before the first `isAuthEvent` in the funnel sequence):
|
|
151
|
+
* `device_id` only — no `user_id` yet.
|
|
152
|
+
* * The stitch step (the first `isAuthEvent`): both `user_id` AND `device_id`.
|
|
153
|
+
* * Post-auth steps in the same funnel: `user_id` only.
|
|
154
|
+
* * All later (non-firstFunnel) events: `user_id` + per-session sticky `device_id`.
|
|
155
|
+
* - Born-in-dataset users on a `Funnel.attempts` retry that does not reach `isAuthEvent`:
|
|
156
|
+
* every event in that failed attempt is `device_id` only (pre-auth, never stitched).
|
|
157
|
+
*
|
|
158
|
+
* @see Dungeon.hasAnonIds (deprecated alias when `true`)
|
|
159
|
+
* @see EventConfig.isAuthEvent
|
|
160
|
+
* @see Funnel.attempts
|
|
161
|
+
*/
|
|
162
|
+
avgDevicePerUser?: number;
|
|
92
163
|
/** If true, users get session IDs attached to events based on temporal clustering. */
|
|
93
164
|
hasSessionIds?: boolean;
|
|
94
165
|
/** Session timeout in minutes. Events with gaps exceeding this start a new session. Default: 30. Only used when hasSessionIds is true. */
|
|
@@ -139,16 +210,13 @@ export interface Dungeon {
|
|
|
139
210
|
engagementDecay?: EngagementDecay;
|
|
140
211
|
/** Data quality imperfections to inject (nulls, duplicates, bots, late-arriving events). */
|
|
141
212
|
dataQuality?: DataQuality;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
features?: FeatureConfig[];
|
|
150
|
-
/** Anomaly/outlier injection: extreme values, bursts, coordinated spikes. */
|
|
151
|
-
anomalies?: AnomalyConfig[];
|
|
213
|
+
|
|
214
|
+
// ── Removed in 1.4 (silently ignored, one deprecation warning per dungeon) ──
|
|
215
|
+
// The following config keys were removed from the engine in 1.4. Existing dungeon
|
|
216
|
+
// files that still set them will load and run — `validateDungeonConfig` strips them
|
|
217
|
+
// with a single deprecation warning per dungeon. Recreate these patterns as hooks
|
|
218
|
+
// (see `lib/hook-patterns/*` once Phase 4 lands).
|
|
219
|
+
// subscription, attribution, geo, features, anomalies
|
|
152
220
|
|
|
153
221
|
/** Allow arbitrary additional properties on the config. */
|
|
154
222
|
[key: string]: any;
|
|
@@ -302,7 +370,19 @@ export interface HookMetaTimeAnchors {
|
|
|
302
370
|
datasetEnd: number;
|
|
303
371
|
}
|
|
304
372
|
|
|
305
|
-
/**
|
|
373
|
+
/**
|
|
374
|
+
* Meta passed to the "event" hook.
|
|
375
|
+
*
|
|
376
|
+
* **Temporal-check warning:** `datasetStart`/`datasetEnd` are unix seconds in the
|
|
377
|
+
* shifted time frame, but `record.time` during the event hook is in the pre-shift
|
|
378
|
+
* fixed window. Comparing them directly (e.g., `dayjs(record.time).diff(dayjs.unix(meta.datasetStart))`)
|
|
379
|
+
* produces unreliable day-in-dataset values. Move any temporal check to the
|
|
380
|
+
* `everything` hook where both timestamps are in the same frame.
|
|
381
|
+
*
|
|
382
|
+
* Safe uses of the event hook: closure-based state (module-level Maps), event
|
|
383
|
+
* replacement (return a different object), simple property mutations not gated
|
|
384
|
+
* on time.
|
|
385
|
+
*/
|
|
306
386
|
export interface HookMetaEvent extends HookMetaTimeAnchors {
|
|
307
387
|
/** The user this event belongs to (only `distinct_id` is guaranteed). */
|
|
308
388
|
user: { distinct_id: string };
|
|
@@ -341,8 +421,29 @@ export interface HookMetaFunnelPre extends HookMetaTimeAnchors {
|
|
|
341
421
|
scd: Record<string, SCDSchema[]>;
|
|
342
422
|
funnel: Funnel;
|
|
343
423
|
config: Dungeon;
|
|
344
|
-
/**
|
|
424
|
+
/**
|
|
425
|
+
* Unix seconds — temporal anchor for this funnel run. For usage funnels, advances
|
|
426
|
+
* after each run so successive funnels spread across the user's active window.
|
|
427
|
+
* For first-funnel attempts, matches the attempt cursor. Use this to implement
|
|
428
|
+
* temporal conversion trends (e.g., "conversion increases after day 30").
|
|
429
|
+
*/
|
|
345
430
|
firstEventTime: number;
|
|
431
|
+
/** True if this funnel is the user's `isFirstFunnel`. */
|
|
432
|
+
isFirstFunnel: boolean;
|
|
433
|
+
/** True if the user's account creation falls inside the dataset window. */
|
|
434
|
+
isBorn: boolean;
|
|
435
|
+
/** Resolved attempts config for this funnel run, or null if attempts is not configured. */
|
|
436
|
+
attemptsConfig: AttemptsConfig | null;
|
|
437
|
+
/** 1-indexed attempt number for this run (1..totalAttempts). */
|
|
438
|
+
attemptNumber: number;
|
|
439
|
+
/** Total number of attempts (failed priors + 1 final). When attempts is omitted, this is 1. */
|
|
440
|
+
totalAttempts: number;
|
|
441
|
+
/** True if this is the final attempt (attemptNumber === totalAttempts). */
|
|
442
|
+
isFinalAttempt: boolean;
|
|
443
|
+
/** The user's assigned persona (if `personas` is configured), or null. */
|
|
444
|
+
persona: Persona | null;
|
|
445
|
+
/** Experiment context for this funnel run, or null if no experiment / pre-start-date. */
|
|
446
|
+
experiment: HookMetaExperiment | null;
|
|
346
447
|
}
|
|
347
448
|
|
|
348
449
|
/** Meta passed to the "funnel-post" hook (mutate generated funnel events in place). */
|
|
@@ -352,11 +453,39 @@ export interface HookMetaFunnelPost extends HookMetaTimeAnchors {
|
|
|
352
453
|
scd: Record<string, SCDSchema[]>;
|
|
353
454
|
funnel: Funnel;
|
|
354
455
|
config: Dungeon;
|
|
456
|
+
/** Unix seconds — temporal anchor for this funnel run (see HookMetaFunnelPre.firstEventTime). */
|
|
457
|
+
firstEventTime: number;
|
|
458
|
+
/** True if this funnel is the user's `isFirstFunnel`. */
|
|
459
|
+
isFirstFunnel: boolean;
|
|
460
|
+
/** True if the user's account creation falls inside the dataset window. */
|
|
461
|
+
isBorn: boolean;
|
|
462
|
+
/** Resolved attempts config for this funnel run, or null if attempts is not configured. */
|
|
463
|
+
attemptsConfig: AttemptsConfig | null;
|
|
464
|
+
/** 1-indexed attempt number for this run (1..totalAttempts). */
|
|
465
|
+
attemptNumber: number;
|
|
466
|
+
/** Total number of attempts. */
|
|
467
|
+
totalAttempts: number;
|
|
468
|
+
/** True if this is the final attempt. */
|
|
469
|
+
isFinalAttempt: boolean;
|
|
470
|
+
/** The user's assigned persona (if `personas` is configured), or null. */
|
|
471
|
+
persona: Persona | null;
|
|
472
|
+
/** Experiment context for this funnel run, or null if no experiment / pre-start-date. */
|
|
473
|
+
experiment: HookMetaExperiment | null;
|
|
355
474
|
}
|
|
356
475
|
|
|
357
|
-
/**
|
|
476
|
+
/**
|
|
477
|
+
* Meta passed to the "everything" hook — most powerful hook (sees all events for one user).
|
|
478
|
+
*
|
|
479
|
+
* **Ordering within the hook matters.** When multiple effects coexist:
|
|
480
|
+
* 1. SuperProp stamping (profile values → events)
|
|
481
|
+
* 2. Non-temporal mutations and event cloning/injection
|
|
482
|
+
* 3. Event filtering (churn, retention, rate-limit drops)
|
|
483
|
+
* 4. Temporal value mutations (price spikes, error windows) — run LAST
|
|
484
|
+
* so cloned events that land in the window also get the mutation
|
|
485
|
+
* 5. Sort by time
|
|
486
|
+
*/
|
|
358
487
|
export interface HookMetaEverything extends HookMetaTimeAnchors {
|
|
359
|
-
/** The user's profile, including merged persona
|
|
488
|
+
/** The user's profile, including any merged persona properties. */
|
|
360
489
|
profile: UserProfile;
|
|
361
490
|
/** All SCD entries for this user, keyed by prop name. */
|
|
362
491
|
scd: Record<string, SCDSchema[]>;
|
|
@@ -364,6 +493,22 @@ export interface HookMetaEverything extends HookMetaTimeAnchors {
|
|
|
364
493
|
config: Dungeon;
|
|
365
494
|
/** True if the user's account creation falls inside the dataset window. */
|
|
366
495
|
userIsBornInDataset: boolean;
|
|
496
|
+
/**
|
|
497
|
+
* Unix milliseconds of the stitch event (the first `isAuthEvent` in the user's stream).
|
|
498
|
+
* `null` if this user never authed (pre-existing users have no stitch event in the
|
|
499
|
+
* dataset window — they're already authed; born-in-dataset users who never converted
|
|
500
|
+
* remain pre-auth forever).
|
|
501
|
+
*/
|
|
502
|
+
authTime: number | null;
|
|
503
|
+
/**
|
|
504
|
+
* Predicate bound to this user's `authTime`. Returns true if the event happened before
|
|
505
|
+
* the stitch (i.e. the user was anonymous at that point). Returns false for pre-existing
|
|
506
|
+
* users (they're considered authed throughout). For born-in-dataset users that never
|
|
507
|
+
* authed, returns true for every event.
|
|
508
|
+
*/
|
|
509
|
+
isPreAuth: (event: EventSchema) => boolean;
|
|
510
|
+
/** The user's assigned persona (if `personas` is configured), or null. */
|
|
511
|
+
persona: Persona | null;
|
|
367
512
|
}
|
|
368
513
|
|
|
369
514
|
export interface hookArrayOptions<T> {
|
|
@@ -400,6 +545,8 @@ export interface HookedArray<T> extends Array<T> {
|
|
|
400
545
|
getWriteDir: () => string;
|
|
401
546
|
/** Absolute path (with extension) of the next batch file. */
|
|
402
547
|
getWritePath: () => string;
|
|
548
|
+
/** Returns all file paths written by this container during the current run. */
|
|
549
|
+
getWrittenFiles: () => string[];
|
|
403
550
|
/** SCD prop name this array carries (only set on SCD HookedArrays). */
|
|
404
551
|
scdKey?: string;
|
|
405
552
|
/** Entity type for SCDs ("user" or a group key). */
|
|
@@ -518,7 +665,7 @@ export interface Context {
|
|
|
518
665
|
export interface EventConfig {
|
|
519
666
|
/** The event name (e.g., "page viewed", "purchase completed"). */
|
|
520
667
|
event?: string;
|
|
521
|
-
/** Relative frequency weight (1-10). Higher = more likely to be selected
|
|
668
|
+
/** Relative frequency weight (1-10, clamped by validator). Higher = more likely to be selected for standalone event generation. Does NOT control funnel event frequency — funnels generate their own events. 0 is clamped to 1. Default: 1 */
|
|
522
669
|
weight?: number;
|
|
523
670
|
/** Properties to attach to this event type. Values can be arrays (random pick), functions, or primitives. */
|
|
524
671
|
properties?: Record<string, ValueValid>;
|
|
@@ -532,8 +679,49 @@ export interface EventConfig {
|
|
|
532
679
|
isSessionStartEvent?: boolean;
|
|
533
680
|
/** Internal: timing offset in milliseconds (set by funnel system, not user-configured). */
|
|
534
681
|
relativeTimeMs?: number;
|
|
535
|
-
/** If true, this event
|
|
682
|
+
/** If true, this event appears ONLY in explicitly-defined funnels that reference it — excluded from standalone event generation and auto-generated funnels. Use for events that should only occur in funnel context (e.g., "application approved" only after "application submitted"). Also useful to suppress standalone generation of events with weight > 0 that you only want from funnels. */
|
|
536
683
|
isStrictEvent?: boolean;
|
|
684
|
+
/**
|
|
685
|
+
* If true, this event marks the moment a user transitions from anonymous (pre-auth)
|
|
686
|
+
* to identified (post-auth) — typically the "Sign Up" or "Login" event. Multiple events
|
|
687
|
+
* in a dungeon may carry this flag; the engine looks at the first occurrence in a user's
|
|
688
|
+
* stream to determine the identity stitch moment.
|
|
689
|
+
*
|
|
690
|
+
* Default: false.
|
|
691
|
+
*
|
|
692
|
+
* Behavior, when in a funnel marked `isFirstFunnel: true`:
|
|
693
|
+
* - All steps before the first `isAuthEvent` step in the funnel sequence are stamped
|
|
694
|
+
* with `device_id` only (pre-auth).
|
|
695
|
+
* - The `isAuthEvent` step itself is the stitch — it carries BOTH `user_id` AND
|
|
696
|
+
* `device_id`. Exactly one such record per converted born-in-dataset user.
|
|
697
|
+
* - Steps after the stitch in that funnel get `user_id` only.
|
|
698
|
+
*
|
|
699
|
+
* Behavior outside `isFirstFunnel`: the flag has no extra effect — those events follow
|
|
700
|
+
* the usual identity rules for that user (per `avgDevicePerUser`).
|
|
701
|
+
*
|
|
702
|
+
* Behavior on born-in-dataset users whose `Funnel.attempts` retries fail to reach the
|
|
703
|
+
* `isAuthEvent`: every event in those failed attempts is `device_id` only (pre-auth,
|
|
704
|
+
* never stitched). If their final attempt also fails, they remain pre-auth forever.
|
|
705
|
+
*
|
|
706
|
+
* @see Dungeon.avgDevicePerUser
|
|
707
|
+
* @see Funnel.isFirstFunnel
|
|
708
|
+
* @see Funnel.attempts
|
|
709
|
+
*/
|
|
710
|
+
isAuthEvent?: boolean;
|
|
711
|
+
/**
|
|
712
|
+
* If true, this event is eligible to carry UTM campaign properties when
|
|
713
|
+
* `Dungeon.hasCampaigns: true`. ~25% of flagged events get a randomly-picked campaign
|
|
714
|
+
* stamped (utm_source / utm_campaign / utm_medium / utm_content / utm_term).
|
|
715
|
+
*
|
|
716
|
+
* Default: false.
|
|
717
|
+
*
|
|
718
|
+
* Backwards compat: if `Dungeon.hasCampaigns: true` but no event carries this flag,
|
|
719
|
+
* ~25% of ALL events are stamped (legacy behavior, preserved). Opt-in by flagging at
|
|
720
|
+
* least one event.
|
|
721
|
+
*
|
|
722
|
+
* @see Dungeon.hasCampaigns
|
|
723
|
+
*/
|
|
724
|
+
isAttributionEvent?: boolean;
|
|
537
725
|
}
|
|
538
726
|
|
|
539
727
|
export interface GroupEventConfig extends EventConfig {
|
|
@@ -618,14 +806,137 @@ export interface Funnel {
|
|
|
618
806
|
*/
|
|
619
807
|
conditions?: Record<string, ValueValid>;
|
|
620
808
|
/**
|
|
621
|
-
*
|
|
809
|
+
* Experiment configuration for this funnel.
|
|
810
|
+
*
|
|
811
|
+
* - `true` — backward-compatible shorthand: 3 variants (Variant A = worse, Variant B = better, Control),
|
|
812
|
+
* active for the entire dataset.
|
|
813
|
+
* - `ExperimentConfig` object — custom variant names, conversion/TTC multipliers, temporal gating,
|
|
814
|
+
* and distribution weights.
|
|
815
|
+
*
|
|
816
|
+
* Variant assignment is **deterministic per user** (hash of user_id + experiment name), so the same
|
|
817
|
+
* user is in the same variant across all funnel runs. `$experiment_started` is prepended to the
|
|
818
|
+
* sequence for every post-start-date funnel run.
|
|
819
|
+
*
|
|
820
|
+
* Hook meta (`meta.experiment`) exposes the resolved variant in `funnel-pre` and `funnel-post`
|
|
821
|
+
* hooks, enabling variant-specific story injection.
|
|
622
822
|
*
|
|
823
|
+
* @see ExperimentConfig
|
|
623
824
|
*/
|
|
624
|
-
experiment?: boolean;
|
|
825
|
+
experiment?: boolean | ExperimentConfig;
|
|
625
826
|
/**
|
|
626
827
|
* optional: if set, in sequential funnels, this will determine WHEN the property is bound to the rest of the events in the funnel
|
|
627
828
|
*/
|
|
628
829
|
bindPropsIndex?: number;
|
|
830
|
+
/**
|
|
831
|
+
* Multi-attempt iteration for this funnel. Models real users who land, abandon, come
|
|
832
|
+
* back, and try again. Additive — omit for legacy single-attempt behavior.
|
|
833
|
+
*
|
|
834
|
+
* @see AttemptsConfig
|
|
835
|
+
*/
|
|
836
|
+
attempts?: AttemptsConfig;
|
|
837
|
+
/** @internal Resolved experiment config set by config-validator. */
|
|
838
|
+
_experiment?: { name: string; variants: Array<{ name: string; conversionMultiplier: number; ttcMultiplier: number; weight: number }>; startUnix: number | null };
|
|
839
|
+
/** @internal Set by funnels.js during experiment handling. */
|
|
840
|
+
_experimentName?: string;
|
|
841
|
+
/** @internal Set by funnels.js during experiment handling. */
|
|
842
|
+
_experimentVariant?: string;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Per-funnel multi-attempt config. `attempts.min`/`attempts.max` describe the count of
|
|
847
|
+
* **failed prior attempts** (NOT total attempts). The engine picks an integer
|
|
848
|
+
* `failedPriors = chance.integer({min, max})` then runs `failedPriors + 1` total
|
|
849
|
+
* passes through the funnel. The last pass is the "final attempt" — it converts per
|
|
850
|
+
* `attempts.conversionRate ?? funnel.conversionRate`. Each prior attempt is a truncated
|
|
851
|
+
* pre-auth pass that drops out at a random step before reaching any `isAuthEvent`.
|
|
852
|
+
*
|
|
853
|
+
* Identity interaction (when the funnel is `isFirstFunnel`):
|
|
854
|
+
* - Failed prior attempts: every event stamped with `device_id` only — never reach the
|
|
855
|
+
* stitch step, so `user_id` is never assigned.
|
|
856
|
+
* - Final attempt: follows the standard pre-auth → stitch → post-auth identity model.
|
|
857
|
+
* If the final attempt also fails, the user remains pre-auth forever.
|
|
858
|
+
*
|
|
859
|
+
* For non-`isFirstFunnel` funnels, each attempt is treated as an independent usage
|
|
860
|
+
* session (e.g. abandon-cart). Identity stamping uses the user's normal post-auth model.
|
|
861
|
+
*
|
|
862
|
+
* @example single attempt (default behavior)
|
|
863
|
+
* { attempts: { min: 0, max: 0 } } // exactly one pass — equivalent to omitting attempts
|
|
864
|
+
*
|
|
865
|
+
* @example up to 3 failed retries before a 60% final conversion
|
|
866
|
+
* { conversionRate: 60, attempts: { min: 0, max: 3 } }
|
|
867
|
+
*
|
|
868
|
+
* @example heavy churn before final attempt with overridden conversion rate
|
|
869
|
+
* { conversionRate: 80, attempts: { min: 1, max: 5, conversionRate: 30 } }
|
|
870
|
+
*/
|
|
871
|
+
export interface AttemptsConfig {
|
|
872
|
+
/** Lower bound on the number of FAILED PRIOR attempts. 0 = a single attempt is possible. Whole number, ≥ 0. Default: 0. */
|
|
873
|
+
min?: number;
|
|
874
|
+
/** Upper bound on the number of FAILED PRIOR attempts (inclusive). Whole number, ≥ min. Default: 0. */
|
|
875
|
+
max?: number;
|
|
876
|
+
/**
|
|
877
|
+
* Conversion rate (0–100) applied to the FINAL attempt only — overrides
|
|
878
|
+
* `funnel.conversionRate` if set. Omit to inherit `funnel.conversionRate`.
|
|
879
|
+
* Matches the existing `Funnel.conversionRate` scale (0–100, NOT 0–1).
|
|
880
|
+
*/
|
|
881
|
+
conversionRate?: number;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Experiment configuration for a funnel. Controls variant assignment, naming,
|
|
886
|
+
* conversion/TTC modifiers, and temporal gating.
|
|
887
|
+
*
|
|
888
|
+
* @example A/B test starting 30 days before dataset end
|
|
889
|
+
* {
|
|
890
|
+
* name: "Checkout Redesign",
|
|
891
|
+
* startDaysBeforeEnd: 30,
|
|
892
|
+
* variants: [
|
|
893
|
+
* { name: "Control" },
|
|
894
|
+
* { name: "New Checkout", conversionMultiplier: 1.25, ttcMultiplier: 0.8 },
|
|
895
|
+
* ]
|
|
896
|
+
* }
|
|
897
|
+
*/
|
|
898
|
+
export interface ExperimentConfig {
|
|
899
|
+
/** Human-readable experiment name. Default: `funnel.name + " Experiment"`. */
|
|
900
|
+
name?: string;
|
|
901
|
+
/**
|
|
902
|
+
* Variant definitions. Each variant gets a deterministic share of users.
|
|
903
|
+
* Default (when omitted): 3 variants — Variant A (worse), Variant B (better), Control.
|
|
904
|
+
*/
|
|
905
|
+
variants?: ExperimentVariant[];
|
|
906
|
+
/**
|
|
907
|
+
* Days before dataset end that the experiment starts. Funnel runs before
|
|
908
|
+
* the start date skip experiment logic entirely (no variant, no $experiment_started).
|
|
909
|
+
* Default: 0 (entire dataset).
|
|
910
|
+
*/
|
|
911
|
+
startDaysBeforeEnd?: number;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/** A single variant in an experiment. */
|
|
915
|
+
export interface ExperimentVariant {
|
|
916
|
+
/** Display name — appears in the "Variant name" property on $experiment_started. */
|
|
917
|
+
name: string;
|
|
918
|
+
/** Multiplier applied to funnel.conversionRate. 1.0 = unchanged. Default: 1.0. */
|
|
919
|
+
conversionMultiplier?: number;
|
|
920
|
+
/** Multiplier applied to funnel.timeToConvert. 1.0 = unchanged. Default: 1.0. */
|
|
921
|
+
ttcMultiplier?: number;
|
|
922
|
+
/** Distribution weight. Default: 1 (equal split across variants). */
|
|
923
|
+
weight?: number;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** Experiment context exposed in funnel-pre and funnel-post hook meta. */
|
|
927
|
+
export interface HookMetaExperiment {
|
|
928
|
+
/** Experiment name. */
|
|
929
|
+
name: string;
|
|
930
|
+
/** Name of the assigned variant. */
|
|
931
|
+
variantName: string;
|
|
932
|
+
/** 0-based index of the assigned variant. */
|
|
933
|
+
variantIndex: number;
|
|
934
|
+
/** Conversion multiplier applied for this variant. */
|
|
935
|
+
conversionMultiplier: number;
|
|
936
|
+
/** TTC multiplier applied for this variant. */
|
|
937
|
+
ttcMultiplier: number;
|
|
938
|
+
/** Unix seconds of experiment start, or null if active for entire dataset. */
|
|
939
|
+
startDate: number | null;
|
|
629
940
|
}
|
|
630
941
|
|
|
631
942
|
/**
|
|
@@ -845,196 +1156,12 @@ export interface DataQuality {
|
|
|
845
1156
|
emptyEvents?: number;
|
|
846
1157
|
}
|
|
847
1158
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
/** Monthly price. 0 for free tier. */
|
|
855
|
-
price: number;
|
|
856
|
-
/** If true, users start on this plan. */
|
|
857
|
-
default?: boolean;
|
|
858
|
-
/** Trial period in days before requiring payment. */
|
|
859
|
-
trialDays?: number;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
* Subscription lifecycle rates.
|
|
864
|
-
*/
|
|
865
|
-
export interface SubscriptionLifecycle {
|
|
866
|
-
/** Rate of trial-to-paid conversion (0-1). */
|
|
867
|
-
trialToPayRate?: number;
|
|
868
|
-
/** Monthly upgrade rate (0-1). */
|
|
869
|
-
upgradeRate?: number;
|
|
870
|
-
/** Monthly downgrade rate (0-1). */
|
|
871
|
-
downgradeRate?: number;
|
|
872
|
-
/** Monthly churn/cancellation rate (0-1). */
|
|
873
|
-
churnRate?: number;
|
|
874
|
-
/** Rate of churned users who come back (0-1). */
|
|
875
|
-
winBackRate?: number;
|
|
876
|
-
/** Days before win-back attempt. */
|
|
877
|
-
winBackDelay?: number;
|
|
878
|
-
/** Rate of payment failures (0-1). */
|
|
879
|
-
paymentFailureRate?: number;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Subscription configuration.
|
|
884
|
-
*/
|
|
885
|
-
export interface Subscription {
|
|
886
|
-
/** Available plans, ordered from lowest to highest tier. */
|
|
887
|
-
plans: SubscriptionPlan[];
|
|
888
|
-
/** Lifecycle transition rates. */
|
|
889
|
-
lifecycle?: SubscriptionLifecycle;
|
|
890
|
-
/** Event names for subscription lifecycle events. */
|
|
891
|
-
events?: {
|
|
892
|
-
trialStarted?: string;
|
|
893
|
-
subscribed?: string;
|
|
894
|
-
upgraded?: string;
|
|
895
|
-
downgraded?: string;
|
|
896
|
-
renewed?: string;
|
|
897
|
-
cancelled?: string;
|
|
898
|
-
paymentFailed?: string;
|
|
899
|
-
wonBack?: string;
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/**
|
|
904
|
-
* Attribution campaign definition.
|
|
905
|
-
*/
|
|
906
|
-
export interface AttributionCampaign {
|
|
907
|
-
/** Campaign name. */
|
|
908
|
-
name: string;
|
|
909
|
-
/** UTM source (e.g., "google", "facebook"). */
|
|
910
|
-
source: string;
|
|
911
|
-
/** UTM medium (e.g., "search_ad", "social"). */
|
|
912
|
-
medium?: string;
|
|
913
|
-
/** UTM content (e.g., "variant_a", "hero_image"). */
|
|
914
|
-
utm_content?: string;
|
|
915
|
-
/** UTM term (e.g., "running+shoes", "best+deals"). */
|
|
916
|
-
utm_term?: string;
|
|
917
|
-
/** Active days range [startDay, endDay] relative to dataset start. */
|
|
918
|
-
activeDays: [number, number];
|
|
919
|
-
/** Daily budget range [min, max]. */
|
|
920
|
-
dailyBudget?: [number, number];
|
|
921
|
-
/** Fraction of impressions that become users (0-1). */
|
|
922
|
-
acquisitionRate?: number;
|
|
923
|
-
/** Persona weight biases for users acquired by this campaign. */
|
|
924
|
-
userPersonaBias?: Record<string, number>;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
* Connected attribution configuration.
|
|
929
|
-
*/
|
|
930
|
-
export interface Attribution {
|
|
931
|
-
/** Attribution model type. */
|
|
932
|
-
model?: "last_touch" | "first_touch" | "linear" | "time_decay";
|
|
933
|
-
/** Attribution window in days. */
|
|
934
|
-
window?: number;
|
|
935
|
-
/** Campaign definitions. */
|
|
936
|
-
campaigns: AttributionCampaign[];
|
|
937
|
-
/** Fraction of users who arrive organically (no campaign) (0-1). */
|
|
938
|
-
organicRate?: number;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* Geographic region definition.
|
|
943
|
-
*/
|
|
944
|
-
export interface GeoRegion {
|
|
945
|
-
/** Region name (e.g., "north_america"). */
|
|
946
|
-
name: string;
|
|
947
|
-
/** Country codes in this region. */
|
|
948
|
-
countries: string[];
|
|
949
|
-
/** Weight for user assignment (higher = more users). */
|
|
950
|
-
weight: number;
|
|
951
|
-
/** UTC timezone offset for this region (e.g., -5 for EST). */
|
|
952
|
-
timezoneOffset: number;
|
|
953
|
-
/** Properties injected for users in this region. */
|
|
954
|
-
properties?: Record<string, ValueValid>;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
/**
|
|
958
|
-
* Regional feature launch definition.
|
|
959
|
-
*/
|
|
960
|
-
export interface RegionalLaunch {
|
|
961
|
-
/** Region name to match. */
|
|
962
|
-
region: string;
|
|
963
|
-
/** Feature name. */
|
|
964
|
-
featureName: string;
|
|
965
|
-
/** Day the feature launches in this region. */
|
|
966
|
-
startDay: number;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/**
|
|
970
|
-
* Geographic intelligence configuration.
|
|
971
|
-
*/
|
|
972
|
-
export interface GeoConfig {
|
|
973
|
-
/** If true, users keep their location across all events (default: false for backwards compat). */
|
|
974
|
-
sticky?: boolean;
|
|
975
|
-
/** Region definitions with timezone offsets and properties. */
|
|
976
|
-
regions?: GeoRegion[];
|
|
977
|
-
/** Regional feature launches. */
|
|
978
|
-
regionalLaunches?: RegionalLaunch[];
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
/**
|
|
982
|
-
* Progressive feature adoption configuration.
|
|
983
|
-
*/
|
|
984
|
-
export interface FeatureConfig {
|
|
985
|
-
/** Feature name (e.g., "dark_mode", "ai_recommendations"). */
|
|
986
|
-
name: string;
|
|
987
|
-
/** Day the feature launches (relative to dataset start). */
|
|
988
|
-
launchDay: number;
|
|
989
|
-
/** Adoption curve speed or custom logistic params. */
|
|
990
|
-
adoptionCurve?: "fast" | "slow" | "instant" | { k: number; midpoint: number };
|
|
991
|
-
/** Property name to inject on events. */
|
|
992
|
-
property: string;
|
|
993
|
-
/** Possible values for the property. First value is the "before" default if defaultBefore not set. */
|
|
994
|
-
values: ValueValid[];
|
|
995
|
-
/** Default value before the feature launches. If not set, property doesn't exist before launch. */
|
|
996
|
-
defaultBefore?: ValueValid;
|
|
997
|
-
/** Which events are affected ("*" for all, or array of event names). */
|
|
998
|
-
affectsEvents?: string[] | "*";
|
|
999
|
-
/** Conversion rate lift for users who adopted the feature. */
|
|
1000
|
-
conversionLift?: number;
|
|
1001
|
-
/** Resolved logistic curve params (set by config-validator). */
|
|
1002
|
-
_resolvedCurve?: { k: number; midpoint: number };
|
|
1003
|
-
/** Pre-computed adopted values (set by config-validator). */
|
|
1004
|
-
_adoptedValues?: ValueValid[];
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
/**
|
|
1008
|
-
* Anomaly/outlier configuration.
|
|
1009
|
-
*/
|
|
1010
|
-
export interface AnomalyConfig {
|
|
1011
|
-
/** Type of anomaly. */
|
|
1012
|
-
type: "extreme_value" | "burst" | "coordinated";
|
|
1013
|
-
/** Event name this anomaly applies to. */
|
|
1014
|
-
event: string;
|
|
1015
|
-
/** For extreme_value: property to modify. */
|
|
1016
|
-
property?: string;
|
|
1017
|
-
/** For extreme_value: fraction of events affected (0-1). */
|
|
1018
|
-
frequency?: number;
|
|
1019
|
-
/** For extreme_value: multiplier applied to the property value. */
|
|
1020
|
-
multiplier?: number;
|
|
1021
|
-
/** Tag property added to anomalous events. */
|
|
1022
|
-
tag?: string;
|
|
1023
|
-
/** For burst/coordinated: day when the anomaly occurs. */
|
|
1024
|
-
day?: number;
|
|
1025
|
-
/** For burst: duration in days (0.083 = ~2 hours). */
|
|
1026
|
-
duration?: number;
|
|
1027
|
-
/** For burst/coordinated: time window in days (0.01 = ~15 minutes). */
|
|
1028
|
-
window?: number;
|
|
1029
|
-
/** For burst/coordinated: number of events to inject. */
|
|
1030
|
-
count?: number;
|
|
1031
|
-
/** Properties injected on anomalous events. */
|
|
1032
|
-
properties?: Record<string, ValueValid>;
|
|
1033
|
-
/** Resolved absolute start time in unix seconds (set by config-validator). */
|
|
1034
|
-
_startUnix?: number;
|
|
1035
|
-
/** Resolved absolute end time in unix seconds (set by config-validator). */
|
|
1036
|
-
_endUnix?: number;
|
|
1037
|
-
}
|
|
1159
|
+
// ── Removed types in 1.4 ──
|
|
1160
|
+
// Subscription, SubscriptionPlan, SubscriptionLifecycle, Attribution, AttributionCampaign,
|
|
1161
|
+
// GeoConfig, GeoRegion, RegionalLaunch, FeatureConfig, AnomalyConfig were removed from
|
|
1162
|
+
// the engine in 1.4. Recreate these patterns via hooks (see lib/hook-patterns/* and the
|
|
1163
|
+
// `write-hooks` skill once Phase 4/5 land). The killed config keys are silently stripped
|
|
1164
|
+
// by `validateDungeonConfig` with a single deprecation warning per dungeon.
|
|
1038
1165
|
|
|
1039
1166
|
/**
|
|
1040
1167
|
* dungeon-master: generate realistic Mixpanel data at scale
|
|
@@ -1380,3 +1507,68 @@ export interface TestContext {
|
|
|
1380
1507
|
runtime: RuntimeState;
|
|
1381
1508
|
[key: string]: unknown;
|
|
1382
1509
|
}
|
|
1510
|
+
|
|
1511
|
+
// ── Subpath module declarations ──
|
|
1512
|
+
|
|
1513
|
+
declare module '@ak--47/dungeon-master/hook-helpers' {
|
|
1514
|
+
export function binUsersByEventCount(events: EventSchema[], eventName: string, bins: Record<string, [number, number]>): string;
|
|
1515
|
+
export function binUsersByEventInRange(events: EventSchema[], eventName: string, startTime: number | string, endTime: number | string, bins: Record<string, [number, number]>): string;
|
|
1516
|
+
export function countEventsBetween(events: EventSchema[], eventA: string, eventB: string): number;
|
|
1517
|
+
export function userInProfileSegment(profile: Record<string, unknown>, segmentKey: string, segmentValues: unknown[]): boolean;
|
|
1518
|
+
export function cloneEvent(template: EventSchema, overrides?: Partial<EventSchema>): EventSchema;
|
|
1519
|
+
export function dropEventsWhere(events: EventSchema[], predicate: (event: EventSchema) => boolean): number;
|
|
1520
|
+
export function scaleEventCount(events: EventSchema[], eventName: string, factor: number): void;
|
|
1521
|
+
export function scalePropertyValue(events: EventSchema[], predicate: (event: EventSchema) => boolean, propertyName: string, factor: number): void;
|
|
1522
|
+
export function shiftEventTime(event: EventSchema, deltaMs: number): EventSchema;
|
|
1523
|
+
export function scaleTimingBetween(events: EventSchema[], eventA: string, eventB: string, factor: number): void;
|
|
1524
|
+
export function scaleFunnelTTC(funnelEvents: EventSchema[], factor: number): void;
|
|
1525
|
+
export function findFirstSequence(events: EventSchema[], eventNames: string[], maxGapMin?: number): EventSchema[] | null;
|
|
1526
|
+
export function injectAfterEvent(events: EventSchema[], sourceEvent: EventSchema, templateEvent: EventSchema, gapMs: number, overrides?: Partial<EventSchema>): void;
|
|
1527
|
+
export function injectBetween(events: EventSchema[], eventA: EventSchema, eventB: EventSchema, templateEvent: EventSchema, overrides?: Partial<EventSchema>): void;
|
|
1528
|
+
export function injectBurst(events: EventSchema[], templateEvent: EventSchema, count: number, anchorTime: number | string, spreadMs: number): void;
|
|
1529
|
+
export function isPreAuthEvent(event: EventSchema, authTime: number | null): boolean;
|
|
1530
|
+
export function splitByAuth(events: EventSchema[], authTime: number | null): { preAuth: EventSchema[]; postAuth: EventSchema[]; stitch: EventSchema | null };
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
declare module '@ak--47/dungeon-master/hook-patterns' {
|
|
1534
|
+
export function applyFrequencyByFrequency(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; targetEvent: string; multipliers: Record<string, number> }): void;
|
|
1535
|
+
export function applyFunnelFrequencyBreakdown(allUserEvents: EventSchema[], profile: Record<string, unknown> | null, funnelEvents: EventSchema[], opts: { cohortEvent: string; bins: Record<string, [number, number]>; dropMultipliers: Record<string, number> }): void;
|
|
1536
|
+
export function applyAggregateByBin(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; event: string; propertyName: string; deltas: Record<string, number> }): void;
|
|
1537
|
+
export function applyTTCBySegment(funnelEvents: EventSchema[], profile: Record<string, unknown>, opts: { segmentKey: string; factors: Record<string, number> }): void;
|
|
1538
|
+
export function applyAttributedBySource(events: EventSchema[], profile: Record<string, unknown> | null, opts: { sourceEvent: string; sourceProperty: string; downstreamEvent: string; weights: Record<string, number>; model?: 'firstTouch' | 'lastTouch' }): void;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Options for `emulateBreakdown`. Each `type` uses a different subset of fields.
|
|
1543
|
+
*
|
|
1544
|
+
* | type | Required fields | Optional |
|
|
1545
|
+
* |---|---|---|
|
|
1546
|
+
* | `frequencyByFrequency` | `metricEvent`, `breakdownByFrequencyOf` | `perUser` |
|
|
1547
|
+
* | `funnelFrequency` | `steps`, `breakdownByFrequencyOf` | — |
|
|
1548
|
+
* | `aggregatePerUser` | `event`, `property`, `breakdownByFrequencyOf` | `agg` (default: `'avg'`) |
|
|
1549
|
+
* | `timeToConvert` | `fromEvent`, `toEvent`, `breakdownByUserProperty`, `profiles` | — |
|
|
1550
|
+
* | `attributedBy` | `conversionEvent`, `attributionEvent`, `attributionProperty` | `model` (default: `'lastTouch'`) |
|
|
1551
|
+
*/
|
|
1552
|
+
export interface EmulateOptions {
|
|
1553
|
+
type: 'frequencyByFrequency' | 'funnelFrequency' | 'aggregatePerUser' | 'timeToConvert' | 'attributedBy';
|
|
1554
|
+
metricEvent?: string;
|
|
1555
|
+
breakdownByFrequencyOf?: string;
|
|
1556
|
+
perUser?: boolean;
|
|
1557
|
+
steps?: string[];
|
|
1558
|
+
event?: string;
|
|
1559
|
+
property?: string;
|
|
1560
|
+
agg?: 'avg' | 'sum' | 'count' | 'max' | 'min';
|
|
1561
|
+
fromEvent?: string;
|
|
1562
|
+
toEvent?: string;
|
|
1563
|
+
breakdownByUserProperty?: string;
|
|
1564
|
+
profiles?: UserProfile[];
|
|
1565
|
+
conversionEvent?: string;
|
|
1566
|
+
attributionEvent?: string;
|
|
1567
|
+
attributionProperty?: string;
|
|
1568
|
+
model?: 'firstTouch' | 'lastTouch';
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
declare module '@ak--47/dungeon-master/verify' {
|
|
1572
|
+
export function emulateBreakdown(events: EventSchema[], config: EmulateOptions): Array<Record<string, unknown>>;
|
|
1573
|
+
export function verifyDungeon(config: Dungeon, checks: Array<{ name: string; breakdown: EmulateOptions; assert: (rows: Array<Record<string, unknown>>, ctx: { events: EventSchema[]; profiles: UserProfile[] }) => { pass: boolean; detail?: string } }>): Promise<{ pass: boolean; results: Array<{ name: string; pass: boolean; detail?: string; rows?: Array<Record<string, unknown>> }> }>;
|
|
1574
|
+
}
|