@funnelsgrove/runtime 0.1.31 → 0.1.33
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.
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import type { FunnelStepKind, FunnelStepType } from '../steps/types.js';
|
|
2
2
|
export type TemplateArchitectureVersion = number;
|
|
3
|
+
export type FunnelBreakpointName = 'small' | 'medium' | 'large' | 'desktop-small';
|
|
4
|
+
export type FunnelManifestBreakpoint = {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
export type FunnelManifestBreakpoints = Record<FunnelBreakpointName, FunnelManifestBreakpoint>;
|
|
9
|
+
export declare const DEFAULT_FUNNEL_BREAKPOINTS: {
|
|
10
|
+
readonly small: {
|
|
11
|
+
readonly width: 375;
|
|
12
|
+
readonly height: 667;
|
|
13
|
+
};
|
|
14
|
+
readonly medium: {
|
|
15
|
+
readonly width: 393;
|
|
16
|
+
readonly height: 852;
|
|
17
|
+
};
|
|
18
|
+
readonly large: {
|
|
19
|
+
readonly width: 402;
|
|
20
|
+
readonly height: 874;
|
|
21
|
+
};
|
|
22
|
+
readonly 'desktop-small': {
|
|
23
|
+
readonly width: 1280;
|
|
24
|
+
readonly height: 800;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
3
27
|
export type FunnelManifestStep = {
|
|
4
28
|
id: string;
|
|
5
29
|
path: string;
|
|
@@ -59,6 +83,7 @@ export type FunnelManifest = {
|
|
|
59
83
|
width: number;
|
|
60
84
|
height: number;
|
|
61
85
|
};
|
|
86
|
+
breakpoints?: FunnelManifestBreakpoints;
|
|
62
87
|
assets: Record<string, FunnelManifestAsset>;
|
|
63
88
|
steps: readonly FunnelManifestStep[];
|
|
64
89
|
entryPoints?: readonly FunnelManifestEntryPoint[];
|
|
@@ -10,9 +10,43 @@ export type FunnelFlowControllerExperiment<StepId extends string> = FunnelManife
|
|
|
10
10
|
};
|
|
11
11
|
type StepComponentRegistry = Record<string, unknown>;
|
|
12
12
|
type FunnelFlowAnalyticsAdapter = {
|
|
13
|
+
trackStepStarted?: (input: {
|
|
14
|
+
stepId: string;
|
|
15
|
+
stepName: string;
|
|
16
|
+
stepType?: string;
|
|
17
|
+
startedAt: string;
|
|
18
|
+
featureFlags?: Record<string, string>;
|
|
19
|
+
}) => string | null;
|
|
20
|
+
trackStepCompleted?: (input: {
|
|
21
|
+
stepId: string;
|
|
22
|
+
stepName: string;
|
|
23
|
+
stepType?: string;
|
|
24
|
+
startedAt: string;
|
|
25
|
+
endedAt: string;
|
|
26
|
+
selected?: Record<string, unknown>;
|
|
27
|
+
featureFlags?: Record<string, string>;
|
|
28
|
+
}) => string | null;
|
|
29
|
+
trackStepEngaged?: (input: {
|
|
30
|
+
stepId: string;
|
|
31
|
+
stepName: string;
|
|
32
|
+
stepType?: string;
|
|
33
|
+
startedAt: string;
|
|
34
|
+
engagedAt: string;
|
|
35
|
+
durationMs: number;
|
|
36
|
+
engagementThresholdMs: number;
|
|
37
|
+
featureFlags?: Record<string, string>;
|
|
38
|
+
}) => string | null;
|
|
39
|
+
trackFunnelStarted?: (input: {
|
|
40
|
+
stepId: string;
|
|
41
|
+
stepName: string;
|
|
42
|
+
stepType?: string;
|
|
43
|
+
occurredAt: string;
|
|
44
|
+
featureFlags?: Record<string, string>;
|
|
45
|
+
}) => string | null;
|
|
13
46
|
trackFirstStepViewed?: (input: {
|
|
14
47
|
stepId: string;
|
|
15
48
|
stepName: string;
|
|
49
|
+
stepType?: string;
|
|
16
50
|
occurredAt: string;
|
|
17
51
|
featureFlags?: Record<string, string>;
|
|
18
52
|
}) => string | null;
|
|
@@ -331,6 +331,7 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
331
331
|
});
|
|
332
332
|
}, [isPreviewRuntime]);
|
|
333
333
|
const recordStepCompletion = useCallback((stepId, explicitChoices) => {
|
|
334
|
+
var _a;
|
|
334
335
|
const meta = stepById[stepId];
|
|
335
336
|
if (!meta) {
|
|
336
337
|
return;
|
|
@@ -359,16 +360,27 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
359
360
|
const durationMs = Math.max(0, new Date(record.completedAt).getTime() - new Date(startedAt).getTime());
|
|
360
361
|
dispatchWindowCustomEvent('funnel:step-completed', record);
|
|
361
362
|
if (!isPreviewRuntime) {
|
|
362
|
-
|
|
363
|
-
userId: currentUserIdRef.current,
|
|
363
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
364
364
|
stepId: record.stepId,
|
|
365
365
|
stepName: record.stepName,
|
|
366
366
|
stepType,
|
|
367
367
|
startedAt,
|
|
368
368
|
endedAt: record.completedAt,
|
|
369
369
|
selected: record.choices,
|
|
370
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
370
371
|
});
|
|
371
|
-
|
|
372
|
+
if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted)) {
|
|
373
|
+
apiService.trackStepCompleted({
|
|
374
|
+
userId: currentUserIdRef.current,
|
|
375
|
+
stepId: record.stepId,
|
|
376
|
+
stepName: record.stepName,
|
|
377
|
+
stepType,
|
|
378
|
+
startedAt,
|
|
379
|
+
endedAt: record.completedAt,
|
|
380
|
+
selected: record.choices,
|
|
381
|
+
});
|
|
382
|
+
capturePostHogStepEvent('step_completed', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: record.stepId, step_name: record.stepName, step_type: stepType, started_at: startedAt, ended_at: record.completedAt, duration_ms: Number.isFinite(durationMs) ? durationMs : undefined, selected: record.choices }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
|
|
383
|
+
}
|
|
372
384
|
}
|
|
373
385
|
setUser((prev) => {
|
|
374
386
|
var _a, _b;
|
|
@@ -391,12 +403,12 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
391
403
|
}
|
|
392
404
|
return updatedUser;
|
|
393
405
|
});
|
|
394
|
-
}, [isPreviewRuntime, stepById]);
|
|
406
|
+
}, [analytics, isPreviewRuntime, runtimeMode, stepById]);
|
|
395
407
|
const completeStep = useCallback((stepId, choices) => {
|
|
396
408
|
recordStepCompletion(stepId, choices);
|
|
397
409
|
}, [recordStepCompletion]);
|
|
398
410
|
useEffect(() => {
|
|
399
|
-
var _a;
|
|
411
|
+
var _a, _b, _c;
|
|
400
412
|
if (safeActiveStepId !== activeStepId) {
|
|
401
413
|
logger.warn(`[FunnelFlow] Unknown or non-renderable step "${activeStepId}", falling back to "${safeActiveStepId}".`);
|
|
402
414
|
setActiveStepId(safeActiveStepId);
|
|
@@ -417,19 +429,36 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
417
429
|
const stepType = renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type;
|
|
418
430
|
stepStartedAtByIdRef.current[renderedStepId] = startedAt;
|
|
419
431
|
if (!isPreviewRuntime) {
|
|
420
|
-
|
|
421
|
-
userId: currentUserIdRef.current,
|
|
432
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
422
433
|
stepId: renderedStepId,
|
|
423
434
|
stepName,
|
|
424
435
|
stepType,
|
|
425
436
|
startedAt,
|
|
437
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
426
438
|
});
|
|
427
|
-
|
|
439
|
+
if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted)) {
|
|
440
|
+
apiService.trackStepStarted({
|
|
441
|
+
userId: currentUserIdRef.current,
|
|
442
|
+
stepId: renderedStepId,
|
|
443
|
+
stepName,
|
|
444
|
+
stepType,
|
|
445
|
+
startedAt,
|
|
446
|
+
});
|
|
447
|
+
capturePostHogStepEvent('step_started', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: renderedStepId, step_name: stepName, step_type: stepType, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
|
|
448
|
+
}
|
|
428
449
|
if (!firstStepViewedTrackedRef.current && safeActiveStepId === defaultStepId) {
|
|
429
450
|
firstStepViewedTrackedRef.current = true;
|
|
430
|
-
(
|
|
451
|
+
(_b = analytics === null || analytics === void 0 ? void 0 : analytics.trackFunnelStarted) === null || _b === void 0 ? void 0 : _b.call(analytics, {
|
|
431
452
|
stepId: renderedStepId,
|
|
432
453
|
stepName,
|
|
454
|
+
stepType,
|
|
455
|
+
occurredAt: startedAt,
|
|
456
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
457
|
+
});
|
|
458
|
+
(_c = analytics === null || analytics === void 0 ? void 0 : analytics.trackFirstStepViewed) === null || _c === void 0 ? void 0 : _c.call(analytics, {
|
|
459
|
+
stepId: renderedStepId,
|
|
460
|
+
stepName,
|
|
461
|
+
stepType,
|
|
433
462
|
occurredAt: startedAt,
|
|
434
463
|
featureFlags: postHogFeatureFlagsRef.current,
|
|
435
464
|
});
|
|
@@ -447,25 +476,38 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
447
476
|
const engagedStepName = stepName;
|
|
448
477
|
const engagedStepType = stepType;
|
|
449
478
|
const timer = window.setTimeout(() => {
|
|
479
|
+
var _a;
|
|
450
480
|
if (prevStepIdRef.current !== engagedStepId || engagedStepIdsRef.current.has(engagedStepId)) {
|
|
451
481
|
return;
|
|
452
482
|
}
|
|
453
483
|
engagedStepIdsRef.current.add(engagedStepId);
|
|
454
484
|
const engagedAt = new Date().toISOString();
|
|
455
|
-
|
|
456
|
-
userId: currentUserIdRef.current,
|
|
457
|
-
eventType: 'step_engaged',
|
|
485
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
458
486
|
stepId: engagedStepId,
|
|
459
487
|
stepName: engagedStepName,
|
|
460
488
|
stepType: engagedStepType,
|
|
461
489
|
startedAt: engagedStartedAt,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
},
|
|
490
|
+
engagedAt,
|
|
491
|
+
durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
492
|
+
engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
493
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
467
494
|
});
|
|
468
|
-
|
|
495
|
+
if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged)) {
|
|
496
|
+
apiService.trackFunnelEvent({
|
|
497
|
+
userId: currentUserIdRef.current,
|
|
498
|
+
eventType: 'step_engaged',
|
|
499
|
+
stepId: engagedStepId,
|
|
500
|
+
stepName: engagedStepName,
|
|
501
|
+
stepType: engagedStepType,
|
|
502
|
+
startedAt: engagedStartedAt,
|
|
503
|
+
endedAt: engagedAt,
|
|
504
|
+
metadata: {
|
|
505
|
+
durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
506
|
+
engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
capturePostHogStepEvent('step_engaged', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: engagedStepId, step_name: engagedStepName, step_type: engagedStepType, started_at: engagedStartedAt, ended_at: engagedAt, duration_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS, engagement_threshold_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
|
|
510
|
+
}
|
|
469
511
|
}, FIRST_STEP_ENGAGEMENT_THRESHOLD_MS);
|
|
470
512
|
return () => window.clearTimeout(timer);
|
|
471
513
|
}, [
|
|
@@ -478,6 +520,8 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
478
520
|
renderedStepId,
|
|
479
521
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
|
|
480
522
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
|
|
523
|
+
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type,
|
|
524
|
+
runtimeMode,
|
|
481
525
|
safeActiveStepId,
|
|
482
526
|
]);
|
|
483
527
|
useEffect(() => {
|
|
@@ -126,6 +126,7 @@ const buildSdkEvent = (event) => {
|
|
|
126
126
|
eventType: event.eventType,
|
|
127
127
|
stepId: event.stepId || null,
|
|
128
128
|
stepName: event.stepName || null,
|
|
129
|
+
stepType: event.stepType || null,
|
|
129
130
|
startedAt: event.startedAt || null,
|
|
130
131
|
endedAt: event.endedAt || null,
|
|
131
132
|
selected: event.selected || {},
|