@funnelsgrove/runtime 0.1.30 → 0.1.32
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.
|
@@ -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;
|
|
@@ -354,19 +355,32 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
354
355
|
completedAt: new Date().toISOString(),
|
|
355
356
|
choices,
|
|
356
357
|
};
|
|
358
|
+
const stepType = meta.type;
|
|
357
359
|
const startedAt = stepStartedAtByIdRef.current[meta.id] || record.completedAt;
|
|
358
360
|
const durationMs = Math.max(0, new Date(record.completedAt).getTime() - new Date(startedAt).getTime());
|
|
359
361
|
dispatchWindowCustomEvent('funnel:step-completed', record);
|
|
360
362
|
if (!isPreviewRuntime) {
|
|
361
|
-
|
|
362
|
-
userId: currentUserIdRef.current,
|
|
363
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
363
364
|
stepId: record.stepId,
|
|
364
365
|
stepName: record.stepName,
|
|
366
|
+
stepType,
|
|
365
367
|
startedAt,
|
|
366
368
|
endedAt: record.completedAt,
|
|
367
369
|
selected: record.choices,
|
|
370
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
368
371
|
});
|
|
369
|
-
|
|
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
|
+
}
|
|
370
384
|
}
|
|
371
385
|
setUser((prev) => {
|
|
372
386
|
var _a, _b;
|
|
@@ -389,12 +403,12 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
389
403
|
}
|
|
390
404
|
return updatedUser;
|
|
391
405
|
});
|
|
392
|
-
}, [isPreviewRuntime, stepById]);
|
|
406
|
+
}, [analytics, isPreviewRuntime, runtimeMode, stepById]);
|
|
393
407
|
const completeStep = useCallback((stepId, choices) => {
|
|
394
408
|
recordStepCompletion(stepId, choices);
|
|
395
409
|
}, [recordStepCompletion]);
|
|
396
410
|
useEffect(() => {
|
|
397
|
-
var _a;
|
|
411
|
+
var _a, _b, _c;
|
|
398
412
|
if (safeActiveStepId !== activeStepId) {
|
|
399
413
|
logger.warn(`[FunnelFlow] Unknown or non-renderable step "${activeStepId}", falling back to "${safeActiveStepId}".`);
|
|
400
414
|
setActiveStepId(safeActiveStepId);
|
|
@@ -412,20 +426,39 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
412
426
|
}
|
|
413
427
|
const startedAt = new Date().toISOString();
|
|
414
428
|
const stepName = (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name) || (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title) || renderedStepId;
|
|
429
|
+
const stepType = renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type;
|
|
415
430
|
stepStartedAtByIdRef.current[renderedStepId] = startedAt;
|
|
416
431
|
if (!isPreviewRuntime) {
|
|
417
|
-
|
|
418
|
-
userId: currentUserIdRef.current,
|
|
432
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
419
433
|
stepId: renderedStepId,
|
|
420
434
|
stepName,
|
|
435
|
+
stepType,
|
|
421
436
|
startedAt,
|
|
437
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
422
438
|
});
|
|
423
|
-
|
|
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
|
+
}
|
|
424
449
|
if (!firstStepViewedTrackedRef.current && safeActiveStepId === defaultStepId) {
|
|
425
450
|
firstStepViewedTrackedRef.current = true;
|
|
426
|
-
(
|
|
451
|
+
(_b = analytics === null || analytics === void 0 ? void 0 : analytics.trackFunnelStarted) === null || _b === void 0 ? void 0 : _b.call(analytics, {
|
|
427
452
|
stepId: renderedStepId,
|
|
428
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,
|
|
429
462
|
occurredAt: startedAt,
|
|
430
463
|
featureFlags: postHogFeatureFlagsRef.current,
|
|
431
464
|
});
|
|
@@ -441,25 +474,40 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
441
474
|
const engagedStepId = renderedStepId;
|
|
442
475
|
const engagedStartedAt = startedAt;
|
|
443
476
|
const engagedStepName = stepName;
|
|
477
|
+
const engagedStepType = stepType;
|
|
444
478
|
const timer = window.setTimeout(() => {
|
|
479
|
+
var _a;
|
|
445
480
|
if (prevStepIdRef.current !== engagedStepId || engagedStepIdsRef.current.has(engagedStepId)) {
|
|
446
481
|
return;
|
|
447
482
|
}
|
|
448
483
|
engagedStepIdsRef.current.add(engagedStepId);
|
|
449
484
|
const engagedAt = new Date().toISOString();
|
|
450
|
-
|
|
451
|
-
userId: currentUserIdRef.current,
|
|
452
|
-
eventType: 'step_engaged',
|
|
485
|
+
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
453
486
|
stepId: engagedStepId,
|
|
454
487
|
stepName: engagedStepName,
|
|
488
|
+
stepType: engagedStepType,
|
|
455
489
|
startedAt: engagedStartedAt,
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
},
|
|
490
|
+
engagedAt,
|
|
491
|
+
durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
492
|
+
engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
|
|
493
|
+
featureFlags: postHogFeatureFlagsRef.current,
|
|
461
494
|
});
|
|
462
|
-
|
|
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
|
+
}
|
|
463
511
|
}, FIRST_STEP_ENGAGEMENT_THRESHOLD_MS);
|
|
464
512
|
return () => window.clearTimeout(timer);
|
|
465
513
|
}, [
|
|
@@ -472,6 +520,8 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
|
|
|
472
520
|
renderedStepId,
|
|
473
521
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
|
|
474
522
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
|
|
523
|
+
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type,
|
|
524
|
+
runtimeMode,
|
|
475
525
|
safeActiveStepId,
|
|
476
526
|
]);
|
|
477
527
|
useEffect(() => {
|
|
@@ -16,6 +16,7 @@ export type FunnelEvent = {
|
|
|
16
16
|
eventType: string;
|
|
17
17
|
stepId?: string;
|
|
18
18
|
stepName?: string;
|
|
19
|
+
stepType?: string;
|
|
19
20
|
startedAt?: string;
|
|
20
21
|
endedAt?: string;
|
|
21
22
|
selected?: Record<string, unknown>;
|
|
@@ -25,6 +26,7 @@ export type StepCompletionEvent = {
|
|
|
25
26
|
userId: string;
|
|
26
27
|
stepId: string;
|
|
27
28
|
stepName: string;
|
|
29
|
+
stepType?: string;
|
|
28
30
|
startedAt: string;
|
|
29
31
|
endedAt: string;
|
|
30
32
|
selected: Record<string, unknown>;
|
|
@@ -104,6 +106,7 @@ declare class ApiService {
|
|
|
104
106
|
userId: string;
|
|
105
107
|
stepId: string;
|
|
106
108
|
stepName: string;
|
|
109
|
+
stepType?: string;
|
|
107
110
|
startedAt: string;
|
|
108
111
|
}): void;
|
|
109
112
|
trackStepCompleted(event: StepCompletionEvent): void;
|
|
@@ -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 || {},
|
|
@@ -493,6 +494,7 @@ class ApiService {
|
|
|
493
494
|
eventType: 'step_start',
|
|
494
495
|
stepId: input.stepId,
|
|
495
496
|
stepName: input.stepName,
|
|
497
|
+
stepType: input.stepType,
|
|
496
498
|
startedAt: input.startedAt,
|
|
497
499
|
});
|
|
498
500
|
}
|
|
@@ -502,6 +504,7 @@ class ApiService {
|
|
|
502
504
|
eventType: 'step_end',
|
|
503
505
|
stepId: event.stepId,
|
|
504
506
|
stepName: event.stepName,
|
|
507
|
+
stepType: event.stepType,
|
|
505
508
|
startedAt: event.startedAt,
|
|
506
509
|
endedAt: event.endedAt,
|
|
507
510
|
selected: event.selected,
|